From b97c745e6c2f1f448ead895e8738fd2d68672b2f Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion)" Date: Thu, 4 Oct 2012 18:30:56 +0200 Subject: [PATCH] Continue working on the Ruby extension --- ext/digest/KeccakF-1600-reference.c | 300 +++++++++++++++++++++++++ ext/digest/brg_endian.h | 142 ++++++++++++ ext/digest/displayIntermediateValues.c | 117 ++++++++++ ext/digest/displayIntermediateValues.h | 29 +++ ext/digest/extconf.rb | 2 +- ext/digest/sha3.c | 121 +++++++++- lib/digest/sha3/helpers.rb | 9 + 7 files changed, 717 insertions(+), 3 deletions(-) create mode 100644 ext/digest/KeccakF-1600-reference.c create mode 100644 ext/digest/brg_endian.h create mode 100644 ext/digest/displayIntermediateValues.c create mode 100644 ext/digest/displayIntermediateValues.h create mode 100644 lib/digest/sha3/helpers.rb diff --git a/ext/digest/KeccakF-1600-reference.c b/ext/digest/KeccakF-1600-reference.c new file mode 100644 index 0000000..628f710 --- /dev/null +++ b/ext/digest/KeccakF-1600-reference.c @@ -0,0 +1,300 @@ +/* +The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, +Michaël Peeters and Gilles Van Assche. For more information, feedback or +questions, please refer to our website: http://keccak.noekeon.org/ + +Implementation by the designers, +hereby denoted as "the implementer". + +To the extent possible under law, the implementer has waived all copyright +and related or neighboring rights to the source code in this file. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +#include +#include +#include "brg_endian.h" +#include "displayIntermediateValues.h" +#include "KeccakNISTInterface.h" +#include "KeccakF-1600-interface.h" + +typedef unsigned char UINT8; +typedef unsigned long long int UINT64; + +#define nrRounds 24 +UINT64 KeccakRoundConstants[nrRounds]; +#define nrLanes 25 +unsigned int KeccakRhoOffsets[nrLanes]; + +void KeccakPermutationOnWords(UINT64 *state); +void theta(UINT64 *A); +void rho(UINT64 *A); +void pi(UINT64 *A); +void chi(UINT64 *A); +void iota(UINT64 *A, unsigned int indexRound); + +void fromBytesToWords(UINT64 *stateAsWords, const unsigned char *state) +{ + unsigned int i, j; + + for(i=0; i<(KeccakPermutationSize/64); i++) { + stateAsWords[i] = 0; + for(j=0; j<(64/8); j++) + stateAsWords[i] |= (UINT64)(state[i*(64/8)+j]) << (8*j); + } +} + +void fromWordsToBytes(unsigned char *state, const UINT64 *stateAsWords) +{ + unsigned int i, j; + + for(i=0; i<(KeccakPermutationSize/64); i++) + for(j=0; j<(64/8); j++) + state[i*(64/8)+j] = (stateAsWords[i] >> (8*j)) & 0xFF; +} + +void KeccakPermutation(unsigned char *state) +{ +#if (PLATFORM_BYTE_ORDER != IS_LITTLE_ENDIAN) + UINT64 stateAsWords[KeccakPermutationSize/64]; +#endif + + displayStateAsBytes(1, "Input of permutation", state); +#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) + KeccakPermutationOnWords((UINT64*)state); +#else + fromBytesToWords(stateAsWords, state); + KeccakPermutationOnWords(stateAsWords); + fromWordsToBytes(state, stateAsWords); +#endif + displayStateAsBytes(1, "State after permutation", state); +} + +void KeccakPermutationAfterXor(unsigned char *state, const unsigned char *data, unsigned int dataLengthInBytes) +{ + unsigned int i; + + for(i=0; i> (64-offset))) : a) + +void theta(UINT64 *A) +{ + unsigned int x, y; + UINT64 C[5], D[5]; + + for(x=0; x<5; x++) { + C[x] = 0; + for(y=0; y<5; y++) + C[x] ^= A[index(x, y)]; + } + for(x=0; x<5; x++) + D[x] = ROL64(C[(x+1)%5], 1) ^ C[(x+4)%5]; + for(x=0; x<5; x++) + for(y=0; y<5; y++) + A[index(x, y)] ^= D[x]; +} + +void rho(UINT64 *A) +{ + unsigned int x, y; + + for(x=0; x<5; x++) for(y=0; y<5; y++) + A[index(x, y)] = ROL64(A[index(x, y)], KeccakRhoOffsets[index(x, y)]); +} + +void pi(UINT64 *A) +{ + unsigned int x, y; + UINT64 tempA[25]; + + for(x=0; x<5; x++) for(y=0; y<5; y++) + tempA[index(x, y)] = A[index(x, y)]; + for(x=0; x<5; x++) for(y=0; y<5; y++) + A[index(0*x+1*y, 2*x+3*y)] = tempA[index(x, y)]; +} + +void chi(UINT64 *A) +{ + unsigned int x, y; + UINT64 C[5]; + + for(y=0; y<5; y++) { + for(x=0; x<5; x++) + C[x] = A[index(x, y)] ^ ((~A[index(x+1, y)]) & A[index(x+2, y)]); + for(x=0; x<5; x++) + A[index(x, y)] = C[x]; + } +} + +void iota(UINT64 *A, unsigned int indexRound) +{ + A[index(0, 0)] ^= KeccakRoundConstants[indexRound]; +} + +int LFSR86540(UINT8 *LFSR) +{ + int result = ((*LFSR) & 0x01) != 0; + if (((*LFSR) & 0x80) != 0) + // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 + (*LFSR) = ((*LFSR) << 1) ^ 0x71; + else + (*LFSR) <<= 1; + return result; +} + +void KeccakInitializeRoundConstants() +{ + UINT8 LFSRstate = 0x01; + unsigned int i, j, bitPosition; + + for(i=0; i> 32)); + fprintf(f, "%08X", (unsigned int)(KeccakRoundConstants[i] & 0xFFFFFFFFULL)); + fprintf(f, "\n"); + } + fprintf(f, "\n"); +} + +void displayRhoOffsets(FILE *f) +{ + unsigned int x, y; + + for(y=0; y<5; y++) for(x=0; x<5; x++) { + fprintf(f, "RhoOffset[%i][%i] = ", x, y); + fprintf(f, "%2i", KeccakRhoOffsets[index(x, y)]); + fprintf(f, "\n"); + } + fprintf(f, "\n"); +} + +void KeccakInitializeState(unsigned char *state) +{ + memset(state, 0, KeccakPermutationSizeInBytes); +} + +#ifdef ProvideFast576 +void KeccakAbsorb576bits(unsigned char *state, const unsigned char *data) +{ + KeccakPermutationAfterXor(state, data, 72); +} +#endif + +#ifdef ProvideFast832 +void KeccakAbsorb832bits(unsigned char *state, const unsigned char *data) +{ + KeccakPermutationAfterXor(state, data, 104); +} +#endif + +#ifdef ProvideFast1024 +void KeccakAbsorb1024bits(unsigned char *state, const unsigned char *data) +{ + KeccakPermutationAfterXor(state, data, 128); +} +#endif + +#ifdef ProvideFast1088 +void KeccakAbsorb1088bits(unsigned char *state, const unsigned char *data) +{ + KeccakPermutationAfterXor(state, data, 136); +} +#endif + +#ifdef ProvideFast1152 +void KeccakAbsorb1152bits(unsigned char *state, const unsigned char *data) +{ + KeccakPermutationAfterXor(state, data, 144); +} +#endif + +#ifdef ProvideFast1344 +void KeccakAbsorb1344bits(unsigned char *state, const unsigned char *data) +{ + KeccakPermutationAfterXor(state, data, 168); +} +#endif + +void KeccakAbsorb(unsigned char *state, const unsigned char *data, unsigned int laneCount) +{ + KeccakPermutationAfterXor(state, data, laneCount*8); +} + +#ifdef ProvideFast1024 +void KeccakExtract1024bits(const unsigned char *state, unsigned char *data) +{ + memcpy(data, state, 128); +} +#endif + +void KeccakExtract(const unsigned char *state, unsigned char *data, unsigned int laneCount) +{ + memcpy(data, state, laneCount*8); +} diff --git a/ext/digest/brg_endian.h b/ext/digest/brg_endian.h new file mode 100644 index 0000000..7226eb3 --- /dev/null +++ b/ext/digest/brg_endian.h @@ -0,0 +1,142 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + Changes for ARM 9/9/2010 +*/ + +#ifndef _BRG_ENDIAN_H +#define _BRG_ENDIAN_H + +#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ +#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ + +#if 0 +/* Include files where endian defines and byteswap functions may reside */ +#if defined( __sun ) +# include +#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ ) +# include +#elif defined( BSD ) && ( BSD >= 199103 ) || defined( __APPLE__ ) || \ + defined( __CYGWIN32__ ) || defined( __DJGPP__ ) || defined( __osf__ ) +# include +#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ ) +# if !defined( __MINGW32__ ) && !defined( _AIX ) +# include +# if !defined( __BEOS__ ) +# include +# endif +# endif +#endif +#endif + +/* Now attempt to set the define for platform byte order using any */ +/* of the four forms SYMBOL, _SYMBOL, __SYMBOL & __SYMBOL__, which */ +/* seem to encompass most endian symbol definitions */ + +#if defined( BIG_ENDIAN ) && defined( LITTLE_ENDIAN ) +# if defined( BYTE_ORDER ) && BYTE_ORDER == BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( BYTE_ORDER ) && BYTE_ORDER == LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( _BIG_ENDIAN ) && defined( _LITTLE_ENDIAN ) +# if defined( _BYTE_ORDER ) && _BYTE_ORDER == _BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( _BYTE_ORDER ) && _BYTE_ORDER == _LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( _BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( _LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN ) && defined( __LITTLE_ENDIAN ) +# if defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN__ ) && defined( __LITTLE_ENDIAN__ ) +# if defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __BIG_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __LITTLE_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +/* if the platform byte order could not be determined, then try to */ +/* set this define using common machine defines */ +#if !defined(PLATFORM_BYTE_ORDER) + +#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \ + defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \ + defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \ + defined( vax ) || defined( vms ) || defined( VMS ) || \ + defined( __VMS ) || defined( _M_X64 ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN + +#elif defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \ + defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \ + defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \ + defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \ + defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \ + defined( __VOS__ ) || defined( __TIGCC__ ) || defined( __TANDEM ) || \ + defined( THINK_C ) || defined( __VMCMS__ ) || defined( _AIX ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN + +#elif defined(__arm__) +# ifdef __BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# else +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif 1 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#else +# error Please edit lines 132 or 134 in brg_endian.h to set the platform byte order +#endif + +#endif + +#endif diff --git a/ext/digest/displayIntermediateValues.c b/ext/digest/displayIntermediateValues.c new file mode 100644 index 0000000..f3bf9e2 --- /dev/null +++ b/ext/digest/displayIntermediateValues.c @@ -0,0 +1,117 @@ +/* +The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, +Michaël Peeters and Gilles Van Assche. For more information, feedback or +questions, please refer to our website: http://keccak.noekeon.org/ + +Implementation by the designers, +hereby denoted as "the implementer". + +To the extent possible under law, the implementer has waived all copyright +and related or neighboring rights to the source code in this file. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +#include +#include "displayIntermediateValues.h" +#include "KeccakNISTInterface.h" + +FILE *intermediateValueFile = 0; +int displayLevel = 0; + +void displaySetIntermediateValueFile(FILE *f) +{ + intermediateValueFile = f; +} + +void displaySetLevel(int level) +{ + displayLevel = level; +} + +void displayBytes(int level, const char *text, const unsigned char *bytes, unsigned int size) +{ + unsigned int i; + + if ((intermediateValueFile) && (level <= displayLevel)) { + fprintf(intermediateValueFile, "%s:\n", text); + for(i=0; i> iBit) & 0x01) != 0); + } + fprintf(intermediateValueFile, "\n"); + fprintf(intermediateValueFile, "\n"); + } +} + +void displayStateAsBytes(int level, const char *text, const unsigned char *state) +{ + displayBytes(level, text, state, KeccakPermutationSizeInBytes); +} + +void displayStateAs32bitWords(int level, const char *text, const unsigned int *state) +{ + unsigned int i; + + if ((intermediateValueFile) && (level <= displayLevel)) { + fprintf(intermediateValueFile, "%s:\n", text); + for(i=0; i> 32)); + fprintf(intermediateValueFile, "%08X", (unsigned int)(state[i] & 0xFFFFFFFFULL)); + if ((i%5) == 4) + fprintf(intermediateValueFile, "\n"); + else + fprintf(intermediateValueFile, " "); + } + } +} + +void displayRoundNumber(int level, unsigned int i) +{ + if ((intermediateValueFile) && (level <= displayLevel)) { + fprintf(intermediateValueFile, "\n"); + fprintf(intermediateValueFile, "--- Round %d ---\n", i); + fprintf(intermediateValueFile, "\n"); + } +} + +void displayText(int level, const char *text) +{ + if ((intermediateValueFile) && (level <= displayLevel)) { + fprintf(intermediateValueFile, text); + fprintf(intermediateValueFile, "\n"); + fprintf(intermediateValueFile, "\n"); + } +} diff --git a/ext/digest/displayIntermediateValues.h b/ext/digest/displayIntermediateValues.h new file mode 100644 index 0000000..1d6c6c8 --- /dev/null +++ b/ext/digest/displayIntermediateValues.h @@ -0,0 +1,29 @@ +/* +The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, +Michaël Peeters and Gilles Van Assche. For more information, feedback or +questions, please refer to our website: http://keccak.noekeon.org/ + +Implementation by the designers, +hereby denoted as "the implementer". + +To the extent possible under law, the implementer has waived all copyright +and related or neighboring rights to the source code in this file. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +#ifndef _displayIntermediateValues_h_ +#define _displayIntermediateValues_h_ + +#include + +void displaySetIntermediateValueFile(FILE *f); +void displaySetLevel(int level); +void displayBytes(int level, const char *text, const unsigned char *bytes, unsigned int size); +void displayBits(int level, const char *text, const unsigned char *data, unsigned int size, int MSBfirst); +void displayStateAsBytes(int level, const char *text, const unsigned char *state); +void displayStateAs32bitWords(int level, const char *text, const unsigned int *state); +void displayStateAs64bitWords(int level, const char *text, const unsigned long long int *state); +void displayRoundNumber(int level, unsigned int i); +void displayText(int level, const char *text); + +#endif diff --git a/ext/digest/extconf.rb b/ext/digest/extconf.rb index 5fa7b8e..4219c0a 100644 --- a/ext/digest/extconf.rb +++ b/ext/digest/extconf.rb @@ -1,2 +1,2 @@ require 'mkmf' -create_makefile('sha3') +create_makefile('digest/sha3') diff --git a/ext/digest/sha3.c b/ext/digest/sha3.c index 30f1434..6d03c65 100644 --- a/ext/digest/sha3.c +++ b/ext/digest/sha3.c @@ -1,9 +1,126 @@ #include "ruby.h" +#include "KeccakNISTInterface.h" -static VALUE mDigest, mSHA3; +#define MAX_DIGEST_SIZE 64 + +static VALUE mDigest, cSHA3; + +typedef struct { + hashState state; + int bitlen; +} RbSHA3; + +static VALUE +rb_sha3_new(VALUE klass, VALUE bitlen) { + RbSHA3 *ctx; + VALUE obj; + + ctx = (RbSHA3 *) xmalloc(sizeof(RbSHA3)); + obj = Data_Wrap_Struct(klass, 0, xfree, ctx); + ctx->bitlen = NUM2INT(bitlen); + + if (ctx->bitlen == 0) { + rb_raise(rb_eRuntimeError, "Unsupported hash length"); + } + + switch (Init(&ctx->state, ctx->bitlen)) { + case SUCCESS: + return obj; + case FAIL: + rb_raise(rb_eRuntimeError, "Unknown error"); + return Qnil; + case BAD_HASHLEN: + rb_raise(rb_eRuntimeError, "Bad hash length (must be 0, 224, 256, 384 or 512)"); + return Qnil; + default: + rb_raise(rb_eRuntimeError, "Unknown error code"); + return Qnil; + } +} + +static VALUE +rb_sha3_copy(VALUE copy, VALUE obj) { + RbSHA3 *ctx_copy, *ctx_obj; + + Data_Get_Struct(copy, RbSHA3, ctx_copy); + Data_Get_Struct(obj, RbSHA3, ctx_obj); + if (copy == obj) { + return copy; + } + rb_check_frozen(copy); + + memcpy(&ctx_copy->state, &ctx_obj->state, sizeof(hashState)); + ctx_copy->bitlen = ctx_obj->bitlen; + return copy; +} + +static VALUE +rb_sha3_reset(VALUE self) { + RbSHA3 *ctx; + + Data_Get_Struct(self, RbSHA3, ctx); + Init(&ctx->state, ctx->bitlen); + return self; +} + +static VALUE +rb_sha3_update(VALUE self, VALUE str) { + RbSHA3 *ctx; + + Data_Get_Struct(self, RbSHA3, ctx); + Update(&ctx->state, RSTRING_PTR(str), RSTRING_LEN(str) * 8); + return self; +} + +static VALUE +rb_sha3_digest(VALUE self, VALUE str) { + RbSHA3 *ctx; + hashState state; + unsigned char digest[MAX_DIGEST_SIZE]; + + Data_Get_Struct(self, RbSHA3, ctx); + memcpy(&state, &ctx->state, sizeof(hashState)); + Final(&state, digest); + return rb_str_new((const char *) digest, ctx->bitlen / 8); +} + +static VALUE +rb_sha3_singleton_digest(int argc, VALUE *argv, VALUE klass) { + VALUE data, hashlen; + int i_hashlen; + unsigned char digest[MAX_DIGEST_SIZE]; + + if (rb_scan_args(argc, argv, "11", &data, &hashlen) == 1) { + i_hashlen = 512; + } else { + i_hashlen = NUM2INT(hashlen); + } + + switch (Hash(i_hashlen, RSTRING_PTR(data), RSTRING_LEN(data) * 8, digest)) { + case SUCCESS: + return rb_str_new(digest, i_hashlen / 8); + case FAIL: + rb_raise(rb_eRuntimeError, "Unknown error"); + return Qnil; + case BAD_HASHLEN: + rb_raise(rb_eRuntimeError, "Bad hash length (must be 0, 224, 256, 384 or 512)"); + return Qnil; + default: + rb_raise(rb_eRuntimeError, "Unknown error code"); + return Qnil; + } +} void Init_sha3() { mDigest = rb_define_module("Digest"); - mSHA3 = rb_define_module_under(mDigest, "SHA3"); + cSHA3 = rb_define_class_under(mDigest, "SHA3", rb_cObject); + rb_define_singleton_method(cSHA3, "new", rb_sha3_new, 1); + rb_define_method(cSHA3, "initialize_copy", rb_sha3_copy, 1); + rb_define_method(cSHA3, "reset", rb_sha3_reset, 0); + rb_define_method(cSHA3, "update", rb_sha3_update, 1); + rb_define_method(cSHA3, "<<", rb_sha3_update, 1); + rb_define_method(cSHA3, "digest", rb_sha3_digest, 0); + rb_define_singleton_method(cSHA3, "digest", rb_sha3_singleton_digest, -1); + rb_require("digest/sha3/helpers"); } diff --git a/lib/digest/sha3/helpers.rb b/lib/digest/sha3/helpers.rb new file mode 100644 index 0000000..d68c5ab --- /dev/null +++ b/lib/digest/sha3/helpers.rb @@ -0,0 +1,9 @@ +Digest::SHA3.class_eval do + def self.hexdigest(*args) + digest(*args).unpack("H*").first + end + + def hexdigest + digest.unpack("H*").first + end +end