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_util.h"
23 : #include "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 38 : do_disconnect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
68 : {
69 38 : GNUNET_break (0 == close (esh->sock));
70 38 : esh->sock = -1;
71 38 : esh->synced = false;
72 38 : }
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 3806 : try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
84 : {
85 3806 : if (-1 != esh->sock)
86 3768 : return GNUNET_OK;
87 38 : esh->sock = socket (AF_UNIX,
88 : SOCK_STREAM,
89 : 0);
90 38 : if (-1 == esh->sock)
91 : {
92 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
93 : "socket");
94 0 : return GNUNET_SYSERR;
95 : }
96 38 : if (0 !=
97 38 : connect (esh->sock,
98 38 : (const struct sockaddr *) &esh->sa,
99 : sizeof (esh->sa)))
100 : {
101 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
102 : "connect",
103 : esh->sa.sun_path);
104 0 : do_disconnect (esh);
105 0 : return GNUNET_SYSERR;
106 : }
107 38 : return GNUNET_OK;
108 : }
109 :
110 :
111 : struct TALER_CRYPTO_ExchangeSignHelper *
112 38 : 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 38 : GNUNET_asprintf (&secname,
123 : "%s-secmod-eddsa",
124 : section);
125 :
126 38 : if (GNUNET_OK !=
127 38 : 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 38 : 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 38 : GNUNET_free (secname);
151 38 : esh = GNUNET_new (struct TALER_CRYPTO_ExchangeSignHelper);
152 38 : esh->ekc = ekc;
153 38 : esh->ekc_cls = ekc_cls;
154 38 : esh->sa.sun_family = AF_UNIX;
155 38 : strncpy (esh->sa.sun_path,
156 : unixpath,
157 : sizeof (esh->sa.sun_path) - 1);
158 38 : GNUNET_free (unixpath);
159 38 : esh->sock = -1;
160 38 : if (GNUNET_OK !=
161 38 : try_connect (esh))
162 : {
163 0 : TALER_CRYPTO_helper_esign_disconnect (esh);
164 0 : return NULL;
165 : }
166 :
167 38 : TALER_CRYPTO_helper_esign_poll (esh);
168 38 : return esh;
169 : }
170 :
171 :
172 : /**
173 : * Handle a #TALER_HELPER_EDDSA_MT_AVAIL message from the helper.
174 : *
175 : * @param esh helper context
176 : * @param hdr message that we received
177 : * @return #GNUNET_OK on success
178 : */
179 : static enum GNUNET_GenericReturnValue
180 198 : handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper *esh,
181 : const struct GNUNET_MessageHeader *hdr)
182 : {
183 198 : const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan
184 : = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr;
185 :
186 198 : if (sizeof (*kan) != ntohs (hdr->size))
187 : {
188 0 : GNUNET_break_op (0);
189 0 : return GNUNET_SYSERR;
190 : }
191 198 : if (GNUNET_OK !=
192 198 : TALER_exchange_secmod_eddsa_verify (
193 : &kan->exchange_pub,
194 : GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
195 : GNUNET_TIME_relative_ntoh (kan->duration),
196 : &kan->secm_pub,
197 : &kan->secm_sig))
198 : {
199 0 : GNUNET_break_op (0);
200 0 : return GNUNET_SYSERR;
201 : }
202 198 : esh->ekc (esh->ekc_cls,
203 : GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
204 : GNUNET_TIME_relative_ntoh (kan->duration),
205 : &kan->exchange_pub,
206 : &kan->secm_pub,
207 : &kan->secm_sig);
208 198 : return GNUNET_OK;
209 : }
210 :
211 :
212 : /**
213 : * Handle a #TALER_HELPER_EDDSA_MT_PURGE message from the helper.
214 : *
215 : * @param esh helper context
216 : * @param hdr message that we received
217 : * @return #GNUNET_OK on success
218 : */
219 : static enum GNUNET_GenericReturnValue
220 3 : handle_mt_purge (struct TALER_CRYPTO_ExchangeSignHelper *esh,
221 : const struct GNUNET_MessageHeader *hdr)
222 : {
223 3 : const struct TALER_CRYPTO_EddsaKeyPurgeNotification *pn
224 : = (const struct TALER_CRYPTO_EddsaKeyPurgeNotification *) hdr;
225 :
226 3 : if (sizeof (*pn) != ntohs (hdr->size))
227 : {
228 0 : GNUNET_break_op (0);
229 0 : return GNUNET_SYSERR;
230 : }
231 3 : esh->ekc (esh->ekc_cls,
232 3 : GNUNET_TIME_UNIT_ZERO_TS,
233 3 : GNUNET_TIME_UNIT_ZERO,
234 : &pn->exchange_pub,
235 : NULL,
236 : NULL);
237 3 : return GNUNET_OK;
238 : }
239 :
240 :
241 : void
242 1796 : TALER_CRYPTO_helper_esign_poll (struct TALER_CRYPTO_ExchangeSignHelper *esh)
243 : {
244 : char buf[UINT16_MAX];
245 1796 : size_t off = 0;
246 1796 : unsigned int retry_limit = 3;
247 1796 : const struct GNUNET_MessageHeader *hdr
248 : = (const struct GNUNET_MessageHeader *) buf;
249 :
250 1796 : if (GNUNET_OK !=
251 1796 : try_connect (esh))
252 0 : return; /* give up */
253 : while (1)
254 160 : {
255 : uint16_t msize;
256 : ssize_t ret;
257 :
258 1956 : ret = recv (esh->sock,
259 : buf + off,
260 : sizeof (buf) - off,
261 1956 : (esh->synced && (0 == off))
262 : ? MSG_DONTWAIT
263 : : 0);
264 1956 : if (ret < 0)
265 : {
266 1796 : if (EINTR == errno)
267 0 : continue;
268 1796 : if (EAGAIN == errno)
269 : {
270 1796 : GNUNET_assert (esh->synced);
271 1796 : GNUNET_assert (0 == off);
272 1796 : break;
273 : }
274 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
275 : "recv");
276 0 : do_disconnect (esh);
277 0 : if (0 == retry_limit)
278 0 : return; /* give up */
279 0 : if (GNUNET_OK !=
280 0 : try_connect (esh))
281 0 : return; /* give up */
282 0 : retry_limit--;
283 0 : continue;
284 : }
285 160 : if (0 == ret)
286 : {
287 0 : GNUNET_break (0 == off);
288 0 : return;
289 : }
290 160 : off += ret;
291 399 : more:
292 399 : if (off < sizeof (struct GNUNET_MessageHeader))
293 160 : continue;
294 239 : msize = ntohs (hdr->size);
295 239 : if (off < msize)
296 0 : continue;
297 239 : switch (ntohs (hdr->type))
298 : {
299 198 : case TALER_HELPER_EDDSA_MT_AVAIL:
300 198 : if (GNUNET_OK !=
301 198 : handle_mt_avail (esh,
302 : hdr))
303 : {
304 0 : GNUNET_break_op (0);
305 0 : do_disconnect (esh);
306 0 : return;
307 : }
308 198 : break;
309 3 : case TALER_HELPER_EDDSA_MT_PURGE:
310 3 : if (GNUNET_OK !=
311 3 : handle_mt_purge (esh,
312 : hdr))
313 : {
314 0 : GNUNET_break_op (0);
315 0 : do_disconnect (esh);
316 0 : return;
317 : }
318 3 : break;
319 38 : case TALER_HELPER_EDDSA_SYNCED:
320 38 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
321 : "Now synchronized with EdDSA helper\n");
322 38 : esh->synced = true;
323 38 : break;
324 0 : default:
325 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
326 : "Received unexpected message of type %d (len: %u)\n",
327 : (unsigned int) ntohs (hdr->type),
328 : (unsigned int) msize);
329 0 : GNUNET_break_op (0);
330 0 : do_disconnect (esh);
331 0 : return;
332 : }
333 239 : memmove (buf,
334 239 : &buf[msize],
335 : off - msize);
336 239 : off -= msize;
337 239 : goto more;
338 : }
339 : }
340 :
341 :
342 : enum TALER_ErrorCode
343 1969 : TALER_CRYPTO_helper_esign_sign_ (
344 : struct TALER_CRYPTO_ExchangeSignHelper *esh,
345 : const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
346 : struct TALER_ExchangePublicKeyP *exchange_pub,
347 : struct TALER_ExchangeSignatureP *exchange_sig)
348 : {
349 1969 : uint32_t purpose_size = ntohl (purpose->size);
350 :
351 1969 : if (GNUNET_OK !=
352 1969 : try_connect (esh))
353 : {
354 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
355 : "Failed to connect to helper\n");
356 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
357 : }
358 1969 : GNUNET_assert (purpose_size <
359 : UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest));
360 1969 : {
361 1969 : char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
362 1969 : - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose)];
363 1969 : struct TALER_CRYPTO_EddsaSignRequest *sr
364 : = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
365 :
366 1969 : sr->header.size = htons (sizeof (buf));
367 1969 : sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
368 1969 : sr->reserved = htonl (0);
369 1969 : GNUNET_memcpy (&sr->purpose,
370 : purpose,
371 : purpose_size);
372 1969 : if (GNUNET_OK !=
373 1969 : TALER_crypto_helper_send_all (esh->sock,
374 : buf,
375 : sizeof (buf)))
376 : {
377 0 : GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
378 : "send",
379 : esh->sa.sun_path);
380 0 : do_disconnect (esh);
381 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
382 : }
383 : }
384 :
385 : {
386 : char buf[UINT16_MAX];
387 1969 : size_t off = 0;
388 1969 : const struct GNUNET_MessageHeader *hdr
389 : = (const struct GNUNET_MessageHeader *) buf;
390 1969 : bool finished = false;
391 1969 : enum TALER_ErrorCode ec = TALER_EC_INVALID;
392 :
393 : while (1)
394 1969 : {
395 : ssize_t ret;
396 : uint16_t msize;
397 :
398 5907 : ret = recv (esh->sock,
399 3938 : &buf[off],
400 : sizeof (buf) - off,
401 1969 : (finished && (0 == off))
402 : ? MSG_DONTWAIT
403 : : 0);
404 3938 : if (ret < 0)
405 : {
406 1969 : if (EINTR == errno)
407 0 : continue;
408 1969 : if (EAGAIN == errno)
409 : {
410 1969 : GNUNET_assert (finished);
411 1969 : GNUNET_assert (0 == off);
412 1969 : break;
413 : }
414 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
415 : "recv");
416 0 : do_disconnect (esh);
417 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
418 : }
419 1969 : if (0 == ret)
420 : {
421 0 : GNUNET_break (0 == off);
422 0 : if (finished)
423 0 : return TALER_EC_NONE;
424 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
425 : }
426 1969 : off += ret;
427 3938 : more:
428 3938 : if (off < sizeof (struct GNUNET_MessageHeader))
429 1969 : continue;
430 1969 : msize = ntohs (hdr->size);
431 1969 : if (off < msize)
432 0 : continue;
433 1969 : switch (ntohs (hdr->type))
434 : {
435 1969 : case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
436 1969 : if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
437 : {
438 0 : GNUNET_break_op (0);
439 0 : do_disconnect (esh);
440 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
441 : }
442 1969 : if (finished)
443 : {
444 0 : GNUNET_break_op (0);
445 0 : do_disconnect (esh);
446 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
447 : }
448 : {
449 1969 : const struct TALER_CRYPTO_EddsaSignResponse *sr =
450 : (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
451 1969 : *exchange_sig = sr->exchange_sig;
452 1969 : *exchange_pub = sr->exchange_pub;
453 1969 : finished = true;
454 1969 : ec = TALER_EC_NONE;
455 1969 : break;
456 : }
457 0 : case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
458 0 : if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
459 : {
460 0 : GNUNET_break_op (0);
461 0 : do_disconnect (esh);
462 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
463 : }
464 : {
465 0 : const struct TALER_CRYPTO_EddsaSignFailure *sf =
466 : (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
467 :
468 0 : finished = true;
469 0 : ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec);
470 0 : break;
471 : }
472 0 : case TALER_HELPER_EDDSA_MT_AVAIL:
473 0 : if (GNUNET_OK !=
474 0 : handle_mt_avail (esh,
475 : hdr))
476 : {
477 0 : GNUNET_break_op (0);
478 0 : do_disconnect (esh);
479 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
480 : }
481 0 : break; /* while(1) loop ensures we recv() again */
482 0 : case TALER_HELPER_EDDSA_MT_PURGE:
483 0 : if (GNUNET_OK !=
484 0 : handle_mt_purge (esh,
485 : hdr))
486 : {
487 0 : GNUNET_break_op (0);
488 0 : do_disconnect (esh);
489 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
490 : }
491 0 : break; /* while(1) loop ensures we recv() again */
492 0 : case TALER_HELPER_EDDSA_SYNCED:
493 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
494 : "Synchronized add odd time with EdDSA helper!\n");
495 0 : esh->synced = true;
496 0 : break;
497 0 : default:
498 0 : GNUNET_break_op (0);
499 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
500 : "Received unexpected message of type %u\n",
501 : ntohs (hdr->type));
502 0 : do_disconnect (esh);
503 0 : return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
504 : }
505 1969 : memmove (buf,
506 1969 : &buf[msize],
507 : off - msize);
508 1969 : off -= msize;
509 1969 : goto more;
510 : } /* while(1) */
511 1969 : return ec;
512 : }
513 : }
514 :
515 :
516 : void
517 3 : TALER_CRYPTO_helper_esign_revoke (
518 : struct TALER_CRYPTO_ExchangeSignHelper *esh,
519 : const struct TALER_ExchangePublicKeyP *exchange_pub)
520 : {
521 3 : if (GNUNET_OK !=
522 3 : try_connect (esh))
523 0 : return; /* give up */
524 : {
525 3 : struct TALER_CRYPTO_EddsaRevokeRequest rr = {
526 3 : .header.size = htons (sizeof (rr)),
527 3 : .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
528 : .exchange_pub = *exchange_pub
529 : };
530 :
531 3 : if (GNUNET_OK !=
532 3 : TALER_crypto_helper_send_all (esh->sock,
533 : &rr,
534 : sizeof (rr)))
535 : {
536 0 : GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
537 : "send");
538 0 : do_disconnect (esh);
539 0 : return;
540 : }
541 : }
542 : }
543 :
544 :
545 : void
546 38 : TALER_CRYPTO_helper_esign_disconnect (
547 : struct TALER_CRYPTO_ExchangeSignHelper *esh)
548 : {
549 38 : if (-1 != esh->sock)
550 38 : do_disconnect (esh);
551 38 : GNUNET_free (esh);
552 38 : }
553 :
554 :
555 : /* end of crypto_helper_esign.c */
|