static int gcm_aes_crypt()

in crypto/aes_s390.c [828:927]


static int gcm_aes_crypt(struct aead_request *req, unsigned int flags)
{
	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
	struct s390_aes_ctx *ctx = crypto_aead_ctx(tfm);
	unsigned int ivsize = crypto_aead_ivsize(tfm);
	unsigned int taglen = crypto_aead_authsize(tfm);
	unsigned int aadlen = req->assoclen;
	unsigned int pclen = req->cryptlen;
	int ret = 0;

	unsigned int n, len, in_bytes, out_bytes,
		     min_bytes, bytes, aad_bytes, pc_bytes;
	struct gcm_sg_walk gw_in, gw_out;
	u8 tag[GHASH_DIGEST_SIZE];

	struct {
		u32 _[3];		/* reserved */
		u32 cv;			/* Counter Value */
		u8 t[GHASH_DIGEST_SIZE];/* Tag */
		u8 h[AES_BLOCK_SIZE];	/* Hash-subkey */
		u64 taadl;		/* Total AAD Length */
		u64 tpcl;		/* Total Plain-/Cipher-text Length */
		u8 j0[GHASH_BLOCK_SIZE];/* initial counter value */
		u8 k[AES_MAX_KEY_SIZE];	/* Key */
	} param;

	/*
	 * encrypt
	 *   req->src: aad||plaintext
	 *   req->dst: aad||ciphertext||tag
	 * decrypt
	 *   req->src: aad||ciphertext||tag
	 *   req->dst: aad||plaintext, return 0 or -EBADMSG
	 * aad, plaintext and ciphertext may be empty.
	 */
	if (flags & CPACF_DECRYPT)
		pclen -= taglen;
	len = aadlen + pclen;

	memset(&param, 0, sizeof(param));
	param.cv = 1;
	param.taadl = aadlen * 8;
	param.tpcl = pclen * 8;
	memcpy(param.j0, req->iv, ivsize);
	*(u32 *)(param.j0 + ivsize) = 1;
	memcpy(param.k, ctx->key, ctx->key_len);

	gcm_walk_start(&gw_in, req->src, len);
	gcm_walk_start(&gw_out, req->dst, len);

	do {
		min_bytes = min_t(unsigned int,
				  aadlen > 0 ? aadlen : pclen, AES_BLOCK_SIZE);
		in_bytes = gcm_in_walk_go(&gw_in, min_bytes);
		out_bytes = gcm_out_walk_go(&gw_out, min_bytes);
		bytes = min(in_bytes, out_bytes);

		if (aadlen + pclen <= bytes) {
			aad_bytes = aadlen;
			pc_bytes = pclen;
			flags |= CPACF_KMA_LAAD | CPACF_KMA_LPC;
		} else {
			if (aadlen <= bytes) {
				aad_bytes = aadlen;
				pc_bytes = (bytes - aadlen) &
					   ~(AES_BLOCK_SIZE - 1);
				flags |= CPACF_KMA_LAAD;
			} else {
				aad_bytes = bytes & ~(AES_BLOCK_SIZE - 1);
				pc_bytes = 0;
			}
		}

		if (aad_bytes > 0)
			memcpy(gw_out.ptr, gw_in.ptr, aad_bytes);

		cpacf_kma(ctx->fc | flags, &param,
			  gw_out.ptr + aad_bytes,
			  gw_in.ptr + aad_bytes, pc_bytes,
			  gw_in.ptr, aad_bytes);

		n = aad_bytes + pc_bytes;
		if (gcm_in_walk_done(&gw_in, n) != n)
			return -ENOMEM;
		if (gcm_out_walk_done(&gw_out, n) != n)
			return -ENOMEM;
		aadlen -= aad_bytes;
		pclen -= pc_bytes;
	} while (aadlen + pclen > 0);

	if (flags & CPACF_DECRYPT) {
		scatterwalk_map_and_copy(tag, req->src, len, taglen, 0);
		if (crypto_memneq(tag, param.t, taglen))
			ret = -EBADMSG;
	} else
		scatterwalk_map_and_copy(param.t, req->dst, len, taglen, 1);

	memzero_explicit(&param, sizeof(param));
	return ret;
}