aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2006-08-21 06:50:52 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2006-09-20 21:46:17 -0400
commit0796ae061e6da5de7cfc1af57dfd42a73908b1bf (patch)
tree83832b65f93f2979483640d994d72f8b37860701
parent055bcee3102dc35f019b69df9c2618e9d6dd1c09 (diff)
[CRYPTO] hmac: Add crypto template implementation
This patch rewrites HMAC as a crypto template. This means that HMAC is no longer a hard-coded part of the API. It's now a template that generates standard digest algorithms like any other. The old HMAC is preserved until all current users are converted. The same structure can be used by other MACs such as AES-XCBC-MAC. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--crypto/Kconfig1
-rw-r--r--crypto/hmac.c241
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
35config CRYPTO_HMAC 35config 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
28struct hmac_ctx {
29 struct crypto_hash *child;
30};
23 31
24static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen) 32static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen)
25{ 33{
@@ -122,3 +130,224 @@ EXPORT_SYMBOL_GPL(crypto_hmac_update);
122EXPORT_SYMBOL_GPL(crypto_hmac_final); 130EXPORT_SYMBOL_GPL(crypto_hmac_final);
123EXPORT_SYMBOL_GPL(crypto_hmac); 131EXPORT_SYMBOL_GPL(crypto_hmac);
124 132
133static inline void *align_ptr(void *p, unsigned int align)
134{
135 return (void *)ALIGN((unsigned long)p, align);
136}
137
138static 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
145static 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
187static 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
205static 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
217static 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
236static 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
262static 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
276static 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
282static void hmac_free(struct crypto_instance *inst)
283{
284 crypto_drop_spawn(crypto_instance_ctx(inst));
285 kfree(inst);
286}
287
288static 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
327out_put_alg:
328 crypto_mod_put(alg);
329 return inst;
330}
331
332static struct crypto_template hmac_tmpl = {
333 .name = "hmac",
334 .alloc = hmac_alloc,
335 .free = hmac_free,
336 .module = THIS_MODULE,
337};
338
339static int __init hmac_module_init(void)
340{
341 return crypto_register_template(&hmac_tmpl);
342}
343
344static void __exit hmac_module_exit(void)
345{
346 crypto_unregister_template(&hmac_tmpl);
347}
348
349module_init(hmac_module_init);
350module_exit(hmac_module_exit);
351
352MODULE_LICENSE("GPL");
353MODULE_DESCRIPTION("HMAC hash algorithm");