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