diff options
Diffstat (limited to '')
-rw-r--r-- | debian/vendor-h2o/deps/picotls/deps/cifra/src/gcm.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/debian/vendor-h2o/deps/picotls/deps/cifra/src/gcm.c b/debian/vendor-h2o/deps/picotls/deps/cifra/src/gcm.c new file mode 100644 index 0000000..5b37473 --- /dev/null +++ b/debian/vendor-h2o/deps/picotls/deps/cifra/src/gcm.c @@ -0,0 +1,249 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton <jpixton@gmail.com> + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include "handy.h" +#include "prp.h" +#include "modes.h" +#include "blockwise.h" +#include "bitops.h" +#include "gf128.h" +#include "tassert.h" + +#include <string.h> + +#define STATE_INVALID 0 +#define STATE_AAD 1 +#define STATE_CIPHER 2 + +static void ghash_init(ghash_ctx *ctx, uint8_t H[16]) +{ + memset(ctx, 0, sizeof *ctx); + cf_gf128_frombytes_be(H, ctx->H); + ctx->state = STATE_AAD; +} + +static void ghash_block(void *vctx, const uint8_t *data) +{ + ghash_ctx *ctx = vctx; + cf_gf128 gfdata; + cf_gf128_frombytes_be(data, gfdata); + cf_gf128_add(gfdata, ctx->Y, ctx->Y); + cf_gf128_mul(ctx->Y, ctx->H, ctx->Y); +} + +static void ghash_add(ghash_ctx *ctx, const uint8_t *buf, size_t n) +{ + cf_blockwise_accumulate(ctx->buffer, &ctx->buffer_used, + sizeof ctx->buffer, + buf, n, + ghash_block, + ctx); +} + +static void ghash_add_pad(ghash_ctx *ctx) +{ + if (ctx->buffer_used == 0) + return; + + memset(ctx->buffer + ctx->buffer_used, 0, sizeof(ctx->buffer) - ctx->buffer_used); + ghash_block(ctx, ctx->buffer); + ctx->buffer_used = 0; +} + +static void ghash_add_aad(ghash_ctx *ctx, const uint8_t *buf, size_t n) +{ + assert(ctx->state == STATE_AAD); + ctx->len_aad += n; + ghash_add(ctx, buf, n); +} + +static void ghash_add_cipher(ghash_ctx *ctx, const uint8_t *buf, size_t n) +{ + if (ctx->state == STATE_AAD) + { + ghash_add_pad(ctx); + ctx->state = STATE_CIPHER; + } + + assert(ctx->state == STATE_CIPHER); + ctx->len_cipher += n; + ghash_add(ctx, buf, n); +} + +static void ghash_final(ghash_ctx *ctx, uint8_t out[16]) +{ + uint8_t lenbuf[8]; + + if (ctx->state == STATE_AAD || ctx->state == STATE_CIPHER) + { + ghash_add_pad(ctx); + ctx->state = STATE_INVALID; + } + + /* Add len(A) || len(C) */ + write64_be(ctx->len_aad * 8, lenbuf); + ghash_add(ctx, lenbuf, sizeof lenbuf); + + write64_be(ctx->len_cipher * 8, lenbuf); + ghash_add(ctx, lenbuf, sizeof lenbuf); + + assert(ctx->buffer_used == 0); + cf_gf128_tobytes_be(ctx->Y, out); +} + +void cf_gcm_encrypt_init(const cf_prp *prp, void *prpctx, cf_gcm_ctx *gcmctx, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce) +{ + uint8_t H[16] = { 0 }; + + /* H = E_K(0^128) */ + prp->encrypt(prpctx, H, H); + + /* Produce CTR nonce, Y_0: + * + * if len(IV) == 96 + * Y_0 = IV || 0^31 || 1 + * otherwise + * Y_0 = GHASH(H, {}, IV) + */ + + if (nnonce == 12) + { + memcpy(gcmctx->Y0, nonce, nnonce); + gcmctx->Y0[12] = gcmctx->Y0[13] = gcmctx->Y0[14] = 0x00; + gcmctx->Y0[15] = 0x01; + } else { + ghash_init(&gcmctx->gh, H); + ghash_add_cipher(&gcmctx->gh, nonce, nnonce); + ghash_final(&gcmctx->gh, gcmctx->Y0); + } + + /* Hash AAD */ + ghash_init(&gcmctx->gh, H); + ghash_add_aad(&gcmctx->gh, header, nheader); + + /* Produce ciphertext */ + memset(gcmctx->e_Y0, 0, sizeof(gcmctx->e_Y0)); + cf_ctr_init(&gcmctx->ctr, prp, prpctx, gcmctx->Y0); + cf_ctr_custom_counter(&gcmctx->ctr, 12, 4); /* counter is 2^32 */ + cf_ctr_cipher(&gcmctx->ctr, gcmctx->e_Y0, gcmctx->e_Y0, sizeof gcmctx->e_Y0); /* first block is tag offset */ + + mem_clean(H, sizeof H); +} + +void cf_gcm_encrypt_update(cf_gcm_ctx *gcmctx, const uint8_t *plain, size_t nplain, uint8_t *cipher) +{ + cf_ctr_cipher(&gcmctx->ctr, plain, cipher, nplain); + ghash_add_cipher(&gcmctx->gh, cipher, nplain); +} + +void cf_gcm_encrypt_final(cf_gcm_ctx *gcmctx, uint8_t *tag, size_t ntag) +{ + /* Post-process ghash output */ + uint8_t full_tag[16] = { 0 }; + ghash_final(&gcmctx->gh, full_tag); + + assert(ntag > 1 && ntag <= 16); + xor_bb(tag, full_tag, gcmctx->e_Y0, ntag); + + mem_clean(full_tag, sizeof full_tag); + mem_clean(gcmctx, sizeof *gcmctx); +} + +void cf_gcm_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, /* the same size as nplain */ + uint8_t *tag, size_t ntag) +{ + cf_gcm_ctx gcmctx; + + cf_gcm_encrypt_init(prp, prpctx, &gcmctx, header, nheader, nonce, nnonce); + cf_gcm_encrypt_update(&gcmctx, plain, nplain, cipher); + cf_gcm_encrypt_final(&gcmctx, tag, ntag); +} + +int cf_gcm_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain) +{ + uint8_t H[16] = { 0 }; + uint8_t Y0[16]; + + /* H = E_K(0^128) */ + prp->encrypt(prpctx, H, H); + + /* Produce CTR nonce, Y_0: + * + * if len(IV) == 96 + * Y_0 = IV || 0^31 || 1 + * otherwise + * Y_0 = GHASH(H, {}, IV) + */ + + if (nnonce == 12) + { + memcpy(Y0, nonce, nnonce); + Y0[12] = Y0[13] = Y0[14] = 0x00; + Y0[15] = 0x01; + } else { + ghash_ctx gh; + ghash_init(&gh, H); + ghash_add_cipher(&gh, nonce, nnonce); + ghash_final(&gh, Y0); + } + + /* Hash AAD. */ + ghash_ctx gh; + ghash_init(&gh, H); + ghash_add_aad(&gh, header, nheader); + + /* Start counter mode, to obtain offset on tag. */ + uint8_t e_Y0[16] = { 0 }; + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, Y0); + cf_ctr_custom_counter(&ctr, 12, 4); + cf_ctr_cipher(&ctr, e_Y0, e_Y0, sizeof e_Y0); + + /* Hash ciphertext. */ + ghash_add_cipher(&gh, cipher, ncipher); + + /* Produce tag. */ + uint8_t full_tag[16]; + ghash_final(&gh, full_tag); + assert(ntag > 1 && ntag <= 16); + xor_bb(full_tag, full_tag, e_Y0, ntag); + + int err = 1; + if (!mem_eq(full_tag, tag, ntag)) + goto x_err; + + /* Complete decryption. */ + cf_ctr_cipher(&ctr, cipher, plain, ncipher); + err = 0; + +x_err: + mem_clean(H, sizeof H); + mem_clean(Y0, sizeof Y0); + mem_clean(e_Y0, sizeof e_Y0); + mem_clean(full_tag, sizeof full_tag); + mem_clean(&gh, sizeof gh); + mem_clean(&ctr, sizeof ctr); + return err; +} |