Blame view

crypto/hmac.c 6.75 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
19
20
21
22
23
  
  #include <crypto/algapi.h>
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
378f058cc   David Hardeman   [PATCH] Use sg_se...
24
  #include <linux/scatterlist.h>
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
25
26
27
28
29
30
  #include <linux/slab.h>
  #include <linux/string.h>
  
  struct hmac_ctx {
  	struct crypto_hash *child;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31

0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
32
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
60
61
62
63
  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;
  		int err;
  
  		desc.tfm = tfm;
  		desc.flags = crypto_hash_get_flags(parent);
  		desc.flags &= CRYPTO_TFM_REQ_MAY_SLEEP;
a6767721a   David S. Miller   [CRYPTO]: HMAC ne...
64
  		sg_init_one(&tmp, inkey, keylen);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  
  		err = crypto_hash_digest(&desc, &tmp, keylen, digest);
  		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...
95
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
96
97
98
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
a6767721a   David S. Miller   [CRYPTO]: HMAC ne...
99
  	sg_init_one(&tmp, ipad, bs);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
100

73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
101
102
103
104
105
  	err = crypto_hash_init(&desc);
  	if (unlikely(err))
  		return err;
  
  	return crypto_hash_update(&desc, &tmp, bs);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  }
  
  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...
130
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
131
132
133
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
a6767721a   David S. Miller   [CRYPTO]: HMAC ne...
134
  	sg_init_one(&tmp, opad, bs + ds);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
135

73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
136
137
138
139
140
  	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...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  }
  
  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...
156
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
157
158
159
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
41fb28543   Vlad Yasevich   [CRYPTO]: Fix hma...
160
  	sg_init_table(sg1, 2);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
161
  	sg_set_buf(sg1, ipad, bs);
41fb28543   Vlad Yasevich   [CRYPTO]: Fix hma...
162
  	sg_set_page(&sg1[1], (void *) sg, 0, 0);
78c2f0b8c   Jens Axboe   [SG] Update crypt...
163

41fb28543   Vlad Yasevich   [CRYPTO]: Fix hma...
164
  	sg_init_table(sg2, 1);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
165
  	sg_set_buf(sg2, opad, bs + ds);
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
166
167
168
169
170
  	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...
171
172
173
174
  }
  
  static int hmac_init_tfm(struct crypto_tfm *tfm)
  {
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
175
  	struct crypto_hash *hash;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
176
177
178
  	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...
179
180
181
  	hash = crypto_spawn_hash(spawn);
  	if (IS_ERR(hash))
  		return PTR_ERR(hash);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
182

2e306ee01   Herbert Xu   [CRYPTO] api: Add...
183
  	ctx->child = hash;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  	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...
198
  static struct crypto_instance *hmac_alloc(struct rtattr **tb)
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
199
200
201
  {
  	struct crypto_instance *inst;
  	struct crypto_alg *alg;
ebc610e5b   Herbert Xu   [CRYPTO] template...
202
203
204
205
206
  	int err;
  
  	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_HASH);
  	if (err)
  		return ERR_PTR(err);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
207

ebc610e5b   Herbert Xu   [CRYPTO] template...
208
209
  	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_HASH,
  				  CRYPTO_ALG_TYPE_HASH_MASK);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  	if (IS_ERR(alg))
  		return ERR_PTR(PTR_ERR(alg));
  
  	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;
  
  	inst->alg.cra_hash.digestsize =
  		(alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
  		CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize :
  				       alg->cra_digest.dia_digestsize;
  
  	inst->alg.cra_ctxsize = sizeof(struct hmac_ctx) +
  				ALIGN(inst->alg.cra_blocksize * 2 +
  				      inst->alg.cra_hash.digestsize,
  				      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");