162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OpenSSL/Cryptogams accelerated Poly1305 transform for MIPS 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 Linaro Ltd. <ard.biesheuvel@linaro.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/unaligned.h> 962306a36Sopenharmony_ci#include <crypto/algapi.h> 1062306a36Sopenharmony_ci#include <crypto/internal/hash.h> 1162306a36Sopenharmony_ci#include <crypto/internal/poly1305.h> 1262306a36Sopenharmony_ci#include <linux/cpufeature.h> 1362306a36Sopenharmony_ci#include <linux/crypto.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ciasmlinkage void poly1305_init_mips(void *state, const u8 *key); 1762306a36Sopenharmony_ciasmlinkage void poly1305_blocks_mips(void *state, const u8 *src, u32 len, u32 hibit); 1862306a36Sopenharmony_ciasmlinkage void poly1305_emit_mips(void *state, u8 *digest, const u32 *nonce); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid poly1305_init_arch(struct poly1305_desc_ctx *dctx, const u8 key[POLY1305_KEY_SIZE]) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci poly1305_init_mips(&dctx->h, key); 2362306a36Sopenharmony_ci dctx->s[0] = get_unaligned_le32(key + 16); 2462306a36Sopenharmony_ci dctx->s[1] = get_unaligned_le32(key + 20); 2562306a36Sopenharmony_ci dctx->s[2] = get_unaligned_le32(key + 24); 2662306a36Sopenharmony_ci dctx->s[3] = get_unaligned_le32(key + 28); 2762306a36Sopenharmony_ci dctx->buflen = 0; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ciEXPORT_SYMBOL(poly1305_init_arch); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int mips_poly1305_init(struct shash_desc *desc) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci dctx->buflen = 0; 3662306a36Sopenharmony_ci dctx->rset = 0; 3762306a36Sopenharmony_ci dctx->sset = false; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void mips_poly1305_blocks(struct poly1305_desc_ctx *dctx, const u8 *src, 4362306a36Sopenharmony_ci u32 len, u32 hibit) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci if (unlikely(!dctx->sset)) { 4662306a36Sopenharmony_ci if (!dctx->rset) { 4762306a36Sopenharmony_ci poly1305_init_mips(&dctx->h, src); 4862306a36Sopenharmony_ci src += POLY1305_BLOCK_SIZE; 4962306a36Sopenharmony_ci len -= POLY1305_BLOCK_SIZE; 5062306a36Sopenharmony_ci dctx->rset = 1; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci if (len >= POLY1305_BLOCK_SIZE) { 5362306a36Sopenharmony_ci dctx->s[0] = get_unaligned_le32(src + 0); 5462306a36Sopenharmony_ci dctx->s[1] = get_unaligned_le32(src + 4); 5562306a36Sopenharmony_ci dctx->s[2] = get_unaligned_le32(src + 8); 5662306a36Sopenharmony_ci dctx->s[3] = get_unaligned_le32(src + 12); 5762306a36Sopenharmony_ci src += POLY1305_BLOCK_SIZE; 5862306a36Sopenharmony_ci len -= POLY1305_BLOCK_SIZE; 5962306a36Sopenharmony_ci dctx->sset = true; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci if (len < POLY1305_BLOCK_SIZE) 6262306a36Sopenharmony_ci return; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci len &= ~(POLY1305_BLOCK_SIZE - 1); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci poly1305_blocks_mips(&dctx->h, src, len, hibit); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int mips_poly1305_update(struct shash_desc *desc, const u8 *src, 7162306a36Sopenharmony_ci unsigned int len) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (unlikely(dctx->buflen)) { 7662306a36Sopenharmony_ci u32 bytes = min(len, POLY1305_BLOCK_SIZE - dctx->buflen); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci memcpy(dctx->buf + dctx->buflen, src, bytes); 7962306a36Sopenharmony_ci src += bytes; 8062306a36Sopenharmony_ci len -= bytes; 8162306a36Sopenharmony_ci dctx->buflen += bytes; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (dctx->buflen == POLY1305_BLOCK_SIZE) { 8462306a36Sopenharmony_ci mips_poly1305_blocks(dctx, dctx->buf, POLY1305_BLOCK_SIZE, 1); 8562306a36Sopenharmony_ci dctx->buflen = 0; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (likely(len >= POLY1305_BLOCK_SIZE)) { 9062306a36Sopenharmony_ci mips_poly1305_blocks(dctx, src, len, 1); 9162306a36Sopenharmony_ci src += round_down(len, POLY1305_BLOCK_SIZE); 9262306a36Sopenharmony_ci len %= POLY1305_BLOCK_SIZE; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (unlikely(len)) { 9662306a36Sopenharmony_ci dctx->buflen = len; 9762306a36Sopenharmony_ci memcpy(dctx->buf, src, len); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_civoid poly1305_update_arch(struct poly1305_desc_ctx *dctx, const u8 *src, 10362306a36Sopenharmony_ci unsigned int nbytes) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci if (unlikely(dctx->buflen)) { 10662306a36Sopenharmony_ci u32 bytes = min(nbytes, POLY1305_BLOCK_SIZE - dctx->buflen); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci memcpy(dctx->buf + dctx->buflen, src, bytes); 10962306a36Sopenharmony_ci src += bytes; 11062306a36Sopenharmony_ci nbytes -= bytes; 11162306a36Sopenharmony_ci dctx->buflen += bytes; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (dctx->buflen == POLY1305_BLOCK_SIZE) { 11462306a36Sopenharmony_ci poly1305_blocks_mips(&dctx->h, dctx->buf, 11562306a36Sopenharmony_ci POLY1305_BLOCK_SIZE, 1); 11662306a36Sopenharmony_ci dctx->buflen = 0; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (likely(nbytes >= POLY1305_BLOCK_SIZE)) { 12162306a36Sopenharmony_ci unsigned int len = round_down(nbytes, POLY1305_BLOCK_SIZE); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci poly1305_blocks_mips(&dctx->h, src, len, 1); 12462306a36Sopenharmony_ci src += len; 12562306a36Sopenharmony_ci nbytes %= POLY1305_BLOCK_SIZE; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (unlikely(nbytes)) { 12962306a36Sopenharmony_ci dctx->buflen = nbytes; 13062306a36Sopenharmony_ci memcpy(dctx->buf, src, nbytes); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ciEXPORT_SYMBOL(poly1305_update_arch); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci if (unlikely(dctx->buflen)) { 13862306a36Sopenharmony_ci dctx->buf[dctx->buflen++] = 1; 13962306a36Sopenharmony_ci memset(dctx->buf + dctx->buflen, 0, 14062306a36Sopenharmony_ci POLY1305_BLOCK_SIZE - dctx->buflen); 14162306a36Sopenharmony_ci poly1305_blocks_mips(&dctx->h, dctx->buf, POLY1305_BLOCK_SIZE, 0); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci poly1305_emit_mips(&dctx->h, dst, dctx->s); 14562306a36Sopenharmony_ci *dctx = (struct poly1305_desc_ctx){}; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ciEXPORT_SYMBOL(poly1305_final_arch); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int mips_poly1305_final(struct shash_desc *desc, u8 *dst) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (unlikely(!dctx->sset)) 15462306a36Sopenharmony_ci return -ENOKEY; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci poly1305_final_arch(dctx, dst); 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic struct shash_alg mips_poly1305_alg = { 16162306a36Sopenharmony_ci .init = mips_poly1305_init, 16262306a36Sopenharmony_ci .update = mips_poly1305_update, 16362306a36Sopenharmony_ci .final = mips_poly1305_final, 16462306a36Sopenharmony_ci .digestsize = POLY1305_DIGEST_SIZE, 16562306a36Sopenharmony_ci .descsize = sizeof(struct poly1305_desc_ctx), 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci .base.cra_name = "poly1305", 16862306a36Sopenharmony_ci .base.cra_driver_name = "poly1305-mips", 16962306a36Sopenharmony_ci .base.cra_priority = 200, 17062306a36Sopenharmony_ci .base.cra_blocksize = POLY1305_BLOCK_SIZE, 17162306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int __init mips_poly1305_mod_init(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci return IS_REACHABLE(CONFIG_CRYPTO_HASH) ? 17762306a36Sopenharmony_ci crypto_register_shash(&mips_poly1305_alg) : 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void __exit mips_poly1305_mod_exit(void) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci if (IS_REACHABLE(CONFIG_CRYPTO_HASH)) 18362306a36Sopenharmony_ci crypto_unregister_shash(&mips_poly1305_alg); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cimodule_init(mips_poly1305_mod_init); 18762306a36Sopenharmony_cimodule_exit(mips_poly1305_mod_exit); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 19062306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305"); 19162306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("poly1305-mips"); 192