Blame view

crypto/hmac.c 7.24 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
  /*
   * Cryptographic API.
   *
   * HMAC: Keyed-Hashing for Message Authentication (RFC2104).
   *
   * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
7
   * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
   *
   * The HMAC implementation is derived from USAGI.
   * Copyright (c) 2002 Kazunori Miyazawa <miyazawa@linux-ipv6.org> / USAGI
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the Free
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
14
   * Software Foundation; either version 2 of the License, or (at your option)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
   * any later version.
   *
   */
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
18

5f7082ed4   Herbert Xu   crypto: hash - Ex...
19
  #include <crypto/internal/hash.h>
b2ab4a57b   Herbert Xu   [CRYPTO] scatterw...
20
  #include <crypto/scatterwalk.h>
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
21
22
23
24
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
378f058cc   David Hardeman   [PATCH] Use sg_se...
25
  #include <linux/scatterlist.h>
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
26
27
28
29
30
31
  #include <linux/slab.h>
  #include <linux/string.h>
  
  struct hmac_ctx {
  	struct crypto_hash *child;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32

0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  static inline void *align_ptr(void *p, unsigned int align)
  {
  	return (void *)ALIGN((unsigned long)p, align);
  }
  
  static inline struct hmac_ctx *hmac_ctx(struct crypto_hash *tfm)
  {
  	return align_ptr(crypto_hash_ctx_aligned(tfm) +
  			 crypto_hash_blocksize(tfm) * 2 +
  			 crypto_hash_digestsize(tfm), sizeof(void *));
  }
  
  static int hmac_setkey(struct crypto_hash *parent,
  		       const u8 *inkey, unsigned int keylen)
  {
  	int bs = crypto_hash_blocksize(parent);
  	int ds = crypto_hash_digestsize(parent);
  	char *ipad = crypto_hash_ctx_aligned(parent);
  	char *opad = ipad + bs;
  	char *digest = opad + bs;
  	struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *));
  	struct crypto_hash *tfm = ctx->child;
  	unsigned int i;
  
  	if (keylen > bs) {
  		struct hash_desc desc;
  		struct scatterlist tmp;
67412f0e7   Herbert Xu   [CRYPTO] hmac: Av...
60
  		int tmplen;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
61
62
63
64
65
  		int err;
  
  		desc.tfm = tfm;
  		desc.flags = crypto_hash_get_flags(parent);
  		desc.flags &= CRYPTO_TFM_REQ_MAY_SLEEP;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
66

67412f0e7   Herbert Xu   [CRYPTO] hmac: Av...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  		err = crypto_hash_init(&desc);
  		if (err)
  			return err;
  
  		tmplen = bs * 2 + ds;
  		sg_init_one(&tmp, ipad, tmplen);
  
  		for (; keylen > tmplen; inkey += tmplen, keylen -= tmplen) {
  			memcpy(ipad, inkey, tmplen);
  			err = crypto_hash_update(&desc, &tmp, tmplen);
  			if (err)
  				return err;
  		}
  
  		if (keylen) {
  			memcpy(ipad, inkey, keylen);
  			err = crypto_hash_update(&desc, &tmp, keylen);
  			if (err)
  				return err;
  		}
  
  		err = crypto_hash_final(&desc, digest);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  		if (err)
  			return err;
  
  		inkey = digest;
  		keylen = ds;
  	}
  
  	memcpy(ipad, inkey, keylen);
  	memset(ipad + keylen, 0, bs - keylen);
  	memcpy(opad, ipad, bs);
  
  	for (i = 0; i < bs; i++) {
  		ipad[i] ^= 0x36;
  		opad[i] ^= 0x5c;
  	}
  
  	return 0;
  }
  
  static int hmac_init(struct hash_desc *pdesc)
  {
  	struct crypto_hash *parent = pdesc->tfm;
  	int bs = crypto_hash_blocksize(parent);
  	int ds = crypto_hash_digestsize(parent);
  	char *ipad = crypto_hash_ctx_aligned(parent);
  	struct hmac_ctx *ctx = align_ptr(ipad + bs * 2 + ds, sizeof(void *));
  	struct hash_desc desc;
  	struct scatterlist tmp;
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
117
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
118
119
120
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
a6767721a   David S. Miller   [CRYPTO]: HMAC ne...
121
  	sg_init_one(&tmp, ipad, bs);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
122

73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
123
124
125
126
127
  	err = crypto_hash_init(&desc);
  	if (unlikely(err))
  		return err;
  
  	return crypto_hash_update(&desc, &tmp, bs);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  }
  
  static int hmac_update(struct hash_desc *pdesc,
  		       struct scatterlist *sg, unsigned int nbytes)
  {
  	struct hmac_ctx *ctx = hmac_ctx(pdesc->tfm);
  	struct hash_desc desc;
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
  
  	return crypto_hash_update(&desc, sg, nbytes);
  }
  
  static int hmac_final(struct hash_desc *pdesc, u8 *out)
  {
  	struct crypto_hash *parent = pdesc->tfm;
  	int bs = crypto_hash_blocksize(parent);
  	int ds = crypto_hash_digestsize(parent);
  	char *opad = crypto_hash_ctx_aligned(parent) + bs;
  	char *digest = opad + bs;
  	struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *));
  	struct hash_desc desc;
  	struct scatterlist tmp;
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
152
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
153
154
155
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
a6767721a   David S. Miller   [CRYPTO]: HMAC ne...
156
  	sg_init_one(&tmp, opad, bs + ds);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
