in axis/artpec6_crypto.c [1827:2039]
static int artpec6_crypto_prepare_aead(struct aead_request *areq)
{
size_t count;
int ret;
size_t input_length;
struct artpec6_cryptotfm_context *ctx = crypto_tfm_ctx(areq->base.tfm);
struct artpec6_crypto_aead_req_ctx *req_ctx = aead_request_ctx(areq);
struct crypto_aead *cipher = crypto_aead_reqtfm(areq);
struct artpec6_crypto_req_common *common = &req_ctx->common;
struct artpec6_crypto *ac = dev_get_drvdata(artpec6_crypto_dev);
enum artpec6_crypto_variant variant = ac->variant;
u32 md_cipher_len;
artpec6_crypto_init_dma_operation(common);
/* Key */
if (variant == ARTPEC6_CRYPTO) {
ctx->key_md = FIELD_PREP(A6_CRY_MD_OPER,
a6_regk_crypto_dlkey);
} else {
ctx->key_md = FIELD_PREP(A7_CRY_MD_OPER,
a7_regk_crypto_dlkey);
}
ret = artpec6_crypto_setup_out_descr(common, (void *)&ctx->key_md,
sizeof(ctx->key_md), false, false);
if (ret)
return ret;
ret = artpec6_crypto_setup_out_descr(common, ctx->aes_key,
ctx->key_length, true, false);
if (ret)
return ret;
req_ctx->cipher_md = 0;
switch (ctx->key_length) {
case 16:
md_cipher_len = regk_crypto_key_128;
break;
case 24:
md_cipher_len = regk_crypto_key_192;
break;
case 32:
md_cipher_len = regk_crypto_key_256;
break;
default:
return -EINVAL;
}
if (variant == ARTPEC6_CRYPTO) {
req_ctx->cipher_md |= FIELD_PREP(A6_CRY_MD_OPER,
regk_crypto_aes_gcm);
req_ctx->cipher_md |= FIELD_PREP(A6_CRY_MD_CIPHER_LEN,
md_cipher_len);
if (req_ctx->decrypt)
req_ctx->cipher_md |= A6_CRY_MD_CIPHER_DECR;
} else {
req_ctx->cipher_md |= FIELD_PREP(A7_CRY_MD_OPER,
regk_crypto_aes_gcm);
req_ctx->cipher_md |= FIELD_PREP(A7_CRY_MD_CIPHER_LEN,
md_cipher_len);
if (req_ctx->decrypt)
req_ctx->cipher_md |= A7_CRY_MD_CIPHER_DECR;
}
ret = artpec6_crypto_setup_out_descr(common,
(void *) &req_ctx->cipher_md,
sizeof(req_ctx->cipher_md), false,
false);
if (ret)
return ret;
ret = artpec6_crypto_setup_in_descr(common, ac->pad_buffer, 4, false);
if (ret)
return ret;
/* For the decryption, cryptlen includes the tag. */
input_length = areq->cryptlen;
if (req_ctx->decrypt)
input_length -= crypto_aead_authsize(cipher);
/* Prepare the context buffer */
req_ctx->hw_ctx.aad_length_bits =
__cpu_to_be64(8*areq->assoclen);
req_ctx->hw_ctx.text_length_bits =
__cpu_to_be64(8*input_length);
memcpy(req_ctx->hw_ctx.J0, areq->iv, crypto_aead_ivsize(cipher));
// The HW omits the initial increment of the counter field.
memcpy(req_ctx->hw_ctx.J0 + GCM_AES_IV_SIZE, "\x00\x00\x00\x01", 4);
ret = artpec6_crypto_setup_out_descr(common, &req_ctx->hw_ctx,
sizeof(struct artpec6_crypto_aead_hw_ctx), false, false);
if (ret)
return ret;
{
struct artpec6_crypto_walk walk;
artpec6_crypto_walk_init(&walk, areq->src);
/* Associated data */
count = areq->assoclen;
ret = artpec6_crypto_setup_sg_descrs_out(common, &walk, count);
if (ret)
return ret;
if (!IS_ALIGNED(areq->assoclen, 16)) {
size_t assoc_pad = 16 - (areq->assoclen % 16);
/* The HW mandates zero padding here */
ret = artpec6_crypto_setup_out_descr(common,
ac->zero_buffer,
assoc_pad, false,
false);
if (ret)
return ret;
}
/* Data to crypto */
count = input_length;
ret = artpec6_crypto_setup_sg_descrs_out(common, &walk, count);
if (ret)
return ret;
if (!IS_ALIGNED(input_length, 16)) {
size_t crypto_pad = 16 - (input_length % 16);
/* The HW mandates zero padding here */
ret = artpec6_crypto_setup_out_descr(common,
ac->zero_buffer,
crypto_pad,
false,
false);
if (ret)
return ret;
}
}
/* Data from crypto */
{
struct artpec6_crypto_walk walk;
size_t output_len = areq->cryptlen;
if (req_ctx->decrypt)
output_len -= crypto_aead_authsize(cipher);
artpec6_crypto_walk_init(&walk, areq->dst);
/* skip associated data in the output */
count = artpec6_crypto_walk_advance(&walk, areq->assoclen);
if (count)
return -EINVAL;
count = output_len;
ret = artpec6_crypto_setup_sg_descrs_in(common, &walk, count);
if (ret)
return ret;
/* Put padding between the cryptotext and the auth tag */
if (!IS_ALIGNED(output_len, 16)) {
size_t crypto_pad = 16 - (output_len % 16);
ret = artpec6_crypto_setup_in_descr(common,
ac->pad_buffer,
crypto_pad, false);
if (ret)
return ret;
}
/* The authentication tag shall follow immediately after
* the output ciphertext. For decryption it is put in a context
* buffer for later compare against the input tag.
*/
if (req_ctx->decrypt) {
ret = artpec6_crypto_setup_in_descr(common,
req_ctx->decryption_tag, AES_BLOCK_SIZE, false);
if (ret)
return ret;
} else {
/* For encryption the requested tag size may be smaller
* than the hardware's generated tag.
*/
size_t authsize = crypto_aead_authsize(cipher);
ret = artpec6_crypto_setup_sg_descrs_in(common, &walk,
authsize);
if (ret)
return ret;
if (authsize < AES_BLOCK_SIZE) {
count = AES_BLOCK_SIZE - authsize;
ret = artpec6_crypto_setup_in_descr(common,
ac->pad_buffer,
count, false);
if (ret)
return ret;
}
}
}
ret = artpec6_crypto_terminate_in_descrs(common);
if (ret)
return ret;
ret = artpec6_crypto_terminate_out_descrs(common);
if (ret)
return ret;
return artpec6_crypto_dma_map_descs(common);
}