diff options
author | Eric Biggers <ebiggers@google.com> | 2018-11-16 20:26:29 -0500 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2018-11-20 01:26:56 -0500 |
commit | 26609a21a9460145e37d90947ad957b358a05288 (patch) | |
tree | 9f45456ea58fde4464ea71910acf0faf55214555 /crypto/nhpoly1305.c | |
parent | 1b6fd3d5d18bbc1b1abf3b0cbc4b95a9a63d407b (diff) |
crypto: nhpoly1305 - add NHPoly1305 support
Add a generic implementation of NHPoly1305, an ε-almost-∆-universal hash
function used in the Adiantum encryption mode.
CONFIG_NHPOLY1305 is not selectable by itself since there won't be any
real reason to enable it without also enabling Adiantum support.
Signed-off-by: Eric Biggers <ebiggers@google.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/nhpoly1305.c')
-rw-r--r-- | crypto/nhpoly1305.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/crypto/nhpoly1305.c b/crypto/nhpoly1305.c new file mode 100644 index 000000000000..c8385853f699 --- /dev/null +++ b/crypto/nhpoly1305.c | |||
@@ -0,0 +1,254 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * NHPoly1305 - ε-almost-∆-universal hash function for Adiantum | ||
4 | * | ||
5 | * Copyright 2018 Google LLC | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * "NHPoly1305" is the main component of Adiantum hashing. | ||
10 | * Specifically, it is the calculation | ||
11 | * | ||
12 | * H_M ← Poly1305_{K_M}(NH_{K_N}(pad_{128}(M))) | ||
13 | * | ||
14 | * from the procedure in section A.5 of the Adiantum paper [1]. It is an | ||
15 | * ε-almost-∆-universal (εA∆U) hash function for equal-length inputs over | ||
16 | * Z/(2^{128}Z), where the "∆" operation is addition. It hashes 1024-byte | ||
17 | * chunks of the input with the NH hash function [2], reducing the input length | ||
18 | * by 32x. The resulting NH digests are evaluated as a polynomial in | ||
19 | * GF(2^{130}-5), like in the Poly1305 MAC [3]. Note that the polynomial | ||
20 | * evaluation by itself would suffice to achieve the εA∆U property; NH is used | ||
21 | * for performance since it's over twice as fast as Poly1305. | ||
22 | * | ||
23 | * This is *not* a cryptographic hash function; do not use it as such! | ||
24 | * | ||
25 | * [1] Adiantum: length-preserving encryption for entry-level processors | ||
26 | * (https://eprint.iacr.org/2018/720.pdf) | ||
27 | * [2] UMAC: Fast and Secure Message Authentication | ||
28 | * (https://fastcrypto.org/umac/umac_proc.pdf) | ||
29 | * [3] The Poly1305-AES message-authentication code | ||
30 | * (https://cr.yp.to/mac/poly1305-20050329.pdf) | ||
31 | */ | ||
32 | |||
33 | #include <asm/unaligned.h> | ||
34 | #include <crypto/algapi.h> | ||
35 | #include <crypto/internal/hash.h> | ||
36 | #include <crypto/nhpoly1305.h> | ||
37 | #include <linux/crypto.h> | ||
38 | #include <linux/kernel.h> | ||
39 | #include <linux/module.h> | ||
40 | |||
41 | static void nh_generic(const u32 *key, const u8 *message, size_t message_len, | ||
42 | __le64 hash[NH_NUM_PASSES]) | ||
43 | { | ||
44 | u64 sums[4] = { 0, 0, 0, 0 }; | ||
45 | |||
46 | BUILD_BUG_ON(NH_PAIR_STRIDE != 2); | ||
47 | BUILD_BUG_ON(NH_NUM_PASSES != 4); | ||
48 | |||
49 | while (message_len) { | ||
50 | u32 m0 = get_unaligned_le32(message + 0); | ||
51 | u32 m1 = get_unaligned_le32(message + 4); | ||
52 | u32 m2 = get_unaligned_le32(message + 8); | ||
53 | u32 m3 = get_unaligned_le32(message + 12); | ||
54 | |||
55 | sums[0] += (u64)(u32)(m0 + key[ 0]) * (u32)(m2 + key[ 2]); | ||
56 | sums[1] += (u64)(u32)(m0 + key[ 4]) * (u32)(m2 + key[ 6]); | ||
57 | sums[2] += (u64)(u32)(m0 + key[ 8]) * (u32)(m2 + key[10]); | ||
58 | sums[3] += (u64)(u32)(m0 + key[12]) * (u32)(m2 + key[14]); | ||
59 | sums[0] += (u64)(u32)(m1 + key[ 1]) * (u32)(m3 + key[ 3]); | ||
60 | sums[1] += (u64)(u32)(m1 + key[ 5]) * (u32)(m3 + key[ 7]); | ||
61 | sums[2] += (u64)(u32)(m1 + key[ 9]) * (u32)(m3 + key[11]); | ||
62 | sums[3] += (u64)(u32)(m1 + key[13]) * (u32)(m3 + key[15]); | ||
63 | key += NH_MESSAGE_UNIT / sizeof(key[0]); | ||
64 | message += NH_MESSAGE_UNIT; | ||
65 | message_len -= NH_MESSAGE_UNIT; | ||
66 | } | ||
67 | |||
68 | hash[0] = cpu_to_le64(sums[0]); | ||
69 | hash[1] = cpu_to_le64(sums[1]); | ||
70 | hash[2] = cpu_to_le64(sums[2]); | ||
71 | hash[3] = cpu_to_le64(sums[3]); | ||
72 | } | ||
73 | |||
74 | /* Pass the next NH hash value through Poly1305 */ | ||
75 | static void process_nh_hash_value(struct nhpoly1305_state *state, | ||
76 | const struct nhpoly1305_key *key) | ||
77 | { | ||
78 | BUILD_BUG_ON(NH_HASH_BYTES % POLY1305_BLOCK_SIZE != 0); | ||
79 | |||
80 | poly1305_core_blocks(&state->poly_state, &key->poly_key, state->nh_hash, | ||
81 | NH_HASH_BYTES / POLY1305_BLOCK_SIZE); | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * Feed the next portion of the source data, as a whole number of 16-byte | ||
86 | * "NH message units", through NH and Poly1305. Each NH hash is taken over | ||
87 | * 1024 bytes, except possibly the final one which is taken over a multiple of | ||
88 | * 16 bytes up to 1024. Also, in the case where data is passed in misaligned | ||
89 | * chunks, we combine partial hashes; the end result is the same either way. | ||
90 | */ | ||
91 | static void nhpoly1305_units(struct nhpoly1305_state *state, | ||
92 | const struct nhpoly1305_key *key, | ||
93 | const u8 *src, unsigned int srclen, nh_t nh_fn) | ||
94 | { | ||
95 | do { | ||
96 | unsigned int bytes; | ||
97 | |||
98 | if (state->nh_remaining == 0) { | ||
99 | /* Starting a new NH message */ | ||
100 | bytes = min_t(unsigned int, srclen, NH_MESSAGE_BYTES); | ||
101 | nh_fn(key->nh_key, src, bytes, state->nh_hash); | ||
102 | state->nh_remaining = NH_MESSAGE_BYTES - bytes; | ||
103 | } else { | ||
104 | /* Continuing a previous NH message */ | ||
105 | __le64 tmp_hash[NH_NUM_PASSES]; | ||
106 | unsigned int pos; | ||
107 | int i; | ||
108 | |||
109 | pos = NH_MESSAGE_BYTES - state->nh_remaining; | ||
110 | bytes = min(srclen, state->nh_remaining); | ||
111 | nh_fn(&key->nh_key[pos / 4], src, bytes, tmp_hash); | ||
112 | for (i = 0; i < NH_NUM_PASSES; i++) | ||
113 | le64_add_cpu(&state->nh_hash[i], | ||
114 | le64_to_cpu(tmp_hash[i])); | ||
115 | state->nh_remaining -= bytes; | ||
116 | } | ||
117 | if (state->nh_remaining == 0) | ||
118 | process_nh_hash_value(state, key); | ||
119 | src += bytes; | ||
120 | srclen -= bytes; | ||
121 | } while (srclen); | ||
122 | } | ||
123 | |||
124 | int crypto_nhpoly1305_setkey(struct crypto_shash *tfm, | ||
125 | const u8 *key, unsigned int keylen) | ||
126 | { | ||
127 | struct nhpoly1305_key *ctx = crypto_shash_ctx(tfm); | ||
128 | int i; | ||
129 | |||
130 | if (keylen != NHPOLY1305_KEY_SIZE) | ||
131 | return -EINVAL; | ||
132 | |||
133 | poly1305_core_setkey(&ctx->poly_key, key); | ||
134 | key += POLY1305_BLOCK_SIZE; | ||
135 | |||
136 | for (i = 0; i < NH_KEY_WORDS; i++) | ||
137 | ctx->nh_key[i] = get_unaligned_le32(key + i * sizeof(u32)); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | EXPORT_SYMBOL(crypto_nhpoly1305_setkey); | ||
142 | |||
143 | int crypto_nhpoly1305_init(struct shash_desc *desc) | ||
144 | { | ||
145 | struct nhpoly1305_state *state = shash_desc_ctx(desc); | ||
146 | |||
147 | poly1305_core_init(&state->poly_state); | ||
148 | state->buflen = 0; | ||
149 | state->nh_remaining = 0; | ||
150 | return 0; | ||
151 | } | ||
152 | EXPORT_SYMBOL(crypto_nhpoly1305_init); | ||
153 | |||
154 | int crypto_nhpoly1305_update_helper(struct shash_desc *desc, | ||
155 | const u8 *src, unsigned int srclen, | ||
156 | nh_t nh_fn) | ||
157 | { | ||
158 | struct nhpoly1305_state *state = shash_desc_ctx(desc); | ||
159 | const struct nhpoly1305_key *key = crypto_shash_ctx(desc->tfm); | ||
160 | unsigned int bytes; | ||
161 | |||
162 | if (state->buflen) { | ||
163 | bytes = min(srclen, (int)NH_MESSAGE_UNIT - state->buflen); | ||
164 | memcpy(&state->buffer[state->buflen], src, bytes); | ||
165 | state->buflen += bytes; | ||
166 | if (state->buflen < NH_MESSAGE_UNIT) | ||
167 | return 0; | ||
168 | nhpoly1305_units(state, key, state->buffer, NH_MESSAGE_UNIT, | ||
169 | nh_fn); | ||
170 | state->buflen = 0; | ||
171 | src += bytes; | ||
172 | srclen -= bytes; | ||
173 | } | ||
174 | |||
175 | if (srclen >= NH_MESSAGE_UNIT) { | ||
176 | bytes = round_down(srclen, NH_MESSAGE_UNIT); | ||
177 | nhpoly1305_units(state, key, src, bytes, nh_fn); | ||
178 | src += bytes; | ||
179 | srclen -= bytes; | ||
180 | } | ||
181 | |||
182 | if (srclen) { | ||
183 | memcpy(state->buffer, src, srclen); | ||
184 | state->buflen = srclen; | ||
185 | } | ||
186 | return 0; | ||
187 | } | ||
188 | EXPORT_SYMBOL(crypto_nhpoly1305_update_helper); | ||
189 | |||
190 | int crypto_nhpoly1305_update(struct shash_desc *desc, | ||
191 | const u8 *src, unsigned int srclen) | ||
192 | { | ||
193 | return crypto_nhpoly1305_update_helper(desc, src, srclen, nh_generic); | ||
194 | } | ||
195 | EXPORT_SYMBOL(crypto_nhpoly1305_update); | ||
196 | |||
197 | int crypto_nhpoly1305_final_helper(struct shash_desc *desc, u8 *dst, nh_t nh_fn) | ||
198 | { | ||
199 | struct nhpoly1305_state *state = shash_desc_ctx(desc); | ||
200 | const struct nhpoly1305_key *key = crypto_shash_ctx(desc->tfm); | ||
201 | |||
202 | if (state->buflen) { | ||
203 | memset(&state->buffer[state->buflen], 0, | ||
204 | NH_MESSAGE_UNIT - state->buflen); | ||
205 | nhpoly1305_units(state, key, state->buffer, NH_MESSAGE_UNIT, | ||
206 | nh_fn); | ||
207 | } | ||
208 | |||
209 | if (state->nh_remaining) | ||
210 | process_nh_hash_value(state, key); | ||
211 | |||
212 | poly1305_core_emit(&state->poly_state, dst); | ||
213 | return 0; | ||
214 | } | ||
215 | EXPORT_SYMBOL(crypto_nhpoly1305_final_helper); | ||
216 | |||
217 | int crypto_nhpoly1305_final(struct shash_desc *desc, u8 *dst) | ||
218 | { | ||
219 | return crypto_nhpoly1305_final_helper(desc, dst, nh_generic); | ||
220 | } | ||
221 | EXPORT_SYMBOL(crypto_nhpoly1305_final); | ||
222 | |||
223 | static struct shash_alg nhpoly1305_alg = { | ||
224 | .base.cra_name = "nhpoly1305", | ||
225 | .base.cra_driver_name = "nhpoly1305-generic", | ||
226 | .base.cra_priority = 100, | ||
227 | .base.cra_ctxsize = sizeof(struct nhpoly1305_key), | ||
228 | .base.cra_module = THIS_MODULE, | ||
229 | .digestsize = POLY1305_DIGEST_SIZE, | ||
230 | .init = crypto_nhpoly1305_init, | ||
231 | .update = crypto_nhpoly1305_update, | ||
232 | .final = crypto_nhpoly1305_final, | ||
233 | .setkey = crypto_nhpoly1305_setkey, | ||
234 | .descsize = sizeof(struct nhpoly1305_state), | ||
235 | }; | ||
236 | |||
237 | static int __init nhpoly1305_mod_init(void) | ||
238 | { | ||
239 | return crypto_register_shash(&nhpoly1305_alg); | ||
240 | } | ||
241 | |||
242 | static void __exit nhpoly1305_mod_exit(void) | ||
243 | { | ||
244 | crypto_unregister_shash(&nhpoly1305_alg); | ||
245 | } | ||
246 | |||
247 | module_init(nhpoly1305_mod_init); | ||
248 | module_exit(nhpoly1305_mod_exit); | ||
249 | |||
250 | MODULE_DESCRIPTION("NHPoly1305 ε-almost-∆-universal hash function"); | ||
251 | MODULE_LICENSE("GPL v2"); | ||
252 | MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>"); | ||
253 | MODULE_ALIAS_CRYPTO("nhpoly1305"); | ||
254 | MODULE_ALIAS_CRYPTO("nhpoly1305-generic"); | ||