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 ecc_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 ecc_key *key) |
|
15 { |
143
|
16 unsigned char *pub_expt, *ecc_shared, *skey; |
|
17 ecc_key pubkey; |
|
18 unsigned long x, y, z, hashsize, pubkeysize; |
|
19 int err; |
3
|
20 |
|
21 _ARGCHK(inkey != NULL); |
|
22 _ARGCHK(out != NULL); |
|
23 _ARGCHK(len != NULL); |
|
24 _ARGCHK(key != NULL); |
|
25 |
|
26 /* check that wprng/cipher/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 |
|
39 /* make a random key and export the public copy */ |
|
40 if ((err = ecc_make_key(prng, wprng, ecc_get_size(key), &pubkey)) != CRYPT_OK) { |
|
41 return err; |
|
42 } |
|
43 |
143
|
44 pub_expt = XMALLOC(ECC_BUF_SIZE); |
|
45 ecc_shared = XMALLOC(ECC_BUF_SIZE); |
|
46 skey = XMALLOC(MAXBLOCKSIZE); |
|
47 if (pub_expt == NULL || ecc_shared == NULL || skey == NULL) { |
|
48 if (pub_expt != NULL) { |
|
49 XFREE(pub_expt); |
|
50 } |
|
51 if (ecc_shared != NULL) { |
|
52 XFREE(ecc_shared); |
|
53 } |
|
54 if (skey != NULL) { |
|
55 XFREE(skey); |
|
56 } |
|
57 ecc_free(&pubkey); |
|
58 return CRYPT_MEM; |
|
59 } |
|
60 |
|
61 pubkeysize = ECC_BUF_SIZE; |
3
|
62 if ((err = ecc_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) { |
|
63 ecc_free(&pubkey); |
143
|
64 goto __ERR; |
3
|
65 } |
|
66 |
|
67 /* now check if the out buffer is big enough */ |
|
68 if (*len < (9 + PACKET_SIZE + pubkeysize + hash_descriptor[hash].hashsize)) { |
|
69 ecc_free(&pubkey); |
143
|
70 err = CRYPT_BUFFER_OVERFLOW; |
|
71 goto __ERR; |
3
|
72 } |
|
73 |
|
74 /* make random key */ |
|
75 hashsize = hash_descriptor[hash].hashsize; |
143
|
76 x = ECC_BUF_SIZE; |
3
|
77 if ((err = ecc_shared_secret(&pubkey, key, ecc_shared, &x)) != CRYPT_OK) { |
|
78 ecc_free(&pubkey); |
143
|
79 goto __ERR; |
3
|
80 } |
|
81 ecc_free(&pubkey); |
143
|
82 z = MAXBLOCKSIZE; |
3
|
83 if ((err = hash_memory(hash, ecc_shared, x, skey, &z)) != CRYPT_OK) { |
143
|
84 goto __ERR; |
3
|
85 } |
|
86 |
|
87 /* store header */ |
|
88 packet_store_header(out, PACKET_SECT_ECC, PACKET_SUB_ENC_KEY); |
|
89 |
|
90 /* output header */ |
|
91 y = PACKET_SIZE; |
|
92 |
|
93 /* size of hash name and the name itself */ |
|
94 out[y++] = hash_descriptor[hash].ID; |
|
95 |
|
96 /* length of ECC pubkey and the key itself */ |
|
97 STORE32L(pubkeysize, out+y); |
|
98 y += 4; |
|
99 |
|
100 for (x = 0; x < pubkeysize; x++, y++) { |
|
101 out[y] = pub_expt[x]; |
|
102 } |
|
103 |
|
104 STORE32L(keylen, out+y); |
|
105 y += 4; |
|
106 |
|
107 /* Encrypt/Store the encrypted key */ |
|
108 for (x = 0; x < keylen; x++, y++) { |
|
109 out[y] = skey[x] ^ inkey[x]; |
|
110 } |
|
111 *len = y; |
|
112 |
143
|
113 err = CRYPT_OK; |
|
114 __ERR: |
3
|
115 #ifdef CLEAN_STACK |
|
116 /* clean up */ |
143
|
117 zeromem(pub_expt, ECC_BUF_SIZE); |
|
118 zeromem(ecc_shared, ECC_BUF_SIZE); |
|
119 zeromem(skey, MAXBLOCKSIZE); |
3
|
120 #endif |
143
|
121 |
|
122 XFREE(skey); |
|
123 XFREE(ecc_shared); |
|
124 XFREE(pub_expt); |
|
125 |
|
126 return err; |
3
|
127 } |
|
128 |
|
129 int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, |
|
130 unsigned char *outkey, unsigned long *keylen, |
|
131 ecc_key *key) |
|
132 { |
143
|
133 unsigned char *shared_secret, *skey; |
|
134 unsigned long x, y, z, hashsize, keysize; |
|
135 int hash, err; |
|
136 ecc_key pubkey; |
3
|
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 |
|
148 /* correct length ? */ |
|
149 if (inlen < PACKET_SIZE+1+4+4) { |
|
150 return CRYPT_INVALID_PACKET; |
|
151 } else { |
|
152 inlen -= PACKET_SIZE+1+4+4; |
|
153 } |
|
154 |
|
155 /* is header correct? */ |
|
156 if ((err = packet_valid_header((unsigned char *)in, PACKET_SECT_ECC, PACKET_SUB_ENC_KEY)) != CRYPT_OK) { |
|
157 return err; |
|
158 } |
|
159 |
|
160 /* now lets get the hash name */ |
|
161 y = PACKET_SIZE; |
|
162 hash = find_hash_id(in[y++]); |
|
163 if (hash == -1) { |
|
164 return CRYPT_INVALID_HASH; |
|
165 } |
|
166 |
|
167 /* common values */ |
|
168 hashsize = hash_descriptor[hash].hashsize; |
|
169 |
|
170 /* get public key */ |
|
171 LOAD32L(x, in+y); |
|
172 if (inlen < x) { |
|
173 return CRYPT_INVALID_PACKET; |
|
174 } else { |
|
175 inlen -= x; |
|
176 } |
|
177 y += 4; |
|
178 if ((err = ecc_import(in+y, x, &pubkey)) != CRYPT_OK) { |
|
179 return err; |
|
180 } |
|
181 y += x; |
|
182 |
143
|
183 /* allocate memory */ |
|
184 shared_secret = XMALLOC(ECC_BUF_SIZE); |
|
185 skey = XMALLOC(MAXBLOCKSIZE); |
|
186 if (shared_secret == NULL || skey == NULL) { |
|
187 if (shared_secret != NULL) { |
|
188 XFREE(shared_secret); |
|
189 } |
|
190 if (skey != NULL) { |
|
191 XFREE(skey); |
|
192 } |
|
193 ecc_free(&pubkey); |
|
194 return CRYPT_MEM; |
|
195 } |
|
196 |
3
|
197 /* make shared key */ |
143
|
198 x = ECC_BUF_SIZE; |
3
|
199 if ((err = ecc_shared_secret(key, &pubkey, shared_secret, &x)) != CRYPT_OK) { |
|
200 ecc_free(&pubkey); |
143
|
201 goto __ERR; |
3
|
202 } |
|
203 ecc_free(&pubkey); |
|
204 |
143
|
205 z = MAXBLOCKSIZE; |
3
|
206 if ((err = hash_memory(hash, shared_secret, x, skey, &z)) != CRYPT_OK) { |
143
|
207 goto __ERR; |
3
|
208 } |
|
209 |
|
210 LOAD32L(keysize, in+y); |
|
211 if (inlen < keysize) { |
143
|
212 err = CRYPT_INVALID_PACKET; |
|
213 goto __ERR; |
3
|
214 } else { |
|
215 inlen -= keysize; |
|
216 } |
|
217 y += 4; |
|
218 |
|
219 if (*keylen < keysize) { |
|
220 err = CRYPT_BUFFER_OVERFLOW; |
143
|
221 goto __ERR; |
3
|
222 } |
|
223 |
|
224 /* Decrypt the key */ |
|
225 for (x = 0; x < keysize; x++, y++) { |
|
226 outkey[x] = skey[x] ^ in[y]; |
|
227 } |
|
228 |
|
229 *keylen = keysize; |
|
230 |
|
231 err = CRYPT_OK; |
143
|
232 __ERR: |
3
|
233 #ifdef CLEAN_STACK |
143
|
234 zeromem(shared_secret, ECC_BUF_SIZE); |
|
235 zeromem(skey, MAXBLOCKSIZE); |
3
|
236 #endif |
143
|
237 |
|
238 XFREE(skey); |
|
239 XFREE(shared_secret); |
|
240 |
3
|
241 return err; |
|
242 } |
|
243 |
|
244 int ecc_sign_hash(const unsigned char *in, unsigned long inlen, |
|
245 unsigned char *out, unsigned long *outlen, |
|
246 prng_state *prng, int wprng, ecc_key *key) |
|
247 { |
143
|
248 ecc_key pubkey; |
|
249 mp_int b, p; |
|
250 unsigned char *epubkey, *er; |
3
|
251 unsigned long x, y, pubkeysize, rsize; |
143
|
252 int err; |
3
|
253 |
|
254 _ARGCHK(in != NULL); |
|
255 _ARGCHK(out != NULL); |
|
256 _ARGCHK(outlen != NULL); |
|
257 _ARGCHK(key != NULL); |
|
258 |
|
259 /* is this a private key? */ |
|
260 if (key->type != PK_PRIVATE) { |
|
261 return CRYPT_PK_NOT_PRIVATE; |
|
262 } |
|
263 |
|
264 /* is the IDX valid ? */ |
|
265 if (is_valid_idx(key->idx) != 1) { |
|
266 return CRYPT_PK_INVALID_TYPE; |
|
267 } |
|
268 |
|
269 if ((err = prng_is_valid(wprng)) != CRYPT_OK) { |
|
270 return err; |
|
271 } |
|
272 |
|
273 /* make up a key and export the public copy */ |
|
274 if ((err = ecc_make_key(prng, wprng, ecc_get_size(key), &pubkey)) != CRYPT_OK) { |
|
275 return err; |
|
276 } |
|
277 |
143
|
278 /* allocate ram */ |
|
279 epubkey = XMALLOC(ECC_BUF_SIZE); |
|
280 er = XMALLOC(ECC_BUF_SIZE); |
|
281 if (epubkey == NULL || er == NULL) { |
|
282 if (epubkey != NULL) { |
|
283 XFREE(epubkey); |
|
284 } |
|
285 if (er != NULL) { |
|
286 XFREE(er); |
|
287 } |
|
288 ecc_free(&pubkey); |
|
289 return CRYPT_MEM; |
|
290 } |
|
291 |
|
292 pubkeysize = ECC_BUF_SIZE; |
3
|
293 if ((err = ecc_export(epubkey, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) { |
|
294 ecc_free(&pubkey); |
143
|
295 goto __ERR; |
3
|
296 } |
|
297 |
|
298 /* get the hash and load it as a bignum into 'b' */ |
|
299 /* init the bignums */ |
|
300 if ((err = mp_init_multi(&b, &p, NULL)) != MP_OKAY) { |
|
301 ecc_free(&pubkey); |
143
|
302 err = mpi_to_ltc_error(err); |
|
303 goto __ERR; |
3
|
304 } |
|
305 if ((err = mp_read_radix(&p, (char *)sets[key->idx].order, 64)) != MP_OKAY) { goto error; } |
|
306 if ((err = mp_read_unsigned_bin(&b, (unsigned char *)in, (int)inlen)) != MP_OKAY) { goto error; } |
|
307 |
|
308 /* find b = (m - x)/k */ |
|
309 if ((err = mp_invmod(&pubkey.k, &p, &pubkey.k)) != MP_OKAY) { goto error; } /* k = 1/k */ |
|
310 if ((err = mp_submod(&b, &key->k, &p, &b)) != MP_OKAY) { goto error; } /* b = m - x */ |
|
311 if ((err = mp_mulmod(&b, &pubkey.k, &p, &b)) != MP_OKAY) { goto error; } /* b = (m - x)/k */ |
|
312 |
|
313 /* export it */ |
|
314 rsize = (unsigned long)mp_unsigned_bin_size(&b); |
143
|
315 if (rsize > ECC_BUF_SIZE) { |
3
|
316 err = CRYPT_BUFFER_OVERFLOW; |
|
317 goto error; |
|
318 } |
|
319 if ((err = mp_to_unsigned_bin(&b, er)) != MP_OKAY) { goto error; } |
|
320 |
|
321 /* now lets check the outlen before we write */ |
|
322 if (*outlen < (12 + rsize + pubkeysize)) { |
|
323 err = CRYPT_BUFFER_OVERFLOW; |
143
|
324 goto __ERR; |
3
|
325 } |
|
326 |
|
327 /* lets output */ |
|
328 y = PACKET_SIZE; |
|
329 |
|
330 /* size of public key */ |
|
331 STORE32L(pubkeysize, out+y); |
|
332 y += 4; |
|
333 |
|
334 /* copy the public key */ |
|
335 for (x = 0; x < pubkeysize; x++, y++) { |
|
336 out[y] = epubkey[x]; |
|
337 } |
|
338 |
|
339 /* size of 'r' */ |
|
340 STORE32L(rsize, out+y); |
|
341 y += 4; |
|
342 |
|
343 /* copy r */ |
|
344 for (x = 0; x < rsize; x++, y++) { |
|
345 out[y] = er[x]; |
|
346 } |
|
347 |
|
348 /* store header */ |
|
349 packet_store_header(out, PACKET_SECT_ECC, PACKET_SUB_SIGNED); |
143
|
350 *outlen = y; |
3
|
351 |
143
|
352 /* all ok */ |
3
|
353 err = CRYPT_OK; |
143
|
354 goto __ERR; |
3
|
355 error: |
|
356 err = mpi_to_ltc_error(err); |
143
|
357 __ERR: |
3
|
358 mp_clear_multi(&b, &p, NULL); |
|
359 ecc_free(&pubkey); |
|
360 #ifdef CLEAN_STACK |
143
|
361 zeromem(er, ECC_BUF_SIZE); |
|
362 zeromem(epubkey, ECC_BUF_SIZE); |
3
|
363 #endif |
143
|
364 |
|
365 XFREE(epubkey); |
|
366 XFREE(er); |
|
367 |
3
|
368 return err; |
|
369 } |
|
370 |
|
371 /* verify that mG = (bA + Y) |
|
372 * |
|
373 * The signatures work by making up a fresh key "a" with a public key "A". Now we want to sign so the |
|
374 * public key Y = xG can verify it. |
|
375 * |
|
376 * b = (m - x)/k, A is the public key embedded and Y is the users public key [who signed it] |
|
377 * A = kG therefore bA == ((m-x)/k)kG == (m-x)G |
|
378 * |
|
379 * Adding Y = xG to the bA gives us (m-x)G + xG == mG |
|
380 * |
|
381 * The user given only xG, kG and b cannot determine k or x which means they can't find the private key. |
|
382 * |
|
383 */ |
|
384 int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, |
|
385 const unsigned char *hash, unsigned long inlen, |
|
386 int *stat, ecc_key *key) |
|
387 { |
143
|
388 ecc_point *mG; |
|
389 ecc_key pubkey; |
|
390 mp_int b, p, m, mu; |
3
|
391 unsigned long x, y; |
143
|
392 int err; |
3
|
393 |
|
394 _ARGCHK(sig != NULL); |
|
395 _ARGCHK(hash != NULL); |
|
396 _ARGCHK(stat != NULL); |
|
397 _ARGCHK(key != NULL); |
|
398 |
|
399 /* default to invalid signature */ |
|
400 *stat = 0; |
|
401 |
|
402 if (siglen < PACKET_SIZE+4+4) { |
|
403 return CRYPT_INVALID_PACKET; |
|
404 } else { |
|
405 siglen -= PACKET_SIZE+4+4; |
|
406 } |
|
407 |
|
408 /* is the message format correct? */ |
|
409 if ((err = packet_valid_header((unsigned char *)sig, PACKET_SECT_ECC, PACKET_SUB_SIGNED)) != CRYPT_OK) { |
|
410 return err; |
|
411 } |
|
412 |
|
413 /* get hash name */ |
|
414 y = PACKET_SIZE; |
|
415 |
|
416 /* get size of public key */ |
|
417 LOAD32L(x, sig+y); |
|
418 if (siglen < x) { |
|
419 return CRYPT_INVALID_PACKET; |
|
420 } else { |
|
421 siglen -= x; |
|
422 } |
|
423 y += 4; |
|
424 |
|
425 /* load the public key */ |
|
426 if ((err = ecc_import((unsigned char*)sig+y, x, &pubkey)) != CRYPT_OK) { |
|
427 return err; |
|
428 } |
|
429 y += x; |
|
430 |
|
431 /* load size of 'b' */ |
|
432 LOAD32L(x, sig+y); |
|
433 if (siglen < x) { |
|
434 return CRYPT_INVALID_PACKET; |
|
435 } else { |
|
436 siglen -= x; |
|
437 } |
|
438 y += 4; |
|
439 |
|
440 /* init values */ |
|
441 if ((err = mp_init_multi(&b, &m, &p, &mu, NULL)) != MP_OKAY) { |
|
442 ecc_free(&pubkey); |
|
443 return mpi_to_ltc_error(err); |
|
444 } |
|
445 |
|
446 mG = new_point(); |
|
447 if (mG == NULL) { |
|
448 mp_clear_multi(&b, &m, &p, &mu, NULL); |
|
449 ecc_free(&pubkey); |
|
450 return CRYPT_MEM; |
|
451 } |
|
452 |
|
453 /* load b */ |
|
454 if ((err = mp_read_unsigned_bin(&b, (unsigned char *)sig+y, (int)x)) != MP_OKAY) { goto error; } |
|
455 y += x; |
|
456 |
|
457 /* get m in binary a bignum */ |
|
458 if ((err = mp_read_unsigned_bin(&m, (unsigned char *)hash, (int)inlen)) != MP_OKAY) { goto error; } |
|
459 |
|
460 /* load prime */ |
|
461 if ((err = mp_read_radix(&p, (char *)sets[key->idx].prime, 64)) != MP_OKAY) { goto error; } |
|
462 |
|
463 /* calculate barrett stuff */ |
|
464 mp_set(&mu, 1); |
|
465 mp_lshd(&mu, 2 * USED(&p)); |
|
466 if ((err = mp_div(&mu, &p, &mu, NULL)) != MP_OKAY) { goto error; } |
|
467 |
|
468 /* get bA */ |
|
469 if ((err = ecc_mulmod(&b, &pubkey.pubkey, &pubkey.pubkey, &p)) != CRYPT_OK) { goto done; } |
|
470 |
|
471 /* get bA + Y */ |
|
472 if ((err = add_point(&pubkey.pubkey, &key->pubkey, &pubkey.pubkey, &p, &mu)) != CRYPT_OK) { goto done; } |
|
473 |
|
474 /* get mG */ |
|
475 if ((err = mp_read_radix(&mG->x, (char *)sets[key->idx].Gx, 64)) != MP_OKAY) { goto error; } |
|
476 if ((err = mp_read_radix(&mG->y, (char *)sets[key->idx].Gy, 64)) != MP_OKAY) { goto error; } |
|
477 if ((err = ecc_mulmod(&m, mG, mG, &p)) != CRYPT_OK) { goto done; } |
|
478 |
|
479 /* compare mG to bA + Y */ |
|
480 if (mp_cmp(&mG->x, &pubkey.pubkey.x) == MP_EQ && mp_cmp(&mG->y, &pubkey.pubkey.y) == MP_EQ) { |
|
481 *stat = 1; |
|
482 } |
|
483 |
|
484 /* clear up and return */ |
|
485 err = CRYPT_OK; |
|
486 goto done; |
|
487 error: |
|
488 err = mpi_to_ltc_error(err); |
|
489 done: |
|
490 del_point(mG); |
|
491 ecc_free(&pubkey); |
|
492 mp_clear_multi(&p, &m, &b, &mu, NULL); |
|
493 return err; |
|
494 } |
|
495 |