3
|
1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis |
|
2 * |
|
3 * LibTomCrypt is a library that provides various cryptographic |
|
4 * algorithms in a highly modular and flexible manner. |
|
5 * |
|
6 * The library is free for all purposes without any express |
|
7 * guarantee it works. |
|
8 * |
|
9 * Tom St Denis, [email protected], http://libtomcrypt.org |
|
10 */ |
|
11 int dh_encrypt_key(const unsigned char *inkey, unsigned long keylen, |
|
12 unsigned char *out, unsigned long *len, |
|
13 prng_state *prng, int wprng, int hash, |
|
14 dh_key *key) |
|
15 { |
143
|
16 unsigned char *pub_expt, *dh_shared, *skey; |
3
|
17 dh_key pubkey; |
|
18 unsigned long x, y, z, hashsize, pubkeysize; |
|
19 int err; |
|
20 |
|
21 _ARGCHK(inkey != NULL); |
|
22 _ARGCHK(out != NULL); |
|
23 _ARGCHK(len != NULL); |
|
24 _ARGCHK(key != NULL); |
|
25 |
|
26 /* check that wprng/hash are not invalid */ |
|
27 if ((err = prng_is_valid(wprng)) != CRYPT_OK) { |
|
28 return err; |
|
29 } |
|
30 |
|
31 if ((err = hash_is_valid(hash)) != CRYPT_OK) { |
|
32 return err; |
|
33 } |
|
34 |
|
35 if (keylen > hash_descriptor[hash].hashsize) { |
|
36 return CRYPT_INVALID_HASH; |
|
37 } |
|
38 |
143
|
39 /* allocate memory */ |
|
40 pub_expt = XMALLOC(DH_BUF_SIZE); |
|
41 dh_shared = XMALLOC(DH_BUF_SIZE); |
|
42 skey = XMALLOC(MAXBLOCKSIZE); |
|
43 if (pub_expt == NULL || dh_shared == NULL || skey == NULL) { |
|
44 if (pub_expt != NULL) { |
|
45 XFREE(pub_expt); |
|
46 } |
|
47 if (dh_shared != NULL) { |
|
48 XFREE(dh_shared); |
|
49 } |
|
50 if (skey != NULL) { |
|
51 XFREE(skey); |
|
52 } |
|
53 return CRYPT_MEM; |
3
|
54 } |
|
55 |
143
|
56 /* make a random key and export the public copy */ |
|
57 if ((err = dh_make_key(prng, wprng, dh_get_size(key), &pubkey)) != CRYPT_OK) { |
|
58 goto __ERR; |
|
59 } |
|
60 |
|
61 pubkeysize = DH_BUF_SIZE; |
3
|
62 if ((err = dh_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) { |
|
63 dh_free(&pubkey); |
143
|
64 goto __ERR; |
3
|
65 } |
|
66 |
|
67 /* now check if the out buffer is big enough */ |
|
68 if (*len < (1 + 4 + 4 + PACKET_SIZE + pubkeysize + keylen)) { |
|
69 dh_free(&pubkey); |
143
|
70 err = CRYPT_BUFFER_OVERFLOW; |
|
71 goto __ERR; |
3
|
72 } |
|
73 |
|
74 /* make random key */ |
|
75 hashsize = hash_descriptor[hash].hashsize; |
|
76 |
143
|
77 x = DH_BUF_SIZE; |
3
|
78 if ((err = dh_shared_secret(&pubkey, key, dh_shared, &x)) != CRYPT_OK) { |
|
79 dh_free(&pubkey); |
143
|
80 goto __ERR; |
3
|
81 } |
|
82 dh_free(&pubkey); |
|
83 |
143
|
84 z = MAXBLOCKSIZE; |
3
|
85 if ((err = hash_memory(hash, dh_shared, x, skey, &z)) != CRYPT_OK) { |
143
|
86 goto __ERR; |
3
|
87 } |
|
88 |
|
89 /* store header */ |
|
90 packet_store_header(out, PACKET_SECT_DH, PACKET_SUB_ENC_KEY); |
|
91 |
|
92 /* output header */ |
|
93 y = PACKET_SIZE; |
|
94 |
|
95 /* size of hash name and the name itself */ |
|
96 out[y++] = hash_descriptor[hash].ID; |
|
97 |
|
98 /* length of DH pubkey and the key itself */ |
|
99 STORE32L(pubkeysize, out+y); |
|
100 y += 4; |
|
101 for (x = 0; x < pubkeysize; x++, y++) { |
|
102 out[y] = pub_expt[x]; |
|
103 } |
|
104 |
|
105 /* Store the encrypted key */ |
|
106 STORE32L(keylen, out+y); |
|
107 y += 4; |
|
108 |
|
109 for (x = 0; x < keylen; x++, y++) { |
|
110 out[y] = skey[x] ^ inkey[x]; |
|
111 } |
|
112 *len = y; |
|
113 |
143
|
114 err = CRYPT_OK; |
|
115 __ERR: |
3
|
116 #ifdef CLEAN_STACK |
|
117 /* clean up */ |
143
|
118 zeromem(pub_expt, DH_BUF_SIZE); |
|
119 zeromem(dh_shared, DH_BUF_SIZE); |
|
120 zeromem(skey, MAXBLOCKSIZE); |
3
|
121 #endif |
143
|
122 XFREE(skey); |
|
123 XFREE(dh_shared); |
|
124 XFREE(pub_expt); |
3
|
125 |
143
|
126 return err; |
3
|
127 } |
|
128 |
|
129 int dh_decrypt_key(const unsigned char *in, unsigned long inlen, |
|
130 unsigned char *outkey, unsigned long *keylen, |
|
131 dh_key *key) |
|
132 { |
143
|
133 unsigned char *shared_secret, *skey; |
3
|
134 unsigned long x, y, z,hashsize, keysize; |
|
135 int hash, err; |
|
136 dh_key pubkey; |
|
137 |
|
138 _ARGCHK(in != NULL); |
|
139 _ARGCHK(outkey != NULL); |
|
140 _ARGCHK(keylen != NULL); |
|
141 _ARGCHK(key != NULL); |
|
142 |
|
143 /* right key type? */ |
|
144 if (key->type != PK_PRIVATE) { |
|
145 return CRYPT_PK_NOT_PRIVATE; |
|
146 } |
|
147 |
143
|
148 /* allocate ram */ |
|
149 shared_secret = XMALLOC(DH_BUF_SIZE); |
|
150 skey = XMALLOC(MAXBLOCKSIZE); |
|
151 if (shared_secret == NULL || skey == NULL) { |
|
152 if (shared_secret != NULL) { |
|
153 XFREE(shared_secret); |
|
154 } |
|
155 if (skey != NULL) { |
|
156 XFREE(skey); |
|
157 } |
|
158 return CRYPT_MEM; |
|
159 } |
|
160 |
3
|
161 /* check if initial header should fit */ |
|
162 if (inlen < PACKET_SIZE+1+4+4) { |
143
|
163 err = CRYPT_INVALID_PACKET; |
|
164 goto __ERR; |
3
|
165 } else { |
|
166 inlen -= PACKET_SIZE+1+4+4; |
|
167 } |
|
168 |
|
169 /* is header correct? */ |
|
170 if ((err = packet_valid_header((unsigned char *)in, PACKET_SECT_DH, PACKET_SUB_ENC_KEY)) != CRYPT_OK) { |
143
|
171 goto __ERR; |
3
|
172 } |
|
173 |
|
174 /* now lets get the hash name */ |
|
175 y = PACKET_SIZE; |
|
176 hash = find_hash_id(in[y++]); |
|
177 if (hash == -1) { |
143
|
178 err = CRYPT_INVALID_HASH; |
|
179 goto __ERR; |
3
|
180 } |
|
181 |
|
182 /* common values */ |
|
183 hashsize = hash_descriptor[hash].hashsize; |
|
184 |
|
185 /* get public key */ |
|
186 LOAD32L(x, in+y); |
|
187 |
|
188 /* now check if the imported key will fit */ |
|
189 if (inlen < x) { |
143
|
190 err = CRYPT_INVALID_PACKET; |
|
191 goto __ERR; |
3
|
192 } else { |
|
193 inlen -= x; |
|
194 } |
|
195 |
|
196 y += 4; |
|
197 if ((err = dh_import(in+y, x, &pubkey)) != CRYPT_OK) { |
143
|
198 goto __ERR; |
3
|
199 } |
|
200 y += x; |
|
201 |
|
202 /* make shared key */ |
143
|
203 x = DH_BUF_SIZE; |
3
|
204 if ((err = dh_shared_secret(key, &pubkey, shared_secret, &x)) != CRYPT_OK) { |
|
205 dh_free(&pubkey); |
143
|
206 goto __ERR; |
3
|
207 } |
|
208 dh_free(&pubkey); |
|
209 |
143
|
210 z = MAXBLOCKSIZE; |
3
|
211 if ((err = hash_memory(hash, shared_secret, x, skey, &z)) != CRYPT_OK) { |
143
|
212 goto __ERR; |
3
|
213 } |
|
214 |
|
215 /* load in the encrypted key */ |
|
216 LOAD32L(keysize, in+y); |
|
217 |
|
218 /* will the outkey fit as part of the input */ |
|
219 if (inlen < keysize) { |
143
|
220 err = CRYPT_INVALID_PACKET; |
|
221 goto __ERR; |
3
|
222 } else { |
|
223 inlen -= keysize; |
|
224 } |
|
225 |
|
226 if (keysize > *keylen) { |
|
227 err = CRYPT_BUFFER_OVERFLOW; |
143
|
228 goto __ERR; |
3
|
229 } |
|
230 y += 4; |
|
231 |
|
232 *keylen = keysize; |
|
233 |
|
234 for (x = 0; x < keysize; x++, y++) { |
|
235 outkey[x] = skey[x] ^ in[y]; |
|
236 } |
|
237 |
|
238 err = CRYPT_OK; |
143
|
239 __ERR: |
3
|
240 #ifdef CLEAN_STACK |
143
|
241 zeromem(shared_secret, DH_BUF_SIZE); |
|
242 zeromem(skey, MAXBLOCKSIZE); |
3
|
243 #endif |
143
|
244 |
|
245 XFREE(skey); |
|
246 XFREE(shared_secret); |
|
247 |
3
|
248 return err; |
|
249 } |
|
250 |
|
251 /* perform an ElGamal Signature of a hash |
|
252 * |
|
253 * The math works as follows. x is the private key, M is the message to sign |
|
254 |
|
255 1. pick a random k |
|
256 2. compute a = g^k mod p |
|
257 3. compute b = (M - xa)/k mod p |
|
258 4. Send (a,b) |
|
259 |
|
260 Now to verify with y=g^x mod p, a and b |
|
261 |
|
262 1. compute y^a * a^b = g^(xa) * g^(k*(M-xa)/k) |
|
263 = g^(xa + (M - xa)) |
|
264 = g^M [all mod p] |
|
265 |
|
266 2. Compare against g^M mod p [based on input hash]. |
|
267 3. If result of #2 == result of #1 then signature valid |
|
268 */ |
|
269 int dh_sign_hash(const unsigned char *in, unsigned long inlen, |
|
270 unsigned char *out, unsigned long *outlen, |
|
271 prng_state *prng, int wprng, dh_key *key) |
|
272 { |
|
273 mp_int a, b, k, m, g, p, p1, tmp; |
143
|
274 unsigned char *buf; |
3
|
275 unsigned long x, y; |
|
276 int err; |
|
277 |
|
278 _ARGCHK(in != NULL); |
|
279 _ARGCHK(out != NULL); |
|
280 _ARGCHK(outlen != NULL); |
|
281 _ARGCHK(key != NULL); |
|
282 |
|
283 /* check parameters */ |
|
284 if (key->type != PK_PRIVATE) { |
|
285 return CRYPT_PK_NOT_PRIVATE; |
|
286 } |
|
287 |
|
288 if ((err = prng_is_valid(wprng)) != CRYPT_OK) { |
|
289 return err; |
|
290 } |
|
291 |
|
292 /* is the IDX valid ? */ |
|
293 if (is_valid_idx(key->idx) != 1) { |
|
294 return CRYPT_PK_INVALID_TYPE; |
|
295 } |
|
296 |
143
|
297 /* allocate ram for buf */ |
|
298 buf = XMALLOC(520); |
|
299 |
3
|
300 /* make up a random value k, |
|
301 * since the order of the group is prime |
|
302 * we need not check if gcd(k, r) is 1 |
|
303 */ |
|
304 if (prng_descriptor[wprng].read(buf, sets[key->idx].size, prng) != |
|
305 (unsigned long)(sets[key->idx].size)) { |
143
|
306 err = CRYPT_ERROR_READPRNG; |
|
307 goto __ERR; |
3
|
308 } |
|
309 |
|
310 /* init bignums */ |
|
311 if ((err = mp_init_multi(&a, &b, &k, &m, &p, &g, &p1, &tmp, NULL)) != MP_OKAY) { |
143
|
312 err = mpi_to_ltc_error(err); |
|
313 goto __ERR; |
3
|
314 } |
|
315 |
|
316 /* load k and m */ |
|
317 if ((err = mp_read_unsigned_bin(&m, (unsigned char *)in, inlen)) != MP_OKAY) { goto error; } |
|
318 #ifdef FAST_PK |
|
319 if ((err = mp_read_unsigned_bin(&k, buf, MIN(32,sets[key->idx].size))) != MP_OKAY) { goto error; } |
|
320 #else |
|
321 if ((err = mp_read_unsigned_bin(&k, buf, sets[key->idx].size)) != MP_OKAY) { goto error; } |
|
322 #endif |
|
323 |
|
324 /* load g, p and p1 */ |
|
325 if ((err = mp_read_radix(&g, sets[key->idx].base, 64)) != MP_OKAY) { goto error; } |
|
326 if ((err = mp_read_radix(&p, sets[key->idx].prime, 64)) != MP_OKAY) { goto error; } |
|
327 if ((err = mp_sub_d(&p, 1, &p1)) != MP_OKAY) { goto error; } |
|
328 if ((err = mp_div_2(&p1, &p1)) != MP_OKAY) { goto error; } /* p1 = (p-1)/2 */ |
|
329 |
|
330 /* now get a = g^k mod p */ |
|
331 if ((err = mp_exptmod(&g, &k, &p, &a)) != MP_OKAY) { goto error; } |
|
332 |
|
333 /* now find M = xa + kb mod p1 or just b = (M - xa)/k mod p1 */ |
|
334 if ((err = mp_invmod(&k, &p1, &k)) != MP_OKAY) { goto error; } /* k = 1/k mod p1 */ |
|
335 if ((err = mp_mulmod(&a, &key->x, &p1, &tmp)) != MP_OKAY) { goto error; } /* tmp = xa */ |
|
336 if ((err = mp_submod(&m, &tmp, &p1, &tmp)) != MP_OKAY) { goto error; } /* tmp = M - xa */ |
|
337 if ((err = mp_mulmod(&k, &tmp, &p1, &b)) != MP_OKAY) { goto error; } /* b = (M - xa)/k */ |
|
338 |
|
339 /* check for overflow */ |
|
340 if ((unsigned long)(PACKET_SIZE + 4 + 4 + mp_unsigned_bin_size(&a) + mp_unsigned_bin_size(&b)) > *outlen) { |
|
341 err = CRYPT_BUFFER_OVERFLOW; |
143
|
342 goto __ERR; |
3
|
343 } |
|
344 |
|
345 /* store header */ |
|
346 y = PACKET_SIZE; |
|
347 |
|
348 /* now store them both (a,b) */ |
|
349 x = (unsigned long)mp_unsigned_bin_size(&a); |
|
350 STORE32L(x, out+y); y += 4; |
|
351 if ((err = mp_to_unsigned_bin(&a, out+y)) != MP_OKAY) { goto error; } |
|
352 y += x; |
|
353 |
|
354 x = (unsigned long)mp_unsigned_bin_size(&b); |
|
355 STORE32L(x, out+y); y += 4; |
|
356 if ((err = mp_to_unsigned_bin(&b, out+y)) != MP_OKAY) { goto error; } |
|
357 y += x; |
|
358 |
|
359 /* check if size too big */ |
|
360 if (*outlen < y) { |
|
361 err = CRYPT_BUFFER_OVERFLOW; |
143
|
362 goto __ERR; |
3
|
363 } |
|
364 |
|
365 /* store header */ |
|
366 packet_store_header(out, PACKET_SECT_DH, PACKET_SUB_SIGNED); |
|
367 *outlen = y; |
|
368 |
|
369 err = CRYPT_OK; |
143
|
370 goto __ERR; |
3
|
371 error: |
|
372 err = mpi_to_ltc_error(err); |
143
|
373 __ERR: |
3
|
374 mp_clear_multi(&tmp, &p1, &g, &p, &m, &k, &b, &a, NULL); |
143
|
375 |
|
376 XFREE(buf); |
|
377 |
3
|
378 return err; |
|
379 } |
|
380 |
|
381 |
|
382 /* verify the signature in sig of the given hash */ |
|
383 int dh_verify_hash(const unsigned char *sig, unsigned long siglen, |
|
384 const unsigned char *hash, unsigned long hashlen, |
|
385 int *stat, dh_key *key) |
|
386 { |
|
387 mp_int a, b, p, g, m, tmp; |
|
388 unsigned long x, y; |
|
389 int err; |
|
390 |
|
391 _ARGCHK(sig != NULL); |
|
392 _ARGCHK(hash != NULL); |
|
393 _ARGCHK(stat != NULL); |
|
394 _ARGCHK(key != NULL); |
|
395 |
|
396 /* default to invalid */ |
|
397 *stat = 0; |
|
398 |
|
399 /* check initial input length */ |
|
400 if (siglen < PACKET_SIZE+4+4) { |
|
401 return CRYPT_INVALID_PACKET; |
|
402 } |
|
403 |
|
404 /* header ok? */ |
|
405 if ((err = packet_valid_header((unsigned char *)sig, PACKET_SECT_DH, PACKET_SUB_SIGNED)) != CRYPT_OK) { |
|
406 return err; |
|
407 } |
|
408 |
|
409 /* get hash out of packet */ |
|
410 y = PACKET_SIZE; |
|
411 |
|
412 /* init all bignums */ |
|
413 if ((err = mp_init_multi(&a, &p, &b, &g, &m, &tmp, NULL)) != MP_OKAY) { |
|
414 return mpi_to_ltc_error(err); |
|
415 } |
|
416 |
|
417 /* load a and b */ |
|
418 INPUT_BIGNUM(&a, sig, x, y, siglen); |
|
419 INPUT_BIGNUM(&b, sig, x, y, siglen); |
|
420 |
|
421 /* load p and g */ |
|
422 if ((err = mp_read_radix(&p, sets[key->idx].prime, 64)) != MP_OKAY) { goto error1; } |
|
423 if ((err = mp_read_radix(&g, sets[key->idx].base, 64)) != MP_OKAY) { goto error1; } |
|
424 |
|
425 /* load m */ |
|
426 if ((err = mp_read_unsigned_bin(&m, (unsigned char *)hash, hashlen)) != MP_OKAY) { goto error1; } |
|
427 |
|
428 /* find g^m mod p */ |
|
429 if ((err = mp_exptmod(&g, &m, &p, &m)) != MP_OKAY) { goto error1; } /* m = g^m mod p */ |
|
430 |
|
431 /* find y^a * a^b */ |
|
432 if ((err = mp_exptmod(&key->y, &a, &p, &tmp)) != MP_OKAY) { goto error1; } /* tmp = y^a mod p */ |
|
433 if ((err = mp_exptmod(&a, &b, &p, &a)) != MP_OKAY) { goto error1; } /* a = a^b mod p */ |
|
434 if ((err = mp_mulmod(&a, &tmp, &p, &a)) != MP_OKAY) { goto error1; } /* a = y^a * a^b mod p */ |
|
435 |
|
436 /* y^a * a^b == g^m ??? */ |
|
437 if (mp_cmp(&a, &m) == 0) { |
|
438 *stat = 1; |
|
439 } |
|
440 |
|
441 /* clean up */ |
|
442 err = CRYPT_OK; |
|
443 goto done; |
|
444 error1: |
|
445 err = mpi_to_ltc_error(err); |
|
446 error: |
|
447 done: |
|
448 mp_clear_multi(&tmp, &m, &g, &p, &b, &a, NULL); |
|
449 return err; |
|
450 } |
|
451 |