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; }
/* 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); }
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); }
static void cbcmac_vector(const void *tag_expect, size_t ntag, const void *key, size_t nkey, const void *msg, size_t nmsg) { uint8_t tag[16]; cf_aes_context aes; cf_aes_init(&aes, key, nkey); cf_cbcmac_stream cm; cf_cbcmac_stream_init(&cm, &cf_aes, &aes); cf_cbcmac_stream_update(&cm, msg, nmsg); cf_cbcmac_stream_pad_final(&cm, tag); TEST_CHECK(sizeof tag == ntag); TEST_CHECK(memcmp(tag, tag_expect, sizeof tag) == 0); }