162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Glue code for POLYVAL using ARMv8 Crypto Extensions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2007 Nokia Siemens Networks - Mikko Herranen <mh1@iki.fi>
662306a36Sopenharmony_ci * Copyright (c) 2009 Intel Corp.
762306a36Sopenharmony_ci *   Author: Huang Ying <ying.huang@intel.com>
862306a36Sopenharmony_ci * Copyright 2021 Google LLC
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Glue code based on ghash-clmulni-intel_glue.c.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * This implementation of POLYVAL uses montgomery multiplication accelerated by
1562306a36Sopenharmony_ci * ARMv8 Crypto Extensions instructions to implement the finite field operations.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <crypto/algapi.h>
1962306a36Sopenharmony_ci#include <crypto/internal/hash.h>
2062306a36Sopenharmony_ci#include <crypto/internal/simd.h>
2162306a36Sopenharmony_ci#include <crypto/polyval.h>
2262306a36Sopenharmony_ci#include <linux/crypto.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/module.h>
2662306a36Sopenharmony_ci#include <linux/cpufeature.h>
2762306a36Sopenharmony_ci#include <asm/neon.h>
2862306a36Sopenharmony_ci#include <asm/simd.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define NUM_KEY_POWERS	8
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct polyval_tfm_ctx {
3362306a36Sopenharmony_ci	/*
3462306a36Sopenharmony_ci	 * These powers must be in the order h^8, ..., h^1.
3562306a36Sopenharmony_ci	 */
3662306a36Sopenharmony_ci	u8 key_powers[NUM_KEY_POWERS][POLYVAL_BLOCK_SIZE];
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct polyval_desc_ctx {
4062306a36Sopenharmony_ci	u8 buffer[POLYVAL_BLOCK_SIZE];
4162306a36Sopenharmony_ci	u32 bytes;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciasmlinkage void pmull_polyval_update(const struct polyval_tfm_ctx *keys,
4562306a36Sopenharmony_ci	const u8 *in, size_t nblocks, u8 *accumulator);
4662306a36Sopenharmony_ciasmlinkage void pmull_polyval_mul(u8 *op1, const u8 *op2);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void internal_polyval_update(const struct polyval_tfm_ctx *keys,
4962306a36Sopenharmony_ci	const u8 *in, size_t nblocks, u8 *accumulator)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if (likely(crypto_simd_usable())) {
5262306a36Sopenharmony_ci		kernel_neon_begin();
5362306a36Sopenharmony_ci		pmull_polyval_update(keys, in, nblocks, accumulator);
5462306a36Sopenharmony_ci		kernel_neon_end();
5562306a36Sopenharmony_ci	} else {
5662306a36Sopenharmony_ci		polyval_update_non4k(keys->key_powers[NUM_KEY_POWERS-1], in,
5762306a36Sopenharmony_ci			nblocks, accumulator);
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void internal_polyval_mul(u8 *op1, const u8 *op2)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	if (likely(crypto_simd_usable())) {
6462306a36Sopenharmony_ci		kernel_neon_begin();
6562306a36Sopenharmony_ci		pmull_polyval_mul(op1, op2);
6662306a36Sopenharmony_ci		kernel_neon_end();
6762306a36Sopenharmony_ci	} else {
6862306a36Sopenharmony_ci		polyval_mul_non4k(op1, op2);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int polyval_arm64_setkey(struct crypto_shash *tfm,
7362306a36Sopenharmony_ci			const u8 *key, unsigned int keylen)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct polyval_tfm_ctx *tctx = crypto_shash_ctx(tfm);
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (keylen != POLYVAL_BLOCK_SIZE)
7962306a36Sopenharmony_ci		return -EINVAL;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	memcpy(tctx->key_powers[NUM_KEY_POWERS-1], key, POLYVAL_BLOCK_SIZE);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	for (i = NUM_KEY_POWERS-2; i >= 0; i--) {
8462306a36Sopenharmony_ci		memcpy(tctx->key_powers[i], key, POLYVAL_BLOCK_SIZE);
8562306a36Sopenharmony_ci		internal_polyval_mul(tctx->key_powers[i],
8662306a36Sopenharmony_ci				     tctx->key_powers[i+1]);
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int polyval_arm64_init(struct shash_desc *desc)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	memset(dctx, 0, sizeof(*dctx));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int polyval_arm64_update(struct shash_desc *desc,
10262306a36Sopenharmony_ci			 const u8 *src, unsigned int srclen)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
10562306a36Sopenharmony_ci	const struct polyval_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
10662306a36Sopenharmony_ci	u8 *pos;
10762306a36Sopenharmony_ci	unsigned int nblocks;
10862306a36Sopenharmony_ci	unsigned int n;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (dctx->bytes) {
11162306a36Sopenharmony_ci		n = min(srclen, dctx->bytes);
11262306a36Sopenharmony_ci		pos = dctx->buffer + POLYVAL_BLOCK_SIZE - dctx->bytes;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		dctx->bytes -= n;
11562306a36Sopenharmony_ci		srclen -= n;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		while (n--)
11862306a36Sopenharmony_ci			*pos++ ^= *src++;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		if (!dctx->bytes)
12162306a36Sopenharmony_ci			internal_polyval_mul(dctx->buffer,
12262306a36Sopenharmony_ci					    tctx->key_powers[NUM_KEY_POWERS-1]);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	while (srclen >= POLYVAL_BLOCK_SIZE) {
12662306a36Sopenharmony_ci		/* allow rescheduling every 4K bytes */
12762306a36Sopenharmony_ci		nblocks = min(srclen, 4096U) / POLYVAL_BLOCK_SIZE;
12862306a36Sopenharmony_ci		internal_polyval_update(tctx, src, nblocks, dctx->buffer);
12962306a36Sopenharmony_ci		srclen -= nblocks * POLYVAL_BLOCK_SIZE;
13062306a36Sopenharmony_ci		src += nblocks * POLYVAL_BLOCK_SIZE;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (srclen) {
13462306a36Sopenharmony_ci		dctx->bytes = POLYVAL_BLOCK_SIZE - srclen;
13562306a36Sopenharmony_ci		pos = dctx->buffer;
13662306a36Sopenharmony_ci		while (srclen--)
13762306a36Sopenharmony_ci			*pos++ ^= *src++;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int polyval_arm64_final(struct shash_desc *desc, u8 *dst)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
14662306a36Sopenharmony_ci	const struct polyval_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (dctx->bytes) {
14962306a36Sopenharmony_ci		internal_polyval_mul(dctx->buffer,
15062306a36Sopenharmony_ci				     tctx->key_powers[NUM_KEY_POWERS-1]);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	memcpy(dst, dctx->buffer, POLYVAL_BLOCK_SIZE);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic struct shash_alg polyval_alg = {
15962306a36Sopenharmony_ci	.digestsize	= POLYVAL_DIGEST_SIZE,
16062306a36Sopenharmony_ci	.init		= polyval_arm64_init,
16162306a36Sopenharmony_ci	.update		= polyval_arm64_update,
16262306a36Sopenharmony_ci	.final		= polyval_arm64_final,
16362306a36Sopenharmony_ci	.setkey		= polyval_arm64_setkey,
16462306a36Sopenharmony_ci	.descsize	= sizeof(struct polyval_desc_ctx),
16562306a36Sopenharmony_ci	.base		= {
16662306a36Sopenharmony_ci		.cra_name		= "polyval",
16762306a36Sopenharmony_ci		.cra_driver_name	= "polyval-ce",
16862306a36Sopenharmony_ci		.cra_priority		= 200,
16962306a36Sopenharmony_ci		.cra_blocksize		= POLYVAL_BLOCK_SIZE,
17062306a36Sopenharmony_ci		.cra_ctxsize		= sizeof(struct polyval_tfm_ctx),
17162306a36Sopenharmony_ci		.cra_module		= THIS_MODULE,
17262306a36Sopenharmony_ci	},
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int __init polyval_ce_mod_init(void)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	return crypto_register_shash(&polyval_alg);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void __exit polyval_ce_mod_exit(void)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	crypto_unregister_shash(&polyval_alg);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cimodule_cpu_feature_match(PMULL, polyval_ce_mod_init)
18662306a36Sopenharmony_cimodule_exit(polyval_ce_mod_exit);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
18962306a36Sopenharmony_ciMODULE_DESCRIPTION("POLYVAL hash function accelerated by ARMv8 Crypto Extensions");
19062306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("polyval");
19162306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("polyval-ce");
192