X-Git-Url: https://git.distorted.org.uk/~mdw/secnet/blobdiff_plain/1047c205103e6da9fc6a317f41583147dbc11aa3..a1a6042e24c9873aa6abf668bcb68d39d0eb4190:/sha3.c diff --git a/sha3.c b/sha3.c new file mode 100644 index 0000000..0fede83 --- /dev/null +++ b/sha3.c @@ -0,0 +1,376 @@ +/* -*-c-*- + * + * The SHA3 algorithm family + * + * (c) 2017 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of secnet. + * See README for full list of copyright holders. + * + * secnet is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version d of the License, or + * (at your option) any later version. + * + * secnet is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with secnet; if not, see + * https://www.gnu.org/licenses/gpl.html. + * + * This file was originally part of Catacomb, but has been automatically + * modified for incorporation into secnet: see `import-catacomb-crypto' + * for details. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include "sha3.h" + +/*----- General utilities -------------------------------------------------*/ + +static void absorb(sha3_ctx *ctx, const octet *p, unsigned r) +{ + kludge64 t[25]; + unsigned i; + +/* memdump("absorb", p, 8*r, 0); */ + for (i = 0; i < r; i++) { LOAD64_L_(t[i], p); p += 8; } + keccak1600_mix(&ctx->s, t, r); +} + +static void step(sha3_ctx *ctx) { keccak1600_p(&ctx->s, &ctx->s, 24); } + +static void pad(sha3_ctx *ctx, unsigned lo, unsigned hi) +{ + size_t spare = ctx->r - ctx->n; + + if (spare == 1) + ctx->buf[ctx->n] = lo | hi; + else { + ctx->buf[ctx->n] = lo; + ctx->buf[ctx->r - 1] = hi; + memset(ctx->buf + ctx->n + 1, 0, spare - 2); + } + absorb(ctx, ctx->buf, ctx->r/8); +} + +static void squeeze(sha3_ctx *ctx, octet *p, unsigned r) +{ + kludge64 t[25]; + unsigned i; + + keccak1600_extract(&ctx->s, t, r); + for (i = 0; i < r; i++) { STORE64_L_(p, t[i]); p += 8; } +/* memdump("squeeze", p - 8*r, 8*r, 0); */ +} + +enum { + OP_CSHAKE = 0x04, + OP_SHA3 = 0x06, + OP_SHAKE = 0x1f +}; + +enum { ST_ABSORB, ST_SQUEEZE, ST_DEAD }; + +/*----- The SHA3 algorithms -----------------------------------------------*/ + +/* --- @sha3_{224,256,384,512}_init@ --- * + * + * Arguments: @sha3_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a SHA3 hashing context for use. + */ + +static void init_sha3(sha3_ctx *ctx, unsigned w) +{ + keccak1600_init(&ctx->s); + ctx->w = w/8; ctx->r = (1600 - 2*w)/8; ctx->n = 0; +} + +void sha3_224_init(sha3_ctx *ctx) { init_sha3(ctx, 224); } +void sha3_256_init(sha3_ctx *ctx) { init_sha3(ctx, 256); } +void sha3_384_init(sha3_ctx *ctx) { init_sha3(ctx, 384); } +void sha3_512_init(sha3_ctx *ctx) { init_sha3(ctx, 512); } + +/* --- @sha3_hash@ --- * + * + * Arguments: @sha3_ctx *ctx@ = pointer to context bock + * @const void *p@ = pointer to data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +void sha3_hash(sha3_ctx *ctx, const void *p, size_t sz) +{ + const octet *q = p; + size_t spare = ctx->r - ctx->n; + + if (sz < spare) { + memcpy(ctx->buf + ctx->n, q, sz); + ctx->n += sz; + return; + } + if (ctx->n) { + memcpy(ctx->buf + ctx->n, q, spare); + absorb(ctx, ctx->buf, ctx->r/8); + step(ctx); + q += spare; sz -= spare; + } + while (sz >= ctx->r) { + absorb(ctx, q, ctx->r/8); + step(ctx); + q += ctx->r; sz -= ctx->r; + } + if (sz) memcpy(ctx->buf, q, sz); + ctx->n = sz; +} + +/* --- @sha3_done@ --- * + * + * Arguments: @sha3_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +void sha3_done(sha3_ctx *ctx, void *hash) +{ + pad(ctx, OP_SHA3, 0x80); + step(ctx); + + if (ctx->w%8 == 0) + squeeze(ctx, hash, ctx->w/8); + else { + squeeze(ctx, ctx->buf, (ctx->w + 7)/8); + memcpy(hash, ctx->buf, ctx->w); + } +} + +/*----- The cSHAKE XOF algorithm ------------------------------------------*/ + +static void leftenc_sz(shake_ctx *ctx, size_t n) +{ + kludge64 t; + octet b[9]; + unsigned i; + + SET64(t, ((n&~(size_t)MASK32) >> 16) >> 16, n&MASK32); + STORE64_B_(b + 1, t); + for (i = 1; i < 8 && !b[i]; i++); + i--; b[i] = 8 - i; + shake_hash(ctx, b + i, 9 - i); +} + +static void rightenc_sz(shake_ctx *ctx, size_t n) +{ + kludge64 t; + octet b[9]; + unsigned i; + + SET64(t, ((n&~(size_t)MASK32) >> 16) >> 16, n&MASK32); + STORE64_B_(b, t); + for (i = 0; i < 7 && !b[i]; i++); + b[8] = 8 - i; + shake_hash(ctx, b + i, 9 - i); +} + +static void stringenc(shake_ctx *ctx, const void *p, size_t sz) + { leftenc_sz(ctx, 8*sz); if (sz) shake_hash(ctx, p, sz); } + +static void bytepad_before(shake_ctx *ctx) + { leftenc_sz(ctx, ctx->h.r); } + +static void bytepad_after(shake_ctx *ctx) +{ + unsigned pad; + + if (ctx->h.n%8) { + pad = 8 - ctx->h.n%8; + memset(ctx->h.buf + ctx->h.n, 0, pad); + ctx->h.n += pad; + } + if (ctx->h.n) { + absorb(&ctx->h, ctx->h.buf, ctx->h.n/8); + step(&ctx->h); ctx->h.n = 0; + } +} + +/* --- @cshake{128,256}_init@ --- * + * + * Arguments: @shake_ctx *ctx@ = pointer to context to initialize + * @const void *func@ = NIST-allocated function name + * @size_t fsz@ = length of function name + * @const void *perso@ = user personalization string + * @size_t psz@ = length of personalization string + * + * Returns: --- + * + * Use: Initializes a cSHAKE context. The context is initially in + * the `absorbing' state: feed it data with @shake_hash@. + */ + +static void init_shake(shake_ctx *ctx, unsigned c0, + const void *func, size_t fsz, + const void *perso, size_t psz) +{ + keccak1600_init(&ctx->h.s); ctx->st = ST_ABSORB; + ctx->h.r = (1600 - 2*c0)/8; ctx->h.w = 0; ctx->h.n = 0; + if (!fsz && !psz) + ctx->op = OP_SHAKE; + else { + bytepad_before(ctx); + stringenc(ctx, func, fsz); + stringenc(ctx, perso, psz); + bytepad_after(ctx); + ctx->op = OP_CSHAKE; + } +} + +void cshake128_init(shake_ctx *ctx, + const void *func, size_t fsz, + const void *perso, size_t psz) + { init_shake(ctx, 128, func, fsz, perso, psz); } + +void cshake256_init(shake_ctx *ctx, + const void *func, size_t fsz, + const void *perso, size_t psz) + { init_shake(ctx, 256, func, fsz, perso, psz); } + +/* --- @shake{128,256}_init@ --- * + * + * Arguments: @sha3_ctx *ctx@ = pointer to context to initialize + * + * Returns: --- + * + * Use: Initializes a SHAKE context. The context is initially in + * the `absorbing' state: feed it data with @shake_hash@. + */ + +void shake128_init(shake_ctx *ctx) { init_shake(ctx, 128, 0, 0, 0, 0); } +void shake256_init(shake_ctx *ctx) { init_shake(ctx, 256, 0, 0, 0, 0); } + +/* --- @shake_hash@ --- * + * + * Arguments: @shake_ctx *ctx@ = context to update + * @const void *p@ = input buffer + * @size_t sz@ = size of input + * + * Returns: --- + * + * Use: Feeds input data into a SHAKE context. The context must be + * in `absorbing' state. + */ + +void shake_hash(shake_ctx *ctx, const void *p, size_t sz) + { assert(ctx->st == ST_ABSORB); sha3_hash(&ctx->h, p, sz); } + +/* --- @shake_xof@ --- * + * + * Arguments: @shake_ctx *ctx@ = context to update + * + * Returns: --- + * + * Use: Switches the context into `squeezing' state. Use @shake_get@ + * or @shake_mask@ to extract data. + */ + +void shake_xof(shake_ctx *ctx) +{ + assert(ctx->st == ST_ABSORB); + pad(&ctx->h, ctx->op, 0x80); + ctx->st = ST_SQUEEZE; + ctx->h.n = ctx->h.r; +} + +/* --- @shake_get@ --- * + * + * Arguments: @shake_ctx *ctx@ = context to update + * @void *p@ = output buffer + * @size_t sz@ = size of output + * + * Returns: --- + * + * Use: Extracts output from a SHAKE context. The context must be + * in `squeezing' state. + */ + +void shake_get(shake_ctx *ctx, void *p, size_t sz) +{ + octet *q = p; + size_t left = ctx->h.r - ctx->h.n; + + assert(ctx->st == ST_SQUEEZE); + if (left >= sz) { + memcpy(q, ctx->h.buf + ctx->h.n, sz); + ctx->h.n += sz; + return; + } + if (left) { + memcpy(q, ctx->h.buf + ctx->h.n, left); + q += left; sz -= left; + } + while (sz >= ctx->h.r) { + step(&ctx->h); + squeeze(&ctx->h, q, ctx->h.r/8); + q += ctx->h.r; sz -= ctx->h.r; + } + if (!sz) + ctx->h.n = ctx->h.r; + else { + step(&ctx->h); + squeeze(&ctx->h, ctx->h.buf, ctx->h.r/8); + memcpy(q, ctx->h.buf, sz); + ctx->h.n = sz; + } +} + +/* --- @shake_done@ --- * + * + * Arguments: @shake_ctx *ctx@ = context to update + * @void *h@ = where to write the hash + * @size_t hsz@ = size of the hash to make + * + * Returns: --- + * + * Use: Switches the context into `squeezing' state. Use @shake_get@ + * or @shake_mask@ to extract data. + */ + +void shake_done(shake_ctx *ctx, void *h, size_t hsz) + { shake_xof(ctx); shake_get(ctx, h, hsz); ctx->st = ST_DEAD; } + +/*----- That's all, folks -------------------------------------------------*/