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; }
static int process(const uint8_t key[32], const uint8_t nonce[12], const uint8_t *header, size_t nheader, const uint8_t *input, size_t nbytes, uint8_t *output, int mode, uint8_t tag[16]) { /* First, generate the Poly1305 key by running ChaCha20 with the * given key and a zero counter. The first half of the * 64-byte output is the key. */ uint8_t fullnonce[16] = { 0 }; memcpy(fullnonce + 4, nonce, 12); uint8_t polykey[32] = { 0 }; cf_chacha20_ctx chacha; cf_chacha20_init_custom(&chacha, key, 32, fullnonce, 4); cf_chacha20_cipher(&chacha, polykey, polykey, sizeof polykey); /* Now initialise Poly1305. */ cf_poly1305 poly; cf_poly1305_init(&poly, polykey, polykey + 16); mem_clean(polykey, sizeof polykey); /* Discard next 32 bytes of chacha20 key stream. */ cf_chacha20_cipher(&chacha, polykey, polykey, sizeof polykey); mem_clean(polykey, sizeof polykey); /* The input to Poly1305 is: * AAD || pad(AAD) || cipher || pad(cipher) || len_64(aad) || len_64(cipher) */ uint8_t padbuf[16] = { 0 }; #define PADLEN(x) (16 - ((x) & 0xf)) /* AAD || pad(AAD) */ cf_poly1305_update(&poly, header, nheader); cf_poly1305_update(&poly, padbuf, PADLEN(nheader)); /* || cipher */ if (mode == ENCRYPT) { /* If we're encrypting, we compute the ciphertext * before inputting it into the MAC. */ cf_chacha20_cipher(&chacha, input, output, nbytes); cf_poly1305_update(&poly, output, nbytes); } else { /* Otherwise: decryption -- input the ciphertext. * Delay actual decryption until we checked the MAC. */ cf_poly1305_update(&poly, input, nbytes); } /* || pad(cipher) */ cf_poly1305_update(&poly, padbuf, PADLEN(nbytes)); /* || len_64(aad) || len_64(cipher) */ write64_le(nheader, padbuf); write64_le(nbytes, padbuf + 8); cf_poly1305_update(&poly, padbuf, sizeof padbuf); /* MAC computation is now complete. */ if (mode == ENCRYPT) { cf_poly1305_finish(&poly, tag); mem_clean(&chacha, sizeof chacha); return SUCCESS; } /* Decrypt mode: calculate tag, and check it. * If it's correct, proceed with decryption. */ uint8_t checktag[16]; cf_poly1305_finish(&poly, checktag); if (mem_eq(checktag, tag, sizeof checktag)) { cf_chacha20_cipher(&chacha, input, output, nbytes); mem_clean(&chacha, sizeof chacha); mem_clean(checktag, sizeof checktag); return SUCCESS; } else { mem_clean(output, nbytes); mem_clean(&chacha, sizeof chacha); mem_clean(checktag, sizeof checktag); return FAILURE; } }