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