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(¶m, 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, ¶m,
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(¶m, sizeof(param));
return ret;
}