diff options
Diffstat (limited to 'crypto/hmac.c')
-rw-r--r-- | crypto/hmac.c | 278 |
1 files changed, 203 insertions, 75 deletions
diff --git a/crypto/hmac.c b/crypto/hmac.c index 46120dee5ada..f403b6946047 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c | |||
@@ -4,121 +4,249 @@ | |||
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> | ||
23 | 27 | ||
24 | static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen) | 28 | struct hmac_ctx { |
29 | struct crypto_hash *child; | ||
30 | }; | ||
31 | |||
32 | static inline void *align_ptr(void *p, unsigned int align) | ||
25 | { | 33 | { |
26 | struct scatterlist tmp; | 34 | return (void *)ALIGN((unsigned long)p, align); |
27 | |||
28 | sg_set_buf(&tmp, key, keylen); | ||
29 | crypto_digest_digest(tfm, &tmp, 1, key); | ||
30 | } | 35 | } |
31 | 36 | ||
32 | int crypto_alloc_hmac_block(struct crypto_tfm *tfm) | 37 | static inline struct hmac_ctx *hmac_ctx(struct crypto_hash *tfm) |
33 | { | 38 | { |
34 | int ret = 0; | 39 | return align_ptr(crypto_hash_ctx_aligned(tfm) + |
40 | crypto_hash_blocksize(tfm) * 2 + | ||
41 | crypto_hash_digestsize(tfm), sizeof(void *)); | ||
42 | } | ||
43 | |||
44 | static int hmac_setkey(struct crypto_hash *parent, | ||
45 | const u8 *inkey, unsigned int keylen) | ||
46 | { | ||
47 | int bs = crypto_hash_blocksize(parent); | ||
48 | int ds = crypto_hash_digestsize(parent); | ||
49 | char *ipad = crypto_hash_ctx_aligned(parent); | ||
50 | char *opad = ipad + bs; | ||
51 | char *digest = opad + bs; | ||
52 | struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *)); | ||
53 | struct crypto_hash *tfm = ctx->child; | ||
54 | unsigned int i; | ||
55 | |||
56 | if (keylen > bs) { | ||
57 | struct hash_desc desc; | ||
58 | struct scatterlist tmp; | ||
59 | int err; | ||
60 | |||
61 | desc.tfm = tfm; | ||
62 | desc.flags = crypto_hash_get_flags(parent); | ||
63 | desc.flags &= CRYPTO_TFM_REQ_MAY_SLEEP; | ||
64 | sg_set_buf(&tmp, inkey, keylen); | ||
35 | 65 | ||
36 | BUG_ON(!crypto_tfm_alg_blocksize(tfm)); | 66 | err = crypto_hash_digest(&desc, &tmp, keylen, digest); |
37 | 67 | if (err) | |
38 | tfm->crt_digest.dit_hmac_block = kmalloc(crypto_tfm_alg_blocksize(tfm), | 68 | return err; |
39 | GFP_KERNEL); | ||
40 | if (tfm->crt_digest.dit_hmac_block == NULL) | ||
41 | ret = -ENOMEM; | ||
42 | 69 | ||
43 | return ret; | 70 | inkey = digest; |
44 | 71 | keylen = ds; | |
72 | } | ||
73 | |||
74 | memcpy(ipad, inkey, keylen); | ||
75 | memset(ipad + keylen, 0, bs - keylen); | ||
76 | memcpy(opad, ipad, bs); | ||
77 | |||
78 | for (i = 0; i < bs; i++) { | ||
79 | ipad[i] ^= 0x36; | ||
80 | opad[i] ^= 0x5c; | ||
81 | } | ||
82 | |||
83 | return 0; | ||
45 | } | 84 | } |
46 | 85 | ||
47 | void crypto_free_hmac_block(struct crypto_tfm *tfm) | 86 | static int hmac_init(struct hash_desc *pdesc) |
48 | { | 87 | { |
49 | kfree(tfm->crt_digest.dit_hmac_block); | 88 | struct crypto_hash *parent = pdesc->tfm; |
89 | int bs = crypto_hash_blocksize(parent); | ||
90 | int ds = crypto_hash_digestsize(parent); | ||
91 | char *ipad = crypto_hash_ctx_aligned(parent); | ||
92 | struct hmac_ctx *ctx = align_ptr(ipad + bs * 2 + ds, sizeof(void *)); | ||
93 | struct hash_desc desc; | ||
94 | struct scatterlist tmp; | ||
95 | |||
96 | desc.tfm = ctx->child; | ||
97 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; | ||
98 | sg_set_buf(&tmp, ipad, bs); | ||
99 | |||
100 | return unlikely(crypto_hash_init(&desc)) ?: | ||
101 | crypto_hash_update(&desc, &tmp, 1); | ||
50 | } | 102 | } |
51 | 103 | ||
52 | void crypto_hmac_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen) | 104 | static int hmac_update(struct hash_desc *pdesc, |
105 | struct scatterlist *sg, unsigned int nbytes) | ||
53 | { | 106 | { |
54 | unsigned int i; | 107 | struct hmac_ctx *ctx = hmac_ctx(pdesc->tfm); |
108 | struct hash_desc desc; | ||
109 | |||
110 | desc.tfm = ctx->child; | ||
111 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; | ||
112 | |||
113 | return crypto_hash_update(&desc, sg, nbytes); | ||
114 | } | ||
115 | |||
116 | static int hmac_final(struct hash_desc *pdesc, u8 *out) | ||
117 | { | ||
118 | struct crypto_hash *parent = pdesc->tfm; | ||
119 | int bs = crypto_hash_blocksize(parent); | ||
120 | int ds = crypto_hash_digestsize(parent); | ||
121 | char *opad = crypto_hash_ctx_aligned(parent) + bs; | ||
122 | char *digest = opad + bs; | ||
123 | struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *)); | ||
124 | struct hash_desc desc; | ||
55 | struct scatterlist tmp; | 125 | struct scatterlist tmp; |
56 | char *ipad = tfm->crt_digest.dit_hmac_block; | ||
57 | |||
58 | if (*keylen > crypto_tfm_alg_blocksize(tfm)) { | ||
59 | hash_key(tfm, key, *keylen); | ||
60 | *keylen = crypto_tfm_alg_digestsize(tfm); | ||
61 | } | ||
62 | 126 | ||
63 | memset(ipad, 0, crypto_tfm_alg_blocksize(tfm)); | 127 | desc.tfm = ctx->child; |
64 | memcpy(ipad, key, *keylen); | 128 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; |
129 | sg_set_buf(&tmp, opad, bs + ds); | ||
65 | 130 | ||
66 | for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++) | 131 | return unlikely(crypto_hash_final(&desc, digest)) ?: |
67 | ipad[i] ^= 0x36; | 132 | crypto_hash_digest(&desc, &tmp, bs + ds, out); |
133 | } | ||
68 | 134 | ||
69 | sg_set_buf(&tmp, ipad, crypto_tfm_alg_blocksize(tfm)); | 135 | static int hmac_digest(struct hash_desc *pdesc, struct scatterlist *sg, |
70 | 136 | unsigned int nbytes, u8 *out) | |
71 | crypto_digest_init(tfm); | 137 | { |
72 | crypto_digest_update(tfm, &tmp, 1); | 138 | struct crypto_hash *parent = pdesc->tfm; |
139 | int bs = crypto_hash_blocksize(parent); | ||
140 | int ds = crypto_hash_digestsize(parent); | ||
141 | char *ipad = crypto_hash_ctx_aligned(parent); | ||
142 | char *opad = ipad + bs; | ||
143 | char *digest = opad + bs; | ||
144 | struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *)); | ||
145 | struct hash_desc desc; | ||
146 | struct scatterlist sg1[2]; | ||
147 | struct scatterlist sg2[1]; | ||
148 | |||
149 | desc.tfm = ctx->child; | ||
150 | desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP; | ||
151 | |||
152 | sg_set_buf(sg1, ipad, bs); | ||
153 | sg1[1].page = (void *)sg; | ||
154 | sg1[1].length = 0; | ||
155 | sg_set_buf(sg2, opad, bs + ds); | ||
156 | |||
157 | return unlikely(crypto_hash_digest(&desc, sg1, nbytes + bs, digest)) ?: | ||
158 | crypto_hash_digest(&desc, sg2, bs + ds, out); | ||
73 | } | 159 | } |
74 | 160 | ||
75 | void crypto_hmac_update(struct crypto_tfm *tfm, | 161 | static int hmac_init_tfm(struct crypto_tfm *tfm) |
76 | struct scatterlist *sg, unsigned int nsg) | ||
77 | { | 162 | { |
78 | crypto_digest_update(tfm, sg, nsg); | 163 | struct crypto_instance *inst = (void *)tfm->__crt_alg; |
164 | struct crypto_spawn *spawn = crypto_instance_ctx(inst); | ||
165 | struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm)); | ||
166 | |||
167 | tfm = crypto_spawn_tfm(spawn); | ||
168 | if (IS_ERR(tfm)) | ||
169 | return PTR_ERR(tfm); | ||
170 | |||
171 | ctx->child = crypto_hash_cast(tfm); | ||
172 | return 0; | ||
79 | } | 173 | } |
80 | 174 | ||
81 | void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key, | 175 | static void hmac_exit_tfm(struct crypto_tfm *tfm) |
82 | unsigned int *keylen, u8 *out) | ||
83 | { | 176 | { |
84 | unsigned int i; | 177 | struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm)); |
85 | struct scatterlist tmp; | 178 | crypto_free_hash(ctx->child); |
86 | char *opad = tfm->crt_digest.dit_hmac_block; | 179 | } |
87 | |||
88 | if (*keylen > crypto_tfm_alg_blocksize(tfm)) { | ||
89 | hash_key(tfm, key, *keylen); | ||
90 | *keylen = crypto_tfm_alg_digestsize(tfm); | ||
91 | } | ||
92 | 180 | ||
93 | crypto_digest_final(tfm, out); | 181 | static void hmac_free(struct crypto_instance *inst) |
182 | { | ||
183 | crypto_drop_spawn(crypto_instance_ctx(inst)); | ||
184 | kfree(inst); | ||
185 | } | ||
94 | 186 | ||
95 | memset(opad, 0, crypto_tfm_alg_blocksize(tfm)); | 187 | static struct crypto_instance *hmac_alloc(void *param, unsigned int len) |
96 | memcpy(opad, key, *keylen); | 188 | { |
97 | 189 | struct crypto_instance *inst; | |
98 | for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++) | 190 | struct crypto_alg *alg; |
99 | opad[i] ^= 0x5c; | 191 | |
192 | alg = crypto_get_attr_alg(param, len, CRYPTO_ALG_TYPE_HASH, | ||
193 | CRYPTO_ALG_TYPE_HASH_MASK | CRYPTO_ALG_ASYNC); | ||
194 | if (IS_ERR(alg)) | ||
195 | return ERR_PTR(PTR_ERR(alg)); | ||
196 | |||
197 | inst = crypto_alloc_instance("hmac", alg); | ||
198 | if (IS_ERR(inst)) | ||
199 | goto out_put_alg; | ||
200 | |||
201 | inst->alg.cra_flags = CRYPTO_ALG_TYPE_HASH; | ||
202 | inst->alg.cra_priority = alg->cra_priority; | ||
203 | inst->alg.cra_blocksize = alg->cra_blocksize; | ||
204 | inst->alg.cra_alignmask = alg->cra_alignmask; | ||
205 | inst->alg.cra_type = &crypto_hash_type; | ||
206 | |||
207 | inst->alg.cra_hash.digestsize = | ||
208 | (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) == | ||
209 | CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize : | ||
210 | alg->cra_digest.dia_digestsize; | ||
211 | |||
212 | inst->alg.cra_ctxsize = sizeof(struct hmac_ctx) + | ||
213 | ALIGN(inst->alg.cra_blocksize * 2 + | ||
214 | inst->alg.cra_hash.digestsize, | ||
215 | sizeof(void *)); | ||
216 | |||
217 | inst->alg.cra_init = hmac_init_tfm; | ||
218 | inst->alg.cra_exit = hmac_exit_tfm; | ||
219 | |||
220 | inst->alg.cra_hash.init = hmac_init; | ||
221 | inst->alg.cra_hash.update = hmac_update; | ||
222 | inst->alg.cra_hash.final = hmac_final; | ||
223 | inst->alg.cra_hash.digest = hmac_digest; | ||
224 | inst->alg.cra_hash.setkey = hmac_setkey; | ||
225 | |||
226 | out_put_alg: | ||
227 | crypto_mod_put(alg); | ||
228 | return inst; | ||
229 | } | ||
100 | 230 | ||
101 | sg_set_buf(&tmp, opad, crypto_tfm_alg_blocksize(tfm)); | 231 | static struct crypto_template hmac_tmpl = { |
232 | .name = "hmac", | ||
233 | .alloc = hmac_alloc, | ||
234 | .free = hmac_free, | ||
235 | .module = THIS_MODULE, | ||
236 | }; | ||
102 | 237 | ||
103 | crypto_digest_init(tfm); | 238 | static int __init hmac_module_init(void) |
104 | crypto_digest_update(tfm, &tmp, 1); | 239 | { |
105 | 240 | return crypto_register_template(&hmac_tmpl); | |
106 | sg_set_buf(&tmp, out, crypto_tfm_alg_digestsize(tfm)); | ||
107 | |||
108 | crypto_digest_update(tfm, &tmp, 1); | ||
109 | crypto_digest_final(tfm, out); | ||
110 | } | 241 | } |
111 | 242 | ||
112 | void crypto_hmac(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen, | 243 | static void __exit hmac_module_exit(void) |
113 | struct scatterlist *sg, unsigned int nsg, u8 *out) | ||
114 | { | 244 | { |
115 | crypto_hmac_init(tfm, key, keylen); | 245 | crypto_unregister_template(&hmac_tmpl); |
116 | crypto_hmac_update(tfm, sg, nsg); | ||
117 | crypto_hmac_final(tfm, key, keylen, out); | ||
118 | } | 246 | } |
119 | 247 | ||
120 | EXPORT_SYMBOL_GPL(crypto_hmac_init); | 248 | module_init(hmac_module_init); |
121 | EXPORT_SYMBOL_GPL(crypto_hmac_update); | 249 | module_exit(hmac_module_exit); |
122 | EXPORT_SYMBOL_GPL(crypto_hmac_final); | ||
123 | EXPORT_SYMBOL_GPL(crypto_hmac); | ||
124 | 250 | ||
251 | MODULE_LICENSE("GPL"); | ||
252 | MODULE_DESCRIPTION("HMAC hash algorithm"); | ||