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