18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OpenSSL/Cryptogams accelerated Poly1305 transform for MIPS
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Linaro Ltd. <ard.biesheuvel@linaro.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
98c2ecf20Sopenharmony_ci#include <crypto/algapi.h>
108c2ecf20Sopenharmony_ci#include <crypto/internal/hash.h>
118c2ecf20Sopenharmony_ci#include <crypto/internal/poly1305.h>
128c2ecf20Sopenharmony_ci#include <linux/cpufeature.h>
138c2ecf20Sopenharmony_ci#include <linux/crypto.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciasmlinkage void poly1305_init_mips(void *state, const u8 *key);
178c2ecf20Sopenharmony_ciasmlinkage void poly1305_blocks_mips(void *state, const u8 *src, u32 len, u32 hibit);
188c2ecf20Sopenharmony_ciasmlinkage void poly1305_emit_mips(void *state, u8 *digest, const u32 *nonce);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_civoid poly1305_init_arch(struct poly1305_desc_ctx *dctx, const u8 key[POLY1305_KEY_SIZE])
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	poly1305_init_mips(&dctx->h, key);
238c2ecf20Sopenharmony_ci	dctx->s[0] = get_unaligned_le32(key + 16);
248c2ecf20Sopenharmony_ci	dctx->s[1] = get_unaligned_le32(key + 20);
258c2ecf20Sopenharmony_ci	dctx->s[2] = get_unaligned_le32(key + 24);
268c2ecf20Sopenharmony_ci	dctx->s[3] = get_unaligned_le32(key + 28);
278c2ecf20Sopenharmony_ci	dctx->buflen = 0;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(poly1305_init_arch);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int mips_poly1305_init(struct shash_desc *desc)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	dctx->buflen = 0;
368c2ecf20Sopenharmony_ci	dctx->rset = 0;
378c2ecf20Sopenharmony_ci	dctx->sset = false;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return 0;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void mips_poly1305_blocks(struct poly1305_desc_ctx *dctx, const u8 *src,
438c2ecf20Sopenharmony_ci				 u32 len, u32 hibit)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	if (unlikely(!dctx->sset)) {
468c2ecf20Sopenharmony_ci		if (!dctx->rset) {
478c2ecf20Sopenharmony_ci			poly1305_init_mips(&dctx->h, src);
488c2ecf20Sopenharmony_ci			src += POLY1305_BLOCK_SIZE;
498c2ecf20Sopenharmony_ci			len -= POLY1305_BLOCK_SIZE;
508c2ecf20Sopenharmony_ci			dctx->rset = 1;
518c2ecf20Sopenharmony_ci		}
528c2ecf20Sopenharmony_ci		if (len >= POLY1305_BLOCK_SIZE) {
538c2ecf20Sopenharmony_ci			dctx->s[0] = get_unaligned_le32(src +  0);
548c2ecf20Sopenharmony_ci			dctx->s[1] = get_unaligned_le32(src +  4);
558c2ecf20Sopenharmony_ci			dctx->s[2] = get_unaligned_le32(src +  8);
568c2ecf20Sopenharmony_ci			dctx->s[3] = get_unaligned_le32(src + 12);
578c2ecf20Sopenharmony_ci			src += POLY1305_BLOCK_SIZE;
588c2ecf20Sopenharmony_ci			len -= POLY1305_BLOCK_SIZE;
598c2ecf20Sopenharmony_ci			dctx->sset = true;
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ci		if (len < POLY1305_BLOCK_SIZE)
628c2ecf20Sopenharmony_ci			return;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	len &= ~(POLY1305_BLOCK_SIZE - 1);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	poly1305_blocks_mips(&dctx->h, src, len, hibit);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int mips_poly1305_update(struct shash_desc *desc, const u8 *src,
718c2ecf20Sopenharmony_ci				unsigned int len)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (unlikely(dctx->buflen)) {
768c2ecf20Sopenharmony_ci		u32 bytes = min(len, POLY1305_BLOCK_SIZE - dctx->buflen);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		memcpy(dctx->buf + dctx->buflen, src, bytes);
798c2ecf20Sopenharmony_ci		src += bytes;
808c2ecf20Sopenharmony_ci		len -= bytes;
818c2ecf20Sopenharmony_ci		dctx->buflen += bytes;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		if (dctx->buflen == POLY1305_BLOCK_SIZE) {
848c2ecf20Sopenharmony_ci			mips_poly1305_blocks(dctx, dctx->buf, POLY1305_BLOCK_SIZE, 1);
858c2ecf20Sopenharmony_ci			dctx->buflen = 0;
868c2ecf20Sopenharmony_ci		}
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (likely(len >= POLY1305_BLOCK_SIZE)) {
908c2ecf20Sopenharmony_ci		mips_poly1305_blocks(dctx, src, len, 1);
918c2ecf20Sopenharmony_ci		src += round_down(len, POLY1305_BLOCK_SIZE);
928c2ecf20Sopenharmony_ci		len %= POLY1305_BLOCK_SIZE;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (unlikely(len)) {
968c2ecf20Sopenharmony_ci		dctx->buflen = len;
978c2ecf20Sopenharmony_ci		memcpy(dctx->buf, src, len);
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_civoid poly1305_update_arch(struct poly1305_desc_ctx *dctx, const u8 *src,
1038c2ecf20Sopenharmony_ci			  unsigned int nbytes)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	if (unlikely(dctx->buflen)) {
1068c2ecf20Sopenharmony_ci		u32 bytes = min(nbytes, POLY1305_BLOCK_SIZE - dctx->buflen);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		memcpy(dctx->buf + dctx->buflen, src, bytes);
1098c2ecf20Sopenharmony_ci		src += bytes;
1108c2ecf20Sopenharmony_ci		nbytes -= bytes;
1118c2ecf20Sopenharmony_ci		dctx->buflen += bytes;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		if (dctx->buflen == POLY1305_BLOCK_SIZE) {
1148c2ecf20Sopenharmony_ci			poly1305_blocks_mips(&dctx->h, dctx->buf,
1158c2ecf20Sopenharmony_ci					     POLY1305_BLOCK_SIZE, 1);
1168c2ecf20Sopenharmony_ci			dctx->buflen = 0;
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (likely(nbytes >= POLY1305_BLOCK_SIZE)) {
1218c2ecf20Sopenharmony_ci		unsigned int len = round_down(nbytes, POLY1305_BLOCK_SIZE);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		poly1305_blocks_mips(&dctx->h, src, len, 1);
1248c2ecf20Sopenharmony_ci		src += len;
1258c2ecf20Sopenharmony_ci		nbytes %= POLY1305_BLOCK_SIZE;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (unlikely(nbytes)) {
1298c2ecf20Sopenharmony_ci		dctx->buflen = nbytes;
1308c2ecf20Sopenharmony_ci		memcpy(dctx->buf, src, nbytes);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(poly1305_update_arch);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_civoid poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	if (unlikely(dctx->buflen)) {
1388c2ecf20Sopenharmony_ci		dctx->buf[dctx->buflen++] = 1;
1398c2ecf20Sopenharmony_ci		memset(dctx->buf + dctx->buflen, 0,
1408c2ecf20Sopenharmony_ci		       POLY1305_BLOCK_SIZE - dctx->buflen);
1418c2ecf20Sopenharmony_ci		poly1305_blocks_mips(&dctx->h, dctx->buf, POLY1305_BLOCK_SIZE, 0);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	poly1305_emit_mips(&dctx->h, dst, dctx->s);
1458c2ecf20Sopenharmony_ci	*dctx = (struct poly1305_desc_ctx){};
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(poly1305_final_arch);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int mips_poly1305_final(struct shash_desc *desc, u8 *dst)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (unlikely(!dctx->sset))
1548c2ecf20Sopenharmony_ci		return -ENOKEY;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	poly1305_final_arch(dctx, dst);
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct shash_alg mips_poly1305_alg = {
1618c2ecf20Sopenharmony_ci	.init			= mips_poly1305_init,
1628c2ecf20Sopenharmony_ci	.update			= mips_poly1305_update,
1638c2ecf20Sopenharmony_ci	.final			= mips_poly1305_final,
1648c2ecf20Sopenharmony_ci	.digestsize		= POLY1305_DIGEST_SIZE,
1658c2ecf20Sopenharmony_ci	.descsize		= sizeof(struct poly1305_desc_ctx),
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	.base.cra_name		= "poly1305",
1688c2ecf20Sopenharmony_ci	.base.cra_driver_name	= "poly1305-mips",
1698c2ecf20Sopenharmony_ci	.base.cra_priority	= 200,
1708c2ecf20Sopenharmony_ci	.base.cra_blocksize	= POLY1305_BLOCK_SIZE,
1718c2ecf20Sopenharmony_ci	.base.cra_module	= THIS_MODULE,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int __init mips_poly1305_mod_init(void)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	return IS_REACHABLE(CONFIG_CRYPTO_HASH) ?
1778c2ecf20Sopenharmony_ci		crypto_register_shash(&mips_poly1305_alg) : 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void __exit mips_poly1305_mod_exit(void)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	if (IS_REACHABLE(CONFIG_CRYPTO_HASH))
1838c2ecf20Sopenharmony_ci		crypto_unregister_shash(&mips_poly1305_alg);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cimodule_init(mips_poly1305_mod_init);
1878c2ecf20Sopenharmony_cimodule_exit(mips_poly1305_mod_exit);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1908c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305");
1918c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305-mips");
192