diff options
Diffstat (limited to '')
-rw-r--r-- | web/server/h2o/libh2o/deps/picotls/deps/cifra/src/ccm.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/picotls/deps/cifra/src/ccm.c b/web/server/h2o/libh2o/deps/picotls/deps/cifra/src/ccm.c new file mode 100644 index 00000000..7ef87fc1 --- /dev/null +++ b/web/server/h2o/libh2o/deps/picotls/deps/cifra/src/ccm.c @@ -0,0 +1,193 @@ +/* + * 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 "tassert.h" + +#include <string.h> + +#define CCM_ADATA_PRESENT 0x40 + +static void write_be(uint8_t *out, size_t value, size_t bytes) +{ + while (bytes) + { + out[bytes - 1] = value & 0xff; + value >>= 8; + bytes--; + } + + assert(value == 0); /* or we couldn't encode the value. */ +} + +static void zero_pad(cf_cbcmac_stream *cm) +{ + cf_cbcmac_stream_finish_block_zero(cm); +} + +/* nb. block is general workspace. */ +static void add_aad(cf_cbcmac_stream *cm, uint8_t block[CF_MAXBLOCK], + const uint8_t *header, size_t nheader) +{ + assert(nheader <= 0xffffffff); /* we don't support 64 bit lengths. */ + + /* Add length using stupidly complicated rules. */ + if (nheader < 0xff00) + { + write_be(block, nheader, 2); + cf_cbcmac_stream_update(cm, block, 2); + } else { + write_be(block, 0xfffe, 2); + write_be(block + 2, nheader, 4); + cf_cbcmac_stream_update(cm, block, 6); + } + + cf_cbcmac_stream_update(cm, header, nheader); + zero_pad(cm); +} + +static void add_block0(cf_cbcmac_stream *cm, + uint8_t block[CF_MAXBLOCK], size_t nblock, + const uint8_t *nonce, size_t nnonce, + size_t L, size_t nplain, + size_t nheader, size_t ntag) +{ + /* Construct first block B_0. */ + block[0] = ((nheader == 0) ? 0x00 : CCM_ADATA_PRESENT) | + ((ntag - 2) / 2) << 3 | + (L - 1); + memcpy(block + 1, nonce, nnonce); + write_be(block + 1 + nnonce, nplain, L); + + cf_cbcmac_stream_update(cm, block, nblock); +} + +static void build_ctr_nonce(uint8_t ctr_nonce[CF_MAXBLOCK], + size_t L, + const uint8_t *nonce, size_t nnonce) +{ + ctr_nonce[0] = (L - 1); + memcpy(ctr_nonce + 1, nonce, nnonce); + memset(ctr_nonce + 1 + nnonce, 0, L); +} + +void cf_ccm_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, size_t L, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, + uint8_t *tag, size_t ntag) +{ + uint8_t block[CF_MAXBLOCK]; + + assert(ntag >= 4 && ntag <= 16 && ntag % 2 == 0); + assert(L >= 2 && L <= 8); + assert(nnonce == prp->blocksz - L - 1); + + cf_cbcmac_stream cm; + cf_cbcmac_stream_init(&cm, prp, prpctx); + + /* Add first block. */ + add_block0(&cm, block, prp->blocksz, + nonce, nnonce, + L, nplain, nheader, ntag); + + /* Add AAD with length prefix, if present. */ + if (nheader) + add_aad(&cm, block, header, nheader); + + /* Add message. */ + cf_cbcmac_stream_update(&cm, plain, nplain); + zero_pad(&cm); + + /* Finish tag. */ + cf_cbcmac_stream_nopad_final(&cm, block); + + /* Start encryption. */ + /* Construct A_0 */ + uint8_t ctr_nonce[CF_MAXBLOCK]; + build_ctr_nonce(ctr_nonce, L, nonce, nnonce); + + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, ctr_nonce); + cf_ctr_custom_counter(&ctr, prp->blocksz - L, L); + + /* Encrypt tag first. */ + cf_ctr_cipher(&ctr, block, block, prp->blocksz); + memcpy(tag, block, ntag); + + /* Then encrypt message. */ + cf_ctr_cipher(&ctr, plain, cipher, nplain); +} + +int cf_ccm_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, size_t L, + 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 block[CF_MAXBLOCK]; + + assert(ntag >= 4 && ntag <= 16 && ntag % 2 == 0); + assert(L >= 2 && L <= 8); + assert(nnonce == prp->blocksz - L - 1); + + uint8_t ctr_nonce[CF_MAXBLOCK]; + build_ctr_nonce(ctr_nonce, L, nonce, nnonce); + + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, ctr_nonce); + cf_ctr_custom_counter(&ctr, prp->blocksz - L, L); + + /* Decrypt tag. */ + uint8_t plain_tag[CF_MAXBLOCK]; + cf_ctr_cipher(&ctr, tag, plain_tag, ntag); + cf_ctr_discard_block(&ctr); + + /* Decrypt message. */ + cf_ctr_cipher(&ctr, cipher, plain, ncipher); + + cf_cbcmac_stream cm; + cf_cbcmac_stream_init(&cm, prp, prpctx); + + /* Add first block. */ + add_block0(&cm, block, prp->blocksz, + nonce, nnonce, + L, ncipher, nheader, ntag); + + if (nheader) + add_aad(&cm, block, header, nheader); + + cf_cbcmac_stream_update(&cm, plain, ncipher); + zero_pad(&cm); + + /* Finish tag. */ + cf_cbcmac_stream_nopad_final(&cm, block); + + int err = 0; + + if (!mem_eq(block, plain_tag, ntag)) + { + err = 1; + mem_clean(plain, ncipher); + } + + mem_clean(block, sizeof block); + mem_clean(plain_tag, sizeof plain_tag); + return err; +} + |