frosix-httpd_dkg-shares.c (21996B)
1 /* 2 This file is part of Frosix 3 Copyright (C) 2022, 2023 Joel Urech 4 5 Frosix is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Frosix 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 Frosix; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file backend/frosix-httpd_dkg-shares.c 18 * @brief functions to handle incoming requests on /dkg-shares 19 * @author Joel Urech 20 */ 21 #include "frosix-httpd_dkg.h" 22 #include "frosix-httpd.h" 23 #include "frosix_database_plugin.h" 24 #include "frosix_service.h" 25 #include "keygen.h" 26 #include <taler/taler_util.h> 27 #include <gnunet/gnunet_util_lib.h> 28 #include <gnunet/gnunet_db_lib.h> 29 30 struct DkgSharesContext 31 { 32 /** 33 * 34 */ 35 uint8_t provider_index; 36 37 /** 38 * 39 */ 40 uint8_t threshold; 41 42 /** 43 * 44 */ 45 uint8_t num_of_participants; 46 47 /** 48 * 49 */ 50 struct FROSIX_DkgContextStringP context_string; 51 52 /** 53 * 54 */ 55 struct FROSIX_ChallengeHashP auth_hash; 56 57 /** 58 * 59 */ 60 struct FROSIX_DkgRequestIdP request_id; 61 62 /** 63 * 64 */ 65 struct FROSIX_ProviderSaltP provider_salt; 66 67 /** 68 * 69 */ 70 struct FROSIX_SecretProviderSaltP secret_provider_salt; 71 72 /** 73 * 74 */ 75 struct FROST_DkgCommitment *dkg_commitments; 76 77 /** 78 * Our handler context 79 */ 80 struct TM_HandlerContext *hc; 81 82 /** 83 * Uploaded JSON data, NULL if upload is not yet complete. 84 */ 85 json_t *json; 86 87 /** 88 * Post parser context. 89 */ 90 void *post_ctx; 91 92 /** 93 * Connection handle for closing or resuming 94 */ 95 struct MHD_Connection *connection; 96 97 /** 98 * When should this request time out? 99 */ 100 struct GNUNET_TIME_Absolute timeout; 101 }; 102 103 /** 104 * Return the generated dkg shares 105 * 106 * @param connection 107 * @param dkg_shares 108 * @return 109 */ 110 static MHD_RESULT 111 return_dkg_shares ( 112 struct MHD_Connection *connection, 113 const struct FROST_DkgShare dkg_shares[], 114 const struct GNUNET_CRYPTO_EddsaPublicKey pub_keys[], 115 uint8_t provider_index, 116 uint8_t num_of_participants) 117 { 118 // Prepare secret shares 119 json_t *shares; 120 shares = json_array (); 121 GNUNET_assert (NULL != shares); 122 int counter = 0; 123 124 for (int i = 0; i < num_of_participants; i++) 125 { 126 if (provider_index - 1 != i) 127 { 128 uint8_t target = i + 1; 129 json_t *share; 130 share = json_object (); 131 132 /* get key material */ 133 struct GNUNET_CRYPTO_EcdhePrivateKey priv_enc_key; 134 GNUNET_CRYPTO_ecdhe_key_create (&priv_enc_key); 135 136 struct GNUNET_CRYPTO_EcdhePublicKey pub_enc_key; 137 GNUNET_CRYPTO_ecdhe_key_get_public (&priv_enc_key, 138 &pub_enc_key); 139 140 struct GNUNET_HashCode key_material; 141 if (GNUNET_OK != GNUNET_CRYPTO_ecdh_eddsa (&priv_enc_key, 142 &pub_keys[counter], 143 &key_material)) 144 { 145 GNUNET_break (0); 146 return MHD_NO; 147 } 148 149 /* derive key */ 150 struct GNUNET_CRYPTO_SymmetricSessionKey sym_key; 151 struct GNUNET_CRYPTO_SymmetricInitializationVector iv; 152 GNUNET_CRYPTO_hash_to_aes_key (&key_material, 153 &sym_key, 154 &iv); 155 156 /* encrypt share */ 157 struct FROSIX_EncryptedShareP encrypted_share; 158 ssize_t len = GNUNET_CRYPTO_symmetric_encrypt (&dkg_shares[i].share, 159 sizeof (dkg_shares[i].share), 160 &sym_key, 161 &iv, 162 &encrypted_share); 163 164 if (-1 == len || sizeof (encrypted_share) != len) 165 { 166 GNUNET_break (0); 167 return MHD_NO; 168 } 169 170 /* pack data in json */ 171 share = GNUNET_JSON_PACK ( 172 GNUNET_JSON_pack_uint64 ("target", 173 target), 174 GNUNET_JSON_pack_uint64 ("issuer", 175 provider_index), 176 GNUNET_JSON_pack_data_auto ("secret_share", 177 &encrypted_share), 178 GNUNET_JSON_pack_data_auto ("ephemeral_key", 179 &pub_enc_key) 180 ); 181 182 GNUNET_assert (0 == 183 json_array_append ( 184 shares, 185 share)); 186 counter++; 187 } 188 } 189 190 return TALER_MHD_REPLY_JSON_PACK ( 191 connection, 192 MHD_HTTP_OK, 193 GNUNET_JSON_pack_array_steal ("secret_shares", 194 shares)); 195 } 196 197 static MHD_RESULT 198 store_commitments (struct DkgSharesContext *dc) 199 { 200 /* copy commitments to a single byte array */ 201 struct FROSIX_DkgCommitmentsRaw commits; 202 commits.length = (sizeof (dc->provider_index) 203 + (sizeof (struct FROST_Point) 204 * dc->threshold)) 205 * (dc->num_of_participants - 1); 206 commits.ptr_commits = calloc (1, commits.length); 207 208 unsigned int offset = 0; 209 210 for (int i = 0; i < dc->num_of_participants - 1; i++) 211 { 212 memcpy ((char *) commits.ptr_commits + offset, 213 &dc->dkg_commitments[i].identifier, 214 1); 215 offset += 1; 216 217 for (int j = 0; j < dc->threshold; j++) 218 { 219 memcpy ((char *) commits.ptr_commits + offset, 220 &dc->dkg_commitments[i].share_comm[j].sc.xcoord, 221 sizeof (struct FROST_Point)); 222 offset += sizeof (struct FROST_Point); 223 } 224 } 225 226 /* store commitments */ 227 enum GNUNET_DB_QueryStatus qs; 228 qs = db->store_dkg_commitment (db->cls, 229 &dc->request_id, 230 &commits); 231 switch (qs) 232 { 233 case GNUNET_DB_STATUS_HARD_ERROR: 234 case GNUNET_DB_STATUS_SOFT_ERROR: 235 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 236 GNUNET_break (0); 237 free (commits.ptr_commits); 238 return TALER_MHD_reply_with_error (dc->connection, 239 MHD_HTTP_INTERNAL_SERVER_ERROR, 240 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 241 "store dkg-commitment"); 242 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 243 break; 244 } 245 246 /* free initialized memory */ 247 free (commits.ptr_commits); 248 return MHD_HTTP_OK; 249 } 250 251 252 MHD_RESULT 253 FH_handler_dkg_shares_post ( 254 struct MHD_Connection *connection, 255 struct TM_HandlerContext *hc, 256 const struct FROSIX_DkgRequestIdP *dkg_id, 257 const struct FROSIX_ProviderSaltP *provider_salt, 258 const struct FROSIX_SecretProviderSaltP *secret_provider_salt, 259 const char *dkg_commitment_data, 260 size_t *dkg_commitment_data_size) 261 { 262 enum GNUNET_GenericReturnValue res; 263 struct DkgSharesContext *dc = hc->ctx; 264 json_t *json_ppk = NULL; 265 json_t *commitments = NULL; 266 267 if (NULL == dc) 268 { 269 dc = GNUNET_new (struct DkgSharesContext); 270 dc->connection = connection; 271 dc->request_id = *dkg_id; 272 dc->provider_salt = *provider_salt; 273 dc->secret_provider_salt = *secret_provider_salt; 274 hc->ctx = dc; 275 } 276 277 /* parse request body */ 278 if (NULL == dc->json) 279 { 280 res = TALER_MHD_parse_post_json (dc->connection, 281 &dc->post_ctx, 282 dkg_commitment_data, 283 dkg_commitment_data_size, 284 &dc->json); 285 if (GNUNET_SYSERR == res) 286 { 287 GNUNET_break (0); 288 return MHD_NO; 289 } 290 if ((GNUNET_NO == res || 291 (NULL == dc->json))) 292 { 293 return MHD_YES; 294 } 295 } 296 297 struct GNUNET_JSON_Specification spec[] = { 298 GNUNET_JSON_spec_uint8 ("provider_index", 299 &dc->provider_index), 300 GNUNET_JSON_spec_uint8 ("threshold", 301 &dc->threshold), 302 GNUNET_JSON_spec_fixed_auto ("context_string", 303 &dc->context_string), 304 GNUNET_JSON_spec_fixed_auto ("auth_hash", 305 &dc->auth_hash), 306 GNUNET_JSON_spec_json ("providers_public_keys", 307 &json_ppk), 308 GNUNET_JSON_spec_json ("dkg_commitments", 309 &commitments), 310 GNUNET_JSON_spec_end () 311 }; 312 313 res = TALER_MHD_parse_json_data (connection, 314 dc->json, 315 spec); 316 317 if (GNUNET_SYSERR == res) 318 { 319 GNUNET_JSON_parse_free (spec); 320 GNUNET_break (0); 321 return MHD_NO; 322 } 323 if (GNUNET_NO == res) 324 { 325 GNUNET_JSON_parse_free (spec); 326 GNUNET_break_op (0); 327 return TALER_MHD_reply_with_error (connection, 328 MHD_HTTP_BAD_REQUEST, 329 TALER_EC_GENERIC_PARAMETER_MALFORMED, 330 "Unable to parse request body"); 331 } 332 333 /* check number of submitted provider public keys*/ 334 if (254 < json_array_size (json_ppk)) 335 { 336 GNUNET_JSON_parse_free (spec); 337 GNUNET_break_op (0); 338 return TALER_MHD_reply_with_error (connection, 339 MHD_HTTP_BAD_REQUEST, 340 TALER_EC_GENERIC_PARAMETER_MALFORMED, 341 "Too many provider public keys"); 342 } 343 344 /* set number of participants */ 345 dc->num_of_participants = json_array_size (json_ppk); 346 347 /* validate provider index, threshold and num_of_participants */ 348 if (GNUNET_OK != FROST_validate_dkg_params (dc->provider_index, 349 dc->threshold, 350 dc->num_of_participants)) 351 { 352 GNUNET_JSON_parse_free (spec); 353 GNUNET_break_op (0); 354 return TALER_MHD_reply_with_error (connection, 355 MHD_HTTP_BAD_REQUEST, 356 TALER_EC_GENERIC_PARAMETER_MALFORMED, 357 "Protocol parameters out of bound"); 358 } 359 360 /* Check if called id is matching the send data */ 361 if (GNUNET_OK != FROSIX_dkg_validate_request_id_ (&dc->request_id, 362 &dc->context_string, 363 &dc->auth_hash, 364 &dc->provider_salt, 365 dc->provider_index, 366 dc->num_of_participants, 367 dc->threshold)) 368 { 369 GNUNET_JSON_parse_free (spec); 370 GNUNET_break_op (0); 371 return TALER_MHD_reply_with_error (connection, 372 MHD_HTTP_BAD_REQUEST, 373 TALER_EC_GENERIC_PARAMETER_MALFORMED, 374 "Unable to validate request ID"); 375 } 376 377 /* parse provider public keys */ 378 struct GNUNET_CRYPTO_EddsaPublicKey 379 providers_public_keys[dc->num_of_participants]; 380 for (unsigned int i = 0; i < dc->num_of_participants; i++) 381 { 382 struct GNUNET_JSON_Specification ppk_spec[] = { 383 GNUNET_JSON_spec_fixed_auto (NULL, 384 &providers_public_keys[i]), 385 GNUNET_JSON_spec_end () 386 }; 387 388 res = TALER_MHD_parse_json_array (connection, 389 json_ppk, 390 ppk_spec, 391 i, 392 -1); 393 394 if (GNUNET_SYSERR == res) 395 { 396 GNUNET_JSON_parse_free (spec); 397 GNUNET_JSON_parse_free (ppk_spec); 398 GNUNET_break (0); 399 return MHD_NO; 400 } 401 if (GNUNET_NO == res) 402 { 403 GNUNET_JSON_parse_free (spec); 404 GNUNET_JSON_parse_free (ppk_spec); 405 GNUNET_break_op (0); 406 return TALER_MHD_reply_with_error (connection, 407 MHD_HTTP_BAD_REQUEST, 408 TALER_EC_GENERIC_PARAMETER_MALFORMED, 409 "Unable to parse request body"); 410 } 411 412 GNUNET_JSON_parse_free (ppk_spec); 413 } 414 415 /* check if there are num_of_participants - 1 commitments */ 416 if (dc->num_of_participants - 1 != json_array_size (commitments)) 417 { 418 GNUNET_break_op (0); 419 GNUNET_JSON_parse_free (spec); 420 421 return TALER_MHD_reply_with_error (connection, 422 MHD_HTTP_BAD_REQUEST, 423 TALER_EC_GENERIC_PARAMETER_MALFORMED, 424 "Number of commitments not matching"); 425 } 426 427 /* parse all commitments */ 428 struct FROST_DkgCommitment dkg_commitments[dc->num_of_participants - 1]; 429 dc->dkg_commitments = dkg_commitments; 430 json_t *coms = json_array (); 431 432 // Place to store the delivered public keys to later encrypt our secret shares 433 // against 434 struct GNUNET_CRYPTO_EddsaPublicKey pub_sig_keys[dc->num_of_participants - 1]; 435 436 for (unsigned int i = 0; i < dc->num_of_participants - 1; i++) 437 { 438 if (GNUNET_OK != FROST_initialize_dkg_commitment (&dc->dkg_commitments[i], 439 dc->provider_index, 440 dc->threshold)) 441 { 442 GNUNET_break (0); 443 return TALER_MHD_reply_with_error (connection, 444 MHD_HTTP_INTERNAL_SERVER_ERROR, 445 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 446 "Initialize DKG commitment"); 447 } 448 449 json_t *json_temp = NULL; 450 struct GNUNET_JSON_Specification com_spec[] = { 451 GNUNET_JSON_spec_uint8 ("provider_index", 452 &dc->dkg_commitments[i].identifier), 453 GNUNET_JSON_spec_json ("dkg_commitment", 454 &json_temp), 455 GNUNET_JSON_spec_fixed_auto ("zkp_r", 456 &dc->dkg_commitments[i].zkp.r), 457 GNUNET_JSON_spec_fixed_auto ("zkp_z", 458 &dc->dkg_commitments[i].zkp.z), 459 GNUNET_JSON_spec_fixed_auto ("public_key", 460 &pub_sig_keys[i]), 461 GNUNET_JSON_spec_end () 462 }; 463 464 res = TALER_MHD_parse_json_array (connection, 465 commitments, 466 com_spec, 467 i, 468 -1); 469 470 if (GNUNET_SYSERR == res) 471 { 472 GNUNET_JSON_parse_free (spec); 473 GNUNET_JSON_parse_free (com_spec); 474 FROST_free_dkg_commitment (dkg_commitments, 475 dc->num_of_participants - 1); 476 GNUNET_break (0); 477 return MHD_NO; 478 } 479 if (GNUNET_NO == res) 480 { 481 GNUNET_JSON_parse_free (spec); 482 GNUNET_JSON_parse_free (com_spec); 483 FROST_free_dkg_commitment (dkg_commitments, 484 dc->num_of_participants - 1); 485 GNUNET_break_op (0); 486 return TALER_MHD_reply_with_error (connection, 487 MHD_HTTP_BAD_REQUEST, 488 TALER_EC_GENERIC_PARAMETER_MALFORMED, 489 "Unable to parse request body"); 490 } 491 492 /* check if there are `threshold` commitment values */ 493 if (dc->threshold != json_array_size (json_temp)) 494 { 495 GNUNET_JSON_parse_free (spec); 496 GNUNET_JSON_parse_free (com_spec); 497 FROST_free_dkg_commitment (dkg_commitments, 498 dc->num_of_participants - 1); 499 GNUNET_break_op (0); 500 return TALER_MHD_reply_with_error (connection, 501 MHD_HTTP_BAD_REQUEST, 502 TALER_EC_GENERIC_PARAMETER_MALFORMED, 503 "Number of commitment values not equal to threshold value"); 504 } 505 dc->dkg_commitments[i].shares_commitments_length = dc->threshold; 506 507 /* add temp value to json array */ 508 json_array_append_new (coms, 509 json_temp); 510 511 /* parse the commitment values */ 512 for (unsigned int j = 0; j < dc->threshold; j++) 513 { 514 struct GNUNET_JSON_Specification coms_spec[] = { 515 GNUNET_JSON_spec_fixed_auto (NULL, 516 &dc->dkg_commitments[i].share_comm[j]), 517 GNUNET_JSON_spec_end () 518 }; 519 520 res = TALER_MHD_parse_json_array (connection, 521 json_array_get (coms, 522 i), 523 coms_spec, 524 j, 525 -1); 526 527 if (GNUNET_SYSERR == res) 528 { 529 GNUNET_JSON_parse_free (spec); 530 GNUNET_JSON_parse_free (com_spec); 531 GNUNET_JSON_parse_free (coms_spec); 532 FROST_free_dkg_commitment (dkg_commitments, 533 dc->num_of_participants - 1); 534 GNUNET_break (0); 535 return MHD_NO; 536 } 537 if (GNUNET_NO == res) 538 { 539 GNUNET_JSON_parse_free (spec); 540 GNUNET_JSON_parse_free (com_spec); 541 GNUNET_JSON_parse_free (coms_spec); 542 FROST_free_dkg_commitment (dkg_commitments, 543 dc->num_of_participants - 1); 544 GNUNET_break_op (0); 545 return TALER_MHD_reply_with_error (connection, 546 MHD_HTTP_BAD_REQUEST, 547 TALER_EC_GENERIC_PARAMETER_MALFORMED, 548 "Unable to parse request body"); 549 } 550 551 /* free json spec */ 552 GNUNET_JSON_parse_free (coms_spec); 553 } 554 555 /* free json spec */ 556 GNUNET_JSON_parse_free (com_spec); 557 } 558 559 /* free json spec */ 560 GNUNET_JSON_parse_free (spec); 561 562 /* check if commitments are valid */ 563 for (unsigned int i = 0; i < dc->num_of_participants - 1; i++) 564 { 565 /* hash received pub key */ 566 struct FROST_HashState h_s; 567 struct FROST_HashCode pub_key_hash; 568 FROST_hash_init (&h_s); 569 FROST_hash_fixed_update (&h_s, 570 &pub_sig_keys[i].q_y, 571 sizeof (pub_sig_keys[i].q_y)); 572 FROST_hash_final (&h_s, 573 &pub_key_hash); 574 575 if (GNUNET_OK != FROST_keygen_validate_commitment (&dc->dkg_commitments[i], 576 &pub_key_hash, 577 dc->num_of_participants)) 578 { 579 FROST_free_dkg_commitment (dkg_commitments, 580 dc->num_of_participants - 1); 581 GNUNET_break_op (0); 582 return TALER_MHD_reply_with_error (connection, 583 MHD_HTTP_BAD_REQUEST, 584 TALER_EC_GENERIC_PARAMETER_MALFORMED, 585 "Could not validate commitment"); 586 } 587 } 588 589 /* check if commitments are already stored */ 590 enum GNUNET_DB_QueryStatus qs; 591 qs = db->lookup_dkg_commitment (db->cls, 592 &dc->request_id); 593 594 switch (qs) 595 { 596 case GNUNET_DB_STATUS_HARD_ERROR: 597 case GNUNET_DB_STATUS_SOFT_ERROR: 598 FROST_free_dkg_commitment (dkg_commitments, 599 dc->num_of_participants - 1); 600 GNUNET_break (0); 601 return TALER_MHD_reply_with_error (connection, 602 MHD_HTTP_INTERNAL_SERVER_ERROR, 603 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 604 "lookup dkg-commitment"); 605 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 606 /* store commitments */ 607 MHD_RESULT mhd_res; 608 mhd_res = store_commitments (dc); 609 if (MHD_HTTP_OK != mhd_res) 610 { 611 FROST_free_dkg_commitment (dkg_commitments, 612 dc->num_of_participants - 1); 613 return mhd_res; 614 } 615 break; 616 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 617 // commitments are already stored - go ahead 618 break; 619 } 620 621 FROST_free_dkg_commitment (dkg_commitments, 622 dc->num_of_participants - 1); 623 624 /* derive the final context string */ 625 struct FROST_DkgContextString cs_h; 626 if (GNUNET_YES != FROSIX_dkg_derive_context_string_ ( 627 &cs_h, 628 dc->provider_index, 629 dc->threshold, 630 &dc->context_string, 631 &dc->auth_hash, 632 providers_public_keys, 633 dc->num_of_participants, 634 &dc->secret_provider_salt)) 635 { 636 GNUNET_break (0); 637 return TALER_MHD_reply_with_error (connection, 638 MHD_HTTP_INTERNAL_SERVER_ERROR, 639 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 640 "Derive context string"); 641 } 642 643 /* create shares */ 644 struct FROST_DkgShare dkg_shares[dc->num_of_participants]; 645 646 if (GNUNET_OK != FROSIX_dkg_shares_generate_ (dkg_shares, 647 &cs_h, 648 NULL, 649 dc->provider_index, 650 dc->threshold, 651 dc->num_of_participants)) 652 { 653 GNUNET_break (0); 654 return TALER_MHD_reply_with_error (connection, 655 MHD_HTTP_INTERNAL_SERVER_ERROR, 656 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 657 "Generate DKG shares"); 658 } 659 660 res = return_dkg_shares (connection, 661 dkg_shares, 662 pub_sig_keys, 663 dc->provider_index, 664 dc->num_of_participants); 665 666 return res; 667 668 }