// SPDX-License-Identifier: GPL-2.0-only /* * GHASH using the RISC-V vector crypto extensions * * Copyright (C) 2023 VRULL GmbH * Author: Heiko Stuebner * * Copyright (C) 2023 SiFive, Inc. * Author: Jerry Shih */ #include #include #include #include #include #include #include asmlinkage void ghash_zvkg(be128 *accumulator, const be128 *key, const u8 *data, size_t len); struct riscv64_ghash_tfm_ctx { be128 key; }; struct riscv64_ghash_desc_ctx { be128 accumulator; u8 buffer[GHASH_BLOCK_SIZE]; u32 bytes; }; static int riscv64_ghash_setkey(struct crypto_shash *tfm, const u8 *key, unsigned int keylen) { struct riscv64_ghash_tfm_ctx *tctx = crypto_shash_ctx(tfm); if (keylen != GHASH_BLOCK_SIZE) return -EINVAL; memcpy(&tctx->key, key, GHASH_BLOCK_SIZE); return 0; } static int riscv64_ghash_init(struct shash_desc *desc) { struct riscv64_ghash_desc_ctx *dctx = shash_desc_ctx(desc); *dctx = (struct riscv64_ghash_desc_ctx){}; return 0; } static inline void riscv64_ghash_blocks(const struct riscv64_ghash_tfm_ctx *tctx, struct riscv64_ghash_desc_ctx *dctx, const u8 *src, size_t srclen) { /* The srclen is nonzero and a multiple of 16. */ if (crypto_simd_usable()) { kernel_vector_begin(); ghash_zvkg(&dctx->accumulator, &tctx->key, src, srclen); kernel_vector_end(); } else { do { crypto_xor((u8 *)&dctx->accumulator, src, GHASH_BLOCK_SIZE); gf128mul_lle(&dctx->accumulator, &tctx->key); src += GHASH_BLOCK_SIZE; srclen -= GHASH_BLOCK_SIZE; } while (srclen); } } static int riscv64_ghash_update(struct shash_desc *desc, const u8 *src, unsigned int srclen) { const struct riscv64_ghash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm); struct riscv64_ghash_desc_ctx *dctx = shash_desc_ctx(desc); unsigned int len; if (dctx->bytes) { if (dctx->bytes + srclen < GHASH_BLOCK_SIZE) { memcpy(dctx->buffer + dctx->bytes, src, srclen); dctx->bytes += srclen; return 0; } memcpy(dctx->buffer + dctx->bytes, src, GHASH_BLOCK_SIZE - dctx->bytes); riscv64_ghash_blocks(tctx, dctx, dctx->buffer, GHASH_BLOCK_SIZE); src += GHASH_BLOCK_SIZE - dctx->bytes; srclen -= GHASH_BLOCK_SIZE - dctx->bytes; dctx->bytes = 0; } len = round_down(srclen, GHASH_BLOCK_SIZE); if (len) { riscv64_ghash_blocks(tctx, dctx, src, len); src += len; srclen -= len; } if (srclen) { memcpy(dctx->buffer, src, srclen); dctx->bytes = srclen; } return 0; } static int riscv64_ghash_final(struct shash_desc *desc, u8 *out) { const struct riscv64_ghash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm); struct riscv64_ghash_desc_ctx *dctx = shash_desc_ctx(desc); int i; if (dctx->bytes) { for (i = dctx->bytes; i < GHASH_BLOCK_SIZE; i++) dctx->buffer[i] = 0; riscv64_ghash_blocks(tctx, dctx, dctx->buffer, GHASH_BLOCK_SIZE); } memcpy(out, &dctx->accumulator, GHASH_DIGEST_SIZE); return 0; } static struct shash_alg riscv64_ghash_alg = { .init = riscv64_ghash_init, .update = riscv64_ghash_update, .final = riscv64_ghash_final, .setkey = riscv64_ghash_setkey, .descsize = sizeof(struct riscv64_ghash_desc_ctx), .digestsize = GHASH_DIGEST_SIZE, .base = { .cra_blocksize = GHASH_BLOCK_SIZE, .cra_ctxsize = sizeof(struct riscv64_ghash_tfm_ctx), .cra_priority = 300, .cra_name = "ghash", .cra_driver_name = "ghash-riscv64-zvkg", .cra_module = THIS_MODULE, }, }; static int __init riscv64_ghash_mod_init(void) { if (riscv_isa_extension_available(NULL, ZVKG) && riscv_vector_vlen() >= 128) return crypto_register_shash(&riscv64_ghash_alg); return -ENODEV; } static void __exit riscv64_ghash_mod_exit(void) { crypto_unregister_shash(&riscv64_ghash_alg); } module_init(riscv64_ghash_mod_init); module_exit(riscv64_ghash_mod_exit); MODULE_DESCRIPTION("GHASH (RISC-V accelerated)"); MODULE_AUTHOR("Heiko Stuebner "); MODULE_LICENSE("GPL"); MODULE_ALIAS_CRYPTO("ghash");