diff options
author | Martin Willi <martin@strongswan.org> | 2015-06-01 07:43:58 -0400 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2015-06-04 03:04:50 -0400 |
commit | f979e014c50ce3f7467f133898dbea2243247a91 (patch) | |
tree | b9b3bb455c76a9a94e3fcbe7fa9726890e083942 /crypto/poly1305_generic.c | |
parent | 3590ebf2b4c40aa4b663c4f2b9dfeb0a1e0b8f32 (diff) |
crypto: poly1305 - Add a generic Poly1305 authenticator implementation
Poly1305 is a fast message authenticator designed by Daniel J. Bernstein.
It is further defined in RFC7539 as a building block for the ChaCha20-Poly1305
AEAD for use in IETF protocols.
This is a portable C implementation of the algorithm without architecture
specific optimizations, based on public domain code by Daniel J. Bernstein and
Andrew Moon.
Signed-off-by: Martin Willi <martin@strongswan.org>
Acked-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/poly1305_generic.c')
-rw-r--r-- | crypto/poly1305_generic.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/crypto/poly1305_generic.c b/crypto/poly1305_generic.c new file mode 100644 index 000000000000..9c1159b991f4 --- /dev/null +++ b/crypto/poly1305_generic.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | * Poly1305 authenticator algorithm, RFC7539 | ||
3 | * | ||
4 | * Copyright (C) 2015 Martin Willi | ||
5 | * | ||
6 | * Based on public domain code by Andrew Moon and Daniel J. Bernstein. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <crypto/algapi.h> | ||
15 | #include <crypto/internal/hash.h> | ||
16 | #include <linux/crypto.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | |||
20 | #define POLY1305_BLOCK_SIZE 16 | ||
21 | #define POLY1305_KEY_SIZE 32 | ||
22 | #define POLY1305_DIGEST_SIZE 16 | ||
23 | |||
24 | struct poly1305_ctx { | ||
25 | /* key */ | ||
26 | u32 r[5]; | ||
27 | /* finalize key */ | ||
28 | u32 s[4]; | ||
29 | }; | ||
30 | |||
31 | struct poly1305_desc_ctx { | ||
32 | /* accumulator */ | ||
33 | u32 h[5]; | ||
34 | /* partial buffer */ | ||
35 | u8 buf[POLY1305_BLOCK_SIZE]; | ||
36 | /* bytes used in partial buffer */ | ||
37 | unsigned int buflen; | ||
38 | }; | ||
39 | |||
40 | static inline u64 mlt(u64 a, u64 b) | ||
41 | { | ||
42 | return a * b; | ||
43 | } | ||
44 | |||
45 | static inline u32 sr(u64 v, u_char n) | ||
46 | { | ||
47 | return v >> n; | ||
48 | } | ||
49 | |||
50 | static inline u32 and(u32 v, u32 mask) | ||
51 | { | ||
52 | return v & mask; | ||
53 | } | ||
54 | |||
55 | static inline u32 le32_to_cpuvp(const void *p) | ||
56 | { | ||
57 | return le32_to_cpup(p); | ||
58 | } | ||
59 | |||
60 | static int poly1305_init(struct shash_desc *desc) | ||
61 | { | ||
62 | struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc); | ||
63 | |||
64 | memset(dctx->h, 0, sizeof(dctx->h)); | ||
65 | dctx->buflen = 0; | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static int poly1305_setkey(struct crypto_shash *tfm, | ||
71 | const u8 *key, unsigned int keylen) | ||
72 | { | ||
73 | struct poly1305_ctx *ctx = crypto_shash_ctx(tfm); | ||
74 | |||
75 | if (keylen != POLY1305_KEY_SIZE) { | ||
76 | crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); | ||
77 | return -EINVAL; | ||
78 | } | ||
79 | |||
80 | /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ | ||
81 | ctx->r[0] = (le32_to_cpuvp(key + 0) >> 0) & 0x3ffffff; | ||
82 | ctx->r[1] = (le32_to_cpuvp(key + 3) >> 2) & 0x3ffff03; | ||
83 | ctx->r[2] = (le32_to_cpuvp(key + 6) >> 4) & 0x3ffc0ff; | ||
84 | ctx->r[3] = (le32_to_cpuvp(key + 9) >> 6) & 0x3f03fff; | ||
85 | ctx->r[4] = (le32_to_cpuvp(key + 12) >> 8) & 0x00fffff; | ||
86 | |||
87 | ctx->s[0] = le32_to_cpuvp(key + 16); | ||
88 | ctx->s[1] = le32_to_cpuvp(key + 20); | ||
89 | ctx->s[2] = le32_to_cpuvp(key + 24); | ||
90 | ctx->s[3] = le32_to_cpuvp(key + 28); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static unsigned int poly1305_blocks(struct poly1305_desc_ctx *dctx, | ||
96 | struct poly1305_ctx *ctx, const u8 *src, | ||
97 | unsigned int srclen, u32 hibit) | ||
98 | { | ||
99 | u32 r0, r1, r2, r3, r4; | ||
100 | u32 s1, s2, s3, s4; | ||
101 | u32 h0, h1, h2, h3, h4; | ||
102 | u64 d0, d1, d2, d3, d4; | ||
103 | |||
104 | r0 = ctx->r[0]; | ||
105 | r1 = ctx->r[1]; | ||
106 | r2 = ctx->r[2]; | ||
107 | r3 = ctx->r[3]; | ||
108 | r4 = ctx->r[4]; | ||
109 | |||
110 | s1 = r1 * 5; | ||
111 | s2 = r2 * 5; | ||
112 | s3 = r3 * 5; | ||
113 | s4 = r4 * 5; | ||
114 | |||
115 | h0 = dctx->h[0]; | ||
116 | h1 = dctx->h[1]; | ||
117 | h2 = dctx->h[2]; | ||
118 | h3 = dctx->h[3]; | ||
119 | h4 = dctx->h[4]; | ||
120 | |||
121 | while (likely(srclen >= POLY1305_BLOCK_SIZE)) { | ||
122 | |||
123 | /* h += m[i] */ | ||
124 | h0 += (le32_to_cpuvp(src + 0) >> 0) & 0x3ffffff; | ||
125 | h1 += (le32_to_cpuvp(src + 3) >> 2) & 0x3ffffff; | ||
126 | h2 += (le32_to_cpuvp(src + 6) >> 4) & 0x3ffffff; | ||
127 | h3 += (le32_to_cpuvp(src + 9) >> 6) & 0x3ffffff; | ||
128 | h4 += (le32_to_cpuvp(src + 12) >> 8) | hibit; | ||
129 | |||
130 | /* h *= r */ | ||
131 | d0 = mlt(h0, r0) + mlt(h1, s4) + mlt(h2, s3) + | ||
132 | mlt(h3, s2) + mlt(h4, s1); | ||
133 | d1 = mlt(h0, r1) + mlt(h1, r0) + mlt(h2, s4) + | ||
134 | mlt(h3, s3) + mlt(h4, s2); | ||
135 | d2 = mlt(h0, r2) + mlt(h1, r1) + mlt(h2, r0) + | ||
136 | mlt(h3, s4) + mlt(h4, s3); | ||
137 | d3 = mlt(h0, r3) + mlt(h1, r2) + mlt(h2, r1) + | ||
138 | mlt(h3, r0) + mlt(h4, s4); | ||
139 | d4 = mlt(h0, r4) + mlt(h1, r3) + mlt(h2, r2) + | ||
140 | mlt(h3, r1) + mlt(h4, r0); | ||
141 | |||
142 | /* (partial) h %= p */ | ||
143 | d1 += sr(d0, 26); h0 = and(d0, 0x3ffffff); | ||
144 | d2 += sr(d1, 26); h1 = and(d1, 0x3ffffff); | ||
145 | d3 += sr(d2, 26); h2 = and(d2, 0x3ffffff); | ||
146 | d4 += sr(d3, 26); h3 = and(d3, 0x3ffffff); | ||
147 | h0 += sr(d4, 26) * 5; h4 = and(d4, 0x3ffffff); | ||
148 | h1 += h0 >> 26; h0 = h0 & 0x3ffffff; | ||
149 | |||
150 | src += POLY1305_BLOCK_SIZE; | ||
151 | srclen -= POLY1305_BLOCK_SIZE; | ||
152 | } | ||
153 | |||
154 | dctx->h[0] = h0; | ||
155 | dctx->h[1] = h1; | ||
156 | dctx->h[2] = h2; | ||
157 | dctx->h[3] = h3; | ||
158 | dctx->h[4] = h4; | ||
159 | |||
160 | return srclen; | ||
161 | } | ||
162 | |||
163 | static int poly1305_update(struct shash_desc *desc, | ||
164 | const u8 *src, unsigned int srclen) | ||
165 | { | ||
166 | struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc); | ||
167 | struct poly1305_ctx *ctx = crypto_shash_ctx(desc->tfm); | ||
168 | unsigned int bytes; | ||
169 | |||
170 | if (unlikely(dctx->buflen)) { | ||
171 | bytes = min(srclen, POLY1305_BLOCK_SIZE - dctx->buflen); | ||
172 | memcpy(dctx->buf + dctx->buflen, src, bytes); | ||
173 | src += bytes; | ||
174 | srclen -= bytes; | ||
175 | dctx->buflen += bytes; | ||
176 | |||
177 | if (dctx->buflen == POLY1305_BLOCK_SIZE) { | ||
178 | poly1305_blocks(dctx, ctx, dctx->buf, | ||
179 | POLY1305_BLOCK_SIZE, 1 << 24); | ||
180 | dctx->buflen = 0; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if (likely(srclen >= POLY1305_BLOCK_SIZE)) { | ||
185 | bytes = poly1305_blocks(dctx, ctx, src, srclen, 1 << 24); | ||
186 | src += srclen - bytes; | ||
187 | srclen = bytes; | ||
188 | } | ||
189 | |||
190 | if (unlikely(srclen)) { | ||
191 | dctx->buflen = srclen; | ||
192 | memcpy(dctx->buf, src, srclen); | ||
193 | } | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int poly1305_final(struct shash_desc *desc, u8 *dst) | ||
199 | { | ||
200 | struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc); | ||
201 | struct poly1305_ctx *ctx = crypto_shash_ctx(desc->tfm); | ||
202 | __le32 *mac = (__le32 *)dst; | ||
203 | u32 h0, h1, h2, h3, h4; | ||
204 | u32 g0, g1, g2, g3, g4; | ||
205 | u32 mask; | ||
206 | u64 f = 0; | ||
207 | |||
208 | if (unlikely(dctx->buflen)) { | ||
209 | dctx->buf[dctx->buflen++] = 1; | ||
210 | memset(dctx->buf + dctx->buflen, 0, | ||
211 | POLY1305_BLOCK_SIZE - dctx->buflen); | ||
212 | poly1305_blocks(dctx, ctx, dctx->buf, POLY1305_BLOCK_SIZE, 0); | ||
213 | } | ||
214 | |||
215 | /* fully carry h */ | ||
216 | h0 = dctx->h[0]; | ||
217 | h1 = dctx->h[1]; | ||
218 | h2 = dctx->h[2]; | ||
219 | h3 = dctx->h[3]; | ||
220 | h4 = dctx->h[4]; | ||
221 | |||
222 | h2 += (h1 >> 26); h1 = h1 & 0x3ffffff; | ||
223 | h3 += (h2 >> 26); h2 = h2 & 0x3ffffff; | ||
224 | h4 += (h3 >> 26); h3 = h3 & 0x3ffffff; | ||
225 | h0 += (h4 >> 26) * 5; h4 = h4 & 0x3ffffff; | ||
226 | h1 += (h0 >> 26); h0 = h0 & 0x3ffffff; | ||
227 | |||
228 | /* compute h + -p */ | ||
229 | g0 = h0 + 5; | ||
230 | g1 = h1 + (g0 >> 26); g0 &= 0x3ffffff; | ||
231 | g2 = h2 + (g1 >> 26); g1 &= 0x3ffffff; | ||
232 | g3 = h3 + (g2 >> 26); g2 &= 0x3ffffff; | ||
233 | g4 = h4 + (g3 >> 26) - (1 << 26); g3 &= 0x3ffffff; | ||
234 | |||
235 | /* select h if h < p, or h + -p if h >= p */ | ||
236 | mask = (g4 >> ((sizeof(u32) * 8) - 1)) - 1; | ||
237 | g0 &= mask; | ||
238 | g1 &= mask; | ||
239 | g2 &= mask; | ||
240 | g3 &= mask; | ||
241 | g4 &= mask; | ||
242 | mask = ~mask; | ||
243 | h0 = (h0 & mask) | g0; | ||
244 | h1 = (h1 & mask) | g1; | ||
245 | h2 = (h2 & mask) | g2; | ||
246 | h3 = (h3 & mask) | g3; | ||
247 | h4 = (h4 & mask) | g4; | ||
248 | |||
249 | /* h = h % (2^128) */ | ||
250 | h0 = (h0 >> 0) | (h1 << 26); | ||
251 | h1 = (h1 >> 6) | (h2 << 20); | ||
252 | h2 = (h2 >> 12) | (h3 << 14); | ||
253 | h3 = (h3 >> 18) | (h4 << 8); | ||
254 | |||
255 | /* mac = (h + s) % (2^128) */ | ||
256 | f = (f >> 32) + h0 + ctx->s[0]; mac[0] = cpu_to_le32(f); | ||
257 | f = (f >> 32) + h1 + ctx->s[1]; mac[1] = cpu_to_le32(f); | ||
258 | f = (f >> 32) + h2 + ctx->s[2]; mac[2] = cpu_to_le32(f); | ||
259 | f = (f >> 32) + h3 + ctx->s[3]; mac[3] = cpu_to_le32(f); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static struct shash_alg poly1305_alg = { | ||
265 | .digestsize = POLY1305_DIGEST_SIZE, | ||
266 | .init = poly1305_init, | ||
267 | .update = poly1305_update, | ||
268 | .final = poly1305_final, | ||
269 | .setkey = poly1305_setkey, | ||
270 | .descsize = sizeof(struct poly1305_desc_ctx), | ||
271 | .base = { | ||
272 | .cra_name = "poly1305", | ||
273 | .cra_driver_name = "poly1305-generic", | ||
274 | .cra_priority = 100, | ||
275 | .cra_flags = CRYPTO_ALG_TYPE_SHASH, | ||
276 | .cra_alignmask = sizeof(u32) - 1, | ||
277 | .cra_blocksize = POLY1305_BLOCK_SIZE, | ||
278 | .cra_ctxsize = sizeof(struct poly1305_ctx), | ||
279 | .cra_module = THIS_MODULE, | ||
280 | }, | ||
281 | }; | ||
282 | |||
283 | static int __init poly1305_mod_init(void) | ||
284 | { | ||
285 | return crypto_register_shash(&poly1305_alg); | ||
286 | } | ||
287 | |||
288 | static void __exit poly1305_mod_exit(void) | ||
289 | { | ||
290 | crypto_unregister_shash(&poly1305_alg); | ||
291 | } | ||
292 | |||
293 | module_init(poly1305_mod_init); | ||
294 | module_exit(poly1305_mod_exit); | ||
295 | |||
296 | MODULE_LICENSE("GPL"); | ||
297 | MODULE_AUTHOR("Martin Willi <martin@strongswan.org>"); | ||
298 | MODULE_DESCRIPTION("Poly1305 authenticator"); | ||
299 | MODULE_ALIAS_CRYPTO("poly1305"); | ||
300 | MODULE_ALIAS_CRYPTO("poly1305-generic"); | ||