Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020, 2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file util/crypto_helper_esign.c
18 : * @brief utility functions for running out-of-process private key operations
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include "taler/taler_util.h"
23 : #include "taler/taler_signatures.h"
24 : #include "secmod_eddsa.h"
25 : #include <poll.h>
26 : #include "crypto_helper_common.h"
27 :
28 :
29 : struct TALER_CRYPTO_ExchangeSignHelper
30 : {
31 : /**
32 : * Function to call with updates to available key material.
33 : */
34 : TALER_CRYPTO_ExchangeKeyStatusCallback ekc;
35 :
36 : /**
37 : * Closure for @e ekc
38 : */
39 : void *ekc_cls;
40 :
41 : /**
42 : * Socket address of the denomination helper process.
43 : * Used to reconnect if the connection breaks.
44 : */
45 : struct sockaddr_un sa;
46 :
47 : /**
48 : * The UNIX domain socket, -1 if we are currently not connected.
49 : */
50 : int sock;
51 :
52 : /**
53 : * Have we reached the sync'ed state?
54 : */
55 : bool synced;
56 :
57 : };
58 :
59 :
60 : /**
61 : * Disconnect from the helper process. Updates
62 : * @e sock field in @a esh.
63 : *
64 : * @param[in,out] esh handle to tear down connection of
65 : */
66 : static void
67 27 : do_disconnect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
68 : {
69 27 : GNUNET_break (0 == close (esh->sock));
70 27 : esh->sock = -1;
71 27 : esh->synced = false;
72 27 : }
73 :
74 :
75 : /**
76 : * Try to connect to the helper process. Updates
77 : * @e sock field in @a esh.
78 : *
79 : * @param[in,out] esh handle to establish connection for
80 : * @return #GNUNET_OK on success
81 : */
82 : static enum GNUNET_GenericReturnValue
83 2807 : try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
84 : {
85 2807 : if (-1 != esh->sock)
86 2780 : return GNUNET_OK;
87 27 : esh->sock = socket (AF_UNIX,
88 : SOCK_STREAM,
89 : 0);
90 27 : if (-1 == esh->sock)
91 : {
92 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
93 : "socket");
94 0 : return GNUNET_SYSERR;
95 : }
96 27 : if (0 !=
97 27 : connect (esh->sock,
98 27 : (const struct sockaddr *) &esh->sa,
99 : sizeof (esh->sa)))
100 : {
101 1 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
102 : "connect",
103 : esh->sa.sun_path);
104 1 : do_disconnect (esh);
105 1 : return GNUNET_SYSERR;
106 : }
107 26 : return GNUNET_OK;
108 : }
109 :
110 :
111 : struct TALER_CRYPTO_ExchangeSignHelper *
112 27 : TALER_CRYPTO_helper_esign_connect (
113 : const struct GNUNET_CONFIGURATION_Handle *cfg,
114 : const char *section,
115 : TALER_CRYPTO_ExchangeKeyStatusCallback ekc,
116 : void *ekc_cls)
117 : {
118 : struct TALER_CRYPTO_ExchangeSignHelper *esh;
119 : char *unixpath;
120 : char *secname;
121 :
122 27 : GNUNET_asprintf (&secname,
123 : "%s-secmod-eddsa",
124 : section);
125 :
126 27 : if (GNUNET_OK !=
127 27 : GNUNET_CONFIGURATION_get_value_filename (cfg,
128 : secname,
129 : "UNIXPATH",
130 : &unixpath))
131 : {
132 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
133 : secname,
134 : "UNIXPATH");
135 0 : GNUNET_free (secname);
136 0 : return NULL;
137 : }
138 : /* we use >= here because we want the sun_path to always
139 : be 0-terminated */
140 27 : if (strlen (unixpath) >= sizeof (esh->sa.sun_path))
141 : {
142 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
143 : secname,
144 : "UNIXPATH",
145 : "path too long");
146 0 : GNUNET_free (unixpath);
147 0 : GNUNET_free (secname);
148 0 : return NULL;
149 : }
150 27 : GNUNET_free (secname);
151 27 : esh = GNUNET_new (struct TALER_CRYPTO_ExchangeSignHelper);
152 27 : esh->ekc = ekc;
153 27 : esh->ekc_cls = ekc_cls;
154 27 : esh->sa.sun_family = AF_UNIX;
155 27 : strncpy (esh->sa.sun_path,
156 : unixpath,
157 : sizeof (esh->sa.sun_path) - 1);
158 27 : GNUNET_free (unixpath);
159 27 : esh->sock = -1;
160 27 : if (GNUNET_OK !=
161 27 : try_connect (esh))
162 : {
163 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
164 : "Could not connect to %s. Will keep trying\n",
165 : "taler-exchange-helper-secmod-eddsa");
166 1 : return esh;
167 : }
168 26 : TALER_CRYPTO_helper_esign_poll (esh);
169 26 : return esh;
170 : }
171 :
172 :
173 : /**
174 : * Handle a #TALER_HELPER_EDDSA_MT_AVAIL message from the helper.
175 : *
176 : * @param esh helper context
177 : * @param hdr message that we received
178 : * @return #GNUNET_OK on success
179 : */
180 : static enum GNUNET_GenericReturnValue
181 102 : handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper *esh,
182 : const struct GNUNET_MessageHeader *hdr)
183 : {
184 102 : const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan
185 : = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr;
186 :
187 102 : if (sizeof (*kan) != ntohs (hdr->size))
188 : {
189 0 : GNUNET_break_op (0);
190 0 : return GNUNET_SYSERR;
191 : }
192 102 : if (GNUNET_OK !=
193 102 : TALER_exchange_secmod_eddsa_verify (
194 : &kan->exchange_pub,
195 : GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
196 : GNUNET_TIME_relative_ntoh (kan->duration),
197 : &kan->secm_pub,
198 : &kan->secm_sig))
199 : {
200 0 : GNUNET_break_op (0);
201 0 : return GNUNET_SYSERR;
202 : }
203 102 : esh->ekc (esh->ekc_cls,
204 : GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
205 : GNUNET_TIME_relative_ntoh (kan->duration),
206 : &kan->exchange_pub,
207 : &kan->secm_pub,
208 : &kan->secm_sig);
209 102 : return GNUNET_OK;
210 : }
211 :
212 :
213 : /**
214 : * Handle a #TALER_HELPER_EDDSA_MT_PURGE message from the helper.
215 : *
216 : * @param esh helper context
217 : * @param hdr message that we received
218 : * @return #GNUNET_OK on success
219 : */
220 : static enum GNUNET_GenericReturnValue
221 3 : handle_mt_purge (struct TALER_CRYPTO_ExchangeSignHelper *esh,
222 : const struct GNUNET_MessageHeader *hdr)
223 : {
224 3 : const struct TALER_CRYPTO_EddsaKeyPurgeNotification *pn
225 : = (const struct TALER_CRYPTO_EddsaKeyPurgeNotification *) hdr;
226 :
227 3 : if (sizeof (*pn) != ntohs (hdr->size))
228 : {
229 0 : GNUNET_break_op (0);
230 0 : return GNUNET_SYSERR;
231 : }
232 3 : esh->ekc (esh->ekc_cls,
233 3 : GNUNET_TIME_UNIT_ZERO_TS,
234 3 : GNUNET_TIME_UNIT_ZERO,
235 : &pn->exchange_pub,
236 : NULL,
237 : NULL);
238 3 : return GNUNET_OK;
239 : }
240 :
241 :
242 : void
243 1629 : TALER_CRYPTO_helper_esign_poll (struct TALER_CRYPTO_ExchangeSignHelper *esh)
244 : {
245 : char buf[UINT16_MAX];
246 1629 : size_t off = 0;
247 1629 : unsigned int retry_limit = 3;
248 1629 : const struct GNUNET_MessageHeader *hdr
249 : = (const struct GNUNET_MessageHeader *) buf;
250 :
251 1629 : if (GNUNET_OK !=
252 1629 : try_connect (esh))
253 0 : return; /* give up */
254 : while (1)
255 71 : {
256 : uint16_t msize;
257 : ssize_t ret;
258 :
259 1700 : ret = recv (esh->sock,
260 : buf + off,
261 : sizeof (buf) - off,
262 1700 : (esh->synced && (0 == off))
263 : ? MSG_DONTWAIT
264 : : 0);
265 1700 : if (ret < 0)
266 : {
267 1629 : if (EINTR == errno)
268 0 : continue;
269 1629 : if (EAGAIN == errno)
270 : {
271 1629 : GNUNET_assert (esh->synced);
272 1629 : GNUNET_assert (0 == off);
273 1629 : break;
274 : }
275 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
276 : "recv");
277 0 : do_disconnect (esh);
278 0 : if (0 == retry_limit)
279 0 : return; /* give up */
280 0 : if (GNUNET_OK !=
281 0 : try_connect (esh))
282 0 : return; /* give up */
283 0 : retry_limit--;
284 0 : continue;
285 : }
286 71 : if (0 == ret)
287 : {
288 0 : GNUNET_break (0 == off);
289 0 : return;
290 : }
291 71 : off += ret;
292 202 : more:
293 202 : if (off < sizeof (struct GNUNET_MessageHeader))
294 71 : continue;
295 131 : msize = ntohs (hdr->size);
296 131 : if (msize < sizeof (struct GNUNET_MessageHeader))
297 : {
298 0 : GNUNET_break_op (0);
299 0 : do_disconnect (esh);
300 0 : return;
301 : }
302 131 : if (off < msize)
303 0 : continue;
304 131 : switch (ntohs (hdr->type))
305 : {
306 102 : case TALER_HELPER_EDDSA_MT_AVAIL:
307 102 : if (GNUNET_OK !=
308 102 : handle_mt_avail (esh,
309 : hdr))
310 : {
311 0 : GNUNET_break_op (0);
312 0 : do_disconnect (esh);
313 0 : return;
314 : }
315 102 : break;
316 3 : case TALER_HELPER_EDDSA_MT_PURGE:
317 3 : if (GNUNET_OK !=
318 3 : handle_mt_purge (esh,
319 : hdr))
320 : {
321 0 : GNUNET_break_op (0);
322 0 : do_disconnect (esh);
323 0 : return;
324 : }
325 3 : break;
326 26 : case TALER_HELPER_EDDSA_SYNCED:
327 26 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
328 : "Now synchronized with EdDSA helper\n");
329 26 : esh->synced = true;
330 26 : break;
331 0 : default:
332 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333 : "Received unexpected message of type %d (len: %u)\n",
334 : (unsigned int) ntohs (hdr->type),
335 : (unsigned int) msize);
336 0 : GNUNET_break_op (0);
337 0 : do_disconnect (esh);
338 0 : return;
339 : }
340 131 : memmove (buf,
341 131 : &buf[msize],
342 : off - msize);
343 131 : off -= msize;
344 131 : goto more;
345 : }
346 : }
347 :
348 :
349 : enum TALER_ErrorCode
350 1148 : TALER_CRYPTO_helper_esign_sign_ (
351 : struct TALER_CRYPTO_ExchangeSignHelper *esh,
352 : const struct GNUNET_CRYPTO_SignaturePurpose *purpose,
353 : struct TALER_ExchangePublicKeyP *exchange_pub,
354 : struct TALER_ExchangeSignatureP *exchange_sig)
355 : {
356 1148 : uint32_t purpose_size = ntohl (purpose->size);
357 :
358 : /* clear the outputs, so that we never return
359 : uninitialized values on failures */
360 1148 : memset (exchange_pub,
361 : 0,
362 : sizeof (*exchange_pub));
363 1148 : memset (exchange_sig,
364 : 0,
365 : sizeof (*exchange_sig));
366 1148 : if (GNUNET_OK !=
367 1148 : try_connect (esh))
368 : {
369 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
370 : "Failed to connect to helper\n");
371 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
372 : }
373 1148 : GNUNET_assert (purpose_size >= sizeof (*purpose));
374 1148 : GNUNET_assert (purpose_size <
375 : UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest));
376 1148 : {
377 1148 : char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
378 1148 : - sizeof (struct GNUNET_CRYPTO_SignaturePurpose)];
379 1148 : struct TALER_CRYPTO_EddsaSignRequest *sr
380 : = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
381 :
382 1148 : sr->header.size = htons (sizeof (buf));
383 1148 : sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
384 1148 : sr->reserved = htonl (0);
385 1148 : GNUNET_memcpy (&sr->purpose,
386 : purpose,
387 : purpose_size);
388 1148 : if (GNUNET_OK !=
389 1148 : TALER_crypto_helper_send_all (esh->sock,
390 : buf,
391 : sizeof (buf)))
392 : {
393 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
394 : "send",
395 : esh->sa.sun_path);
396 0 : do_disconnect (esh);
397 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
398 : }
399 : }
400 :
401 : {
402 : char buf[UINT16_MAX];
403 1148 : size_t off = 0;
404 1148 : const struct GNUNET_MessageHeader *hdr
405 : = (const struct GNUNET_MessageHeader *) buf;
406 1148 : bool finished = false;
407 1148 : enum TALER_ErrorCode ec = TALER_EC_INVALID;
408 :
409 : while (1)
410 1148 : {
411 : ssize_t ret;
412 : uint16_t msize;
413 :
414 3444 : ret = recv (esh->sock,
415 2296 : &buf[off],
416 : sizeof (buf) - off,
417 1148 : (finished && (0 == off))
418 : ? MSG_DONTWAIT
419 : : 0);
420 2296 : if (ret < 0)
421 : {
422 1148 : if (EINTR == errno)
423 0 : continue;
424 1148 : if (EAGAIN == errno)
425 : {
426 1148 : GNUNET_assert (finished);
427 1148 : GNUNET_assert (0 == off);
428 1148 : break;
429 : }
430 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
431 : "recv");
432 0 : do_disconnect (esh);
433 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
434 : }
435 1148 : if (0 == ret)
436 : {
437 : /* helper closed the connection */
438 0 : GNUNET_break (0 == off);
439 0 : do_disconnect (esh);
440 0 : if (! finished)
441 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
442 0 : if (TALER_EC_NONE == ec)
443 0 : break;
444 0 : return ec;
445 : }
446 1148 : off += ret;
447 2296 : more:
448 2296 : if (off < sizeof (struct GNUNET_MessageHeader))
449 1148 : continue;
450 1148 : msize = ntohs (hdr->size);
451 1148 : if (msize < sizeof (struct GNUNET_MessageHeader))
452 : {
453 0 : GNUNET_break_op (0);
454 0 : do_disconnect (esh);
455 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
456 : }
457 1148 : if (off < msize)
458 0 : continue;
459 1148 : switch (ntohs (hdr->type))
460 : {
461 1148 : case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
462 1148 : if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
463 : {
464 0 : GNUNET_break_op (0);
465 0 : do_disconnect (esh);
466 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
467 : }
468 1148 : if (finished)
469 : {
470 0 : GNUNET_break_op (0);
471 0 : do_disconnect (esh);
472 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
473 : }
474 : {
475 1148 : const struct TALER_CRYPTO_EddsaSignResponse *sr =
476 : (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
477 1148 : *exchange_sig = sr->exchange_sig;
478 1148 : *exchange_pub = sr->exchange_pub;
479 1148 : finished = true;
480 1148 : ec = TALER_EC_NONE;
481 1148 : break;
482 : }
483 0 : case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
484 0 : if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
485 : {
486 0 : GNUNET_break_op (0);
487 0 : do_disconnect (esh);
488 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
489 : }
490 : {
491 0 : const struct TALER_CRYPTO_EddsaSignFailure *sf =
492 : (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
493 :
494 0 : finished = true;
495 0 : ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec);
496 0 : break;
497 : }
498 0 : case TALER_HELPER_EDDSA_MT_AVAIL:
499 0 : if (GNUNET_OK !=
500 0 : handle_mt_avail (esh,
501 : hdr))
502 : {
503 0 : GNUNET_break_op (0);
504 0 : do_disconnect (esh);
505 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
506 : }
507 0 : break; /* while(1) loop ensures we recv() again */
508 0 : case TALER_HELPER_EDDSA_MT_PURGE:
509 0 : if (GNUNET_OK !=
510 0 : handle_mt_purge (esh,
511 : hdr))
512 : {
513 0 : GNUNET_break_op (0);
514 0 : do_disconnect (esh);
515 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
516 : }
517 0 : break; /* while(1) loop ensures we recv() again */
518 0 : case TALER_HELPER_EDDSA_SYNCED:
519 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
520 : "Synchronized add odd time with EdDSA helper!\n");
521 0 : esh->synced = true;
522 0 : break;
523 0 : default:
524 0 : GNUNET_break_op (0);
525 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
526 : "Received unexpected message of type %u\n",
527 : ntohs (hdr->type));
528 0 : do_disconnect (esh);
529 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
530 : }
531 1148 : memmove (buf,
532 1148 : &buf[msize],
533 : off - msize);
534 1148 : off -= msize;
535 1148 : goto more;
536 : } /* while(1) */
537 1148 : return ec;
538 : }
539 : }
540 :
541 :
542 : void
543 3 : TALER_CRYPTO_helper_esign_revoke (
544 : struct TALER_CRYPTO_ExchangeSignHelper *esh,
545 : const struct TALER_ExchangePublicKeyP *exchange_pub)
546 : {
547 3 : if (GNUNET_OK !=
548 3 : try_connect (esh))
549 0 : return; /* give up */
550 : {
551 3 : struct TALER_CRYPTO_EddsaRevokeRequest rr = {
552 3 : .header.size = htons (sizeof (rr)),
553 3 : .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
554 : .exchange_pub = *exchange_pub
555 : };
556 :
557 3 : if (GNUNET_OK !=
558 3 : TALER_crypto_helper_send_all (esh->sock,
559 : &rr,
560 : sizeof (rr)))
561 : {
562 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
563 : "send");
564 0 : do_disconnect (esh);
565 0 : return;
566 : }
567 : }
568 : }
569 :
570 :
571 : void
572 27 : TALER_CRYPTO_helper_esign_disconnect (
573 : struct TALER_CRYPTO_ExchangeSignHelper *esh)
574 : {
575 27 : if (-1 != esh->sock)
576 26 : do_disconnect (esh);
577 27 : GNUNET_free (esh);
578 27 : }
579 :
580 :
581 : /* end of crypto_helper_esign.c */
|