[PATCH 3/3] crypto: caam: Validate the protected key header in setkey
Richard Weinberger <[email protected]> Sun, 26 Jul 2026 10:15:04 +0200
| Newsgroups | org.kernel.vger.linux-crypto,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
paes_skcipher_setkey() trusts struct caam_pkey_info as supplied by the caller, which for AF_ALG means userspace. plain_key_sz is never checked against the key material passed in, yet the blob length the hardware reads from ctx->key is derived from it, so a short blob makes it read data a previous key left behind, outside the range synced for the device. Require the two to agree, and reject non-protected keys. key_cmd_opt is OR'ed into and never reset, so KEY_EKT survives a re-key with an ECB key. Assign it instead. Nothing accounts for the CAAM_CCM_OVERHEAD bytes an EKT black key adds: AES-256 CCM blobs exceed max_keysize, AES-128 ones are decapsulated truncated, and caam_process_blob() encapsulates only part of the key. Reject CCM until the length arithmetic is fixed end to end. Assisted-by: Claude-Code:claude-fable-5 Reported-by: Eric Biggers <[email protected]> Signed-off-by: Richard Weinberger <[email protected]> --- drivers/crypto/caam/caamalg.c | 52 +++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 8826797dc..d00caa9cd 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -797,6 +797,45 @@ static int paes_skcipher_setkey(struct crypto_skcipher *skcipher, keylen = keylen - CAAM_PKEY_HEADER; + if (pkey_info->is_pkey != 1) { + dev_dbg(jrdev, "not a protected key\n"); + return -EINVAL; + } + + /* + * An EKT (CCM) black key occupies plain_key_sz + CAAM_CCM_OVERHEAD + * bytes, which neither the key size range of this algorithm, nor the + * blob length computed by cnstr_desc_protected_blob_decap(), nor the + * KEY command length in cnstr_desc_skcipher_enc_dec() account for. + * Encapsulation has the same problem: caam_process_blob() stores only + * input_len bytes of an input_len + CAAM_CCM_OVERHEAD byte black key. + * Reject such keys instead of submitting a job that is bound to fail + * or, worse, to derive a key from a truncated blob. + */ + if (pkey_info->key_enc_algo != CAAM_ENC_ALGO_ECB) { + dev_dbg(jrdev, "unsupported protected key encoding %u\n", + pkey_info->key_enc_algo); + return -EOPNOTSUPP; + } + + /* Validate key length for AES algorithms */ + err = aes_check_keylen(pkey_info->plain_key_sz); + if (err) { + dev_dbg(jrdev, "bad key length\n"); + return err; + } + + /* + * plain_key_sz comes straight from userspace and determines how much + * data the hardware reads out of ctx->key, so it has to agree with the + * amount of key material actually supplied. + */ + if (keylen != pkey_info->plain_key_sz + CAAM_BLOB_OVERHEAD) { + dev_dbg(jrdev, "blob length %u does not match key size %u\n", + keylen, pkey_info->plain_key_sz); + return -EINVAL; + } + /* Retrieve the length of key */ ctx->cdata.plain_keylen = pkey_info->plain_key_sz; @@ -806,19 +845,8 @@ static int paes_skcipher_setkey(struct crypto_skcipher *skcipher, /* Retrieve the address of the blob */ ctx->cdata.key_virt = pkey_info->key_buf; - /* Validate key length for AES algorithms */ - err = aes_check_keylen(ctx->cdata.plain_keylen); - if (err) { - dev_err(jrdev, "bad key length\n"); - return err; - } - /* set command option */ - ctx->cdata.key_cmd_opt |= KEY_ENC; - - /* check if the Protected-Key is CCM key */ - if (pkey_info->key_enc_algo == CAAM_ENC_ALGO_CCM) - ctx->cdata.key_cmd_opt |= KEY_EKT; + ctx->cdata.key_cmd_opt = KEY_ENC; memcpy(ctx->key, ctx->cdata.key_virt, keylen); dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, DMA_TO_DEVICE); -- 2.55.0