162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Accelerated GHASH implementation with ARMv8 PMULL instructions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 - 2018 Linaro Ltd. <ard.biesheuvel@linaro.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/neon.h> 962306a36Sopenharmony_ci#include <asm/simd.h> 1062306a36Sopenharmony_ci#include <asm/unaligned.h> 1162306a36Sopenharmony_ci#include <crypto/aes.h> 1262306a36Sopenharmony_ci#include <crypto/gcm.h> 1362306a36Sopenharmony_ci#include <crypto/algapi.h> 1462306a36Sopenharmony_ci#include <crypto/b128ops.h> 1562306a36Sopenharmony_ci#include <crypto/gf128mul.h> 1662306a36Sopenharmony_ci#include <crypto/internal/aead.h> 1762306a36Sopenharmony_ci#include <crypto/internal/hash.h> 1862306a36Sopenharmony_ci#include <crypto/internal/simd.h> 1962306a36Sopenharmony_ci#include <crypto/internal/skcipher.h> 2062306a36Sopenharmony_ci#include <crypto/scatterwalk.h> 2162306a36Sopenharmony_ci#include <linux/cpufeature.h> 2262306a36Sopenharmony_ci#include <linux/crypto.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciMODULE_DESCRIPTION("GHASH and AES-GCM using ARMv8 Crypto Extensions"); 2662306a36Sopenharmony_ciMODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); 2762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ghash"); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define GHASH_BLOCK_SIZE 16 3162306a36Sopenharmony_ci#define GHASH_DIGEST_SIZE 16 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define RFC4106_NONCE_SIZE 4 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct ghash_key { 3662306a36Sopenharmony_ci be128 k; 3762306a36Sopenharmony_ci u64 h[][2]; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct ghash_desc_ctx { 4162306a36Sopenharmony_ci u64 digest[GHASH_DIGEST_SIZE/sizeof(u64)]; 4262306a36Sopenharmony_ci u8 buf[GHASH_BLOCK_SIZE]; 4362306a36Sopenharmony_ci u32 count; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct gcm_aes_ctx { 4762306a36Sopenharmony_ci struct crypto_aes_ctx aes_key; 4862306a36Sopenharmony_ci u8 nonce[RFC4106_NONCE_SIZE]; 4962306a36Sopenharmony_ci struct ghash_key ghash_key; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciasmlinkage void pmull_ghash_update_p64(int blocks, u64 dg[], const char *src, 5362306a36Sopenharmony_ci u64 const h[][2], const char *head); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciasmlinkage void pmull_ghash_update_p8(int blocks, u64 dg[], const char *src, 5662306a36Sopenharmony_ci u64 const h[][2], const char *head); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciasmlinkage void pmull_gcm_encrypt(int bytes, u8 dst[], const u8 src[], 5962306a36Sopenharmony_ci u64 const h[][2], u64 dg[], u8 ctr[], 6062306a36Sopenharmony_ci u32 const rk[], int rounds, u8 tag[]); 6162306a36Sopenharmony_ciasmlinkage int pmull_gcm_decrypt(int bytes, u8 dst[], const u8 src[], 6262306a36Sopenharmony_ci u64 const h[][2], u64 dg[], u8 ctr[], 6362306a36Sopenharmony_ci u32 const rk[], int rounds, const u8 l[], 6462306a36Sopenharmony_ci const u8 tag[], u64 authsize); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int ghash_init(struct shash_desc *desc) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci *ctx = (struct ghash_desc_ctx){}; 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void ghash_do_update(int blocks, u64 dg[], const char *src, 7562306a36Sopenharmony_ci struct ghash_key *key, const char *head) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci be128 dst = { cpu_to_be64(dg[1]), cpu_to_be64(dg[0]) }; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci do { 8062306a36Sopenharmony_ci const u8 *in = src; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (head) { 8362306a36Sopenharmony_ci in = head; 8462306a36Sopenharmony_ci blocks++; 8562306a36Sopenharmony_ci head = NULL; 8662306a36Sopenharmony_ci } else { 8762306a36Sopenharmony_ci src += GHASH_BLOCK_SIZE; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci crypto_xor((u8 *)&dst, in, GHASH_BLOCK_SIZE); 9162306a36Sopenharmony_ci gf128mul_lle(&dst, &key->k); 9262306a36Sopenharmony_ci } while (--blocks); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci dg[0] = be64_to_cpu(dst.b); 9562306a36Sopenharmony_ci dg[1] = be64_to_cpu(dst.a); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic __always_inline 9962306a36Sopenharmony_civoid ghash_do_simd_update(int blocks, u64 dg[], const char *src, 10062306a36Sopenharmony_ci struct ghash_key *key, const char *head, 10162306a36Sopenharmony_ci void (*simd_update)(int blocks, u64 dg[], 10262306a36Sopenharmony_ci const char *src, 10362306a36Sopenharmony_ci u64 const h[][2], 10462306a36Sopenharmony_ci const char *head)) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci if (likely(crypto_simd_usable())) { 10762306a36Sopenharmony_ci kernel_neon_begin(); 10862306a36Sopenharmony_ci simd_update(blocks, dg, src, key->h, head); 10962306a36Sopenharmony_ci kernel_neon_end(); 11062306a36Sopenharmony_ci } else { 11162306a36Sopenharmony_ci ghash_do_update(blocks, dg, src, key, head); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* avoid hogging the CPU for too long */ 11662306a36Sopenharmony_ci#define MAX_BLOCKS (SZ_64K / GHASH_BLOCK_SIZE) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int ghash_update(struct shash_desc *desc, const u8 *src, 11962306a36Sopenharmony_ci unsigned int len) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); 12262306a36Sopenharmony_ci unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ctx->count += len; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if ((partial + len) >= GHASH_BLOCK_SIZE) { 12762306a36Sopenharmony_ci struct ghash_key *key = crypto_shash_ctx(desc->tfm); 12862306a36Sopenharmony_ci int blocks; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (partial) { 13162306a36Sopenharmony_ci int p = GHASH_BLOCK_SIZE - partial; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci memcpy(ctx->buf + partial, src, p); 13462306a36Sopenharmony_ci src += p; 13562306a36Sopenharmony_ci len -= p; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci blocks = len / GHASH_BLOCK_SIZE; 13962306a36Sopenharmony_ci len %= GHASH_BLOCK_SIZE; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci do { 14262306a36Sopenharmony_ci int chunk = min(blocks, MAX_BLOCKS); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ghash_do_simd_update(chunk, ctx->digest, src, key, 14562306a36Sopenharmony_ci partial ? ctx->buf : NULL, 14662306a36Sopenharmony_ci pmull_ghash_update_p8); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci blocks -= chunk; 14962306a36Sopenharmony_ci src += chunk * GHASH_BLOCK_SIZE; 15062306a36Sopenharmony_ci partial = 0; 15162306a36Sopenharmony_ci } while (unlikely(blocks > 0)); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci if (len) 15462306a36Sopenharmony_ci memcpy(ctx->buf + partial, src, len); 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int ghash_final(struct shash_desc *desc, u8 *dst) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); 16162306a36Sopenharmony_ci unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (partial) { 16462306a36Sopenharmony_ci struct ghash_key *key = crypto_shash_ctx(desc->tfm); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci memset(ctx->buf + partial, 0, GHASH_BLOCK_SIZE - partial); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ghash_do_simd_update(1, ctx->digest, ctx->buf, key, NULL, 16962306a36Sopenharmony_ci pmull_ghash_update_p8); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci put_unaligned_be64(ctx->digest[1], dst); 17262306a36Sopenharmony_ci put_unaligned_be64(ctx->digest[0], dst + 8); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci memzero_explicit(ctx, sizeof(*ctx)); 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void ghash_reflect(u64 h[], const be128 *k) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci u64 carry = be64_to_cpu(k->a) & BIT(63) ? 1 : 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci h[0] = (be64_to_cpu(k->b) << 1) | carry; 18362306a36Sopenharmony_ci h[1] = (be64_to_cpu(k->a) << 1) | (be64_to_cpu(k->b) >> 63); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (carry) 18662306a36Sopenharmony_ci h[1] ^= 0xc200000000000000UL; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int ghash_setkey(struct crypto_shash *tfm, 19062306a36Sopenharmony_ci const u8 *inkey, unsigned int keylen) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ghash_key *key = crypto_shash_ctx(tfm); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (keylen != GHASH_BLOCK_SIZE) 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* needed for the fallback */ 19862306a36Sopenharmony_ci memcpy(&key->k, inkey, GHASH_BLOCK_SIZE); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ghash_reflect(key->h[0], &key->k); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct shash_alg ghash_alg = { 20562306a36Sopenharmony_ci .base.cra_name = "ghash", 20662306a36Sopenharmony_ci .base.cra_driver_name = "ghash-neon", 20762306a36Sopenharmony_ci .base.cra_priority = 150, 20862306a36Sopenharmony_ci .base.cra_blocksize = GHASH_BLOCK_SIZE, 20962306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(struct ghash_key) + sizeof(u64[2]), 21062306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci .digestsize = GHASH_DIGEST_SIZE, 21362306a36Sopenharmony_ci .init = ghash_init, 21462306a36Sopenharmony_ci .update = ghash_update, 21562306a36Sopenharmony_ci .final = ghash_final, 21662306a36Sopenharmony_ci .setkey = ghash_setkey, 21762306a36Sopenharmony_ci .descsize = sizeof(struct ghash_desc_ctx), 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int num_rounds(struct crypto_aes_ctx *ctx) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * # of rounds specified by AES: 22462306a36Sopenharmony_ci * 128 bit key 10 rounds 22562306a36Sopenharmony_ci * 192 bit key 12 rounds 22662306a36Sopenharmony_ci * 256 bit key 14 rounds 22762306a36Sopenharmony_ci * => n byte key => 6 + (n/4) rounds 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci return 6 + ctx->key_length / 4; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int gcm_aes_setkey(struct crypto_aead *tfm, const u8 *inkey, 23362306a36Sopenharmony_ci unsigned int keylen) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct gcm_aes_ctx *ctx = crypto_aead_ctx(tfm); 23662306a36Sopenharmony_ci u8 key[GHASH_BLOCK_SIZE]; 23762306a36Sopenharmony_ci be128 h; 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = aes_expandkey(&ctx->aes_key, inkey, keylen); 24162306a36Sopenharmony_ci if (ret) 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci aes_encrypt(&ctx->aes_key, key, (u8[AES_BLOCK_SIZE]){}); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* needed for the fallback */ 24762306a36Sopenharmony_ci memcpy(&ctx->ghash_key.k, key, GHASH_BLOCK_SIZE); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ghash_reflect(ctx->ghash_key.h[0], &ctx->ghash_key.k); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci h = ctx->ghash_key.k; 25262306a36Sopenharmony_ci gf128mul_lle(&h, &ctx->ghash_key.k); 25362306a36Sopenharmony_ci ghash_reflect(ctx->ghash_key.h[1], &h); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci gf128mul_lle(&h, &ctx->ghash_key.k); 25662306a36Sopenharmony_ci ghash_reflect(ctx->ghash_key.h[2], &h); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci gf128mul_lle(&h, &ctx->ghash_key.k); 25962306a36Sopenharmony_ci ghash_reflect(ctx->ghash_key.h[3], &h); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int gcm_aes_setauthsize(struct crypto_aead *tfm, unsigned int authsize) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return crypto_gcm_check_authsize(authsize); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void gcm_update_mac(u64 dg[], const u8 *src, int count, u8 buf[], 27062306a36Sopenharmony_ci int *buf_count, struct gcm_aes_ctx *ctx) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci if (*buf_count > 0) { 27362306a36Sopenharmony_ci int buf_added = min(count, GHASH_BLOCK_SIZE - *buf_count); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci memcpy(&buf[*buf_count], src, buf_added); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci *buf_count += buf_added; 27862306a36Sopenharmony_ci src += buf_added; 27962306a36Sopenharmony_ci count -= buf_added; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (count >= GHASH_BLOCK_SIZE || *buf_count == GHASH_BLOCK_SIZE) { 28362306a36Sopenharmony_ci int blocks = count / GHASH_BLOCK_SIZE; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ghash_do_simd_update(blocks, dg, src, &ctx->ghash_key, 28662306a36Sopenharmony_ci *buf_count ? buf : NULL, 28762306a36Sopenharmony_ci pmull_ghash_update_p64); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci src += blocks * GHASH_BLOCK_SIZE; 29062306a36Sopenharmony_ci count %= GHASH_BLOCK_SIZE; 29162306a36Sopenharmony_ci *buf_count = 0; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (count > 0) { 29562306a36Sopenharmony_ci memcpy(buf, src, count); 29662306a36Sopenharmony_ci *buf_count = count; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void gcm_calculate_auth_mac(struct aead_request *req, u64 dg[], u32 len) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct crypto_aead *aead = crypto_aead_reqtfm(req); 30362306a36Sopenharmony_ci struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead); 30462306a36Sopenharmony_ci u8 buf[GHASH_BLOCK_SIZE]; 30562306a36Sopenharmony_ci struct scatter_walk walk; 30662306a36Sopenharmony_ci int buf_count = 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci scatterwalk_start(&walk, req->src); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci do { 31162306a36Sopenharmony_ci u32 n = scatterwalk_clamp(&walk, len); 31262306a36Sopenharmony_ci u8 *p; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!n) { 31562306a36Sopenharmony_ci scatterwalk_start(&walk, sg_next(walk.sg)); 31662306a36Sopenharmony_ci n = scatterwalk_clamp(&walk, len); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci p = scatterwalk_map(&walk); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci gcm_update_mac(dg, p, n, buf, &buf_count, ctx); 32162306a36Sopenharmony_ci len -= n; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci scatterwalk_unmap(p); 32462306a36Sopenharmony_ci scatterwalk_advance(&walk, n); 32562306a36Sopenharmony_ci scatterwalk_done(&walk, 0, len); 32662306a36Sopenharmony_ci } while (len); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (buf_count) { 32962306a36Sopenharmony_ci memset(&buf[buf_count], 0, GHASH_BLOCK_SIZE - buf_count); 33062306a36Sopenharmony_ci ghash_do_simd_update(1, dg, buf, &ctx->ghash_key, NULL, 33162306a36Sopenharmony_ci pmull_ghash_update_p64); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int gcm_encrypt(struct aead_request *req, char *iv, int assoclen) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct crypto_aead *aead = crypto_aead_reqtfm(req); 33862306a36Sopenharmony_ci struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead); 33962306a36Sopenharmony_ci int nrounds = num_rounds(&ctx->aes_key); 34062306a36Sopenharmony_ci struct skcipher_walk walk; 34162306a36Sopenharmony_ci u8 buf[AES_BLOCK_SIZE]; 34262306a36Sopenharmony_ci u64 dg[2] = {}; 34362306a36Sopenharmony_ci be128 lengths; 34462306a36Sopenharmony_ci u8 *tag; 34562306a36Sopenharmony_ci int err; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci lengths.a = cpu_to_be64(assoclen * 8); 34862306a36Sopenharmony_ci lengths.b = cpu_to_be64(req->cryptlen * 8); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (assoclen) 35162306a36Sopenharmony_ci gcm_calculate_auth_mac(req, dg, assoclen); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci put_unaligned_be32(2, iv + GCM_AES_IV_SIZE); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci err = skcipher_walk_aead_encrypt(&walk, req, false); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci do { 35862306a36Sopenharmony_ci const u8 *src = walk.src.virt.addr; 35962306a36Sopenharmony_ci u8 *dst = walk.dst.virt.addr; 36062306a36Sopenharmony_ci int nbytes = walk.nbytes; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci tag = (u8 *)&lengths; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE)) { 36562306a36Sopenharmony_ci src = dst = memcpy(buf + sizeof(buf) - nbytes, 36662306a36Sopenharmony_ci src, nbytes); 36762306a36Sopenharmony_ci } else if (nbytes < walk.total) { 36862306a36Sopenharmony_ci nbytes &= ~(AES_BLOCK_SIZE - 1); 36962306a36Sopenharmony_ci tag = NULL; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci kernel_neon_begin(); 37362306a36Sopenharmony_ci pmull_gcm_encrypt(nbytes, dst, src, ctx->ghash_key.h, 37462306a36Sopenharmony_ci dg, iv, ctx->aes_key.key_enc, nrounds, 37562306a36Sopenharmony_ci tag); 37662306a36Sopenharmony_ci kernel_neon_end(); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (unlikely(!nbytes)) 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE)) 38262306a36Sopenharmony_ci memcpy(walk.dst.virt.addr, 38362306a36Sopenharmony_ci buf + sizeof(buf) - nbytes, nbytes); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci err = skcipher_walk_done(&walk, walk.nbytes - nbytes); 38662306a36Sopenharmony_ci } while (walk.nbytes); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (err) 38962306a36Sopenharmony_ci return err; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* copy authtag to end of dst */ 39262306a36Sopenharmony_ci scatterwalk_map_and_copy(tag, req->dst, req->assoclen + req->cryptlen, 39362306a36Sopenharmony_ci crypto_aead_authsize(aead), 1); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int gcm_decrypt(struct aead_request *req, char *iv, int assoclen) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct crypto_aead *aead = crypto_aead_reqtfm(req); 40162306a36Sopenharmony_ci struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead); 40262306a36Sopenharmony_ci unsigned int authsize = crypto_aead_authsize(aead); 40362306a36Sopenharmony_ci int nrounds = num_rounds(&ctx->aes_key); 40462306a36Sopenharmony_ci struct skcipher_walk walk; 40562306a36Sopenharmony_ci u8 otag[AES_BLOCK_SIZE]; 40662306a36Sopenharmony_ci u8 buf[AES_BLOCK_SIZE]; 40762306a36Sopenharmony_ci u64 dg[2] = {}; 40862306a36Sopenharmony_ci be128 lengths; 40962306a36Sopenharmony_ci u8 *tag; 41062306a36Sopenharmony_ci int ret; 41162306a36Sopenharmony_ci int err; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci lengths.a = cpu_to_be64(assoclen * 8); 41462306a36Sopenharmony_ci lengths.b = cpu_to_be64((req->cryptlen - authsize) * 8); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (assoclen) 41762306a36Sopenharmony_ci gcm_calculate_auth_mac(req, dg, assoclen); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci put_unaligned_be32(2, iv + GCM_AES_IV_SIZE); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci scatterwalk_map_and_copy(otag, req->src, 42262306a36Sopenharmony_ci req->assoclen + req->cryptlen - authsize, 42362306a36Sopenharmony_ci authsize, 0); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci err = skcipher_walk_aead_decrypt(&walk, req, false); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci do { 42862306a36Sopenharmony_ci const u8 *src = walk.src.virt.addr; 42962306a36Sopenharmony_ci u8 *dst = walk.dst.virt.addr; 43062306a36Sopenharmony_ci int nbytes = walk.nbytes; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci tag = (u8 *)&lengths; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE)) { 43562306a36Sopenharmony_ci src = dst = memcpy(buf + sizeof(buf) - nbytes, 43662306a36Sopenharmony_ci src, nbytes); 43762306a36Sopenharmony_ci } else if (nbytes < walk.total) { 43862306a36Sopenharmony_ci nbytes &= ~(AES_BLOCK_SIZE - 1); 43962306a36Sopenharmony_ci tag = NULL; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci kernel_neon_begin(); 44362306a36Sopenharmony_ci ret = pmull_gcm_decrypt(nbytes, dst, src, ctx->ghash_key.h, 44462306a36Sopenharmony_ci dg, iv, ctx->aes_key.key_enc, 44562306a36Sopenharmony_ci nrounds, tag, otag, authsize); 44662306a36Sopenharmony_ci kernel_neon_end(); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (unlikely(!nbytes)) 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE)) 45262306a36Sopenharmony_ci memcpy(walk.dst.virt.addr, 45362306a36Sopenharmony_ci buf + sizeof(buf) - nbytes, nbytes); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci err = skcipher_walk_done(&walk, walk.nbytes - nbytes); 45662306a36Sopenharmony_ci } while (walk.nbytes); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (err) 45962306a36Sopenharmony_ci return err; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return ret ? -EBADMSG : 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int gcm_aes_encrypt(struct aead_request *req) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci u8 iv[AES_BLOCK_SIZE]; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci memcpy(iv, req->iv, GCM_AES_IV_SIZE); 46962306a36Sopenharmony_ci return gcm_encrypt(req, iv, req->assoclen); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int gcm_aes_decrypt(struct aead_request *req) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci u8 iv[AES_BLOCK_SIZE]; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci memcpy(iv, req->iv, GCM_AES_IV_SIZE); 47762306a36Sopenharmony_ci return gcm_decrypt(req, iv, req->assoclen); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int rfc4106_setkey(struct crypto_aead *tfm, const u8 *inkey, 48162306a36Sopenharmony_ci unsigned int keylen) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct gcm_aes_ctx *ctx = crypto_aead_ctx(tfm); 48462306a36Sopenharmony_ci int err; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci keylen -= RFC4106_NONCE_SIZE; 48762306a36Sopenharmony_ci err = gcm_aes_setkey(tfm, inkey, keylen); 48862306a36Sopenharmony_ci if (err) 48962306a36Sopenharmony_ci return err; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci memcpy(ctx->nonce, inkey + keylen, RFC4106_NONCE_SIZE); 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int rfc4106_setauthsize(struct crypto_aead *tfm, unsigned int authsize) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci return crypto_rfc4106_check_authsize(authsize); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int rfc4106_encrypt(struct aead_request *req) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct crypto_aead *aead = crypto_aead_reqtfm(req); 50362306a36Sopenharmony_ci struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead); 50462306a36Sopenharmony_ci u8 iv[AES_BLOCK_SIZE]; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci memcpy(iv, ctx->nonce, RFC4106_NONCE_SIZE); 50762306a36Sopenharmony_ci memcpy(iv + RFC4106_NONCE_SIZE, req->iv, GCM_RFC4106_IV_SIZE); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return crypto_ipsec_check_assoclen(req->assoclen) ?: 51062306a36Sopenharmony_ci gcm_encrypt(req, iv, req->assoclen - GCM_RFC4106_IV_SIZE); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int rfc4106_decrypt(struct aead_request *req) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct crypto_aead *aead = crypto_aead_reqtfm(req); 51662306a36Sopenharmony_ci struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead); 51762306a36Sopenharmony_ci u8 iv[AES_BLOCK_SIZE]; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci memcpy(iv, ctx->nonce, RFC4106_NONCE_SIZE); 52062306a36Sopenharmony_ci memcpy(iv + RFC4106_NONCE_SIZE, req->iv, GCM_RFC4106_IV_SIZE); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return crypto_ipsec_check_assoclen(req->assoclen) ?: 52362306a36Sopenharmony_ci gcm_decrypt(req, iv, req->assoclen - GCM_RFC4106_IV_SIZE); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic struct aead_alg gcm_aes_algs[] = {{ 52762306a36Sopenharmony_ci .ivsize = GCM_AES_IV_SIZE, 52862306a36Sopenharmony_ci .chunksize = AES_BLOCK_SIZE, 52962306a36Sopenharmony_ci .maxauthsize = AES_BLOCK_SIZE, 53062306a36Sopenharmony_ci .setkey = gcm_aes_setkey, 53162306a36Sopenharmony_ci .setauthsize = gcm_aes_setauthsize, 53262306a36Sopenharmony_ci .encrypt = gcm_aes_encrypt, 53362306a36Sopenharmony_ci .decrypt = gcm_aes_decrypt, 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci .base.cra_name = "gcm(aes)", 53662306a36Sopenharmony_ci .base.cra_driver_name = "gcm-aes-ce", 53762306a36Sopenharmony_ci .base.cra_priority = 300, 53862306a36Sopenharmony_ci .base.cra_blocksize = 1, 53962306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(struct gcm_aes_ctx) + 54062306a36Sopenharmony_ci 4 * sizeof(u64[2]), 54162306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 54262306a36Sopenharmony_ci}, { 54362306a36Sopenharmony_ci .ivsize = GCM_RFC4106_IV_SIZE, 54462306a36Sopenharmony_ci .chunksize = AES_BLOCK_SIZE, 54562306a36Sopenharmony_ci .maxauthsize = AES_BLOCK_SIZE, 54662306a36Sopenharmony_ci .setkey = rfc4106_setkey, 54762306a36Sopenharmony_ci .setauthsize = rfc4106_setauthsize, 54862306a36Sopenharmony_ci .encrypt = rfc4106_encrypt, 54962306a36Sopenharmony_ci .decrypt = rfc4106_decrypt, 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci .base.cra_name = "rfc4106(gcm(aes))", 55262306a36Sopenharmony_ci .base.cra_driver_name = "rfc4106-gcm-aes-ce", 55362306a36Sopenharmony_ci .base.cra_priority = 300, 55462306a36Sopenharmony_ci .base.cra_blocksize = 1, 55562306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(struct gcm_aes_ctx) + 55662306a36Sopenharmony_ci 4 * sizeof(u64[2]), 55762306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 55862306a36Sopenharmony_ci}}; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int __init ghash_ce_mod_init(void) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci if (!cpu_have_named_feature(ASIMD)) 56362306a36Sopenharmony_ci return -ENODEV; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (cpu_have_named_feature(PMULL)) 56662306a36Sopenharmony_ci return crypto_register_aeads(gcm_aes_algs, 56762306a36Sopenharmony_ci ARRAY_SIZE(gcm_aes_algs)); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return crypto_register_shash(&ghash_alg); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void __exit ghash_ce_mod_exit(void) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci if (cpu_have_named_feature(PMULL)) 57562306a36Sopenharmony_ci crypto_unregister_aeads(gcm_aes_algs, ARRAY_SIZE(gcm_aes_algs)); 57662306a36Sopenharmony_ci else 57762306a36Sopenharmony_ci crypto_unregister_shash(&ghash_alg); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic const struct cpu_feature __maybe_unused ghash_cpu_feature[] = { 58162306a36Sopenharmony_ci { cpu_feature(PMULL) }, { } 58262306a36Sopenharmony_ci}; 58362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(cpu, ghash_cpu_feature); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cimodule_init(ghash_ce_mod_init); 58662306a36Sopenharmony_cimodule_exit(ghash_ce_mod_exit); 587