Blame view

crypto/hmac.c 6.7 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
64
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
  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;
  		sg_set_buf(&tmp, inkey, keylen);
  
  		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
99
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
  	sg_set_buf(&tmp, ipad, bs);
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
100
101
102
103
104
  	err = crypto_hash_init(&desc);
  	if (unlikely(err))
  		return err;
  
  	return crypto_hash_update(&desc, &tmp, bs);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  }
  
  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...
129
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
130
131
132
133
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
  	sg_set_buf(&tmp, opad, bs + ds);
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
134
135
136
137
138
  	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...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  }
  
  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...
154
  	int err;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
155
156
157
158
159
160
161
162
  
  	desc.tfm = ctx->child;
  	desc.flags = pdesc->flags & CRYPTO_TFM_REQ_MAY_SLEEP;
  
  	sg_set_buf(sg1, ipad, bs);
  	sg1[1].page = (void *)sg;
  	sg1[1].length = 0;
  	sg_set_buf(sg2, opad, bs + ds);
73af07de3   Herbert Xu   [CRYPTO] hmac: Fi...
163
164
165
166
167
  	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...
168
169
170
171
  }
  
  static int hmac_init_tfm(struct crypto_tfm *tfm)
  {
2e306ee01   Herbert Xu   [CRYPTO] api: Add...
172
  	struct crypto_hash *hash;
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
173
174
175
  	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...
176
177
178
  	hash = crypto_spawn_hash(spawn);
  	if (IS_ERR(hash))
  		return PTR_ERR(hash);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
179

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

ebc610e5b   Herbert Xu   [CRYPTO] template...
205
206
  	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_HASH,
  				  CRYPTO_ALG_TYPE_HASH_MASK);
0796ae061   Herbert Xu   [CRYPTO] hmac: Ad...
207
208
209
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
  	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");