frosix-httpd_sig-share.c (13663B)
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_sign-share.c 18 * @brief functions to handle incoming requests on /sign-share 19 * @author Joel Urech 20 */ 21 #include "frosix-httpd_sig.h" 22 #include "frosix-httpd.h" 23 #include "frosix_database_plugin.h" 24 #include "frost_high.h" 25 #include "frost_verify.h" 26 #include <taler/taler_util.h> 27 #include <gnunet/gnunet_util_lib.h> 28 #include <gnunet/gnunet_db_lib.h> 29 #include "frosix_authorization_plugin.h" 30 31 /** 32 * What is the maximum frequency at which we allow 33 * clients to attempt to answer security questions? 34 */ 35 #define MAX_QUESTION_FREQ GNUNET_TIME_relative_multiply ( \ 36 GNUNET_TIME_UNIT_SECONDS, 30) 37 38 /** 39 * How many retries do we allow per code? 40 */ 41 #define INITIAL_RETRY_COUNTER 3 42 43 struct SignShareContext 44 { 45 /** 46 * 47 */ 48 uint32_t identifier; 49 50 /** 51 * 52 */ 53 uint8_t threshold; 54 55 /** 56 * 57 */ 58 struct FROST_Commitment *commitments; 59 60 /** 61 * 62 */ 63 struct FROSIX_EncryptionKey encryption_key; 64 65 66 /** 67 * 68 */ 69 struct FROST_HashCode enc_key_hash; 70 71 /** 72 * 73 */ 74 struct FROST_MessageHash message_hash; 75 76 /** 77 * 78 */ 79 struct FROSIX_SigRequestIdP id; 80 81 /** 82 * Our handler context 83 */ 84 struct TM_HandlerContext *hc; 85 86 /** 87 * Uploaded JSON data, NULL if upload is not yet complete. 88 */ 89 json_t *json; 90 91 /** 92 * Post parser context. 93 */ 94 void *post_ctx; 95 96 /** 97 * Connection handle for closing or resuming 98 */ 99 struct MHD_Connection *connection; 100 }; 101 102 103 /** 104 * FIXME 105 */ 106 static struct FROST_Commitment * 107 get_my_commitment (uint32_t identifier, 108 struct FROST_Commitment commitments[], 109 uint8_t len) 110 { 111 for (unsigned int i = 0; i < len; i++) 112 { 113 if (identifier == commitments[i].identifier) 114 return &commitments[i]; 115 } 116 return NULL; 117 } 118 119 120 static MHD_RESULT 121 return_sig_share (struct SignShareContext *gc) 122 { 123 /* get key data from DB */ 124 struct FROSIX_EncryptionNonceP nonce; 125 struct FROSIX_KeyDataEncrypted enc_key_data; 126 127 enum GNUNET_DB_QueryStatus qs_key; 128 qs_key = db->get_key_data (db->cls, 129 &gc->enc_key_hash, 130 &gc->identifier, 131 &nonce, 132 &enc_key_data); 133 134 switch (qs_key) 135 { 136 case GNUNET_DB_STATUS_HARD_ERROR: 137 case GNUNET_DB_STATUS_SOFT_ERROR: 138 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 139 GNUNET_free (gc->commitments); 140 GNUNET_break (0); 141 /* FROSIX_EC_KEY_NOT_FOUND */ 142 return TALER_MHD_reply_with_error (gc->connection, 143 MHD_HTTP_INTERNAL_SERVER_ERROR, 144 TALER_EC_GENERIC_DB_FETCH_FAILED, 145 "key_data_select"); 146 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 147 break; 148 } 149 150 /* validate identifier from db */ 151 if (gc->identifier <= 0 || gc->identifier >= 255 ) 152 { 153 GNUNET_free (gc->commitments); 154 GNUNET_break_op (0); 155 /* FROSIX_EC_KEY_DATA_CORRUPT */ 156 return TALER_MHD_reply_with_error (gc->connection, 157 MHD_HTTP_BAD_REQUEST, 158 TALER_EC_GENERIC_PARAMETER_MALFORMED, 159 "Got invalid data from DB"); 160 } 161 162 /* get commitment */ 163 struct FROST_Commitment *my_comm = get_my_commitment (gc->identifier, 164 gc->commitments, 165 gc->threshold); 166 167 if (NULL == my_comm) 168 { 169 GNUNET_free (gc->commitments); 170 GNUNET_break_op (0); 171 /* FROSIX_COMMITMENT NOT_FOUND */ 172 return TALER_MHD_reply_with_error (gc->connection, 173 MHD_HTTP_BAD_REQUEST, 174 TALER_EC_GENERIC_PARAMETER_MALFORMED, 175 "Commitment not found"); 176 } 177 178 /* compute seed id in db */ 179 struct GNUNET_HashCode db_seed_id; 180 FROSIX_compute_db_commitment_id (&db_seed_id, 181 &gc->enc_key_hash, 182 my_comm); 183 184 /* get commitment seed from DB */ 185 struct FROST_CommitmentSeed seed; 186 187 enum GNUNET_DB_QueryStatus qs_seed; 188 qs_seed = db->get_and_delete_commitment_seed (db->cls, 189 &db_seed_id, 190 &seed); 191 192 switch (qs_seed) 193 { 194 case GNUNET_DB_STATUS_HARD_ERROR: 195 case GNUNET_DB_STATUS_SOFT_ERROR: 196 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 197 GNUNET_free (gc->commitments); 198 GNUNET_break (0); 199 /* FROSIX_COMMITMENT_NOT_FOUND */ 200 return TALER_MHD_reply_with_error (gc->connection, 201 MHD_HTTP_INTERNAL_SERVER_ERROR, 202 TALER_EC_GENERIC_DB_FETCH_FAILED, 203 "get_commitment_seed"); 204 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 205 break; 206 } 207 208 /* compute nonce and commitment */ 209 struct FROST_Nonce com_nonce; 210 struct FROST_Commitment commitment; 211 FROST_generate_nonce_and_commitment (&com_nonce, 212 &commitment, 213 &gc->message_hash, 214 &seed); 215 216 /* FIXME check if computed commitment equals send commitment */ 217 218 /* decrypt key data from db */ 219 struct FROSIX_KeyDataRaw decrypted; 220 if (GNUNET_OK != FROSIX_secretbox_keydata_open (&decrypted, 221 &enc_key_data, 222 &nonce, 223 &gc->encryption_key)) 224 { 225 GNUNET_free (gc->commitments); 226 GNUNET_break_op (0); 227 /* FROSIX_EC_DECRYPTION_FAILED */ 228 return TALER_MHD_reply_with_error (gc->connection, 229 MHD_HTTP_BAD_REQUEST, 230 TALER_EC_GENERIC_PARAMETER_MALFORMED, 231 "Unable to decrypt data from DB"); 232 } 233 234 /* create key pair */ 235 struct FROST_KeyPair key_pair; 236 key_pair.identifier = gc->identifier; 237 238 FROSIX_raw_key_to_struct (&key_pair, 239 &decrypted); 240 241 /* compute signature share */ 242 struct FROST_SignatureShare sig_share; 243 FROST_sign_message_hash (&sig_share, 244 &gc->message_hash, 245 gc->commitments, 246 gc->threshold, 247 &key_pair, 248 &com_nonce); 249 250 GNUNET_free (gc->commitments); 251 252 return TALER_MHD_REPLY_JSON_PACK ( 253 gc->connection, 254 MHD_HTTP_OK, 255 GNUNET_JSON_pack_data_auto ("signature_share", 256 &sig_share.sig_share), 257 GNUNET_JSON_pack_data_auto ("public_key_share", 258 &sig_share.pk_i)); 259 } 260 261 262 MHD_RESULT 263 FH_handler_sig_share_post ( 264 struct MHD_Connection *connection, 265 struct TM_HandlerContext *hc, 266 const struct FROSIX_SigRequestIdP *id, 267 const char *sign_share_data, 268 size_t *sign_share_data_size) 269 { 270 enum GNUNET_GenericReturnValue res; 271 struct SignShareContext *dc = hc->ctx; 272 json_t *json_commitments = NULL; 273 274 if (NULL == dc) 275 { 276 dc = GNUNET_new (struct SignShareContext); 277 dc->connection = connection; 278 dc->id = *id; 279 hc->ctx = dc; 280 } 281 282 /* parse request body */ 283 if (NULL == dc->json) 284 { 285 res = TALER_MHD_parse_post_json (dc->connection, 286 &dc->post_ctx, 287 sign_share_data, 288 sign_share_data_size, 289 &dc->json); 290 if (GNUNET_SYSERR == res) 291 { 292 GNUNET_break (0); 293 return MHD_NO; 294 } 295 if ((GNUNET_NO == res || 296 (NULL == dc->json))) 297 { 298 return MHD_YES; 299 } 300 } 301 302 struct GNUNET_JSON_Specification spec[] = { 303 GNUNET_JSON_spec_fixed_auto ("message_hash", 304 &dc->message_hash), 305 GNUNET_JSON_spec_fixed_auto ("encryption_key", 306 &dc->encryption_key), 307 GNUNET_JSON_spec_json ("commitments", 308 &json_commitments), 309 GNUNET_JSON_spec_end () 310 }; 311 312 res = TALER_MHD_parse_json_data (connection, 313 dc->json, 314 spec); 315 316 if (GNUNET_SYSERR == res) 317 { 318 GNUNET_JSON_parse_free (spec); 319 GNUNET_break (0); 320 return MHD_NO; 321 } 322 if (GNUNET_NO == res) 323 { 324 GNUNET_JSON_parse_free (spec); 325 /* FROSIX_EC_PARAMETER_MALFORMED */ 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 /* hash encryption key */ 334 FROSIX_hash_encryption_key (&dc->enc_key_hash, 335 &dc->encryption_key); 336 337 /* validate id */ 338 { 339 struct FROSIX_SigRequestIdP req_id; 340 FROSIX_compute_signature_request_id (&req_id, 341 &dc->enc_key_hash, 342 &dc->message_hash); 343 if (GNUNET_NO == FROST_hash_cmp (&req_id.id, 344 &id->id)) 345 { 346 GNUNET_JSON_parse_free (spec); 347 /* FROSIX_EC_REQUEST_ID_NOT_MATCHING */ 348 GNUNET_break_op (0); 349 return TALER_MHD_reply_with_error (connection, 350 MHD_HTTP_BAD_REQUEST, 351 TALER_EC_GENERIC_PARAMETER_MALFORMED, 352 "ID in URL not matching data in body"); 353 } 354 } 355 356 /* get number of provided commitments */ 357 dc->threshold = json_array_size (json_commitments); 358 if (0 >= dc->threshold || dc->threshold >= 253) 359 { 360 GNUNET_JSON_parse_free (spec); 361 GNUNET_break_op (0); 362 /* FROSIX_EC_NO_OR_TOO_MANY_COMMITMENTS */ 363 return TALER_MHD_reply_with_error (connection, 364 MHD_HTTP_BAD_REQUEST, 365 TALER_EC_GENERIC_PARAMETER_MALFORMED, 366 "No or too many commitments"); 367 } 368 369 /* parse commitments */ 370 dc->commitments = GNUNET_new_array (dc->threshold, 371 struct FROST_Commitment); 372 373 for (int i = 0; i < dc->threshold; i++) 374 { 375 struct GNUNET_JSON_Specification comm_spec[] = { 376 GNUNET_JSON_spec_uint8 ("identifier", 377 &dc->commitments[i].identifier), 378 GNUNET_JSON_spec_fixed_auto ("hiding_commitment", 379 &dc->commitments[i].hiding_commitment), 380 GNUNET_JSON_spec_fixed_auto ("binding_commitment", 381 &dc->commitments[i].binding_commitment), 382 GNUNET_JSON_spec_end () 383 }; 384 385 res = TALER_MHD_parse_json_array (connection, 386 json_commitments, 387 comm_spec, 388 i, 389 -1); 390 391 if (GNUNET_SYSERR == res) 392 { 393 GNUNET_free (dc->commitments); 394 GNUNET_JSON_parse_free (spec); 395 GNUNET_JSON_parse_free (comm_spec); 396 GNUNET_break (0); 397 return MHD_NO; 398 } 399 if (GNUNET_NO == res) 400 { 401 GNUNET_free (dc->commitments); 402 GNUNET_JSON_parse_free (spec); 403 GNUNET_JSON_parse_free (comm_spec); 404 GNUNET_break_op (0); 405 /* FROSIX_EC_COMMITMENTS_MALFORMED */ 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 (comm_spec); 413 } 414 415 GNUNET_JSON_parse_free (spec); 416 417 /* validate commitments */ 418 for (unsigned int i = 0; i < dc->threshold; i++) 419 { 420 if (dc->commitments[i].identifier <= 0 || dc->commitments[i].identifier >= 421 255 ) 422 { 423 GNUNET_free (dc->commitments); 424 GNUNET_break_op (0); 425 /* FROSIX_EC_COMMITMENT_IDENTIFIER_WRONG */ 426 return TALER_MHD_reply_with_error (connection, 427 MHD_HTTP_BAD_REQUEST, 428 TALER_EC_GENERIC_PARAMETER_MALFORMED, 429 "Unable to validate commitments"); 430 } 431 432 if (GNUNET_OK != FROST_validate_commitment (&dc->commitments[i])) 433 { 434 GNUNET_free (dc->commitments); 435 GNUNET_break_op (0); 436 /* FROSIX_EC_COMMITMENT_VALIDATION_FAILED (return id of failing 437 commitment?) */ 438 return TALER_MHD_reply_with_error (connection, 439 MHD_HTTP_BAD_REQUEST, 440 TALER_EC_GENERIC_PARAMETER_MALFORMED, 441 "Unable to validate commitments"); 442 } 443 } 444 445 return return_sig_share (dc); 446 }