162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Glue code for accelerated AES-GCM stitched implementation for ppc64le.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2022- IBM Inc. All rights reserved
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <asm/unaligned.h>
962306a36Sopenharmony_ci#include <asm/simd.h>
1062306a36Sopenharmony_ci#include <asm/switch_to.h>
1162306a36Sopenharmony_ci#include <crypto/aes.h>
1262306a36Sopenharmony_ci#include <crypto/algapi.h>
1362306a36Sopenharmony_ci#include <crypto/b128ops.h>
1462306a36Sopenharmony_ci#include <crypto/gf128mul.h>
1562306a36Sopenharmony_ci#include <crypto/internal/simd.h>
1662306a36Sopenharmony_ci#include <crypto/internal/aead.h>
1762306a36Sopenharmony_ci#include <crypto/internal/hash.h>
1862306a36Sopenharmony_ci#include <crypto/internal/skcipher.h>
1962306a36Sopenharmony_ci#include <crypto/scatterwalk.h>
2062306a36Sopenharmony_ci#include <linux/cpufeature.h>
2162306a36Sopenharmony_ci#include <linux/crypto.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define	PPC_ALIGN		16
2662306a36Sopenharmony_ci#define GCM_IV_SIZE		12
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciMODULE_DESCRIPTION("PPC64le AES-GCM with Stitched implementation");
2962306a36Sopenharmony_ciMODULE_AUTHOR("Danny Tsen <dtsen@linux.ibm.com");
3062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
3162306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("aes");
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciasmlinkage int aes_p10_set_encrypt_key(const u8 *userKey, const int bits,
3462306a36Sopenharmony_ci				      void *key);
3562306a36Sopenharmony_ciasmlinkage void aes_p10_encrypt(const u8 *in, u8 *out, const void *key);
3662306a36Sopenharmony_ciasmlinkage void aes_p10_gcm_encrypt(u8 *in, u8 *out, size_t len,
3762306a36Sopenharmony_ci				    void *rkey, u8 *iv, void *Xi);
3862306a36Sopenharmony_ciasmlinkage void aes_p10_gcm_decrypt(u8 *in, u8 *out, size_t len,
3962306a36Sopenharmony_ci				    void *rkey, u8 *iv, void *Xi);
4062306a36Sopenharmony_ciasmlinkage void gcm_init_htable(unsigned char htable[], unsigned char Xi[]);
4162306a36Sopenharmony_ciasmlinkage void gcm_ghash_p10(unsigned char *Xi, unsigned char *Htable,
4262306a36Sopenharmony_ci		unsigned char *aad, unsigned int alen);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct aes_key {
4562306a36Sopenharmony_ci	u8 key[AES_MAX_KEYLENGTH];
4662306a36Sopenharmony_ci	u64 rounds;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct gcm_ctx {
5062306a36Sopenharmony_ci	u8 iv[16];
5162306a36Sopenharmony_ci	u8 ivtag[16];
5262306a36Sopenharmony_ci	u8 aad_hash[16];
5362306a36Sopenharmony_ci	u64 aadLen;
5462306a36Sopenharmony_ci	u64 Plen;	/* offset 56 - used in aes_p10_gcm_{en/de}crypt */
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_cistruct Hash_ctx {
5762306a36Sopenharmony_ci	u8 H[16];	/* subkey */
5862306a36Sopenharmony_ci	u8 Htable[256];	/* Xi, Hash table(offset 32) */
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct p10_aes_gcm_ctx {
6262306a36Sopenharmony_ci	struct aes_key enc_key;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void vsx_begin(void)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	preempt_disable();
6862306a36Sopenharmony_ci	enable_kernel_vsx();
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void vsx_end(void)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	disable_kernel_vsx();
7462306a36Sopenharmony_ci	preempt_enable();
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void set_subkey(unsigned char *hash)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	*(u64 *)&hash[0] = be64_to_cpup((__be64 *)&hash[0]);
8062306a36Sopenharmony_ci	*(u64 *)&hash[8] = be64_to_cpup((__be64 *)&hash[8]);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Compute aad if any.
8562306a36Sopenharmony_ci *   - Hash aad and copy to Xi.
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic void set_aad(struct gcm_ctx *gctx, struct Hash_ctx *hash,
8862306a36Sopenharmony_ci		    unsigned char *aad, int alen)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	int i;
9162306a36Sopenharmony_ci	u8 nXi[16] = {0, };
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	gctx->aadLen = alen;
9462306a36Sopenharmony_ci	i = alen & ~0xf;
9562306a36Sopenharmony_ci	if (i) {
9662306a36Sopenharmony_ci		gcm_ghash_p10(nXi, hash->Htable+32, aad, i);
9762306a36Sopenharmony_ci		aad += i;
9862306a36Sopenharmony_ci		alen -= i;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	if (alen) {
10162306a36Sopenharmony_ci		for (i = 0; i < alen; i++)
10262306a36Sopenharmony_ci			nXi[i] ^= aad[i];
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		memset(gctx->aad_hash, 0, 16);
10562306a36Sopenharmony_ci		gcm_ghash_p10(gctx->aad_hash, hash->Htable+32, nXi, 16);
10662306a36Sopenharmony_ci	} else {
10762306a36Sopenharmony_ci		memcpy(gctx->aad_hash, nXi, 16);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	memcpy(hash->Htable, gctx->aad_hash, 16);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void gcmp10_init(struct gcm_ctx *gctx, u8 *iv, unsigned char *rdkey,
11462306a36Sopenharmony_ci			struct Hash_ctx *hash, u8 *assoc, unsigned int assoclen)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	__be32 counter = cpu_to_be32(1);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	aes_p10_encrypt(hash->H, hash->H, rdkey);
11962306a36Sopenharmony_ci	set_subkey(hash->H);
12062306a36Sopenharmony_ci	gcm_init_htable(hash->Htable+32, hash->H);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	*((__be32 *)(iv+12)) = counter;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	gctx->Plen = 0;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/*
12762306a36Sopenharmony_ci	 * Encrypt counter vector as iv tag and increment counter.
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	aes_p10_encrypt(iv, gctx->ivtag, rdkey);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	counter = cpu_to_be32(2);
13262306a36Sopenharmony_ci	*((__be32 *)(iv+12)) = counter;
13362306a36Sopenharmony_ci	memcpy(gctx->iv, iv, 16);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	gctx->aadLen = assoclen;
13662306a36Sopenharmony_ci	memset(gctx->aad_hash, 0, 16);
13762306a36Sopenharmony_ci	if (assoclen)
13862306a36Sopenharmony_ci		set_aad(gctx, hash, assoc, assoclen);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void finish_tag(struct gcm_ctx *gctx, struct Hash_ctx *hash, int len)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	int i;
14462306a36Sopenharmony_ci	unsigned char len_ac[16 + PPC_ALIGN];
14562306a36Sopenharmony_ci	unsigned char *aclen = PTR_ALIGN((void *)len_ac, PPC_ALIGN);
14662306a36Sopenharmony_ci	__be64 clen = cpu_to_be64(len << 3);
14762306a36Sopenharmony_ci	__be64 alen = cpu_to_be64(gctx->aadLen << 3);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (len == 0 && gctx->aadLen == 0) {
15062306a36Sopenharmony_ci		memcpy(hash->Htable, gctx->ivtag, 16);
15162306a36Sopenharmony_ci		return;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/*
15562306a36Sopenharmony_ci	 * Len is in bits.
15662306a36Sopenharmony_ci	 */
15762306a36Sopenharmony_ci	*((__be64 *)(aclen)) = alen;
15862306a36Sopenharmony_ci	*((__be64 *)(aclen+8)) = clen;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * hash (AAD len and len)
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	gcm_ghash_p10(hash->Htable, hash->Htable+32, aclen, 16);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
16662306a36Sopenharmony_ci		hash->Htable[i] ^= gctx->ivtag[i];
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int set_authsize(struct crypto_aead *tfm, unsigned int authsize)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	switch (authsize) {
17262306a36Sopenharmony_ci	case 4:
17362306a36Sopenharmony_ci	case 8:
17462306a36Sopenharmony_ci	case 12:
17562306a36Sopenharmony_ci	case 13:
17662306a36Sopenharmony_ci	case 14:
17762306a36Sopenharmony_ci	case 15:
17862306a36Sopenharmony_ci	case 16:
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	default:
18162306a36Sopenharmony_ci		return -EINVAL;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int p10_aes_gcm_setkey(struct crypto_aead *aead, const u8 *key,
18862306a36Sopenharmony_ci			     unsigned int keylen)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct crypto_tfm *tfm = crypto_aead_tfm(aead);
19162306a36Sopenharmony_ci	struct p10_aes_gcm_ctx *ctx = crypto_tfm_ctx(tfm);
19262306a36Sopenharmony_ci	int ret;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	vsx_begin();
19562306a36Sopenharmony_ci	ret = aes_p10_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
19662306a36Sopenharmony_ci	vsx_end();
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return ret ? -EINVAL : 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int p10_aes_gcm_crypt(struct aead_request *req, int enc)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct crypto_tfm *tfm = req->base.tfm;
20462306a36Sopenharmony_ci	struct p10_aes_gcm_ctx *ctx = crypto_tfm_ctx(tfm);
20562306a36Sopenharmony_ci	u8 databuf[sizeof(struct gcm_ctx) + PPC_ALIGN];
20662306a36Sopenharmony_ci	struct gcm_ctx *gctx = PTR_ALIGN((void *)databuf, PPC_ALIGN);
20762306a36Sopenharmony_ci	u8 hashbuf[sizeof(struct Hash_ctx) + PPC_ALIGN];
20862306a36Sopenharmony_ci	struct Hash_ctx *hash = PTR_ALIGN((void *)hashbuf, PPC_ALIGN);
20962306a36Sopenharmony_ci	struct scatter_walk assoc_sg_walk;
21062306a36Sopenharmony_ci	struct skcipher_walk walk;
21162306a36Sopenharmony_ci	u8 *assocmem = NULL;
21262306a36Sopenharmony_ci	u8 *assoc;
21362306a36Sopenharmony_ci	unsigned int assoclen = req->assoclen;
21462306a36Sopenharmony_ci	unsigned int cryptlen = req->cryptlen;
21562306a36Sopenharmony_ci	unsigned char ivbuf[AES_BLOCK_SIZE+PPC_ALIGN];
21662306a36Sopenharmony_ci	unsigned char *iv = PTR_ALIGN((void *)ivbuf, PPC_ALIGN);
21762306a36Sopenharmony_ci	int ret;
21862306a36Sopenharmony_ci	unsigned long auth_tag_len = crypto_aead_authsize(__crypto_aead_cast(tfm));
21962306a36Sopenharmony_ci	u8 otag[16];
22062306a36Sopenharmony_ci	int total_processed = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	memset(databuf, 0, sizeof(databuf));
22362306a36Sopenharmony_ci	memset(hashbuf, 0, sizeof(hashbuf));
22462306a36Sopenharmony_ci	memset(ivbuf, 0, sizeof(ivbuf));
22562306a36Sopenharmony_ci	memcpy(iv, req->iv, GCM_IV_SIZE);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Linearize assoc, if not already linear */
22862306a36Sopenharmony_ci	if (req->src->length >= assoclen && req->src->length) {
22962306a36Sopenharmony_ci		scatterwalk_start(&assoc_sg_walk, req->src);
23062306a36Sopenharmony_ci		assoc = scatterwalk_map(&assoc_sg_walk);
23162306a36Sopenharmony_ci	} else {
23262306a36Sopenharmony_ci		gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
23362306a36Sopenharmony_ci			      GFP_KERNEL : GFP_ATOMIC;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		/* assoc can be any length, so must be on heap */
23662306a36Sopenharmony_ci		assocmem = kmalloc(assoclen, flags);
23762306a36Sopenharmony_ci		if (unlikely(!assocmem))
23862306a36Sopenharmony_ci			return -ENOMEM;
23962306a36Sopenharmony_ci		assoc = assocmem;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		scatterwalk_map_and_copy(assoc, req->src, 0, assoclen, 0);
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	vsx_begin();
24562306a36Sopenharmony_ci	gcmp10_init(gctx, iv, (unsigned char *) &ctx->enc_key, hash, assoc, assoclen);
24662306a36Sopenharmony_ci	vsx_end();
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!assocmem)
24962306a36Sopenharmony_ci		scatterwalk_unmap(assoc);
25062306a36Sopenharmony_ci	else
25162306a36Sopenharmony_ci		kfree(assocmem);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (enc)
25462306a36Sopenharmony_ci		ret = skcipher_walk_aead_encrypt(&walk, req, false);
25562306a36Sopenharmony_ci	else
25662306a36Sopenharmony_ci		ret = skcipher_walk_aead_decrypt(&walk, req, false);
25762306a36Sopenharmony_ci	if (ret)
25862306a36Sopenharmony_ci		return ret;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	while (walk.nbytes > 0 && ret == 0) {
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		vsx_begin();
26362306a36Sopenharmony_ci		if (enc)
26462306a36Sopenharmony_ci			aes_p10_gcm_encrypt(walk.src.virt.addr,
26562306a36Sopenharmony_ci					    walk.dst.virt.addr,
26662306a36Sopenharmony_ci					    walk.nbytes,
26762306a36Sopenharmony_ci					    &ctx->enc_key, gctx->iv, hash->Htable);
26862306a36Sopenharmony_ci		else
26962306a36Sopenharmony_ci			aes_p10_gcm_decrypt(walk.src.virt.addr,
27062306a36Sopenharmony_ci					    walk.dst.virt.addr,
27162306a36Sopenharmony_ci					    walk.nbytes,
27262306a36Sopenharmony_ci					    &ctx->enc_key, gctx->iv, hash->Htable);
27362306a36Sopenharmony_ci		vsx_end();
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		total_processed += walk.nbytes;
27662306a36Sopenharmony_ci		ret = skcipher_walk_done(&walk, 0);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (ret)
28062306a36Sopenharmony_ci		return ret;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Finalize hash */
28362306a36Sopenharmony_ci	vsx_begin();
28462306a36Sopenharmony_ci	finish_tag(gctx, hash, total_processed);
28562306a36Sopenharmony_ci	vsx_end();
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* copy Xi to end of dst */
28862306a36Sopenharmony_ci	if (enc)
28962306a36Sopenharmony_ci		scatterwalk_map_and_copy(hash->Htable, req->dst, req->assoclen + cryptlen,
29062306a36Sopenharmony_ci					 auth_tag_len, 1);
29162306a36Sopenharmony_ci	else {
29262306a36Sopenharmony_ci		scatterwalk_map_and_copy(otag, req->src,
29362306a36Sopenharmony_ci					 req->assoclen + cryptlen - auth_tag_len,
29462306a36Sopenharmony_ci					 auth_tag_len, 0);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		if (crypto_memneq(otag, hash->Htable, auth_tag_len)) {
29762306a36Sopenharmony_ci			memzero_explicit(hash->Htable, 16);
29862306a36Sopenharmony_ci			return -EBADMSG;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int p10_aes_gcm_encrypt(struct aead_request *req)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	return p10_aes_gcm_crypt(req, 1);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic int p10_aes_gcm_decrypt(struct aead_request *req)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	return p10_aes_gcm_crypt(req, 0);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct aead_alg gcm_aes_alg = {
31662306a36Sopenharmony_ci	.ivsize			= GCM_IV_SIZE,
31762306a36Sopenharmony_ci	.maxauthsize		= 16,
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	.setauthsize		= set_authsize,
32062306a36Sopenharmony_ci	.setkey			= p10_aes_gcm_setkey,
32162306a36Sopenharmony_ci	.encrypt		= p10_aes_gcm_encrypt,
32262306a36Sopenharmony_ci	.decrypt		= p10_aes_gcm_decrypt,
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	.base.cra_name		= "gcm(aes)",
32562306a36Sopenharmony_ci	.base.cra_driver_name	= "aes_gcm_p10",
32662306a36Sopenharmony_ci	.base.cra_priority	= 2100,
32762306a36Sopenharmony_ci	.base.cra_blocksize	= 1,
32862306a36Sopenharmony_ci	.base.cra_ctxsize	= sizeof(struct p10_aes_gcm_ctx),
32962306a36Sopenharmony_ci	.base.cra_module	= THIS_MODULE,
33062306a36Sopenharmony_ci};
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int __init p10_init(void)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	return crypto_register_aead(&gcm_aes_alg);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void __exit p10_exit(void)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	crypto_unregister_aead(&gcm_aes_alg);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cimodule_cpu_feature_match(PPC_MODULE_FEATURE_P10, p10_init);
34362306a36Sopenharmony_cimodule_exit(p10_exit);
344