1a11d055eSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0
2a11d055eSArd Biesheuvel /*
3a11d055eSArd Biesheuvel * OpenSSL/Cryptogams accelerated Poly1305 transform for MIPS
4a11d055eSArd Biesheuvel *
5a11d055eSArd Biesheuvel * Copyright (C) 2019 Linaro Ltd. <ard.biesheuvel@linaro.org>
6a11d055eSArd Biesheuvel */
7a11d055eSArd Biesheuvel
8a11d055eSArd Biesheuvel #include <asm/unaligned.h>
9a11d055eSArd Biesheuvel #include <crypto/algapi.h>
10a11d055eSArd Biesheuvel #include <crypto/internal/hash.h>
11a11d055eSArd Biesheuvel #include <crypto/internal/poly1305.h>
12a11d055eSArd Biesheuvel #include <linux/cpufeature.h>
13a11d055eSArd Biesheuvel #include <linux/crypto.h>
14a11d055eSArd Biesheuvel #include <linux/module.h>
15a11d055eSArd Biesheuvel
16a11d055eSArd Biesheuvel asmlinkage void poly1305_init_mips(void *state, const u8 *key);
17a11d055eSArd Biesheuvel asmlinkage void poly1305_blocks_mips(void *state, const u8 *src, u32 len, u32 hibit);
1831899908SJason A. Donenfeld asmlinkage void poly1305_emit_mips(void *state, u8 *digest, const u32 *nonce);
19a11d055eSArd Biesheuvel
poly1305_init_arch(struct poly1305_desc_ctx * dctx,const u8 key[POLY1305_KEY_SIZE])20*8d195e7aSArnd Bergmann void poly1305_init_arch(struct poly1305_desc_ctx *dctx, const u8 key[POLY1305_KEY_SIZE])
21a11d055eSArd Biesheuvel {
22a11d055eSArd Biesheuvel poly1305_init_mips(&dctx->h, key);
23a11d055eSArd Biesheuvel dctx->s[0] = get_unaligned_le32(key + 16);
24a11d055eSArd Biesheuvel dctx->s[1] = get_unaligned_le32(key + 20);
25a11d055eSArd Biesheuvel dctx->s[2] = get_unaligned_le32(key + 24);
26a11d055eSArd Biesheuvel dctx->s[3] = get_unaligned_le32(key + 28);
27a11d055eSArd Biesheuvel dctx->buflen = 0;
28a11d055eSArd Biesheuvel }
29a11d055eSArd Biesheuvel EXPORT_SYMBOL(poly1305_init_arch);
30a11d055eSArd Biesheuvel
mips_poly1305_init(struct shash_desc * desc)31a11d055eSArd Biesheuvel static int mips_poly1305_init(struct shash_desc *desc)
32a11d055eSArd Biesheuvel {
33a11d055eSArd Biesheuvel struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
34a11d055eSArd Biesheuvel
35a11d055eSArd Biesheuvel dctx->buflen = 0;
36a11d055eSArd Biesheuvel dctx->rset = 0;
37a11d055eSArd Biesheuvel dctx->sset = false;
38a11d055eSArd Biesheuvel
39a11d055eSArd Biesheuvel return 0;
40a11d055eSArd Biesheuvel }
41a11d055eSArd Biesheuvel
mips_poly1305_blocks(struct poly1305_desc_ctx * dctx,const u8 * src,u32 len,u32 hibit)42a11d055eSArd Biesheuvel static void mips_poly1305_blocks(struct poly1305_desc_ctx *dctx, const u8 *src,
43a11d055eSArd Biesheuvel u32 len, u32 hibit)
44a11d055eSArd Biesheuvel {
45a11d055eSArd Biesheuvel if (unlikely(!dctx->sset)) {
46a11d055eSArd Biesheuvel if (!dctx->rset) {
47a11d055eSArd Biesheuvel poly1305_init_mips(&dctx->h, src);
48a11d055eSArd Biesheuvel src += POLY1305_BLOCK_SIZE;
49a11d055eSArd Biesheuvel len -= POLY1305_BLOCK_SIZE;
50a11d055eSArd Biesheuvel dctx->rset = 1;
51a11d055eSArd Biesheuvel }
52a11d055eSArd Biesheuvel if (len >= POLY1305_BLOCK_SIZE) {
53a11d055eSArd Biesheuvel dctx->s[0] = get_unaligned_le32(src + 0);
54a11d055eSArd Biesheuvel dctx->s[1] = get_unaligned_le32(src + 4);
55a11d055eSArd Biesheuvel dctx->s[2] = get_unaligned_le32(src + 8);
56a11d055eSArd Biesheuvel dctx->s[3] = get_unaligned_le32(src + 12);
57a11d055eSArd Biesheuvel src += POLY1305_BLOCK_SIZE;
58a11d055eSArd Biesheuvel len -= POLY1305_BLOCK_SIZE;
59a11d055eSArd Biesheuvel dctx->sset = true;
60a11d055eSArd Biesheuvel }
61a11d055eSArd Biesheuvel if (len < POLY1305_BLOCK_SIZE)
62a11d055eSArd Biesheuvel return;
63a11d055eSArd Biesheuvel }
64a11d055eSArd Biesheuvel
65a11d055eSArd Biesheuvel len &= ~(POLY1305_BLOCK_SIZE - 1);
66a11d055eSArd Biesheuvel
67a11d055eSArd Biesheuvel poly1305_blocks_mips(&dctx->h, src, len, hibit);
68a11d055eSArd Biesheuvel }
69a11d055eSArd Biesheuvel
mips_poly1305_update(struct shash_desc * desc,const u8 * src,unsigned int len)70a11d055eSArd Biesheuvel static int mips_poly1305_update(struct shash_desc *desc, const u8 *src,
71a11d055eSArd Biesheuvel unsigned int len)
72a11d055eSArd Biesheuvel {
73a11d055eSArd Biesheuvel struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
74a11d055eSArd Biesheuvel
75a11d055eSArd Biesheuvel if (unlikely(dctx->buflen)) {
76a11d055eSArd Biesheuvel u32 bytes = min(len, POLY1305_BLOCK_SIZE - dctx->buflen);
77a11d055eSArd Biesheuvel
78a11d055eSArd Biesheuvel memcpy(dctx->buf + dctx->buflen, src, bytes);
79a11d055eSArd Biesheuvel src += bytes;
80a11d055eSArd Biesheuvel len -= bytes;
81a11d055eSArd Biesheuvel dctx->buflen += bytes;
82a11d055eSArd Biesheuvel
83a11d055eSArd Biesheuvel if (dctx->buflen == POLY1305_BLOCK_SIZE) {
84a11d055eSArd Biesheuvel mips_poly1305_blocks(dctx, dctx->buf, POLY1305_BLOCK_SIZE, 1);
85a11d055eSArd Biesheuvel dctx->buflen = 0;
86a11d055eSArd Biesheuvel }
87a11d055eSArd Biesheuvel }
88a11d055eSArd Biesheuvel
89a11d055eSArd Biesheuvel if (likely(len >= POLY1305_BLOCK_SIZE)) {
90a11d055eSArd Biesheuvel mips_poly1305_blocks(dctx, src, len, 1);
91a11d055eSArd Biesheuvel src += round_down(len, POLY1305_BLOCK_SIZE);
92a11d055eSArd Biesheuvel len %= POLY1305_BLOCK_SIZE;
93a11d055eSArd Biesheuvel }
94a11d055eSArd Biesheuvel
95a11d055eSArd Biesheuvel if (unlikely(len)) {
96a11d055eSArd Biesheuvel dctx->buflen = len;
97a11d055eSArd Biesheuvel memcpy(dctx->buf, src, len);
98a11d055eSArd Biesheuvel }
99a11d055eSArd Biesheuvel return 0;
100a11d055eSArd Biesheuvel }
101a11d055eSArd Biesheuvel
poly1305_update_arch(struct poly1305_desc_ctx * dctx,const u8 * src,unsigned int nbytes)102a11d055eSArd Biesheuvel void poly1305_update_arch(struct poly1305_desc_ctx *dctx, const u8 *src,
103a11d055eSArd Biesheuvel unsigned int nbytes)
104a11d055eSArd Biesheuvel {
105a11d055eSArd Biesheuvel if (unlikely(dctx->buflen)) {
106a11d055eSArd Biesheuvel u32 bytes = min(nbytes, POLY1305_BLOCK_SIZE - dctx->buflen);
107a11d055eSArd Biesheuvel
108a11d055eSArd Biesheuvel memcpy(dctx->buf + dctx->buflen, src, bytes);
109a11d055eSArd Biesheuvel src += bytes;
110a11d055eSArd Biesheuvel nbytes -= bytes;
111a11d055eSArd Biesheuvel dctx->buflen += bytes;
112a11d055eSArd Biesheuvel
113a11d055eSArd Biesheuvel if (dctx->buflen == POLY1305_BLOCK_SIZE) {
114a11d055eSArd Biesheuvel poly1305_blocks_mips(&dctx->h, dctx->buf,
115a11d055eSArd Biesheuvel POLY1305_BLOCK_SIZE, 1);
116a11d055eSArd Biesheuvel dctx->buflen = 0;
117a11d055eSArd Biesheuvel }
118a11d055eSArd Biesheuvel }
119a11d055eSArd Biesheuvel
120a11d055eSArd Biesheuvel if (likely(nbytes >= POLY1305_BLOCK_SIZE)) {
121a11d055eSArd Biesheuvel unsigned int len = round_down(nbytes, POLY1305_BLOCK_SIZE);
122a11d055eSArd Biesheuvel
123a11d055eSArd Biesheuvel poly1305_blocks_mips(&dctx->h, src, len, 1);
124a11d055eSArd Biesheuvel src += len;
125a11d055eSArd Biesheuvel nbytes %= POLY1305_BLOCK_SIZE;
126a11d055eSArd Biesheuvel }
127a11d055eSArd Biesheuvel
128a11d055eSArd Biesheuvel if (unlikely(nbytes)) {
129a11d055eSArd Biesheuvel dctx->buflen = nbytes;
130a11d055eSArd Biesheuvel memcpy(dctx->buf, src, nbytes);
131a11d055eSArd Biesheuvel }
132a11d055eSArd Biesheuvel }
133a11d055eSArd Biesheuvel EXPORT_SYMBOL(poly1305_update_arch);
134a11d055eSArd Biesheuvel
poly1305_final_arch(struct poly1305_desc_ctx * dctx,u8 * dst)135a11d055eSArd Biesheuvel void poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst)
136a11d055eSArd Biesheuvel {
137a11d055eSArd Biesheuvel if (unlikely(dctx->buflen)) {
138a11d055eSArd Biesheuvel dctx->buf[dctx->buflen++] = 1;
139a11d055eSArd Biesheuvel memset(dctx->buf + dctx->buflen, 0,
140a11d055eSArd Biesheuvel POLY1305_BLOCK_SIZE - dctx->buflen);
141a11d055eSArd Biesheuvel poly1305_blocks_mips(&dctx->h, dctx->buf, POLY1305_BLOCK_SIZE, 0);
142a11d055eSArd Biesheuvel }
143a11d055eSArd Biesheuvel
14431899908SJason A. Donenfeld poly1305_emit_mips(&dctx->h, dst, dctx->s);
145a11d055eSArd Biesheuvel *dctx = (struct poly1305_desc_ctx){};
146a11d055eSArd Biesheuvel }
147a11d055eSArd Biesheuvel EXPORT_SYMBOL(poly1305_final_arch);
148a11d055eSArd Biesheuvel
mips_poly1305_final(struct shash_desc * desc,u8 * dst)149a11d055eSArd Biesheuvel static int mips_poly1305_final(struct shash_desc *desc, u8 *dst)
150a11d055eSArd Biesheuvel {
151a11d055eSArd Biesheuvel struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
152a11d055eSArd Biesheuvel
153a11d055eSArd Biesheuvel if (unlikely(!dctx->sset))
154a11d055eSArd Biesheuvel return -ENOKEY;
155a11d055eSArd Biesheuvel
156a11d055eSArd Biesheuvel poly1305_final_arch(dctx, dst);
157a11d055eSArd Biesheuvel return 0;
158a11d055eSArd Biesheuvel }
159a11d055eSArd Biesheuvel
160a11d055eSArd Biesheuvel static struct shash_alg mips_poly1305_alg = {
161a11d055eSArd Biesheuvel .init = mips_poly1305_init,
162a11d055eSArd Biesheuvel .update = mips_poly1305_update,
163a11d055eSArd Biesheuvel .final = mips_poly1305_final,
164a11d055eSArd Biesheuvel .digestsize = POLY1305_DIGEST_SIZE,
165a11d055eSArd Biesheuvel .descsize = sizeof(struct poly1305_desc_ctx),
166a11d055eSArd Biesheuvel
167a11d055eSArd Biesheuvel .base.cra_name = "poly1305",
168a11d055eSArd Biesheuvel .base.cra_driver_name = "poly1305-mips",
169a11d055eSArd Biesheuvel .base.cra_priority = 200,
170a11d055eSArd Biesheuvel .base.cra_blocksize = POLY1305_BLOCK_SIZE,
171a11d055eSArd Biesheuvel .base.cra_module = THIS_MODULE,
172a11d055eSArd Biesheuvel };
173a11d055eSArd Biesheuvel
mips_poly1305_mod_init(void)174a11d055eSArd Biesheuvel static int __init mips_poly1305_mod_init(void)
175a11d055eSArd Biesheuvel {
1768394bfecSJason A. Donenfeld return IS_REACHABLE(CONFIG_CRYPTO_HASH) ?
1778394bfecSJason A. Donenfeld crypto_register_shash(&mips_poly1305_alg) : 0;
178a11d055eSArd Biesheuvel }
179a11d055eSArd Biesheuvel
mips_poly1305_mod_exit(void)180a11d055eSArd Biesheuvel static void __exit mips_poly1305_mod_exit(void)
181a11d055eSArd Biesheuvel {
1828394bfecSJason A. Donenfeld if (IS_REACHABLE(CONFIG_CRYPTO_HASH))
183a11d055eSArd Biesheuvel crypto_unregister_shash(&mips_poly1305_alg);
184a11d055eSArd Biesheuvel }
185a11d055eSArd Biesheuvel
186a11d055eSArd Biesheuvel module_init(mips_poly1305_mod_init);
187a11d055eSArd Biesheuvel module_exit(mips_poly1305_mod_exit);
188a11d055eSArd Biesheuvel
189a11d055eSArd Biesheuvel MODULE_LICENSE("GPL v2");
190a11d055eSArd Biesheuvel MODULE_ALIAS_CRYPTO("poly1305");
191a11d055eSArd Biesheuvel MODULE_ALIAS_CRYPTO("poly1305-mips");
192