diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-10-17 15:37:12 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-10-17 15:37:12 +0000 |
commit | 4b452a365b674cc69bf62be7d15950ccbdd7329c (patch) | |
tree | 9bd4f631da592363ead707b388d06aebbe55e00f /src/util/crypto_ecc.c | |
parent | 58a9e9e741a6ae984d1e8332f905eb0c6fa7bfb3 (diff) | |
download | gnunet-4b452a365b674cc69bf62be7d15950ccbdd7329c.tar.gz gnunet-4b452a365b674cc69bf62be7d15950ccbdd7329c.zip |
-ecc draft code
Diffstat (limited to 'src/util/crypto_ecc.c')
-rw-r--r-- | src/util/crypto_ecc.c | 1319 |
1 files changed, 1319 insertions, 0 deletions
diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c new file mode 100644 index 000000000..1ef1ec7b5 --- /dev/null +++ b/src/util/crypto_ecc.c | |||
@@ -0,0 +1,1319 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2012 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/crypto_ecc.c | ||
23 | * @brief public key cryptography (ECC) with libgcrypt | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * This is just a first, completely untested, draft hack for future ECC support. | ||
27 | * TODO: | ||
28 | * - declare public API in gnunet_crypto_lib (move some structs, etc.) | ||
29 | * - implement gnunet-ecc binary | ||
30 | * - convert existing RSA testcases | ||
31 | * - adjust encoding length and other parameters | ||
32 | * - actually test it! | ||
33 | */ | ||
34 | #include "platform.h" | ||
35 | #include <gcrypt.h> | ||
36 | #include "gnunet_common.h" | ||
37 | #include "gnunet_util_lib.h" | ||
38 | |||
39 | #define EXTRA_CHECKS ALLOW_EXTRA_CHECKS | ||
40 | |||
41 | #define CURVE "NIST P-521" | ||
42 | |||
43 | #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) | ||
44 | |||
45 | #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) | ||
46 | |||
47 | #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) | ||
48 | |||
49 | |||
50 | /** | ||
51 | * FIXME: what is an acceptable value here? | ||
52 | */ | ||
53 | #define GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH 64 | ||
54 | |||
55 | /** | ||
56 | * Length of the q-point (Q = dP) in the public key. | ||
57 | * FIXME: double-check that this is right. | ||
58 | */ | ||
59 | #define GNUNET_CRYPTO_ECC_PUBLIC_KEY_LENGTH 64 | ||
60 | |||
61 | |||
62 | /** | ||
63 | * @brief an ECC signature | ||
64 | */ | ||
65 | struct GNUNET_CRYPTO_EccSignature | ||
66 | { | ||
67 | unsigned char sig[GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH]; | ||
68 | }; | ||
69 | |||
70 | |||
71 | GNUNET_NETWORK_STRUCT_BEGIN | ||
72 | |||
73 | /** | ||
74 | * @brief header of what an ECC signature signs | ||
75 | * this must be followed by "size - 8" bytes of | ||
76 | * the actual signed data | ||
77 | */ | ||
78 | struct GNUNET_CRYPTO_EccSignaturePurpose | ||
79 | { | ||
80 | /** | ||
81 | * How many bytes does this signature sign? | ||
82 | * (including this purpose header); in network | ||
83 | * byte order (!). | ||
84 | */ | ||
85 | uint32_t size GNUNET_PACKED; | ||
86 | |||
87 | /** | ||
88 | * What does this signature vouch for? This | ||
89 | * must contain a GNUNET_SIGNATURE_PURPOSE_XXX | ||
90 | * constant (from gnunet_signatures.h). In | ||
91 | * network byte order! | ||
92 | */ | ||
93 | uint32_t purpose GNUNET_PACKED; | ||
94 | |||
95 | }; | ||
96 | |||
97 | |||
98 | /** | ||
99 | * Public ECC key (always for NIST P-521) encoded in a format suitable | ||
100 | * for network transmission. | ||
101 | */ | ||
102 | struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded | ||
103 | { | ||
104 | /** | ||
105 | * Size of the encoding, in network byte order. | ||
106 | */ | ||
107 | uint16_t size; | ||
108 | |||
109 | /** | ||
110 | * Actual length of the q-point binary encoding. | ||
111 | */ | ||
112 | uint16_t len; | ||
113 | |||
114 | /** | ||
115 | * 0-padded q-point in binary encoding (GCRYPT_MPI_FMT_USG). | ||
116 | */ | ||
117 | unsigned char key[GNUNET_CRYPTO_ECC_PUBLIC_KEY_LENGTH]; | ||
118 | }; | ||
119 | |||
120 | |||
121 | struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded | ||
122 | { | ||
123 | /** | ||
124 | * Overall size of the private key. | ||
125 | */ | ||
126 | uint16_t size; | ||
127 | |||
128 | /** | ||
129 | * Size of the q and d components of the private key. | ||
130 | * Note that the other parameters are from NIST P-521. | ||
131 | */ | ||
132 | uint16_t sizes[2]; | ||
133 | }; | ||
134 | |||
135 | |||
136 | /** | ||
137 | * ECC Encrypted data. | ||
138 | */ | ||
139 | struct GNUNET_CRYPTO_EccEncryptedData | ||
140 | { | ||
141 | unsigned char encoding[GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH]; | ||
142 | }; | ||
143 | |||
144 | GNUNET_NETWORK_STRUCT_END | ||
145 | |||
146 | |||
147 | |||
148 | /** | ||
149 | * The private information of an ECC private key. | ||
150 | */ | ||
151 | struct GNUNET_CRYPTO_EccPrivateKey | ||
152 | { | ||
153 | |||
154 | /** | ||
155 | * Libgcrypt S-expression for the ECC key. | ||
156 | */ | ||
157 | gcry_sexp_t sexp; | ||
158 | }; | ||
159 | |||
160 | |||
161 | /** | ||
162 | * Function called upon completion of 'GNUNET_CRYPTO_ecc_key_create_async'. | ||
163 | * | ||
164 | * @param cls closure | ||
165 | * @param pk NULL on error, otherwise the private key (which must be free'd by the callee) | ||
166 | * @param emsg NULL on success, otherwise an error message | ||
167 | */ | ||
168 | typedef void (*GNUNET_CRYPTO_EccKeyCallback)(void *cls, | ||
169 | struct GNUNET_CRYPTO_EccPrivateKey *pk, | ||
170 | const char *emsg); | ||
171 | |||
172 | |||
173 | |||
174 | /** | ||
175 | * Log an error message at log-level 'level' that indicates | ||
176 | * a failure of the command 'cmd' with the message given | ||
177 | * by gcry_strerror(rc). | ||
178 | */ | ||
179 | #define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0); | ||
180 | |||
181 | |||
182 | /** | ||
183 | * If target != size, move target bytes to the | ||
184 | * end of the size-sized buffer and zero out the | ||
185 | * first target-size bytes. | ||
186 | * | ||
187 | * @param buf original buffer | ||
188 | * @param size number of bytes in the buffer | ||
189 | * @param target target size of the buffer | ||
190 | */ | ||
191 | static void | ||
192 | adjust (unsigned char *buf, size_t size, size_t target) | ||
193 | { | ||
194 | if (size < target) | ||
195 | { | ||
196 | memmove (&buf[target - size], buf, size); | ||
197 | memset (buf, 0, target - size); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | |||
202 | /** | ||
203 | * Free memory occupied by hostkey | ||
204 | * @param privatekey pointer to the memory to free | ||
205 | */ | ||
206 | void | ||
207 | GNUNET_CRYPTO_ecc_key_free (struct GNUNET_CRYPTO_EccPrivateKey *privatekey) | ||
208 | { | ||
209 | gcry_sexp_release (privatekey->sexp); | ||
210 | GNUNET_free (privatekey); | ||
211 | } | ||
212 | |||
213 | |||
214 | /** | ||
215 | * Extract values from an S-expression. | ||
216 | * | ||
217 | * @param array where to store the result(s) | ||
218 | * @param sexp S-expression to parse | ||
219 | * @param topname top-level name in the S-expression that is of interest | ||
220 | * @param elems names of the elements to extract | ||
221 | * @return 0 on success | ||
222 | */ | ||
223 | static int | ||
224 | key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, | ||
225 | const char *elems) | ||
226 | { | ||
227 | gcry_sexp_t list; | ||
228 | gcry_sexp_t l2; | ||
229 | const char *s; | ||
230 | unsigned int i; | ||
231 | unsigned int idx; | ||
232 | |||
233 | list = gcry_sexp_find_token (sexp, topname, 0); | ||
234 | if (! list) | ||
235 | return 1; | ||
236 | l2 = gcry_sexp_cadr (list); | ||
237 | gcry_sexp_release (list); | ||
238 | list = l2; | ||
239 | if (! list) | ||
240 | return 2; | ||
241 | |||
242 | idx = 0; | ||
243 | for (s = elems; *s; s++, idx++) | ||
244 | { | ||
245 | l2 = gcry_sexp_find_token (list, s, 1); | ||
246 | if (! l2) | ||
247 | { | ||
248 | for (i = 0; i < idx; i++) | ||
249 | { | ||
250 | gcry_free (array[i]); | ||
251 | array[i] = NULL; | ||
252 | } | ||
253 | gcry_sexp_release (list); | ||
254 | return 3; /* required parameter not found */ | ||
255 | } | ||
256 | array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); | ||
257 | gcry_sexp_release (l2); | ||
258 | if (! array[idx]) | ||
259 | { | ||
260 | for (i = 0; i < idx; i++) | ||
261 | { | ||
262 | gcry_free (array[i]); | ||
263 | array[i] = NULL; | ||
264 | } | ||
265 | gcry_sexp_release (list); | ||
266 | return 4; /* required parameter is invalid */ | ||
267 | } | ||
268 | } | ||
269 | gcry_sexp_release (list); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | |||
274 | /** | ||
275 | * Extract the public key for the given private key. | ||
276 | * | ||
277 | * @param priv the private key | ||
278 | * @param pub where to write the public key | ||
279 | */ | ||
280 | void | ||
281 | GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv, | ||
282 | struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) | ||
283 | { | ||
284 | gcry_mpi_t skey; | ||
285 | size_t size; | ||
286 | int rc; | ||
287 | |||
288 | rc = key_from_sexp (&skey, priv->sexp, "public-key", "q"); | ||
289 | if (rc) | ||
290 | rc = key_from_sexp (&skey, priv->sexp, "private-key", "q"); | ||
291 | if (rc) | ||
292 | rc = key_from_sexp (&skey, priv->sexp, "ecc", "q"); | ||
293 | GNUNET_assert (0 == rc); | ||
294 | pub->size = htons (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)); | ||
295 | size = GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH; | ||
296 | GNUNET_assert (0 == | ||
297 | gcry_mpi_print (GCRYMPI_FMT_USG, pub->key, size, &size, | ||
298 | skey)); | ||
299 | pub->len = htons (size); | ||
300 | adjust (&pub->key[0], size, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH); | ||
301 | gcry_mpi_release (skey); | ||
302 | } | ||
303 | |||
304 | |||
305 | /** | ||
306 | * Convert a public key to a string. | ||
307 | * | ||
308 | * @param pub key to convert | ||
309 | * @return string representing 'pub' | ||
310 | */ | ||
311 | char * | ||
312 | GNUNET_CRYPTO_ecc_public_key_to_string (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) | ||
313 | { | ||
314 | char *pubkeybuf; | ||
315 | size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8; | ||
316 | char *end; | ||
317 | |||
318 | if (keylen % 5 > 0) | ||
319 | keylen += 5 - keylen % 5; | ||
320 | keylen /= 5; | ||
321 | pubkeybuf = GNUNET_malloc (keylen + 1); | ||
322 | end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub, | ||
323 | sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded), | ||
324 | pubkeybuf, | ||
325 | keylen); | ||
326 | if (NULL == end) | ||
327 | { | ||
328 | GNUNET_free (pubkeybuf); | ||
329 | return NULL; | ||
330 | } | ||
331 | *end = '\0'; | ||
332 | return pubkeybuf; | ||
333 | } | ||
334 | |||
335 | |||
336 | /** | ||
337 | * Convert a string representing a public key to a public key. | ||
338 | * | ||
339 | * @param enc encoded public key | ||
340 | * @param enclen number of bytes in enc (without 0-terminator) | ||
341 | * @param pub where to store the public key | ||
342 | * @return GNUNET_OK on success | ||
343 | */ | ||
344 | int | ||
345 | GNUNET_CRYPTO_ecc_public_key_from_string (const char *enc, | ||
346 | size_t enclen, | ||
347 | struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) | ||
348 | { | ||
349 | size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8; | ||
350 | |||
351 | if (keylen % 5 > 0) | ||
352 | keylen += 5 - keylen % 5; | ||
353 | keylen /= 5; | ||
354 | if (enclen != keylen) | ||
355 | return GNUNET_SYSERR; | ||
356 | |||
357 | if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, | ||
358 | (unsigned char*) pub, | ||
359 | sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded))) | ||
360 | return GNUNET_SYSERR; | ||
361 | if ( (ntohs (pub->size) != sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) || | ||
362 | (ntohs (pub->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH) ) | ||
363 | return GNUNET_SYSERR; | ||
364 | return GNUNET_OK; | ||
365 | } | ||
366 | |||
367 | |||
368 | /** | ||
369 | * Convert the given public key from the network format to the | ||
370 | * S-expression that can be used by libgcrypt. | ||
371 | * | ||
372 | * @param publicKey public key to decode | ||
373 | * @return NULL on error | ||
374 | */ | ||
375 | static gcry_sexp_t | ||
376 | decode_public_key (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *publicKey) | ||
377 | { | ||
378 | gcry_sexp_t result; | ||
379 | gcry_mpi_t q; | ||
380 | size_t size; | ||
381 | size_t erroff; | ||
382 | int rc; | ||
383 | |||
384 | if (ntohs (publicKey->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH) | ||
385 | { | ||
386 | GNUNET_break (0); | ||
387 | return NULL; | ||
388 | } | ||
389 | size = ntohs (publicKey->size); | ||
390 | if (0 != (rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG, publicKey->key, size, &size))) | ||
391 | { | ||
392 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); | ||
393 | return NULL; | ||
394 | } | ||
395 | rc = gcry_sexp_build (&result, &erroff, "(public-key(ecc((curve \"" CURVE "\")(q %m)))", q); | ||
396 | gcry_mpi_release (q); | ||
397 | if (0 != rc) | ||
398 | { | ||
399 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */ | ||
400 | return NULL; | ||
401 | } | ||
402 | return result; | ||
403 | } | ||
404 | |||
405 | |||
406 | /** | ||
407 | * Encode the private key in a format suitable for | ||
408 | * storing it into a file. | ||
409 | * | ||
410 | * @returns encoding of the private key. | ||
411 | * The first 4 bytes give the size of the array, as usual. | ||
412 | */ | ||
413 | struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded * | ||
414 | GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *hostkey) | ||
415 | { | ||
416 | struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *retval; | ||
417 | gcry_mpi_t pkv[2]; | ||
418 | void *pbu[2]; | ||
419 | size_t sizes[2]; | ||
420 | size_t off; | ||
421 | int rc; | ||
422 | unsigned int i; | ||
423 | size_t size; | ||
424 | |||
425 | #if EXTRA_CHECKS | ||
426 | if (gcry_pk_testkey (hostkey->sexp)) | ||
427 | { | ||
428 | GNUNET_break (0); | ||
429 | return NULL; | ||
430 | } | ||
431 | #endif | ||
432 | |||
433 | memset (pkv, 0, sizeof (gcry_mpi_t) * 2); | ||
434 | rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "qd"); | ||
435 | if (rc) | ||
436 | rc = key_from_sexp (pkv, hostkey->sexp, "ecc", "qd"); | ||
437 | GNUNET_assert (0 == rc); | ||
438 | size = sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded); | ||
439 | for (i=0;i<2;i++) | ||
440 | { | ||
441 | if (NULL != pkv[i]) | ||
442 | { | ||
443 | GNUNET_assert (0 == | ||
444 | gcry_mpi_aprint (GCRYMPI_FMT_USG, | ||
445 | (unsigned char **) &pbu[i], &sizes[i], | ||
446 | pkv[i])); | ||
447 | size += sizes[i]; | ||
448 | } | ||
449 | else | ||
450 | { | ||
451 | pbu[i] = NULL; | ||
452 | sizes[i] = 0; | ||
453 | } | ||
454 | } | ||
455 | GNUNET_assert (size < 65536); | ||
456 | retval = GNUNET_malloc (size); | ||
457 | retval->size = htons (size); | ||
458 | off = 0; | ||
459 | for (i=0;i<2;i++) | ||
460 | { | ||
461 | retval->sizes[i] = htons (sizes[0]); | ||
462 | memcpy (&((char *) (&retval[1]))[off], pbu[i], sizes[i]); | ||
463 | off += sizes[i]; | ||
464 | if (NULL != pkv[i]) | ||
465 | gcry_mpi_release (pkv[i]); | ||
466 | if (NULL != pbu[i]) | ||
467 | free (pbu[i]); | ||
468 | } | ||
469 | return retval; | ||
470 | } | ||
471 | |||
472 | |||
473 | /** | ||
474 | * Decode the private key from the file-format back | ||
475 | * to the "normal", internal format. | ||
476 | * | ||
477 | * @param buf the buffer where the private key data is stored | ||
478 | * @param len the length of the data in 'buffer' | ||
479 | * @return NULL on error | ||
480 | */ | ||
481 | struct GNUNET_CRYPTO_EccPrivateKey * | ||
482 | GNUNET_CRYPTO_ecc_decode_key (const char *buf, uint16_t len) | ||
483 | { | ||
484 | struct GNUNET_CRYPTO_EccPrivateKey *ret; | ||
485 | const struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *encoding = | ||
486 | (const struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *) buf; | ||
487 | gcry_sexp_t res; | ||
488 | gcry_mpi_t q; | ||
489 | gcry_mpi_t d; | ||
490 | int rc; | ||
491 | size_t size; | ||
492 | size_t pos; | ||
493 | uint16_t enc_len; | ||
494 | size_t erroff; | ||
495 | |||
496 | enc_len = ntohs (encoding->size); | ||
497 | if (len != enc_len) | ||
498 | return NULL; | ||
499 | pos = 0; | ||
500 | size = ntohs (encoding->sizes[0]); | ||
501 | rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG, | ||
502 | &((const unsigned char *) (&encoding[1]))[pos], size, | ||
503 | &size); | ||
504 | pos += ntohs (encoding->sizes[0]); | ||
505 | if (0 != rc) | ||
506 | { | ||
507 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); | ||
508 | return NULL; | ||
509 | } | ||
510 | size = ntohs (encoding->sizes[1]); | ||
511 | rc = gcry_mpi_scan (&d, GCRYMPI_FMT_USG, | ||
512 | &((const unsigned char *) (&encoding[1]))[pos], size, | ||
513 | &size); | ||
514 | pos += ntohs (encoding->sizes[1]); | ||
515 | if (0 != rc) | ||
516 | { | ||
517 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); | ||
518 | gcry_mpi_release (d); | ||
519 | return NULL; | ||
520 | } | ||
521 | rc = gcry_sexp_build (&res, &erroff, | ||
522 | "(private-key(ecc(q %m)(d %m)(curve \"" CURVE "\")))", | ||
523 | q, d); | ||
524 | gcry_mpi_release (q); | ||
525 | gcry_mpi_release (d); | ||
526 | if (0 != rc) | ||
527 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); | ||
528 | #if EXTRA_CHECKS | ||
529 | if (0 != (rc = gcry_pk_testkey (res))) | ||
530 | { | ||
531 | LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); | ||
532 | return NULL; | ||
533 | } | ||
534 | #endif | ||
535 | ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey)); | ||
536 | ret->sexp = res; | ||
537 | return ret; | ||
538 | } | ||
539 | |||
540 | |||
541 | /** | ||
542 | * Create a new private key. Caller must free return value. | ||
543 | * | ||
544 | * @return fresh private key | ||
545 | */ | ||
546 | static struct GNUNET_CRYPTO_EccPrivateKey * | ||
547 | ecc_key_create () | ||
548 | { | ||
549 | struct GNUNET_CRYPTO_EccPrivateKey *ret; | ||
550 | gcry_sexp_t s_key; | ||
551 | gcry_sexp_t s_keyparam; | ||
552 | |||
553 | GNUNET_assert (0 == | ||
554 | gcry_sexp_build (&s_keyparam, NULL, | ||
555 | "(genkey(ecc(curve \"" CURVE "\")))")); | ||
556 | GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam)); | ||
557 | gcry_sexp_release (s_keyparam); | ||
558 | #if EXTRA_CHECKS | ||
559 | GNUNET_assert (0 == gcry_pk_testkey (s_key)); | ||
560 | #endif | ||
561 | ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey)); | ||
562 | ret->sexp = s_key; | ||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | |||
567 | /** | ||
568 | * Try to read the private key from the given file. | ||
569 | * | ||
570 | * @param filename file to read the key from | ||
571 | * @return NULL on error | ||
572 | */ | ||
573 | static struct GNUNET_CRYPTO_EccPrivateKey * | ||
574 | try_read_key (const char *filename) | ||
575 | { | ||
576 | struct GNUNET_CRYPTO_EccPrivateKey *ret; | ||
577 | struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *enc; | ||
578 | struct GNUNET_DISK_FileHandle *fd; | ||
579 | OFF_T fs; | ||
580 | uint16_t len; | ||
581 | |||
582 | if (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
583 | return NULL; | ||
584 | |||
585 | /* hostkey file exists already, read it! */ | ||
586 | if (NULL == (fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, | ||
587 | GNUNET_DISK_PERM_NONE))) | ||
588 | { | ||
589 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); | ||
590 | return NULL; | ||
591 | } | ||
592 | if (GNUNET_OK != (GNUNET_DISK_file_handle_size (fd, &fs))) | ||
593 | { | ||
594 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "stat", filename); | ||
595 | (void) GNUNET_DISK_file_close (fd); | ||
596 | return NULL; | ||
597 | } | ||
598 | if (0 == fs) | ||
599 | { | ||
600 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
601 | return NULL; | ||
602 | } | ||
603 | if (fs > UINT16_MAX) | ||
604 | { | ||
605 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
606 | _("File `%s' does not contain a valid private key (too long, %llu bytes). Deleting it.\n"), | ||
607 | filename, | ||
608 | (unsigned long long) fs); | ||
609 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
610 | if (0 != UNLINK (filename)) | ||
611 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
612 | return NULL; | ||
613 | } | ||
614 | |||
615 | enc = GNUNET_malloc (fs); | ||
616 | GNUNET_break (fs == GNUNET_DISK_file_read (fd, enc, fs)); | ||
617 | len = ntohs (enc->size); | ||
618 | ret = NULL; | ||
619 | if ((len != fs) || | ||
620 | (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, len)))) | ||
621 | { | ||
622 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
623 | _("File `%s' does not contain a valid private key (failed decode, %llu bytes). Deleting it.\n"), | ||
624 | filename, | ||
625 | (unsigned long long) fs); | ||
626 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
627 | if (0 != UNLINK (filename)) | ||
628 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
629 | GNUNET_free (enc); | ||
630 | return NULL; | ||
631 | } | ||
632 | GNUNET_free (enc); | ||
633 | |||
634 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
635 | return ret; | ||
636 | } | ||
637 | |||
638 | |||
639 | /** | ||
640 | * Wait for a short time (we're trying to lock a file or want | ||
641 | * to give another process a shot at finishing a disk write, etc.). | ||
642 | * Sleeps for 100ms (as that should be long enough for virtually all | ||
643 | * modern systems to context switch and allow another process to do | ||
644 | * some 'real' work). | ||
645 | */ | ||
646 | static void | ||
647 | short_wait () | ||
648 | { | ||
649 | struct GNUNET_TIME_Relative timeout; | ||
650 | |||
651 | timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100); | ||
652 | (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout); | ||
653 | } | ||
654 | |||
655 | |||
656 | /** | ||
657 | * Create a new private key by reading it from a file. If the | ||
658 | * files does not exist, create a new key and write it to the | ||
659 | * file. Caller must free return value. Note that this function | ||
660 | * can not guarantee that another process might not be trying | ||
661 | * the same operation on the same file at the same time. | ||
662 | * If the contents of the file | ||
663 | * are invalid the old file is deleted and a fresh key is | ||
664 | * created. | ||
665 | * | ||
666 | * @return new private key, NULL on error (for example, | ||
667 | * permission denied) | ||
668 | */ | ||
669 | struct GNUNET_CRYPTO_EccPrivateKey * | ||
670 | GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename) | ||
671 | { | ||
672 | struct GNUNET_CRYPTO_EccPrivateKey *ret; | ||
673 | struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *enc; | ||
674 | uint16_t len; | ||
675 | struct GNUNET_DISK_FileHandle *fd; | ||
676 | unsigned int cnt; | ||
677 | int ec; | ||
678 | uint64_t fs; | ||
679 | struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pub; | ||
680 | struct GNUNET_PeerIdentity pid; | ||
681 | |||
682 | if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename)) | ||
683 | return NULL; | ||
684 | while (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
685 | { | ||
686 | fd = GNUNET_DISK_file_open (filename, | ||
687 | GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | ||
688 | | GNUNET_DISK_OPEN_FAILIFEXISTS, | ||
689 | GNUNET_DISK_PERM_USER_READ | | ||
690 | GNUNET_DISK_PERM_USER_WRITE); | ||
691 | if (NULL == fd) | ||
692 | { | ||
693 | if (errno == EEXIST) | ||
694 | { | ||
695 | if (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
696 | { | ||
697 | /* must exist but not be accessible, fail for good! */ | ||
698 | if (0 != ACCESS (filename, R_OK)) | ||
699 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", filename); | ||
700 | else | ||
701 | GNUNET_break (0); /* what is going on!? */ | ||
702 | return NULL; | ||
703 | } | ||
704 | continue; | ||
705 | } | ||
706 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); | ||
707 | return NULL; | ||
708 | } | ||
709 | cnt = 0; | ||
710 | |||
711 | while (GNUNET_YES != | ||
712 | GNUNET_DISK_file_lock (fd, 0, | ||
713 | sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded), | ||
714 | GNUNET_YES)) | ||
715 | { | ||
716 | short_wait (); | ||
717 | if (0 == ++cnt % 10) | ||
718 | { | ||
719 | ec = errno; | ||
720 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
721 | _("Could not acquire lock on file `%s': %s...\n"), filename, | ||
722 | STRERROR (ec)); | ||
723 | } | ||
724 | } | ||
725 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
726 | _("Creating a new private key. This may take a while.\n")); | ||
727 | ret = ecc_key_create (); | ||
728 | GNUNET_assert (ret != NULL); | ||
729 | enc = GNUNET_CRYPTO_ecc_encode_key (ret); | ||
730 | GNUNET_assert (enc != NULL); | ||
731 | GNUNET_assert (ntohs (enc->size) == | ||
732 | GNUNET_DISK_file_write (fd, enc, ntohs (enc->size))); | ||
733 | GNUNET_free (enc); | ||
734 | |||
735 | GNUNET_DISK_file_sync (fd); | ||
736 | if (GNUNET_YES != | ||
737 | GNUNET_DISK_file_unlock (fd, 0, | ||
738 | sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) | ||
739 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); | ||
740 | GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd)); | ||
741 | GNUNET_CRYPTO_ecc_key_get_public (ret, &pub); | ||
742 | GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey); | ||
743 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
744 | _("I am host `%s'. Stored new private key in `%s'.\n"), | ||
745 | GNUNET_i2s (&pid), filename); | ||
746 | return ret; | ||
747 | } | ||
748 | /* hostkey file exists already, read it! */ | ||
749 | fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, | ||
750 | GNUNET_DISK_PERM_NONE); | ||
751 | if (NULL == fd) | ||
752 | { | ||
753 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); | ||
754 | return NULL; | ||
755 | } | ||
756 | cnt = 0; | ||
757 | while (1) | ||
758 | { | ||
759 | if (GNUNET_YES != | ||
760 | GNUNET_DISK_file_lock (fd, 0, | ||
761 | sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded), | ||
762 | GNUNET_NO)) | ||
763 | { | ||
764 | if (0 == ++cnt % 60) | ||
765 | { | ||
766 | ec = errno; | ||
767 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
768 | _("Could not acquire lock on file `%s': %s...\n"), filename, | ||
769 | STRERROR (ec)); | ||
770 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
771 | _ | ||
772 | ("This may be ok if someone is currently generating a hostkey.\n")); | ||
773 | } | ||
774 | short_wait (); | ||
775 | continue; | ||
776 | } | ||
777 | if (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
778 | { | ||
779 | /* eh, what!? File we opened is now gone!? */ | ||
780 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", filename); | ||
781 | if (GNUNET_YES != | ||
782 | GNUNET_DISK_file_unlock (fd, 0, | ||
783 | sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) | ||
784 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); | ||
785 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
786 | |||
787 | return NULL; | ||
788 | } | ||
789 | if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES)) | ||
790 | fs = 0; | ||
791 | if (fs < sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded)) | ||
792 | { | ||
793 | /* maybe we got the read lock before the hostkey generating | ||
794 | * process had a chance to get the write lock; give it up! */ | ||
795 | if (GNUNET_YES != | ||
796 | GNUNET_DISK_file_unlock (fd, 0, | ||
797 | sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) | ||
798 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); | ||
799 | if (0 == ++cnt % 10) | ||
800 | { | ||
801 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
802 | _ | ||
803 | ("When trying to read hostkey file `%s' I found %u bytes but I need at least %u.\n"), | ||
804 | filename, (unsigned int) fs, | ||
805 | (unsigned int) sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded)); | ||
806 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
807 | _ | ||
808 | ("This may be ok if someone is currently generating a hostkey.\n")); | ||
809 | } | ||
810 | short_wait (); /* wait a bit longer! */ | ||
811 | continue; | ||
812 | } | ||
813 | break; | ||
814 | } | ||
815 | enc = GNUNET_malloc (fs); | ||
816 | GNUNET_assert (fs == GNUNET_DISK_file_read (fd, enc, fs)); | ||
817 | len = ntohs (enc->size); | ||
818 | ret = NULL; | ||
819 | if ((len != fs) || | ||
820 | (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, len)))) | ||
821 | { | ||
822 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
823 | _("File `%s' does not contain a valid private key. Deleting it.\n"), | ||
824 | filename); | ||
825 | if (0 != UNLINK (filename)) | ||
826 | { | ||
827 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
828 | } | ||
829 | } | ||
830 | GNUNET_free (enc); | ||
831 | if (GNUNET_YES != | ||
832 | GNUNET_DISK_file_unlock (fd, 0, | ||
833 | sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) | ||
834 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); | ||
835 | GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd)); | ||
836 | if (ret != NULL) | ||
837 | { | ||
838 | GNUNET_CRYPTO_ecc_key_get_public (ret, &pub); | ||
839 | GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey); | ||
840 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
841 | _("I am host `%s'. Read private key from `%s'.\n"), GNUNET_i2s (&pid), | ||
842 | filename); | ||
843 | } | ||
844 | return ret; | ||
845 | } | ||
846 | |||
847 | |||
848 | /** | ||
849 | * Handle to cancel private key generation and state for the | ||
850 | * key generation operation. | ||
851 | */ | ||
852 | struct GNUNET_CRYPTO_EccKeyGenerationContext | ||
853 | { | ||
854 | |||
855 | /** | ||
856 | * Continuation to call upon completion. | ||
857 | */ | ||
858 | GNUNET_CRYPTO_EccKeyCallback cont; | ||
859 | |||
860 | /** | ||
861 | * Closure for 'cont'. | ||
862 | */ | ||
863 | void *cont_cls; | ||
864 | |||
865 | /** | ||
866 | * Name of the file. | ||
867 | */ | ||
868 | char *filename; | ||
869 | |||
870 | /** | ||
871 | * Handle to the helper process which does the key generation. | ||
872 | */ | ||
873 | struct GNUNET_OS_Process *gnunet_ecc; | ||
874 | |||
875 | /** | ||
876 | * Handle to 'stdout' of gnunet-ecc. We 'read' on stdout to detect | ||
877 | * process termination (instead of messing with SIGCHLD). | ||
878 | */ | ||
879 | struct GNUNET_DISK_PipeHandle *gnunet_ecc_out; | ||
880 | |||
881 | /** | ||
882 | * Location where we store the private key if it already existed. | ||
883 | * (if this is used, 'filename', 'gnunet_ecc' and 'gnunet_ecc_out' will | ||
884 | * not be used). | ||
885 | */ | ||
886 | struct GNUNET_CRYPTO_EccPrivateKey *pk; | ||
887 | |||
888 | /** | ||
889 | * Task reading from 'gnunet_ecc_out' to wait for process termination. | ||
890 | */ | ||
891 | GNUNET_SCHEDULER_TaskIdentifier read_task; | ||
892 | |||
893 | }; | ||
894 | |||
895 | |||
896 | /** | ||
897 | * Abort ECC key generation. | ||
898 | * | ||
899 | * @param gc key generation context to abort | ||
900 | */ | ||
901 | void | ||
902 | GNUNET_CRYPTO_ecc_key_create_stop (struct GNUNET_CRYPTO_EccKeyGenerationContext *gc) | ||
903 | { | ||
904 | if (GNUNET_SCHEDULER_NO_TASK != gc->read_task) | ||
905 | { | ||
906 | GNUNET_SCHEDULER_cancel (gc->read_task); | ||
907 | gc->read_task = GNUNET_SCHEDULER_NO_TASK; | ||
908 | } | ||
909 | if (NULL != gc->gnunet_ecc) | ||
910 | { | ||
911 | (void) GNUNET_OS_process_kill (gc->gnunet_ecc, SIGKILL); | ||
912 | GNUNET_break (GNUNET_OK == | ||
913 | GNUNET_OS_process_wait (gc->gnunet_ecc)); | ||
914 | GNUNET_OS_process_destroy (gc->gnunet_ecc); | ||
915 | GNUNET_DISK_pipe_close (gc->gnunet_ecc_out); | ||
916 | } | ||
917 | |||
918 | if (NULL != gc->filename) | ||
919 | { | ||
920 | if (0 != UNLINK (gc->filename)) | ||
921 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", gc->filename); | ||
922 | GNUNET_free (gc->filename); | ||
923 | } | ||
924 | if (NULL != gc->pk) | ||
925 | GNUNET_CRYPTO_ecc_key_free (gc->pk); | ||
926 | GNUNET_free (gc); | ||
927 | } | ||
928 | |||
929 | |||
930 | /** | ||
931 | * Task called upon shutdown or process termination of 'gnunet-ecc' during | ||
932 | * ECC key generation. Check where we are and perform the appropriate | ||
933 | * action. | ||
934 | * | ||
935 | * @param cls the 'struct GNUNET_CRYPTO_EccKeyGenerationContext' | ||
936 | * @param tc scheduler context | ||
937 | */ | ||
938 | static void | ||
939 | check_key_generation_completion (void *cls, | ||
940 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
941 | { | ||
942 | struct GNUNET_CRYPTO_EccKeyGenerationContext *gc = cls; | ||
943 | struct GNUNET_CRYPTO_EccPrivateKey *pk; | ||
944 | |||
945 | gc->read_task = GNUNET_SCHEDULER_NO_TASK; | ||
946 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
947 | { | ||
948 | gc->cont (gc->cont_cls, NULL, _("interrupted by shutdown")); | ||
949 | GNUNET_CRYPTO_ecc_key_create_stop (gc); | ||
950 | return; | ||
951 | } | ||
952 | GNUNET_assert (GNUNET_OK == | ||
953 | GNUNET_OS_process_wait (gc->gnunet_ecc)); | ||
954 | GNUNET_OS_process_destroy (gc->gnunet_ecc); | ||
955 | gc->gnunet_ecc = NULL; | ||
956 | if (NULL == (pk = try_read_key (gc->filename))) | ||
957 | { | ||
958 | GNUNET_break (0); | ||
959 | gc->cont (gc->cont_cls, NULL, _("gnunet-ecc failed")); | ||
960 | GNUNET_CRYPTO_ecc_key_create_stop (gc); | ||
961 | return; | ||
962 | } | ||
963 | gc->cont (gc->cont_cls, pk, NULL); | ||
964 | GNUNET_DISK_pipe_close (gc->gnunet_ecc_out); | ||
965 | GNUNET_free (gc->filename); | ||
966 | GNUNET_free (gc); | ||
967 | } | ||
968 | |||
969 | |||
970 | /** | ||
971 | * Return the private ECC key which already existed on disk | ||
972 | * (asynchronously) to the caller. | ||
973 | * | ||
974 | * @param cls the 'struct GNUNET_CRYPTO_EccKeyGenerationContext' | ||
975 | * @param tc scheduler context (unused) | ||
976 | */ | ||
977 | static void | ||
978 | async_return_key (void *cls, | ||
979 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
980 | { | ||
981 | struct GNUNET_CRYPTO_EccKeyGenerationContext *gc = cls; | ||
982 | |||
983 | gc->cont (gc->cont_cls, | ||
984 | gc->pk, | ||
985 | NULL); | ||
986 | GNUNET_free (gc); | ||
987 | } | ||
988 | |||
989 | |||
990 | /** | ||
991 | * Create a new private key by reading it from a file. If the files | ||
992 | * does not exist, create a new key and write it to the file. If the | ||
993 | * contents of the file are invalid the old file is deleted and a | ||
994 | * fresh key is created. | ||
995 | * | ||
996 | * @param filename name of file to use for storage | ||
997 | * @param cont function to call when done (or on errors) | ||
998 | * @param cont_cls closure for 'cont' | ||
999 | * @return handle to abort operation, NULL on fatal errors (cont will not be called if NULL is returned) | ||
1000 | */ | ||
1001 | struct GNUNET_CRYPTO_EccKeyGenerationContext * | ||
1002 | GNUNET_CRYPTO_ecc_key_create_start (const char *filename, | ||
1003 | GNUNET_CRYPTO_EccKeyCallback cont, | ||
1004 | void *cont_cls) | ||
1005 | { | ||
1006 | struct GNUNET_CRYPTO_EccKeyGenerationContext *gc; | ||
1007 | struct GNUNET_CRYPTO_EccPrivateKey *pk; | ||
1008 | const char *weak_random; | ||
1009 | |||
1010 | if (NULL != (pk = try_read_key (filename))) | ||
1011 | { | ||
1012 | /* quick happy ending: key already exists! */ | ||
1013 | gc = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccKeyGenerationContext)); | ||
1014 | gc->pk = pk; | ||
1015 | gc->cont = cont; | ||
1016 | gc->cont_cls = cont_cls; | ||
1017 | gc->read_task = GNUNET_SCHEDULER_add_now (&async_return_key, | ||
1018 | gc); | ||
1019 | return gc; | ||
1020 | } | ||
1021 | gc = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccKeyGenerationContext)); | ||
1022 | gc->filename = GNUNET_strdup (filename); | ||
1023 | gc->cont = cont; | ||
1024 | gc->cont_cls = cont_cls; | ||
1025 | gc->gnunet_ecc_out = GNUNET_DISK_pipe (GNUNET_NO, | ||
1026 | GNUNET_NO, | ||
1027 | GNUNET_NO, | ||
1028 | GNUNET_YES); | ||
1029 | if (NULL == gc->gnunet_ecc_out) | ||
1030 | { | ||
1031 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "pipe"); | ||
1032 | GNUNET_free (gc->filename); | ||
1033 | GNUNET_free (gc); | ||
1034 | return NULL; | ||
1035 | } | ||
1036 | weak_random = NULL; | ||
1037 | if (GNUNET_YES == | ||
1038 | GNUNET_CRYPTO_random_is_weak ()) | ||
1039 | weak_random = "-w"; | ||
1040 | gc->gnunet_ecc = GNUNET_OS_start_process (GNUNET_NO, | ||
1041 | GNUNET_OS_INHERIT_STD_ERR, | ||
1042 | NULL, | ||
1043 | gc->gnunet_ecc_out, | ||
1044 | "gnunet-ecc", | ||
1045 | "gnunet-ecc", | ||
1046 | gc->filename, | ||
1047 | weak_random, | ||
1048 | NULL); | ||
1049 | if (NULL == gc->gnunet_ecc) | ||
1050 | { | ||
1051 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fork"); | ||
1052 | GNUNET_DISK_pipe_close (gc->gnunet_ecc_out); | ||
1053 | GNUNET_free (gc->filename); | ||
1054 | GNUNET_free (gc); | ||
1055 | return NULL; | ||
1056 | } | ||
1057 | GNUNET_assert (GNUNET_OK == | ||
1058 | GNUNET_DISK_pipe_close_end (gc->gnunet_ecc_out, | ||
1059 | GNUNET_DISK_PIPE_END_WRITE)); | ||
1060 | gc->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
1061 | GNUNET_DISK_pipe_handle (gc->gnunet_ecc_out, | ||
1062 | GNUNET_DISK_PIPE_END_READ), | ||
1063 | &check_key_generation_completion, | ||
1064 | gc); | ||
1065 | return gc; | ||
1066 | } | ||
1067 | |||
1068 | |||
1069 | /** | ||
1070 | * Setup a hostkey file for a peer given the name of the | ||
1071 | * configuration file (!). This function is used so that | ||
1072 | * at a later point code can be certain that reading a | ||
1073 | * hostkey is fast (for example in time-dependent testcases). | ||
1074 | * | ||
1075 | * @param cfg_name name of the configuration file to use | ||
1076 | */ | ||
1077 | void | ||
1078 | GNUNET_CRYPTO_ecc_setup_hostkey (const char *cfg_name) | ||
1079 | { | ||
1080 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
1081 | struct GNUNET_CRYPTO_EccPrivateKey *pk; | ||
1082 | char *fn; | ||
1083 | |||
1084 | cfg = GNUNET_CONFIGURATION_create (); | ||
1085 | (void) GNUNET_CONFIGURATION_load (cfg, cfg_name); | ||
1086 | if (GNUNET_OK == | ||
1087 | GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY", &fn)) | ||
1088 | { | ||
1089 | pk = GNUNET_CRYPTO_ecc_key_create_from_file (fn); | ||
1090 | if (NULL != pk) | ||
1091 | GNUNET_CRYPTO_ecc_key_free (pk); | ||
1092 | GNUNET_free (fn); | ||
1093 | } | ||
1094 | GNUNET_CONFIGURATION_destroy (cfg); | ||
1095 | } | ||
1096 | |||
1097 | |||
1098 | /** | ||
1099 | * Encrypt a block with the public key of another host that uses the | ||
1100 | * same cipher. | ||
1101 | * | ||
1102 | * @param block the block to encrypt | ||
1103 | * @param size the size of block | ||
1104 | * @param publicKey the encoded public key used to encrypt | ||
1105 | * @param target where to store the encrypted block | ||
1106 | * @returns GNUNET_SYSERR on error, GNUNET_OK if ok | ||
1107 | */ | ||
1108 | int | ||
1109 | GNUNET_CRYPTO_ecc_encrypt (const void *block, size_t size, | ||
1110 | const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded | ||
1111 | *publicKey, | ||
1112 | struct GNUNET_CRYPTO_EccEncryptedData *target) | ||
1113 | { | ||
1114 | gcry_sexp_t result; | ||
1115 | gcry_sexp_t data; | ||
1116 | gcry_sexp_t psexp; | ||
1117 | gcry_mpi_t val; | ||
1118 | gcry_mpi_t rval; | ||
1119 | size_t isize; | ||
1120 | size_t erroff; | ||
1121 | |||
1122 | GNUNET_assert (size <= sizeof (struct GNUNET_HashCode)); | ||
1123 | if (! (psexp = decode_public_key (publicKey))) | ||
1124 | return GNUNET_SYSERR; | ||
1125 | isize = size; | ||
1126 | GNUNET_assert (0 == | ||
1127 | gcry_mpi_scan (&val, GCRYMPI_FMT_USG, block, isize, &isize)); | ||
1128 | GNUNET_assert (0 == | ||
1129 | gcry_sexp_build (&data, &erroff, | ||
1130 | "(data (flags pkcs1)(value %m))", val)); | ||
1131 | gcry_mpi_release (val); | ||
1132 | GNUNET_assert (0 == gcry_pk_encrypt (&result, data, psexp)); | ||
1133 | gcry_sexp_release (data); | ||
1134 | gcry_sexp_release (psexp); | ||
1135 | |||
1136 | GNUNET_assert (0 == key_from_sexp (&rval, result, "ecc", "a")); | ||
1137 | gcry_sexp_release (result); | ||
1138 | isize = sizeof (struct GNUNET_CRYPTO_EccEncryptedData); | ||
1139 | GNUNET_assert (0 == | ||
1140 | gcry_mpi_print (GCRYMPI_FMT_USG, (unsigned char *) target, | ||
1141 | isize, &isize, rval)); | ||
1142 | gcry_mpi_release (rval); | ||
1143 | adjust (&target->encoding[0], isize, | ||
1144 | sizeof (struct GNUNET_CRYPTO_EccEncryptedData)); | ||
1145 | return GNUNET_OK; | ||
1146 | } | ||
1147 | |||
1148 | |||
1149 | /** | ||
1150 | * Decrypt a given block with the hostkey. | ||
1151 | * | ||
1152 | * @param key the key with which to decrypt this block | ||
1153 | * @param block the data to decrypt, encoded as returned by encrypt | ||
1154 | * @param result pointer to a location where the result can be stored | ||
1155 | * @param max the maximum number of bits to store for the result, if | ||
1156 | * the decrypted block is bigger, an error is returned | ||
1157 | * @return the size of the decrypted block, -1 on error | ||
1158 | */ | ||
1159 | ssize_t | ||
1160 | GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey * key, | ||
1161 | const struct GNUNET_CRYPTO_EccEncryptedData * block, | ||
1162 | void *result, size_t max) | ||
1163 | { | ||
1164 | gcry_sexp_t resultsexp; | ||
1165 | gcry_sexp_t data; | ||
1166 | size_t erroff; | ||
1167 | size_t size; | ||
1168 | gcry_mpi_t val; | ||
1169 | unsigned char *endp; | ||
1170 | unsigned char *tmp; | ||
1171 | |||
1172 | #if EXTRA_CHECKS | ||
1173 | GNUNET_assert (0 == gcry_pk_testkey (key->sexp)); | ||
1174 | #endif | ||
1175 | size = sizeof (struct GNUNET_CRYPTO_EccEncryptedData); | ||
1176 | GNUNET_assert (0 == | ||
1177 | gcry_mpi_scan (&val, GCRYMPI_FMT_USG, &block->encoding[0], | ||
1178 | size, &size)); | ||
1179 | GNUNET_assert (0 == | ||
1180 | gcry_sexp_build (&data, &erroff, "(enc-val(flags)(ecc(a %m)))", | ||
1181 | val)); | ||
1182 | gcry_mpi_release (val); | ||
1183 | GNUNET_assert (0 == gcry_pk_decrypt (&resultsexp, data, key->sexp)); | ||
1184 | gcry_sexp_release (data); | ||
1185 | /* resultsexp has format "(value %m)" */ | ||
1186 | GNUNET_assert (NULL != | ||
1187 | (val = gcry_sexp_nth_mpi (resultsexp, 1, GCRYMPI_FMT_USG))); | ||
1188 | gcry_sexp_release (resultsexp); | ||
1189 | size = max + GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH * 2; | ||
1190 | tmp = GNUNET_malloc (size); | ||
1191 | GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, tmp, size, &size, val)); | ||
1192 | gcry_mpi_release (val); | ||
1193 | endp = tmp; | ||
1194 | endp += (size - max); | ||
1195 | size = max; | ||
1196 | memcpy (result, endp, size); | ||
1197 | GNUNET_free (tmp); | ||
1198 | return size; | ||
1199 | } | ||
1200 | |||
1201 | |||
1202 | /** | ||
1203 | * Sign a given block. | ||
1204 | * | ||
1205 | * @param key private key to use for the signing | ||
1206 | * @param purpose what to sign (size, purpose) | ||
1207 | * @param sig where to write the signature | ||
1208 | * @return GNUNET_SYSERR on error, GNUNET_OK on success | ||
1209 | */ | ||
1210 | int | ||
1211 | GNUNET_CRYPTO_ecc_sign (const struct GNUNET_CRYPTO_EccPrivateKey *key, | ||
1212 | const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, | ||
1213 | struct GNUNET_CRYPTO_EccSignature *sig) | ||
1214 | { | ||
1215 | gcry_sexp_t result; | ||
1216 | gcry_sexp_t data; | ||
1217 | size_t ssize; | ||
1218 | gcry_mpi_t rval; | ||
1219 | struct GNUNET_HashCode hc; | ||
1220 | char *buff; | ||
1221 | int bufSize; | ||
1222 | |||
1223 | GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc); | ||
1224 | #define FORMATSTRING "(4:data(5:flags5:pkcs1)(4:hash6:sha51264:0123456789012345678901234567890123456789012345678901234567890123))" | ||
1225 | bufSize = strlen (FORMATSTRING) + 1; | ||
1226 | buff = GNUNET_malloc (bufSize); | ||
1227 | memcpy (buff, FORMATSTRING, bufSize); | ||
1228 | memcpy (&buff | ||
1229 | [bufSize - | ||
1230 | strlen | ||
1231 | ("0123456789012345678901234567890123456789012345678901234567890123))") | ||
1232 | - 1], &hc, sizeof (struct GNUNET_HashCode)); | ||
1233 | GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0)); | ||
1234 | GNUNET_free (buff); | ||
1235 | GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp)); | ||
1236 | gcry_sexp_release (data); | ||
1237 | GNUNET_assert (0 == key_from_sexp (&rval, result, "ecc", "s")); | ||
1238 | gcry_sexp_release (result); | ||
1239 | ssize = sizeof (struct GNUNET_CRYPTO_EccSignature); | ||
1240 | GNUNET_assert (0 == | ||
1241 | gcry_mpi_print (GCRYMPI_FMT_USG, (unsigned char *) sig, ssize, | ||
1242 | &ssize, rval)); | ||
1243 | gcry_mpi_release (rval); | ||
1244 | adjust (sig->sig, ssize, sizeof (struct GNUNET_CRYPTO_EccSignature)); | ||
1245 | return GNUNET_OK; | ||
1246 | } | ||
1247 | |||
1248 | |||
1249 | /** | ||
1250 | * Verify signature. | ||
1251 | * | ||
1252 | * @param purpose what is the purpose that the signature should have? | ||
1253 | * @param validate block to validate (size, purpose, data) | ||
1254 | * @param sig signature that is being validated | ||
1255 | * @param publicKey public key of the signer | ||
1256 | * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid | ||
1257 | */ | ||
1258 | int | ||
1259 | GNUNET_CRYPTO_ecc_verify (uint32_t purpose, | ||
1260 | const struct GNUNET_CRYPTO_EccSignaturePurpose | ||
1261 | *validate, | ||
1262 | const struct GNUNET_CRYPTO_EccSignature *sig, | ||
1263 | const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded | ||
1264 | *publicKey) | ||
1265 | { | ||
1266 | gcry_sexp_t data; | ||
1267 | gcry_sexp_t sigdata; | ||
1268 | size_t size; | ||
1269 | gcry_mpi_t val; | ||
1270 | gcry_sexp_t psexp; | ||
1271 | struct GNUNET_HashCode hc; | ||
1272 | char *buff; | ||
1273 | int bufSize; | ||
1274 | size_t erroff; | ||
1275 | int rc; | ||
1276 | |||
1277 | if (purpose != ntohl (validate->purpose)) | ||
1278 | return GNUNET_SYSERR; /* purpose mismatch */ | ||
1279 | GNUNET_CRYPTO_hash (validate, ntohl (validate->size), &hc); | ||
1280 | size = sizeof (struct GNUNET_CRYPTO_EccSignature); | ||
1281 | GNUNET_assert (0 == | ||
1282 | gcry_mpi_scan (&val, GCRYMPI_FMT_USG, | ||
1283 | (const unsigned char *) sig, size, &size)); | ||
1284 | GNUNET_assert (0 == | ||
1285 | gcry_sexp_build (&sigdata, &erroff, "(sig-val(ecc(s %m)))", | ||
1286 | val)); | ||
1287 | gcry_mpi_release (val); | ||
1288 | bufSize = strlen (FORMATSTRING) + 1; | ||
1289 | buff = GNUNET_malloc (bufSize); | ||
1290 | memcpy (buff, FORMATSTRING, bufSize); | ||
1291 | memcpy (&buff | ||
1292 | [strlen (FORMATSTRING) - | ||
1293 | strlen | ||
1294 | ("0123456789012345678901234567890123456789012345678901234567890123))")], | ||
1295 | &hc, sizeof (struct GNUNET_HashCode)); | ||
1296 | GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0)); | ||
1297 | GNUNET_free (buff); | ||
1298 | if (! (psexp = decode_public_key (publicKey))) | ||
1299 | { | ||
1300 | gcry_sexp_release (data); | ||
1301 | gcry_sexp_release (sigdata); | ||
1302 | return GNUNET_SYSERR; | ||
1303 | } | ||
1304 | rc = gcry_pk_verify (sigdata, data, psexp); | ||
1305 | gcry_sexp_release (psexp); | ||
1306 | gcry_sexp_release (data); | ||
1307 | gcry_sexp_release (sigdata); | ||
1308 | if (rc) | ||
1309 | { | ||
1310 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1311 | _("ECC signature verification failed at %s:%d: %s\n"), __FILE__, | ||
1312 | __LINE__, gcry_strerror (rc)); | ||
1313 | return GNUNET_SYSERR; | ||
1314 | } | ||
1315 | return GNUNET_OK; | ||
1316 | } | ||
1317 | |||
1318 | |||
1319 | /* end of crypto_ecc.c */ | ||