162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Glue code for POLYVAL using PCMULQDQ-NI 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 1562306a36Sopenharmony_ci * accelerated by PCLMULQDQ-NI to implement the finite field 1662306a36Sopenharmony_ci * operations. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <crypto/algapi.h> 2062306a36Sopenharmony_ci#include <crypto/internal/hash.h> 2162306a36Sopenharmony_ci#include <crypto/internal/simd.h> 2262306a36Sopenharmony_ci#include <crypto/polyval.h> 2362306a36Sopenharmony_ci#include <linux/crypto.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 2862306a36Sopenharmony_ci#include <asm/simd.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define POLYVAL_ALIGN 16 3162306a36Sopenharmony_ci#define POLYVAL_ALIGN_ATTR __aligned(POLYVAL_ALIGN) 3262306a36Sopenharmony_ci#define POLYVAL_ALIGN_EXTRA ((POLYVAL_ALIGN - 1) & ~(CRYPTO_MINALIGN - 1)) 3362306a36Sopenharmony_ci#define POLYVAL_CTX_SIZE (sizeof(struct polyval_tfm_ctx) + POLYVAL_ALIGN_EXTRA) 3462306a36Sopenharmony_ci#define NUM_KEY_POWERS 8 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct polyval_tfm_ctx { 3762306a36Sopenharmony_ci /* 3862306a36Sopenharmony_ci * These powers must be in the order h^8, ..., h^1. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci u8 key_powers[NUM_KEY_POWERS][POLYVAL_BLOCK_SIZE] POLYVAL_ALIGN_ATTR; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct polyval_desc_ctx { 4462306a36Sopenharmony_ci u8 buffer[POLYVAL_BLOCK_SIZE]; 4562306a36Sopenharmony_ci u32 bytes; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciasmlinkage void clmul_polyval_update(const struct polyval_tfm_ctx *keys, 4962306a36Sopenharmony_ci const u8 *in, size_t nblocks, u8 *accumulator); 5062306a36Sopenharmony_ciasmlinkage void clmul_polyval_mul(u8 *op1, const u8 *op2); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline struct polyval_tfm_ctx *polyval_tfm_ctx(struct crypto_shash *tfm) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci return PTR_ALIGN(crypto_shash_ctx(tfm), POLYVAL_ALIGN); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void internal_polyval_update(const struct polyval_tfm_ctx *keys, 5862306a36Sopenharmony_ci const u8 *in, size_t nblocks, u8 *accumulator) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (likely(crypto_simd_usable())) { 6162306a36Sopenharmony_ci kernel_fpu_begin(); 6262306a36Sopenharmony_ci clmul_polyval_update(keys, in, nblocks, accumulator); 6362306a36Sopenharmony_ci kernel_fpu_end(); 6462306a36Sopenharmony_ci } else { 6562306a36Sopenharmony_ci polyval_update_non4k(keys->key_powers[NUM_KEY_POWERS-1], in, 6662306a36Sopenharmony_ci nblocks, accumulator); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void internal_polyval_mul(u8 *op1, const u8 *op2) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci if (likely(crypto_simd_usable())) { 7362306a36Sopenharmony_ci kernel_fpu_begin(); 7462306a36Sopenharmony_ci clmul_polyval_mul(op1, op2); 7562306a36Sopenharmony_ci kernel_fpu_end(); 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci polyval_mul_non4k(op1, op2); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int polyval_x86_setkey(struct crypto_shash *tfm, 8262306a36Sopenharmony_ci const u8 *key, unsigned int keylen) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct polyval_tfm_ctx *tctx = polyval_tfm_ctx(tfm); 8562306a36Sopenharmony_ci int i; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (keylen != POLYVAL_BLOCK_SIZE) 8862306a36Sopenharmony_ci return -EINVAL; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci memcpy(tctx->key_powers[NUM_KEY_POWERS-1], key, POLYVAL_BLOCK_SIZE); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for (i = NUM_KEY_POWERS-2; i >= 0; i--) { 9362306a36Sopenharmony_ci memcpy(tctx->key_powers[i], key, POLYVAL_BLOCK_SIZE); 9462306a36Sopenharmony_ci internal_polyval_mul(tctx->key_powers[i], 9562306a36Sopenharmony_ci tctx->key_powers[i+1]); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int polyval_x86_init(struct shash_desc *desc) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct polyval_desc_ctx *dctx = shash_desc_ctx(desc); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci memset(dctx, 0, sizeof(*dctx)); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int polyval_x86_update(struct shash_desc *desc, 11162306a36Sopenharmony_ci const u8 *src, unsigned int srclen) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct polyval_desc_ctx *dctx = shash_desc_ctx(desc); 11462306a36Sopenharmony_ci const struct polyval_tfm_ctx *tctx = polyval_tfm_ctx(desc->tfm); 11562306a36Sopenharmony_ci u8 *pos; 11662306a36Sopenharmony_ci unsigned int nblocks; 11762306a36Sopenharmony_ci unsigned int n; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (dctx->bytes) { 12062306a36Sopenharmony_ci n = min(srclen, dctx->bytes); 12162306a36Sopenharmony_ci pos = dctx->buffer + POLYVAL_BLOCK_SIZE - dctx->bytes; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci dctx->bytes -= n; 12462306a36Sopenharmony_ci srclen -= n; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci while (n--) 12762306a36Sopenharmony_ci *pos++ ^= *src++; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!dctx->bytes) 13062306a36Sopenharmony_ci internal_polyval_mul(dctx->buffer, 13162306a36Sopenharmony_ci tctx->key_powers[NUM_KEY_POWERS-1]); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci while (srclen >= POLYVAL_BLOCK_SIZE) { 13562306a36Sopenharmony_ci /* Allow rescheduling every 4K bytes. */ 13662306a36Sopenharmony_ci nblocks = min(srclen, 4096U) / POLYVAL_BLOCK_SIZE; 13762306a36Sopenharmony_ci internal_polyval_update(tctx, src, nblocks, dctx->buffer); 13862306a36Sopenharmony_ci srclen -= nblocks * POLYVAL_BLOCK_SIZE; 13962306a36Sopenharmony_ci src += nblocks * POLYVAL_BLOCK_SIZE; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (srclen) { 14362306a36Sopenharmony_ci dctx->bytes = POLYVAL_BLOCK_SIZE - srclen; 14462306a36Sopenharmony_ci pos = dctx->buffer; 14562306a36Sopenharmony_ci while (srclen--) 14662306a36Sopenharmony_ci *pos++ ^= *src++; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int polyval_x86_final(struct shash_desc *desc, u8 *dst) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct polyval_desc_ctx *dctx = shash_desc_ctx(desc); 15562306a36Sopenharmony_ci const struct polyval_tfm_ctx *tctx = polyval_tfm_ctx(desc->tfm); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (dctx->bytes) { 15862306a36Sopenharmony_ci internal_polyval_mul(dctx->buffer, 15962306a36Sopenharmony_ci tctx->key_powers[NUM_KEY_POWERS-1]); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci memcpy(dst, dctx->buffer, POLYVAL_BLOCK_SIZE); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic struct shash_alg polyval_alg = { 16862306a36Sopenharmony_ci .digestsize = POLYVAL_DIGEST_SIZE, 16962306a36Sopenharmony_ci .init = polyval_x86_init, 17062306a36Sopenharmony_ci .update = polyval_x86_update, 17162306a36Sopenharmony_ci .final = polyval_x86_final, 17262306a36Sopenharmony_ci .setkey = polyval_x86_setkey, 17362306a36Sopenharmony_ci .descsize = sizeof(struct polyval_desc_ctx), 17462306a36Sopenharmony_ci .base = { 17562306a36Sopenharmony_ci .cra_name = "polyval", 17662306a36Sopenharmony_ci .cra_driver_name = "polyval-clmulni", 17762306a36Sopenharmony_ci .cra_priority = 200, 17862306a36Sopenharmony_ci .cra_blocksize = POLYVAL_BLOCK_SIZE, 17962306a36Sopenharmony_ci .cra_ctxsize = POLYVAL_CTX_SIZE, 18062306a36Sopenharmony_ci .cra_module = THIS_MODULE, 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci__maybe_unused static const struct x86_cpu_id pcmul_cpu_id[] = { 18562306a36Sopenharmony_ci X86_MATCH_FEATURE(X86_FEATURE_PCLMULQDQ, NULL), 18662306a36Sopenharmony_ci {} 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, pcmul_cpu_id); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int __init polyval_clmulni_mod_init(void) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci if (!x86_match_cpu(pcmul_cpu_id)) 19362306a36Sopenharmony_ci return -ENODEV; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_AVX)) 19662306a36Sopenharmony_ci return -ENODEV; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return crypto_register_shash(&polyval_alg); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void __exit polyval_clmulni_mod_exit(void) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci crypto_unregister_shash(&polyval_alg); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cimodule_init(polyval_clmulni_mod_init); 20762306a36Sopenharmony_cimodule_exit(polyval_clmulni_mod_exit); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 21062306a36Sopenharmony_ciMODULE_DESCRIPTION("POLYVAL hash function accelerated by PCLMULQDQ-NI"); 21162306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("polyval"); 21262306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("polyval-clmulni"); 213