157

73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
158
159
160
161
162
  	err = crypto_hash_final(&desc, digest);
  	if (unlikely(err))
  		return err;
  
  	return crypto_hash_digest(&desc, &tmp, bs + ds, out);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  }
  
  static int hmac_digest(struct hash_desc *pdesc, struct scatterlist *sg,
  		       unsigned int nbytes, u8 *out)
  {
  	struct crypto_hash *parent = pdesc->tfm;
  	int bs = crypto_hash_blocksize(parent);
  	int ds = crypto_hash_digestsize(parent);
  	char *ipad = crypto_hash_ctx_aligned(parent);
  	char *opad = ipad + bs;
  	char *digest = opad + bs;
  	struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *));
  	struct hash_desc desc;
  	struct scatterlist sg1[2];
  	struct scatterlist sg2[1];
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
178
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
179
180
181
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
41fb28543   Vlad Yasevich   [CRYPTO]: Fix hma...
182
  	sg_init_table(sg1, 2);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
183
  	sg_set_buf(sg1, ipad, bs);
b2ab4a57b   Herbert Xu   [CRYPTO] scatterw...
184
  	scatterwalk_sg_chain(sg1, 2, sg);
78c2f0b8c   Jens Axboe   [SG] Update crypt...
185

41fb28543   Vlad Yasevich   [CRYPTO]: Fix hma...
186
  	sg_init_table(sg2, 1);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
187
  	sg_set_buf(sg2, opad, bs + ds);
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
188
189
190
191
192
  	err = crypto_hash_digest(&desc, sg1, nbytes + bs, digest);
  	if (unlikely(err))
  		return err;
  
  	return crypto_hash_digest(&desc, sg2, bs + ds, out);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
193
194
195
196
  }
  
  static int hmac_init_tfm(struct crypto_tfm *tfm)
  {
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
197
  	struct crypto_hash *hash;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
198
199
200
  	struct crypto_instance *inst = (void *)tfm->__crt_alg;
  	struct crypto_spawn *spawn = crypto_instance_ctx(inst);
  	struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm));
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
201
202
203
  	hash = crypto_spawn_hash(spawn);
  	if (IS_ERR(hash))
  		return PTR_ERR(hash);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
