diff options
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/Kconfig | 1 | ||||
-rw-r--r-- | crypto/hmac.c | 241 |
2 files changed, 236 insertions, 6 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig index 69c5f992bcd4..f07d9237950f 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig | |||
@@ -34,6 +34,7 @@ config CRYPTO_MANAGER | |||
34 | 34 | ||
35 | config CRYPTO_HMAC | 35 | config CRYPTO_HMAC |
36 | bool "HMAC support" | 36 | bool "HMAC support" |
37 | select CRYPTO_HASH | ||
37 | help | 38 | help |
38 | HMAC: Keyed-Hashing for Message Authentication (RFC2104). | 39 | HMAC: Keyed-Hashing for Message Authentication (RFC2104). |
39 | This is required for IPSec. | 40 | This is required for IPSec. |
diff --git a/crypto/hmac.c b/crypto/hmac.c index ecf7b0a95b56..eac77e294740 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c | |||
@@ -4,22 +4,30 @@ | |||
4 | * HMAC: Keyed-Hashing for Message Authentication (RFC2104). | 4 | * HMAC: Keyed-Hashing for Message Authentication (RFC2104). |
5 | * | 5 | * |
6 | * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> | 6 | * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> |
7 | * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> | ||
7 | * | 8 | * |
8 | * The HMAC implementation is derived from USAGI. | 9 | * The HMAC implementation is derived from USAGI. |
9 | * Copyright (c) 2002 Kazunori Miyazawa <miyazawa@linux-ipv6.org> / USAGI | 10 | * Copyright (c) 2002 Kazunori Miyazawa <miyazawa@linux-ipv6.org> / USAGI |
10 | * | 11 | * |
11 | * This program is free software; you can redistribute it and/or modify it | 12 | * This program is free software; you can redistribute it and/or modify it |
12 | * under the terms of the GNU General Public License as published by the Free | 13 | * under the terms of the GNU General Public License as published by the Free |
13 | * Software Foundation; either version 2 of the License, or (at your option) | 14 | * Software Foundation; either version 2 of the License, or (at your option) |
14 | * any later version. | 15 | * any later version. |
15 | * | 16 | * |
16 | */ | 17 | */ |
17 | #include <linux/crypto.h> | 18 | |
18 | #include <linux/mm.h> | 19 | #include <crypto/algapi.h> |
19 | #include <linux/highmem.h> | 20 | #include <linux/err.h> |
20 | #include <linux/slab.h> | 21 | #include <linux/init.h> |
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
21 | #include <linux/scatterlist.h> | 24 | #include <linux/scatterlist.h> |
22 | #include "internal.h" | 25 | #include <linux/slab.h> |
26 | #include <linux/string.h> | ||
27 | |||
28 | struct hmac_ctx { | ||
29 | struct crypto_hash *child; | ||
30 | }; | ||
23 | 31 | ||
24 | static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen) | 32 | static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen) |
25 | { | 33 | { |
@@ -122,3 +130,224 @@ EXPORT_SYMBOL_GPL(crypto_hmac_update); | |||
122 | EXPORT_SYMBOL_GPL(crypto_hmac_final); | 130 | EXPORT_SYMBOL_GPL(crypto_hmac_final); |
123 | EXPORT_SYMBOL_GPL(crypto_hmac); | 131 | EXPORT_SYMBOL_GPL(crypto_hmac); |
124 | 132 | ||
133 | static inline void *align_ptr(void *p, unsigned int align) | ||
134 | { | ||
135 | return (void *)ALIGN((unsigned long)p, align); | ||
136 | } | ||
137 | |||
138 | static inline struct hmac_ctx *hmac_ctx(struct crypto_hash *tfm) | ||
139 | { | ||
140 | return align_ptr(crypto_hash_ctx_aligned(tfm) + | ||
141 | crypto_hash_blocksize(tfm) * 2 + | ||
142 | crypto_hash_digestsize(tfm), sizeof(void *)); | ||
143 | } | ||
144 | |||
145 | static int hmac_setkey(struct crypto_hash *parent, | ||
146 | const u8 *inkey, unsigned int keylen) | ||
147 | { | ||
148 | int bs = crypto_hash_blocksize(parent); | ||
149 | int ds = crypto_hash_digestsize(parent); | ||
150 | char *ipad = crypto_hash_ctx_aligned(parent); | ||
151 | char *opad = ipad + bs; | ||
152 | char *digest = opad + bs; | ||
153 | struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *)); | ||
154 | struct crypto_hash *tfm = ctx->child; | ||
155 | unsigned int i; | ||
156 | |||
157 | if (keylen > bs) { | ||
158 | struct hash_desc desc; | ||
159 | struct scatterlist tmp; | ||
160 | int err; | ||
161 | |||
162 | desc.tfm = tfm; | ||
163 | desc.flags = crypto_hash_get_flags(parent); | ||
164 | desc.flags &= CRYPTO_TFM_REQ_MAY_SLEEP; | ||
165 | sg_set_buf(&tmp, inkey, keylen); | ||
166 | |||
167 | err = crypto_hash_digest(&desc, &tmp, keylen, digest); | ||
168 | if (err) | ||
169 | return err; | ||
170 | |||
171 | inkey = digest; | ||
172 | keylen = ds; | ||
173 | } | ||
174 | |||
175 | memcpy(ipad, inkey, keylen); | ||
176 | memset(ipad + keylen, 0, bs - keylen); | ||
177 | memcpy(opad, ipad, bs); | ||
178 | |||
179 | for (i = 0; i < bs; i++) { | ||
180 | ipad[i] ^= 0x36; | ||
181 | opad[i] ^= 0x5c; | ||
182 | } | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int hmac_init(struct hash_desc *pdesc) | ||
188 | { | ||
189 | struct crypto_hash *parent = pdesc->tfm; | ||
190 | int bs = crypto_hash_blocksize(parent); | ||
191 | int ds = crypto_hash_digestsize(parent); | ||
192 | char *ipad = crypto_hash_ctx_aligned(parent); | ||
193 | struct hmac_ctx *ctx = align_ptr(ipad + bs * 2 + ds, sizeof(void *)); | ||
194 | struct hash_desc desc; | ||
195 | struct scatterlist tmp; | ||
196 | |||
197 | desc.tfm = ctx->child; | ||
198 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; | ||
199 | sg_set_buf(&tmp, ipad, bs); | ||
200 | |||
201 | return unlikely(crypto_hash_init(&desc)) ?: | ||
202 | crypto_hash_update(&desc, &tmp, 1); | ||
203 | } | ||
204 | |||
205 | static int hmac_update(struct hash_desc *pdesc, | ||
206 | struct scatterlist *sg, unsigned int nbytes) | ||
207 | { | ||
208 | struct hmac_ctx *ctx = hmac_ctx(pdesc->tfm); | ||
209 | struct hash_desc desc; | ||
210 | |||
211 | desc.tfm = ctx->child; | ||
212 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; | ||
213 | |||
214 | return crypto_hash_update(&desc, sg, nbytes); | ||
215 | } | ||
216 | |||
217 | static int hmac_final(struct hash_desc *pdesc, u8 *out) | ||
218 | { | ||
219 | struct crypto_hash *parent = pdesc->tfm; | ||
220 | int bs = crypto_hash_blocksize(parent); | ||
221 | int ds = crypto_hash_digestsize(parent); | ||
222 | char *opad = crypto_hash_ctx_aligned(parent) + bs; | ||
223 | char *digest = opad + bs; | ||
224 | struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *)); | ||
225 | struct hash_desc desc; | ||
226 | struct scatterlist tmp; | ||
227 | |||
228 | desc.tfm = ctx->child; | ||
229 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; | ||
230 | sg_set_buf(&tmp, opad, bs + ds); | ||
231 | |||
232 | return unlikely(crypto_hash_final(&desc, digest)) ?: | ||
233 | crypto_hash_digest(&desc, &tmp, bs + ds, out); | ||
234 | } | ||
235 | |||
236 | static int hmac_digest(struct hash_desc *pdesc, struct scatterlist *sg, | ||
237 | unsigned int nbytes, u8 *out) | ||
238 | { | ||
239 | struct crypto_hash *parent = pdesc->tfm; | ||
240 | int bs = crypto_hash_blocksize(parent); | ||
241 | int ds = crypto_hash_digestsize(parent); | ||
242 | char *ipad = crypto_hash_ctx_aligned(parent); | ||
243 | char *opad = ipad + bs; | ||
244 | char *digest = opad + bs; | ||
245 | struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *)); | ||
246 | struct hash_desc desc; | ||
247 | struct scatterlist sg1[2]; | ||
248 | struct scatterlist sg2[1]; | ||
249 | |||
250 | desc.tfm = ctx->child; | ||
251 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; | ||
252 | |||
253 | sg_set_buf(sg1, ipad, bs); | ||
254 | sg1[1].page = (void *)sg; | ||
255 | sg1[1].length = 0; | ||
256 | sg_set_buf(sg2, opad, bs + ds); | ||
257 | |||
258 | return unlikely(crypto_hash_digest(&desc, sg1, nbytes + bs, digest)) ?: | ||
259 | crypto_hash_digest(&desc, sg2, bs + ds, out); | ||
260 | } | ||
261 | |||
262 | static int hmac_init_tfm(struct crypto_tfm *tfm) | ||
263 | { | ||
264 | struct crypto_instance *inst = (void *)tfm->__crt_alg; | ||
265 | struct crypto_spawn *spawn = crypto_instance_ctx(inst); | ||
266 | struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm)); | ||
267 | |||
268 | tfm = crypto_spawn_tfm(spawn); | ||
269 | if (IS_ERR(tfm)) | ||
270 | return PTR_ERR(tfm); | ||
271 | |||
272 | ctx->child = crypto_hash_cast(tfm); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static void hmac_exit_tfm(struct crypto_tfm *tfm) | ||
277 | { | ||
278 | struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm)); | ||
279 | crypto_free_hash(ctx->child); | ||
280 | } | ||
281 | |||
282 | static void hmac_free(struct crypto_instance *inst) | ||
283 | { | ||
284 | crypto_drop_spawn(crypto_instance_ctx(inst)); | ||
285 | kfree(inst); | ||
286 | } | ||
287 | |||
288 | static struct crypto_instance *hmac_alloc(void *param, unsigned int len) | ||
289 | { | ||
290 | struct crypto_instance *inst; | ||
291 | struct crypto_alg *alg; | ||
292 | |||
293 | alg = crypto_get_attr_alg(param, len, CRYPTO_ALG_TYPE_HASH, | ||
294 | CRYPTO_ALG_TYPE_HASH_MASK | CRYPTO_ALG_ASYNC); | ||
295 | if (IS_ERR(alg)) | ||
296 | return ERR_PTR(PTR_ERR(alg)); | ||
297 | |||
298 | inst = crypto_alloc_instance("hmac", alg); | ||
299 | if (IS_ERR(inst)) | ||
300 | goto out_put_alg; | ||
301 | |||
302 | inst->alg.cra_flags = CRYPTO_ALG_TYPE_HASH; | ||
303 | inst->alg.cra_priority = alg->cra_priority; | ||
304 | inst->alg.cra_blocksize = alg->cra_blocksize; | ||
305 | inst->alg.cra_alignmask = alg->cra_alignmask; | ||
306 | inst->alg.cra_type = &crypto_hash_type; | ||
307 | |||
308 | inst->alg.cra_hash.digestsize = | ||
309 | (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == | ||
310 | CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize : | ||
311 | alg->cra_digest.dia_digestsize; | ||
312 | |||
313 | inst->alg.cra_ctxsize = sizeof(struct hmac_ctx) + | ||
314 | ALIGN(inst->alg.cra_blocksize * 2 + | ||
315 | inst->alg.cra_hash.digestsize, | ||
316 | sizeof(void *)); | ||
317 | |||
318 | inst->alg.cra_init = hmac_init_tfm; | ||
319 | inst->alg.cra_exit = hmac_exit_tfm; | ||
320 | |||
321 | inst->alg.cra_hash.init = hmac_init; | ||
322 | inst->alg.cra_hash.update = hmac_update; | ||
323 | inst->alg.cra_hash.final = hmac_final; | ||
324 | inst->alg.cra_hash.digest = hmac_digest; | ||
325 | inst->alg.cra_hash.setkey = hmac_setkey; | ||
326 | |||
327 | out_put_alg: | ||
328 | crypto_mod_put(alg); | ||
329 | return inst; | ||
330 | } | ||
331 | |||
332 | static struct crypto_template hmac_tmpl = { | ||
333 | .name = "hmac", | ||
334 | .alloc = hmac_alloc, | ||
335 | .free = hmac_free, | ||
336 | .module = THIS_MODULE, | ||
337 | }; | ||
338 | |||
339 | static int __init hmac_module_init(void) | ||
340 | { | ||
341 | return crypto_register_template(&hmac_tmpl); | ||
342 | } | ||
343 | |||
344 | static void __exit hmac_module_exit(void) | ||
345 | { | ||
346 | crypto_unregister_template(&hmac_tmpl); | ||
347 | } | ||
348 | |||
349 | module_init(hmac_module_init); | ||
350 | module_exit(hmac_module_exit); | ||
351 | |||
352 | MODULE_LICENSE("GPL"); | ||
353 | MODULE_DESCRIPTION("HMAC hash algorithm"); | ||