crypto_helper_esign.c (15825B)
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 do_disconnect (struct TALER_CRYPTO_ExchangeSignHelper *esh) 68 { 69 GNUNET_break (0 == close (esh->sock)); 70 esh->sock = -1; 71 esh->synced = false; 72 } 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 try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh) 84 { 85 if (-1 != esh->sock) 86 return GNUNET_OK; 87 esh->sock = socket (AF_UNIX, 88 SOCK_STREAM, 89 0); 90 if (-1 == esh->sock) 91 { 92 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 93 "socket"); 94 return GNUNET_SYSERR; 95 } 96 if (0 != 97 connect (esh->sock, 98 (const struct sockaddr *) &esh->sa, 99 sizeof (esh->sa))) 100 { 101 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 102 "connect", 103 esh->sa.sun_path); 104 do_disconnect (esh); 105 return GNUNET_SYSERR; 106 } 107 return GNUNET_OK; 108 } 109 110 111 struct TALER_CRYPTO_ExchangeSignHelper * 112 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 GNUNET_asprintf (&secname, 123 "%s-secmod-eddsa", 124 section); 125 126 if (GNUNET_OK != 127 GNUNET_CONFIGURATION_get_value_filename (cfg, 128 secname, 129 "UNIXPATH", 130 &unixpath)) 131 { 132 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 133 secname, 134 "UNIXPATH"); 135 GNUNET_free (secname); 136 return NULL; 137 } 138 /* we use >= here because we want the sun_path to always 139 be 0-terminated */ 140 if (strlen (unixpath) >= sizeof (esh->sa.sun_path)) 141 { 142 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 143 secname, 144 "UNIXPATH", 145 "path too long"); 146 GNUNET_free (unixpath); 147 GNUNET_free (secname); 148 return NULL; 149 } 150 GNUNET_free (secname); 151 esh = GNUNET_new (struct TALER_CRYPTO_ExchangeSignHelper); 152 esh->ekc = ekc; 153 esh->ekc_cls = ekc_cls; 154 esh->sa.sun_family = AF_UNIX; 155 strncpy (esh->sa.sun_path, 156 unixpath, 157 sizeof (esh->sa.sun_path) - 1); 158 GNUNET_free (unixpath); 159 esh->sock = -1; 160 if (GNUNET_OK != 161 try_connect (esh)) 162 { 163 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 164 "Could not connect to %s. Will keep trying\n", 165 "taler-exchange-helper-secmod-eddsa"); 166 return esh; 167 } 168 TALER_CRYPTO_helper_esign_poll (esh); 169 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 handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper *esh, 182 const struct GNUNET_MessageHeader *hdr) 183 { 184 const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan 185 = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr; 186 187 if (sizeof (*kan) != ntohs (hdr->size)) 188 { 189 GNUNET_break_op (0); 190 return GNUNET_SYSERR; 191 } 192 if (GNUNET_OK != 193 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 GNUNET_break_op (0); 201 return GNUNET_SYSERR; 202 } 203 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 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 handle_mt_purge (struct TALER_CRYPTO_ExchangeSignHelper *esh, 222 const struct GNUNET_MessageHeader *hdr) 223 { 224 const struct TALER_CRYPTO_EddsaKeyPurgeNotification *pn 225 = (const struct TALER_CRYPTO_EddsaKeyPurgeNotification *) hdr; 226 227 if (sizeof (*pn) != ntohs (hdr->size)) 228 { 229 GNUNET_break_op (0); 230 return GNUNET_SYSERR; 231 } 232 esh->ekc (esh->ekc_cls, 233 GNUNET_TIME_UNIT_ZERO_TS, 234 GNUNET_TIME_UNIT_ZERO, 235 &pn->exchange_pub, 236 NULL, 237 NULL); 238 return GNUNET_OK; 239 } 240 241 242 void 243 TALER_CRYPTO_helper_esign_poll (struct TALER_CRYPTO_ExchangeSignHelper *esh) 244 { 245 char buf[UINT16_MAX]; 246 size_t off = 0; 247 unsigned int retry_limit = 3; 248 const struct GNUNET_MessageHeader *hdr 249 = (const struct GNUNET_MessageHeader *) buf; 250 251 if (GNUNET_OK != 252 try_connect (esh)) 253 return; /* give up */ 254 while (1) 255 { 256 uint16_t msize; 257 ssize_t ret; 258 259 ret = recv (esh->sock, 260 buf + off, 261 sizeof (buf) - off, 262 (esh->synced && (0 == off)) 263 ? MSG_DONTWAIT 264 : 0); 265 if (ret < 0) 266 { 267 if (EINTR == errno) 268 continue; 269 if (EAGAIN == errno) 270 { 271 GNUNET_assert (esh->synced); 272 GNUNET_assert (0 == off); 273 break; 274 } 275 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 276 "recv"); 277 do_disconnect (esh); 278 if (0 == retry_limit) 279 return; /* give up */ 280 if (GNUNET_OK != 281 try_connect (esh)) 282 return; /* give up */ 283 retry_limit--; 284 continue; 285 } 286 if (0 == ret) 287 { 288 GNUNET_break (0 == off); 289 return; 290 } 291 off += ret; 292 more: 293 if (off < sizeof (struct GNUNET_MessageHeader)) 294 continue; 295 msize = ntohs (hdr->size); 296 if (msize < sizeof (struct GNUNET_MessageHeader)) 297 { 298 GNUNET_break_op (0); 299 do_disconnect (esh); 300 return; 301 } 302 if (off < msize) 303 continue; 304 switch (ntohs (hdr->type)) 305 { 306 case TALER_HELPER_EDDSA_MT_AVAIL: 307 if (GNUNET_OK != 308 handle_mt_avail (esh, 309 hdr)) 310 { 311 GNUNET_break_op (0); 312 do_disconnect (esh); 313 return; 314 } 315 break; 316 case TALER_HELPER_EDDSA_MT_PURGE: 317 if (GNUNET_OK != 318 handle_mt_purge (esh, 319 hdr)) 320 { 321 GNUNET_break_op (0); 322 do_disconnect (esh); 323 return; 324 } 325 break; 326 case TALER_HELPER_EDDSA_SYNCED: 327 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 328 "Now synchronized with EdDSA helper\n"); 329 esh->synced = true; 330 break; 331 default: 332 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 GNUNET_break_op (0); 337 do_disconnect (esh); 338 return; 339 } 340 memmove (buf, 341 &buf[msize], 342 off - msize); 343 off -= msize; 344 goto more; 345 } 346 } 347 348 349 enum TALER_ErrorCode 350 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 uint32_t purpose_size = ntohl (purpose->size); 357 358 /* clear the outputs, so that we never return 359 uninitialized values on failures */ 360 memset (exchange_pub, 361 0, 362 sizeof (*exchange_pub)); 363 memset (exchange_sig, 364 0, 365 sizeof (*exchange_sig)); 366 if (GNUNET_OK != 367 try_connect (esh)) 368 { 369 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 370 "Failed to connect to helper\n"); 371 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE; 372 } 373 GNUNET_assert (purpose_size >= 374 sizeof (struct TALER_CRYPTO_EddsaSignRequest)); 375 GNUNET_assert (purpose_size < 376 UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest)); 377 { 378 char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size 379 - sizeof (struct GNUNET_CRYPTO_SignaturePurpose)]; 380 struct TALER_CRYPTO_EddsaSignRequest *sr 381 = (struct TALER_CRYPTO_EddsaSignRequest *) buf; 382 383 sr->header.size = htons (sizeof (buf)); 384 sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN); 385 sr->reserved = htonl (0); 386 GNUNET_memcpy (&sr->purpose, 387 purpose, 388 purpose_size); 389 if (GNUNET_OK != 390 TALER_crypto_helper_send_all (esh->sock, 391 buf, 392 sizeof (buf))) 393 { 394 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 395 "send", 396 esh->sa.sun_path); 397 do_disconnect (esh); 398 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE; 399 } 400 } 401 402 { 403 char buf[UINT16_MAX]; 404 size_t off = 0; 405 const struct GNUNET_MessageHeader *hdr 406 = (const struct GNUNET_MessageHeader *) buf; 407 bool finished = false; 408 enum TALER_ErrorCode ec = TALER_EC_INVALID; 409 410 while (1) 411 { 412 ssize_t ret; 413 uint16_t msize; 414 415 ret = recv (esh->sock, 416 &buf[off], 417 sizeof (buf) - off, 418 (finished && (0 == off)) 419 ? MSG_DONTWAIT 420 : 0); 421 if (ret < 0) 422 { 423 if (EINTR == errno) 424 continue; 425 if (EAGAIN == errno) 426 { 427 GNUNET_assert (finished); 428 GNUNET_assert (0 == off); 429 break; 430 } 431 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 432 "recv"); 433 do_disconnect (esh); 434 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE; 435 } 436 if (0 == ret) 437 { 438 /* helper closed the connection */ 439 GNUNET_break (0 == off); 440 do_disconnect (esh); 441 if (! finished) 442 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 443 if (TALER_EC_NONE == ec) 444 break; 445 return ec; 446 } 447 off += ret; 448 more: 449 if (off < sizeof (struct GNUNET_MessageHeader)) 450 continue; 451 msize = ntohs (hdr->size); 452 if (msize < sizeof (struct GNUNET_MessageHeader)) 453 { 454 GNUNET_break_op (0); 455 do_disconnect (esh); 456 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 457 } 458 if (off < msize) 459 continue; 460 switch (ntohs (hdr->type)) 461 { 462 case TALER_HELPER_EDDSA_MT_RES_SIGNATURE: 463 if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse)) 464 { 465 GNUNET_break_op (0); 466 do_disconnect (esh); 467 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 468 } 469 if (finished) 470 { 471 GNUNET_break_op (0); 472 do_disconnect (esh); 473 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 474 } 475 { 476 const struct TALER_CRYPTO_EddsaSignResponse *sr = 477 (const struct TALER_CRYPTO_EddsaSignResponse *) buf; 478 *exchange_sig = sr->exchange_sig; 479 *exchange_pub = sr->exchange_pub; 480 finished = true; 481 ec = TALER_EC_NONE; 482 break; 483 } 484 case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE: 485 if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure)) 486 { 487 GNUNET_break_op (0); 488 do_disconnect (esh); 489 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 490 } 491 { 492 const struct TALER_CRYPTO_EddsaSignFailure *sf = 493 (const struct TALER_CRYPTO_EddsaSignFailure *) buf; 494 495 finished = true; 496 ec = (enum TALER_ErrorCode) (int) ntohl (sf->ec); 497 break; 498 } 499 case TALER_HELPER_EDDSA_MT_AVAIL: 500 if (GNUNET_OK != 501 handle_mt_avail (esh, 502 hdr)) 503 { 504 GNUNET_break_op (0); 505 do_disconnect (esh); 506 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 507 } 508 break; /* while(1) loop ensures we recv() again */ 509 case TALER_HELPER_EDDSA_MT_PURGE: 510 if (GNUNET_OK != 511 handle_mt_purge (esh, 512 hdr)) 513 { 514 GNUNET_break_op (0); 515 do_disconnect (esh); 516 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 517 } 518 break; /* while(1) loop ensures we recv() again */ 519 case TALER_HELPER_EDDSA_SYNCED: 520 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 521 "Synchronized add odd time with EdDSA helper!\n"); 522 esh->synced = true; 523 break; 524 default: 525 GNUNET_break_op (0); 526 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 527 "Received unexpected message of type %u\n", 528 ntohs (hdr->type)); 529 do_disconnect (esh); 530 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; 531 } 532 memmove (buf, 533 &buf[msize], 534 off - msize); 535 off -= msize; 536 goto more; 537 } /* while(1) */ 538 return ec; 539 } 540 } 541 542 543 void 544 TALER_CRYPTO_helper_esign_revoke ( 545 struct TALER_CRYPTO_ExchangeSignHelper *esh, 546 const struct TALER_ExchangePublicKeyP *exchange_pub) 547 { 548 if (GNUNET_OK != 549 try_connect (esh)) 550 return; /* give up */ 551 { 552 struct TALER_CRYPTO_EddsaRevokeRequest rr = { 553 .header.size = htons (sizeof (rr)), 554 .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE), 555 .exchange_pub = *exchange_pub 556 }; 557 558 if (GNUNET_OK != 559 TALER_crypto_helper_send_all (esh->sock, 560 &rr, 561 sizeof (rr))) 562 { 563 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 564 "send"); 565 do_disconnect (esh); 566 return; 567 } 568 } 569 } 570 571 572 void 573 TALER_CRYPTO_helper_esign_disconnect ( 574 struct TALER_CRYPTO_ExchangeSignHelper *esh) 575 { 576 if (-1 != esh->sock) 577 do_disconnect (esh); 578 GNUNET_free (esh); 579 } 580 581 582 /* end of crypto_helper_esign.c */