204

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
205
  	ctx->child = hash;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  	return 0;
  }
  
  static void hmac_exit_tfm(struct crypto_tfm *tfm)
  {
  	struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm));
  	crypto_free_hash(ctx->child);
  }
  
  static void hmac_free(struct crypto_instance *inst)
  {
  	crypto_drop_spawn(crypto_instance_ctx(inst));
  	kfree(inst);
  }
ebc610e5b   Herbert Xu   [CRYPTO] template...
220
  static struct crypto_instance *hmac_alloc(struct rtattr **tb)
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
221
222
223
  {
  	struct crypto_instance *inst;
  	struct crypto_alg *alg;
ebc610e5b   Herbert Xu   [CRYPTO] template...
224
  	int err;
ca786dc73   Herbert Xu   crypto: hash - Fi...
225
  	int ds;
ebc610e5b   Herbert Xu   [CRYPTO] template...
226
227
228
229
  
  	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_HASH);
  	if (err)
  		return ERR_PTR(err);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
230

ebc610e5b   Herbert Xu   [CRYPTO] template...
231
232
  	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_HASH,
  				  CRYPTO_ALG_TYPE_HASH_MASK);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
233
  	if (IS_ERR(alg))
e231c2ee6   David Howells   Convert ERR_PTR(P...
234
  		return ERR_CAST(alg);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
235

ca786dc73   Herbert Xu   crypto: hash - Fi...
236
  	inst = ERR_PTR(-EINVAL);
5f7082ed4   Herbert Xu   crypto: hash - Ex...
237
238
239
240
241
  	ds = alg->cra_type == &crypto_hash_type ?
  	     alg->cra_hash.digestsize :
  	     alg->cra_type ?
  	     __crypto_shash_alg(alg)->digestsize :
  	     alg->cra_digest.dia_digestsize;
ca786dc73   Herbert Xu   crypto: hash - Fi...
242
243
  	if (ds > alg->cra_blocksize)
  		goto out_put_alg;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
244
245
246
247
248
249
250
251
252
  	inst = crypto_alloc_instance("hmac", alg);
  	if (IS_ERR(inst))
  		goto out_put_alg;
  
  	inst->alg.cra_flags = CRYPTO_ALG_TYPE_HASH;
  	inst->alg.cra_priority = alg->cra_priority;
  	inst->alg.cra_blocksize = alg->cra_blocksize;
  	inst->alg.cra_alignmask = alg->cra_alignmask;
  	inst->alg.cra_type = &crypto_hash_type;
ca786dc73   Herbert Xu   crypto: hash - Fi...
253
  	inst->alg.cra_hash.digestsize = ds;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
254
255
  
  	inst->alg.cra_ctxsize = sizeof(struct hmac_ctx) +
ca786dc73   Herbert Xu   crypto: hash - Fi...
256
  				ALIGN(inst->alg.cra_blocksize * 2 + ds,
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  				      sizeof(void *));
  
  	inst->alg.cra_init = hmac_init_tfm;
  	inst->alg.cra_exit = hmac_exit_tfm;
  
  	inst->alg.cra_hash.init = hmac_init;
  	inst->alg.cra_hash.update = hmac_update;
  	inst->alg.cra_hash.final = hmac_final;
  	inst->alg.cra_hash.digest = hmac_digest;
  	inst->alg.cra_hash.setkey = hmac_setkey;
  
  out_put_alg:
  	crypto_mod_put(alg);
  	return inst;
  }
  
  static struct crypto_template hmac_tmpl = {
  	.name = "hmac",
  	.alloc = hmac_alloc,
  	.free = hmac_free,
  	.module = THIS_MODULE,
  };
  
  static int __init hmac_module_init(void)
  {
  	return crypto_register_template(&hmac_tmpl);
  }
  
  static void __exit hmac_module_exit(void)
  {
  	crypto_unregister_template(&hmac_tmpl);
  }
  
  module_init(hmac_module_init);
  module_exit(hmac_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("HMAC hash algorithm");