diff options
Diffstat (limited to 'sys/opencrypto/cryptosoft.c')
-rw-r--r-- | sys/opencrypto/cryptosoft.c | 124 |
1 files changed, 94 insertions, 30 deletions
diff --git a/sys/opencrypto/cryptosoft.c b/sys/opencrypto/cryptosoft.c index ae71f0d6c096..e51bade8a3f8 100644 --- a/sys/opencrypto/cryptosoft.c +++ b/sys/opencrypto/cryptosoft.c @@ -636,16 +636,69 @@ out: return (error); } +static void +build_ccm_b0(const char *nonce, u_int nonce_length, u_int aad_length, + u_int data_length, u_int tag_length, uint8_t *b0) +{ + uint8_t *bp; + uint8_t flags, L; + + KASSERT(nonce_length >= 7 && nonce_length <= 13, + ("nonce_length must be between 7 and 13 bytes")); + + /* + * Need to determine the L field value. This is the number of + * bytes needed to specify the length of the message; the length + * is whatever is left in the 16 bytes after specifying flags and + * the nonce. + */ + L = 15 - nonce_length; + + flags = ((aad_length > 0) << 6) + + (((tag_length - 2) / 2) << 3) + + L - 1; + + /* + * Now we need to set up the first block, which has flags, nonce, + * and the message length. + */ + b0[0] = flags; + memcpy(b0 + 1, nonce, nonce_length); + bp = b0 + 1 + nonce_length; + + /* Need to copy L' [aka L-1] bytes of data_length */ + for (uint8_t *dst = b0 + CCM_CBC_BLOCK_LEN - 1; dst >= bp; dst--) { + *dst = data_length; + data_length >>= 8; + } +} + +/* NB: OCF only supports AAD lengths < 2^32. */ +static int +build_ccm_aad_length(u_int aad_length, uint8_t *blk) +{ + if (aad_length < ((1 << 16) - (1 << 8))) { + be16enc(blk, aad_length); + return (sizeof(uint16_t)); + } else { + blk[0] = 0xff; + blk[1] = 0xfe; + be32enc(blk + 2, aad_length); + return (2 + sizeof(uint32_t)); + } +} + static int swcr_ccm_cbc_mac(struct swcr_session *ses, struct cryptop *crp) { - u_char tag[AES_CBC_MAC_HASH_LEN]; u_char iv[AES_BLOCK_LEN]; + u_char blk[CCM_CBC_BLOCK_LEN]; + u_char tag[AES_CBC_MAC_HASH_LEN]; union authctx ctx; const struct crypto_session_params *csp; struct swcr_auth *swa; const struct auth_hash *axf; - int error, ivlen; + int error, ivlen, len; csp = crypto_get_params(crp->crp_session); swa = &ses->swcr_auth; @@ -657,25 +710,24 @@ swcr_ccm_cbc_mac(struct swcr_session *ses, struct cryptop *crp) ivlen = csp->csp_ivlen; crypto_read_iv(crp, iv); - /* - * AES CCM-CBC-MAC needs to know the length of both the auth - * data and payload data before doing the auth computation. - */ - ctx.aes_cbc_mac_ctx.authDataLength = crp->crp_payload_length; - ctx.aes_cbc_mac_ctx.cryptDataLength = 0; + /* Supply MAC with IV */ + axf->Reinit(&ctx, crp->crp_iv, ivlen); - axf->Reinit(&ctx, iv, ivlen); - if (crp->crp_aad != NULL) - error = axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length); - else - error = crypto_apply(crp, crp->crp_payload_start, - crp->crp_payload_length, axf->Update, &ctx); - if (error) - return (error); + /* Supply MAC with b0. */ + build_ccm_b0(crp->crp_iv, ivlen, crp->crp_payload_length, 0, + swa->sw_mlen, blk); + axf->Update(&ctx, blk, CCM_CBC_BLOCK_LEN); + + len = build_ccm_aad_length(crp->crp_payload_length, blk); + axf->Update(&ctx, blk, len); + + crypto_apply(crp, crp->crp_payload_start, crp->crp_payload_length, + axf->Update, &ctx); /* Finalize MAC */ axf->Final(tag, &ctx); + error = 0; if (crp->crp_op & CRYPTO_OP_VERIFY_DIGEST) { u_char tag2[AES_CBC_MAC_HASH_LEN]; @@ -689,6 +741,7 @@ swcr_ccm_cbc_mac(struct swcr_session *ses, struct cryptop *crp) crypto_copyback(crp, crp->crp_digest_start, swa->sw_mlen, tag); } explicit_bzero(tag, sizeof(tag)); + explicit_bzero(blk, sizeof(blk)); explicit_bzero(iv, sizeof(iv)); return (error); } @@ -733,24 +786,35 @@ swcr_ccm(struct swcr_session *ses, struct cryptop *crp) ivlen = csp->csp_ivlen; - /* - * AES CCM-CBC-MAC needs to know the length of both the auth - * data and payload data before doing the auth computation. - */ - ctx.aes_cbc_mac_ctx.authDataLength = crp->crp_aad_length; - ctx.aes_cbc_mac_ctx.cryptDataLength = crp->crp_payload_length; - /* Supply MAC with IV */ axf->Reinit(&ctx, crp->crp_iv, ivlen); + /* Supply MAC with b0. */ + _Static_assert(sizeof(blkbuf) >= CCM_CBC_BLOCK_LEN, + "blkbuf too small for b0"); + build_ccm_b0(crp->crp_iv, ivlen, crp->crp_aad_length, + crp->crp_payload_length, swa->sw_mlen, blk); + axf->Update(&ctx, blk, CCM_CBC_BLOCK_LEN); + /* Supply MAC with AAD */ - if (crp->crp_aad != NULL) - error = axf->Update(&ctx, crp->crp_aad, crp->crp_aad_length); - else - error = crypto_apply(crp, crp->crp_aad_start, - crp->crp_aad_length, axf->Update, &ctx); - if (error) - return (error); + if (crp->crp_aad_length != 0) { + len = build_ccm_aad_length(crp->crp_aad_length, blk); + axf->Update(&ctx, blk, len); + if (crp->crp_aad != NULL) + axf->Update(&ctx, crp->crp_aad, + crp->crp_aad_length); + else + crypto_apply(crp, crp->crp_aad_start, + crp->crp_aad_length, axf->Update, &ctx); + + /* Pad the AAD (including length field) to a full block. */ + len = (len + crp->crp_aad_length) % CCM_CBC_BLOCK_LEN; + if (len != 0) { + len = CCM_CBC_BLOCK_LEN - len; + memset(blk, 0, CCM_CBC_BLOCK_LEN); + axf->Update(&ctx, blk, len); + } + } if (crp->crp_cipher_key != NULL) exf->setkey(swe->sw_kschedule, crp->crp_cipher_key, |