static int artpec6_crypto_prepare_aead()

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);
}