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 "taler/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 3500 : try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
84 : {
85 3500 : if (-1 != esh->sock)
86 3473 : 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 138 : handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper *esh,
182 : const struct GNUNET_MessageHeader *hdr)
183 : {
184 138 : const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan
185 : = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr;
186 :
187 138 : if (sizeof (*kan) != ntohs (hdr->size))
188 : {
189 0 : GNUNET_break_op (0);
190 0 : return GNUNET_SYSERR;
191 : }
192 138 : if (GNUNET_OK !=
193 138 : 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 138 : 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 138 : 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 1728 : TALER_CRYPTO_helper_esign_poll (struct TALER_CRYPTO_ExchangeSignHelper *esh)
244 : {
245 : char buf[UINT16_MAX];
246 1728 : size_t off = 0;
247 1728 : unsigned int retry_limit = 3;
248 1728 : const struct GNUNET_MessageHeader *hdr
249 : = (const struct GNUNET_MessageHeader *) buf;
250 :
251 1728 : if (GNUNET_OK !=
252 1728 : try_connect (esh))
253 0 : return; /* give up */
254 : while (1)
255 86 : {
256 : uint16_t msize;
257 : ssize_t ret;
258 :
259 1814 : ret = recv (esh->sock,
260 : buf + off,
261 : sizeof (buf) - off,
262 1814 : (esh->synced && (0 == off))
263 : ? MSG_DONTWAIT
264 : : 0);
265 1814 : if (ret < 0)
266 : {
267 1728 : if (EINTR == errno)
268 0 : continue;
269 1728 : if (EAGAIN == errno)
270 : {
271 1728 : GNUNET_assert (esh->synced);
272 1728 : GNUNET_assert (0 == off);
273 1728 : 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 86 : if (0 == ret)
287 : {
288 0 : GNUNET_break (0 == off);
289 0 : return;
290 : }
291 86 : off += ret;
292 253 : more:
293 253 : if (off < sizeof (struct GNUNET_MessageHeader))
294 86 : continue;
295 167 : msize = ntohs (hdr->size);
296 167 : if (off < msize)
297 0 : continue;
298 167 : switch (ntohs (hdr->type))
299 : {
300 138 : case TALER_HELPER_EDDSA_MT_AVAIL:
301 138 : if (GNUNET_OK !=
302 138 : handle_mt_avail (esh,
303 : hdr))
304 : {
305 0 : GNUNET_break_op (0);
306 0 : do_disconnect (esh);
307 0 : return;
308 : }
309 138 : break;
310 3 : case TALER_HELPER_EDDSA_MT_PURGE:
311 3 : if (GNUNET_OK !=
312 3 : handle_mt_purge (esh,
313 : hdr))
314 : {
315 0 : GNUNET_break_op (0);
316 0 : do_disconnect (esh);
317 0 : return;
318 : }
319 3 : break;
320 26 : case TALER_HELPER_EDDSA_SYNCED:
321 26 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
322 : "Now synchronized with EdDSA helper\n");
323 26 : esh->synced = true;
324 26 : break;
325 0 : default:
326 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
327 : "Received unexpected message of type %d (len: %u)\n",
328 : (unsigned int) ntohs (hdr->type),
329 : (unsigned int) msize);
330 0 : GNUNET_break_op (0);
331 0 : do_disconnect (esh);
332 0 : return;
333 : }
334 167 : memmove (buf,
335 167 : &buf[msize],
336 : off - msize);
337 167 : off -= msize;
338 167 : goto more;
339 : }
340 : }
341 :
342 :
343 : enum TALER_ErrorCode
344 1742 : TALER_CRYPTO_helper_esign_sign_ (
345 : struct TALER_CRYPTO_ExchangeSignHelper *esh,
346 : const struct GNUNET_CRYPTO_SignaturePurpose *purpose,
347 : struct TALER_ExchangePublicKeyP *exchange_pub,
348 : struct TALER_ExchangeSignatureP *exchange_sig)
349 : {
350 1742 : uint32_t purpose_size = ntohl (purpose->size);
351 :
352 1742 : if (GNUNET_OK !=
353 1742 : try_connect (esh))
354 : {
355 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
356 : "Failed to connect to helper\n");
357 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
358 : }
359 1742 : GNUNET_assert (purpose_size <
360 : UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest));
361 1742 : {
362 1742 : char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
363 1742 : - sizeof (struct GNUNET_CRYPTO_SignaturePurpose)];
364 1742 : struct TALER_CRYPTO_EddsaSignRequest *sr
365 : = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
366 :
367 1742 : sr->header.size = htons (sizeof (buf));
368 1742 : sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
369 1742 : sr->reserved = htonl (0);
370 1742 : GNUNET_memcpy (&sr->purpose,
371 : purpose,
372 : purpose_size);
373 1742 : if (GNUNET_OK !=
374 1742 : TALER_crypto_helper_send_all (esh->sock,
375 : buf,
376 : sizeof (buf)))
377 : {
378 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
379 : "send",
380 : esh->sa.sun_path);
381 0 : do_disconnect (esh);
382 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
383 : }
384 : }
385 :
386 : {
387 : char buf[UINT16_MAX];
388 1742 : size_t off = 0;
389 1742 : const struct GNUNET_MessageHeader *hdr
390 : = (const struct GNUNET_MessageHeader *) buf;
391 1742 : bool finished = false;
392 1742 : enum TALER_ErrorCode ec = TALER_EC_INVALID;
393 :
394 : while (1)
395 1742 : {
396 : ssize_t ret;
397 : uint16_t msize;
398 :
399 5226 : ret = recv (esh->sock,
400 3484 : &buf[off],
401 : sizeof (buf) - off,
402 1742 : (finished && (0 == off))
403 : ? MSG_DONTWAIT
404 : : 0);
405 3484 : if (ret < 0)
406 : {
407 1742 : if (EINTR == errno)
408 0 : continue;
409 1742 : if (EAGAIN == errno)
410 : {
411 1742 : GNUNET_assert (finished);
412 1742 : GNUNET_assert (0 == off);
413 1742 : break;
414 : }
415 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
416 : "recv");
417 0 : do_disconnect (esh);
418 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
419 : }
420 1742 : if (0 == ret)
421 : {
422 0 : GNUNET_break (0 == off);
423 0 : if (finished)
424 0 : return TALER_EC_NONE;
425 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
426 : }
427 1742 : off += ret;
428 3484 : more:
429 3484 : if (off < sizeof (struct GNUNET_MessageHeader))
430 1742 : continue;
431 1742 : msize = ntohs (hdr->size);
432 1742 : if (off < msize)
433 0 : continue;
434 1742 : switch (ntohs (hdr->type))
435 : {
436 1742 : case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
437 1742 : if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
438 : {
439 0 : GNUNET_break_op (0);
440 0 : do_disconnect (esh);
441 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
442 : }
443 1742 : if (finished)
444 : {
445 0 : GNUNET_break_op (0);
446 0 : do_disconnect (esh);
447 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
448 : }
449 : {
450 1742 : const struct TALER_CRYPTO_EddsaSignResponse *sr =
451 : (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
452 1742 : *exchange_sig = sr->exchange_sig;
453 1742 : *exchange_pub = sr->exchange_pub;
454 1742 : finished = true;
455 1742 : ec = TALER_EC_NONE;
456 1742 : break;
457 : }
458 0 : case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
459 0 : if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
460 : {
461 0 : GNUNET_break_op (0);
462 0 : do_disconnect (esh);
463 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
464 : }
465 : {
466 0 : const struct TALER_CRYPTO_EddsaSignFailure *sf =
467 : (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
468 :
469 0 : finished = true;
470 0 : ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec);
471 0 : break;
472 : }
473 0 : case TALER_HELPER_EDDSA_MT_AVAIL:
474 0 : if (GNUNET_OK !=
475 0 : handle_mt_avail (esh,
476 : hdr))
477 : {
478 0 : GNUNET_break_op (0);
479 0 : do_disconnect (esh);
480 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
481 : }
482 0 : break; /* while(1) loop ensures we recv() again */
483 0 : case TALER_HELPER_EDDSA_MT_PURGE:
484 0 : if (GNUNET_OK !=
485 0 : handle_mt_purge (esh,
486 : hdr))
487 : {
488 0 : GNUNET_break_op (0);
489 0 : do_disconnect (esh);
490 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
491 : }
492 0 : break; /* while(1) loop ensures we recv() again */
493 0 : case TALER_HELPER_EDDSA_SYNCED:
494 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
495 : "Synchronized add odd time with EdDSA helper!\n");
496 0 : esh->synced = true;
497 0 : break;
498 0 : default:
499 0 : GNUNET_break_op (0);
500 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
501 : "Received unexpected message of type %u\n",
502 : ntohs (hdr->type));
503 0 : do_disconnect (esh);
504 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
505 : }
506 1742 : memmove (buf,
507 1742 : &buf[msize],
508 : off - msize);
509 1742 : off -= msize;
510 1742 : goto more;
511 : } /* while(1) */
512 1742 : return ec;
513 : }
514 : }
515 :
516 :
517 : void
518 3 : TALER_CRYPTO_helper_esign_revoke (
519 : struct TALER_CRYPTO_ExchangeSignHelper *esh,
520 : const struct TALER_ExchangePublicKeyP *exchange_pub)
521 : {
522 3 : if (GNUNET_OK !=
523 3 : try_connect (esh))
524 0 : return; /* give up */
525 : {
526 3 : struct TALER_CRYPTO_EddsaRevokeRequest rr = {
527 3 : .header.size = htons (sizeof (rr)),
528 3 : .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
529 : .exchange_pub = *exchange_pub
530 : };
531 :
532 3 : if (GNUNET_OK !=
533 3 : TALER_crypto_helper_send_all (esh->sock,
534 : &rr,
535 : sizeof (rr)))
536 : {
537 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
538 : "send");
539 0 : do_disconnect (esh);
540 0 : return;
541 : }
542 : }
543 : }
544 :
545 :
546 : void
547 27 : TALER_CRYPTO_helper_esign_disconnect (
548 : struct TALER_CRYPTO_ExchangeSignHelper *esh)
549 : {
550 27 : if (-1 != esh->sock)
551 26 : do_disconnect (esh);
552 27 : GNUNET_free (esh);
553 27 : }
554 :
555 :
556 : /* end of crypto_helper_esign.c */
|