From f4c76b7487c50d2401c300ec14719916d34fb9f2 Mon Sep 17 00:00:00 2001 From: Michael Hamburg Date: Wed, 18 Feb 2015 17:50:16 -0800 Subject: [PATCH] SHAKE and SHA3 instances (experimental) based on code from David Leon Gil. Tested by hand but needs automatic KAT. I might also want to include Keyak or some similar duplex construction eventually. --- Makefile | 5 +- include/shake.h | 132 +++++++++++++++++++++++++ src/shake.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++ test/shakesum.c | 63 ++++++++++++ 4 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 include/shake.h create mode 100644 src/shake.c create mode 100644 test/shakesum.c diff --git a/Makefile b/Makefile index a8b4f3f..cd9aefd 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ BENCHCOMPONENTS=build/bench.o BATBASE=ed448goldilocks-bats-$(TODAY) BATNAME=build/$(BATBASE) -all: lib build/test build/bench +all: lib build/test build/bench build/shakesum scan: clean scan-build --use-analyzer=`which clang` \ @@ -89,6 +89,9 @@ build/bench: $(LIBCOMPONENTS) $(BENCHCOMPONENTS) build/test: $(LIBCOMPONENTS) $(TESTCOMPONENTS) $(LD) $(LDFLAGS) -o $@ $^ -lgmp + +build/shakesum: build/shakesum.o build/shake.o + $(LD) $(LDFLAGS) -o $@ $^ lib: build/goldilocks.so diff --git a/include/shake.h b/include/shake.h new file mode 100644 index 0000000..e47d2df --- /dev/null +++ b/include/shake.h @@ -0,0 +1,132 @@ +/** + * @file shake.h + * @copyright + * Based on CC0 code by David Leon Gil, 2015 \n + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * @author Mike Hamburg + * @brief SHA-3-n and SHAKE-n instances. + * @warning EXPERIMENTAL! The names, parameter orders etc are likely to change. + */ + +#ifndef __SHAKE_H__ +#define __SHAKE_H__ + +#include + +#ifndef INTERNAL_SPONGE_STRUCT + typedef struct keccak_sponge_s { + uint64_t opaque[26]; + } keccak_sponge_t[1]; + struct kparams_s; +#endif + +/** + * @brief Initialize a sponge context object. + * @param [out] sponge The object to initialize. + * @param [in] params The sponge's parameter description. + */ +void sponge_init ( + keccak_sponge_t sponge, + const struct kparams_s *params +); + +/** + * @brief Absorb data into a SHA3 or SHAKE hash context. + * @param [inout] sponge The context. + * @param [in] in The input data. + * @param [in] len The input data's length in bytes. + */ +void sha3_update ( + struct keccak_sponge_s * __restrict__ sponge, + const uint8_t *in, + size_t len +); + +/** + * @brief Squeeze output data from a SHA3 or SHAKE hash context. + * This does not destroy or re-initialize the hash context, and + * sha3 output can be called more times. + * + * @param [inout] sponge The context. + * @param [out] in The output data. + * @param [in] len The requested output data length in bytes. + */ +void sha3_output ( + keccak_sponge_t sponge, + uint8_t * __restrict__ out, + size_t len +); + +/** + * @brief Destroy a SHA3 or SHAKE sponge context by overwriting it with 0. + * @param [out] sponge The context. + */ +void sponge_destroy ( + keccak_sponge_t sponge +); + + +/** + * @brief Hash (in) to (out) + * @param [in] in The input data. + * @param [in] inlen The length of the input data. + * @param [out] out A buffer for the output data. + * @param [in] outlen The length of the output data. + */ +void sponge_hash ( + const uint8_t *in, + size_t inlen, + uint8_t *out, + size_t outlen, + const struct kparams_s *params +); + +/* TODO: expand/doxygenate individual SHAKE/SHA3 instances? */ + +#define DECSHAKE(n) \ + extern const struct kparams_s *SHAKE##n##_params; \ + static inline void shake##n##_init(keccak_sponge_t sponge) { \ + sponge_init(sponge, SHAKE##n##_params); \ + } \ + static inline void shake##n##_update(keccak_sponge_t sponge, const uint8_t *in, size_t inlen ) { \ + sha3_update(sponge, in, inlen); \ + } \ + static inline void shake##n##_final(keccak_sponge_t sponge, uint8_t *out, size_t outlen ) { \ + sha3_output(sponge, out, outlen); \ + sponge_init(sponge, SHAKE##n##_params); \ + } \ + static inline void shake##n##_hash(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen ) { \ + sponge_hash(in,inlen,out,outlen,SHAKE##n##_params); \ + } \ + static inline void shake##n##_destroy( keccak_sponge_t sponge ) { \ + sponge_destroy(sponge); \ + } + +#define DECSHA3(n) \ + extern const struct kparams_s *SHA3_##n##_params; \ + static inline void sha3_##n##_init(keccak_sponge_t sponge) { \ + sponge_init(sponge, SHA3_##n##_params); \ + } \ + static inline void sha3_##n##_update(keccak_sponge_t sponge, const uint8_t *in, size_t inlen ) { \ + sha3_update(sponge, in, inlen); \ + } \ + static inline void sha3_##n##_final(keccak_sponge_t sponge, uint8_t *out, size_t outlen ) { \ + sha3_output(sponge, out, outlen); \ + sponge_init(sponge, SHA3_##n##_params); \ + } \ + static inline void sha3_##n##_hash(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen ) { \ + sponge_hash(in,inlen,out,outlen,SHA3_##n##_params); \ + } \ + static inline void sha3_##n##_destroy( keccak_sponge_t sponge ) { \ + sponge_destroy(sponge); \ + } + +DECSHAKE(128) +DECSHAKE(256) +DECSHA3(224) +DECSHA3(256) +DECSHA3(384) +DECSHA3(512) + +#endif /* __SHAKE_H__ */ diff --git a/src/shake.c b/src/shake.c new file mode 100644 index 0000000..06dcda7 --- /dev/null +++ b/src/shake.c @@ -0,0 +1,252 @@ +/** + * @cond internal + * @file shake.c + * @copyright + * Uses public domain code by Mathias Panzenböck \n + * Uses CC0 code by David Leon Gil, 2015 \n + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * @author Mike Hamburg + * @brief SHA-3-n and SHAKE-n instances. + * @warning EXPERIMENTAL! The names, parameter orders etc are likely to change. + */ + +#define __STDC_WANT_LIB_EXT1__ 1 /* for memset_s */ +#include +#include +#include + +/* Subset of Mathias Panzenböck's portable endian code, public domain */ +#if defined(__linux__) || defined(__CYGWIN__) +# include +#elif defined(__OpenBSD__) +# include +#elif defined(__APPLE__) +# include +# define htole64(x) OSSwapHostToLittleInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +# include +# define le64toh(x) letoh64(x) +#elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) +# include +# include +# if BYTE_ORDER == LITTLE_ENDIAN +# define htole64(x) (x) +# define le64toh(x) (x) +# elif BYTE_ORDER == BIG_ENDIAN +# define htole64(x) __builtin_bswap64(x) +# define le64toh(x) __builtin_bswap64(x) +# else +# error byte order not supported +# endif +#else +# error platform not supported +#endif + +/* The internal, non-opaque definition of the sponge struct. */ +typedef union { + uint64_t w[25]; uint8_t b[25*8]; +} kdomain_t[1]; + +typedef struct kparams_s { + uint8_t position, flags, rate, startRound, pad, ratePad, maxOut, _; +} kparams_t[1]; + +typedef struct keccak_sponge_s { + kdomain_t state; + kparams_t params; +} keccak_sponge_t[1]; + +#define INTERNAL_SPONGE_STRUCT 1 +#include "shake.h" + +#define FLAG_ABSORBING 'A' +#define FLAG_SQUEEZING 'Z' + +/** Constants. **/ +static const uint8_t pi[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 +}; + +#define RC_B(x,n) ((((x##ull)>>n)&1)<<((1<> (64 - s)); +} + +/* Helper macros to unroll the permutation. TODO: opt tradeoffs. */ +#define REPEAT5(e) e e e e e +#define FOR51(v, e) v = 0; REPEAT5(e; v += 1;) +#if (defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__)) +# define FOR55(v, e) v = 0; REPEAT5(e; v += 5;) +# define REPEAT24(e) e e e e e e e e e e e e e e e e e e e e e e e e +#else +# define FOR55(v, e) for (v=0; v<25; v+= 5) { e; } +# define REPEAT24(e) {int _j=0; for (_j=0; _j<24; _j++) { e }} +#endif + +/*** The Keccak-f[1600] permutation ***/ +static void keccakf(kdomain_t state, uint8_t startRound) { + uint64_t* a = state->w; + uint64_t b[5] = {0}, t, u; + uint8_t x, y, i; + + for (i=0; i<25; i++) a[i] = le64toh(a[i]); + + for (i = startRound; i < 24; i++) { + FOR51(x, b[x] = 0; FOR55(y, b[x] ^= a[x + y];)) + FOR51(x, FOR55(y, + a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); + )) + // Rho and pi + t = a[1]; + x = y = 0; + REPEAT24(u = a[pi[x]]; y += x+1; a[pi[x]] = rol(t, y % 64); t = u; x++; ) + // Chi + FOR55(y, + FOR51(x, b[x] = a[y + x];) + FOR51(x, a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]);) + ) + // Iota + a[0] ^= RC[i]; + } + + for (i=0; i<25; i++) a[i] = htole64(a[i]); +} + +static inline void dokeccak (keccak_sponge_t sponge) { + keccakf(sponge->state, sponge->params->startRound); + sponge->params->position = 0; +} + +void sha3_update ( + struct keccak_sponge_s * __restrict__ sponge, + const uint8_t *in, + size_t len +) { + if (!len) return; + assert(sponge->params->position < sponge->params->rate); + assert(sponge->params->rate < sizeof(sponge->state)); + assert(sponge->params->flags == FLAG_ABSORBING); + while (len) { + size_t cando = sponge->params->rate - sponge->params->position; + uint8_t* state = &sponge->state->b[sponge->params->position]; + if (cando > len) { + for (size_t i = 0; i < len; i += 1) state[i] ^= in[i]; + sponge->params->position += len; + return; + } else { + for (size_t i = 0; i < cando; i += 1) state[i] ^= in[i]; + dokeccak(sponge); + len -= cando; + in += cando; + } + } +} + +void sha3_output ( + keccak_sponge_t sponge, + uint8_t * __restrict__ out, + size_t len +) { + assert(sponge->params->position < sponge->params->rate); + assert(sponge->params->rate < sizeof(sponge->state)); + + if (sponge->params->maxOut != 0xFF) { + assert(sponge->params->maxOut >= len); + sponge->params->maxOut -= len; + } + + switch (sponge->params->flags) { + case FLAG_SQUEEZING: break; + case FLAG_ABSORBING: + { + uint8_t* state = sponge->state->b; + state[sponge->params->position] ^= sponge->params->pad; + state[sponge->params->rate - 1] ^= sponge->params->ratePad; + dokeccak(sponge); + break; + } + default: + assert(0); + } + + while (len) { + size_t cando = sponge->params->rate - sponge->params->position; + uint8_t* state = &sponge->state->b[sponge->params->position]; + if (cando > len) { + memcpy(out, state, len); + sponge->params->position += len; + return; + } else { + memcpy(out, state, cando); + dokeccak(sponge); + len -= cando; + out += cando; + } + } +} + +void sponge_destroy ( + keccak_sponge_t sponge +) { +#ifdef __STDC_LIB_EXT1__ + memset_s(sponge, sizeof(sponge), 0, sizeof(sponge)); +#else + volatile uint64_t *destroy = (volatile uint64_t *)sponge; + unsigned i; + for (i=0; istate, 0, sizeof(sponge->state)); + sponge->params[0] = params[0]; +} + +void sponge_hash ( + const uint8_t *in, + size_t inlen, + uint8_t *out, + size_t outlen, + const struct kparams_s *params +) { + keccak_sponge_t sponge; + sponge_init(sponge, params); + sha3_update(sponge, in, inlen); + sha3_output(sponge, out, outlen); + sponge_destroy(sponge); +} + +#define DEFSHAKE(n) \ + const struct kparams_s SHAKE##n##_params_s = \ + { 0, FLAG_ABSORBING, 200-n/4, 0, 0x1f, 0x80, 0xFF, 0 }, \ + *SHAKE##n##_params = &SHAKE##n##_params_s; + +#define DEFSHA3(n) \ + const struct kparams_s SHA3_##n##_params_s = \ + { 0, FLAG_ABSORBING, 200-n/4, 0, 0x06, 0x80, n/8, 0 }, \ + *SHA3_##n##_params = &SHA3_##n##_params_s; + +DEFSHAKE(128) +DEFSHAKE(256) +DEFSHA3(224) +DEFSHA3(256) +DEFSHA3(384) +DEFSHA3(512) + +/* TODO: Keyak instances, etc */ diff --git a/test/shakesum.c b/test/shakesum.c new file mode 100644 index 0000000..5be1a80 --- /dev/null +++ b/test/shakesum.c @@ -0,0 +1,63 @@ +/** + * @cond internal + * @file shakesum.c + * @copyright + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * @author Mike Hamburg + * @brief SHA3 utility, to be combined with test vectors eventually... + */ + +#include +#include +#include +#include "shake.h" + +int main(int argc, char **argv) { + (void)argc; (void)argv; + + keccak_sponge_t sponge; + unsigned char buf[1024]; + + unsigned int outlen = 512; + shake256_init(sponge); + + /* Sloppy. Real utility would parse --algo, --size ... */ + if (argc > 1) { + if (!strcmp(argv[1], "shake256") || !strcmp(argv[1], "SHAKE256")) { + outlen = 512; + shake256_init(sponge); + } else if (!strcmp(argv[1], "shake128") || !strcmp(argv[1], "SHAKE128")) { + outlen = 512; + shake128_init(sponge); + } else if (!strcmp(argv[1], "sha3-224") || !strcmp(argv[1], "SHA3-224")) { + outlen = 224/8; + sha3_224_init(sponge); + } else if (!strcmp(argv[1], "sha3-256") || !strcmp(argv[1], "SHA3-256")) { + outlen = 256/8; + sha3_256_init(sponge); + } else if (!strcmp(argv[1], "sha3-384") || !strcmp(argv[1], "SHA3-384")) { + outlen = 384/8; + sha3_384_init(sponge); + } else if (!strcmp(argv[1], "sha3-512") || !strcmp(argv[1], "SHA3-512")) { + outlen = 512/8; + sha3_512_init(sponge); + } + } + + ssize_t red; + do { + red = read(0, buf, sizeof(buf)); + if (red>0) sha3_update(sponge,buf,red); + } while (red>0); + + sha3_output(sponge,buf,outlen); + sponge_destroy(sponge); + unsigned i; + for (i=0; i