exchange_api_management_get_keys.c (12893B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015-2023 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 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_management_get_keys.c 19 * @brief functions to obtain future online keys of the exchange 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include "taler/taler_json_lib.h" 24 #include <gnunet/gnunet_curl_lib.h> 25 #include <microhttpd.h> 26 #include "taler/taler_exchange_service.h" 27 #include "exchange_api_curl_defaults.h" 28 #include "taler/taler_signatures.h" 29 #include "taler/taler_curl_lib.h" 30 #include "taler/taler_util.h" 31 #include "taler/taler_json_lib.h" 32 33 /** 34 * Set to 1 for extra debug logging. 35 */ 36 #define DEBUG 0 37 38 39 /** 40 * @brief Handle for a GET /management/keys request. 41 */ 42 struct TALER_EXCHANGE_ManagementGetKeysHandle 43 { 44 45 /** 46 * The url for this request. 47 */ 48 char *url; 49 50 /** 51 * Handle for the request. 52 */ 53 struct GNUNET_CURL_Job *job; 54 55 /** 56 * Function to call with the result. 57 */ 58 TALER_EXCHANGE_ManagementGetKeysCallback cb; 59 60 /** 61 * Closure for @a cb. 62 */ 63 void *cb_cls; 64 65 /** 66 * Reference to the execution context. 67 */ 68 struct GNUNET_CURL_Context *ctx; 69 }; 70 71 72 /** 73 * Handle the case that the response was of type #MHD_HTTP_OK. 74 * 75 * @param[in,out] gh request handle 76 * @param response the response 77 * @return #GNUNET_OK if the response was well-formed 78 */ 79 static enum GNUNET_GenericReturnValue 80 handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh, 81 const json_t *response) 82 { 83 struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = { 84 .hr.http_status = MHD_HTTP_OK, 85 .hr.reply = response, 86 }; 87 struct TALER_EXCHANGE_FutureKeys *fk 88 = &gkr.details.ok.keys; 89 const json_t *sk; 90 const json_t *dk; 91 bool ok; 92 struct GNUNET_JSON_Specification spec[] = { 93 GNUNET_JSON_spec_array_const ("future_denoms", 94 &dk), 95 GNUNET_JSON_spec_array_const ("future_signkeys", 96 &sk), 97 GNUNET_JSON_spec_fixed_auto ("master_pub", 98 &fk->master_pub), 99 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 100 &fk->denom_secmod_public_key), 101 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 102 &fk->denom_secmod_cs_public_key), 103 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 104 &fk->signkey_secmod_public_key), 105 GNUNET_JSON_spec_end () 106 }; 107 108 if (GNUNET_OK != 109 GNUNET_JSON_parse (response, 110 spec, 111 NULL, NULL)) 112 { 113 GNUNET_break_op (0); 114 return GNUNET_SYSERR; 115 } 116 fk->num_sign_keys = json_array_size (sk); 117 fk->num_denom_keys = json_array_size (dk); 118 fk->sign_keys = GNUNET_new_array ( 119 fk->num_sign_keys, 120 struct TALER_EXCHANGE_FutureSigningPublicKey); 121 fk->denom_keys = GNUNET_new_array ( 122 fk->num_denom_keys, 123 struct TALER_EXCHANGE_FutureDenomPublicKey); 124 ok = true; 125 for (unsigned int i = 0; i<fk->num_sign_keys; i++) 126 { 127 json_t *j = json_array_get (sk, 128 i); 129 struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key 130 = &fk->sign_keys[i]; 131 struct GNUNET_JSON_Specification ispec[] = { 132 GNUNET_JSON_spec_fixed_auto ("key", 133 &sign_key->key), 134 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 135 &sign_key->signkey_secmod_sig), 136 GNUNET_JSON_spec_timestamp ("stamp_start", 137 &sign_key->valid_from), 138 GNUNET_JSON_spec_timestamp ("stamp_expire", 139 &sign_key->valid_until), 140 GNUNET_JSON_spec_timestamp ("stamp_end", 141 &sign_key->valid_legal), 142 GNUNET_JSON_spec_end () 143 }; 144 145 if (GNUNET_OK != 146 GNUNET_JSON_parse (j, 147 ispec, 148 NULL, NULL)) 149 { 150 GNUNET_break_op (0); 151 ok = false; 152 break; 153 } 154 { 155 struct GNUNET_TIME_Relative duration 156 = GNUNET_TIME_absolute_get_difference (sign_key->valid_from.abs_time, 157 sign_key->valid_until.abs_time); 158 159 if (GNUNET_OK != 160 TALER_exchange_secmod_eddsa_verify ( 161 &sign_key->key, 162 sign_key->valid_from, 163 duration, 164 &fk->signkey_secmod_public_key, 165 &sign_key->signkey_secmod_sig)) 166 { 167 GNUNET_break_op (0); 168 ok = false; 169 break; 170 } 171 } 172 } 173 for (unsigned int i = 0; i<fk->num_denom_keys; i++) 174 { 175 json_t *j = json_array_get (dk, 176 i); 177 struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key 178 = &fk->denom_keys[i]; 179 const char *section_name; 180 struct GNUNET_JSON_Specification ispec[] = { 181 TALER_JSON_spec_amount_any ("value", 182 &denom_key->value), 183 GNUNET_JSON_spec_timestamp ("stamp_start", 184 &denom_key->valid_from), 185 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 186 &denom_key->withdraw_valid_until), 187 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 188 &denom_key->expire_deposit), 189 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 190 &denom_key->expire_legal), 191 TALER_JSON_spec_denom_pub ("denom_pub", 192 &denom_key->key), 193 TALER_JSON_spec_amount_any ("fee_withdraw", 194 &denom_key->fee_withdraw), 195 TALER_JSON_spec_amount_any ("fee_deposit", 196 &denom_key->fee_deposit), 197 TALER_JSON_spec_amount_any ("fee_refresh", 198 &denom_key->fee_refresh), 199 TALER_JSON_spec_amount_any ("fee_refund", 200 &denom_key->fee_refund), 201 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 202 &denom_key->denom_secmod_sig), 203 GNUNET_JSON_spec_string ("section_name", 204 §ion_name), 205 GNUNET_JSON_spec_end () 206 }; 207 208 if (GNUNET_OK != 209 GNUNET_JSON_parse (j, 210 ispec, 211 NULL, NULL)) 212 { 213 GNUNET_break_op (0); 214 #if DEBUG 215 json_dumpf (j, 216 stderr, 217 JSON_INDENT (2)); 218 #endif 219 ok = false; 220 break; 221 } 222 223 { 224 struct TALER_DenominationHashP h_denom_pub; 225 struct GNUNET_TIME_Relative duration 226 = GNUNET_TIME_absolute_get_difference ( 227 denom_key->valid_from.abs_time, 228 denom_key->withdraw_valid_until.abs_time); 229 230 TALER_denom_pub_hash (&denom_key->key, 231 &h_denom_pub); 232 switch (denom_key->key.bsign_pub_key->cipher) 233 { 234 case GNUNET_CRYPTO_BSA_RSA: 235 { 236 struct TALER_RsaPubHashP h_rsa; 237 238 TALER_rsa_pub_hash ( 239 denom_key->key.bsign_pub_key->details.rsa_public_key, 240 &h_rsa); 241 if (GNUNET_OK != 242 TALER_exchange_secmod_rsa_verify (&h_rsa, 243 section_name, 244 denom_key->valid_from, 245 duration, 246 &fk->denom_secmod_public_key, 247 &denom_key->denom_secmod_sig)) 248 { 249 GNUNET_break_op (0); 250 ok = false; 251 break; 252 } 253 } 254 break; 255 case GNUNET_CRYPTO_BSA_CS: 256 { 257 struct TALER_CsPubHashP h_cs; 258 259 TALER_cs_pub_hash ( 260 &denom_key->key.bsign_pub_key->details.cs_public_key, 261 &h_cs); 262 if (GNUNET_OK != 263 TALER_exchange_secmod_cs_verify (&h_cs, 264 section_name, 265 denom_key->valid_from, 266 duration, 267 &fk->denom_secmod_cs_public_key, 268 &denom_key->denom_secmod_sig)) 269 { 270 GNUNET_break_op (0); 271 ok = false; 272 break; 273 } 274 } 275 break; 276 default: 277 GNUNET_break_op (0); 278 ok = false; 279 break; 280 } 281 } 282 if (! ok) 283 break; 284 } 285 if (ok) 286 { 287 gh->cb (gh->cb_cls, 288 &gkr); 289 } 290 for (unsigned int i = 0; i<fk->num_denom_keys; i++) 291 TALER_denom_pub_free (&fk->denom_keys[i].key); 292 GNUNET_free (fk->sign_keys); 293 GNUNET_free (fk->denom_keys); 294 return (ok) ? GNUNET_OK : GNUNET_SYSERR; 295 } 296 297 298 /** 299 * Function called when we're done processing the 300 * HTTP GET /management/keys request. 301 * 302 * @param cls the `struct TALER_EXCHANGE_ManagementGetKeysHandle *` 303 * @param response_code HTTP response code, 0 on error 304 * @param response response body, NULL if not in JSON 305 */ 306 static void 307 handle_get_keys_finished (void *cls, 308 long response_code, 309 const void *response) 310 { 311 struct TALER_EXCHANGE_ManagementGetKeysHandle *gh = cls; 312 const json_t *json = response; 313 struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = { 314 .hr.http_status = (unsigned int) response_code, 315 .hr.reply = json 316 }; 317 318 gh->job = NULL; 319 switch (response_code) 320 { 321 case MHD_HTTP_OK: 322 if (GNUNET_OK == 323 handle_ok (gh, 324 response)) 325 { 326 gh->cb = NULL; 327 } 328 else 329 { 330 response_code = 0; 331 } 332 break; 333 case MHD_HTTP_NOT_FOUND: 334 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 335 "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n", 336 gh->url); 337 if (NULL != json) 338 { 339 gkr.hr.ec = TALER_JSON_get_error_code (json); 340 gkr.hr.hint = TALER_JSON_get_error_hint (json); 341 } 342 else 343 { 344 gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 345 gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec); 346 } 347 break; 348 default: 349 /* unexpected response code */ 350 if (NULL != json) 351 { 352 gkr.hr.ec = TALER_JSON_get_error_code (json); 353 gkr.hr.hint = TALER_JSON_get_error_hint (json); 354 } 355 else 356 { 357 gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 358 gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec); 359 } 360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 361 "Unexpected response code %u/%d for exchange management get keys\n", 362 (unsigned int) response_code, 363 (int) gkr.hr.ec); 364 break; 365 } 366 if (NULL != gh->cb) 367 { 368 gh->cb (gh->cb_cls, 369 &gkr); 370 gh->cb = NULL; 371 } 372 TALER_EXCHANGE_get_management_keys_cancel (gh); 373 }; 374 375 376 struct TALER_EXCHANGE_ManagementGetKeysHandle * 377 TALER_EXCHANGE_get_management_keys (struct GNUNET_CURL_Context *ctx, 378 const char *url, 379 TALER_EXCHANGE_ManagementGetKeysCallback cb, 380 void *cb_cls) 381 { 382 struct TALER_EXCHANGE_ManagementGetKeysHandle *gh; 383 CURL *eh; 384 385 gh = GNUNET_new (struct TALER_EXCHANGE_ManagementGetKeysHandle); 386 gh->cb = cb; 387 gh->cb_cls = cb_cls; 388 gh->ctx = ctx; 389 gh->url = TALER_url_join (url, 390 "management/keys", 391 NULL); 392 if (NULL == gh->url) 393 { 394 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 395 "Could not construct request URL.\n"); 396 GNUNET_free (gh); 397 return NULL; 398 } 399 eh = TALER_EXCHANGE_curl_easy_get_ (gh->url); 400 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 401 "Requesting URL '%s'\n", 402 gh->url); 403 gh->job = GNUNET_CURL_job_add (ctx, 404 eh, 405 &handle_get_keys_finished, 406 gh); 407 if (NULL == gh->job) 408 { 409 TALER_EXCHANGE_get_management_keys_cancel (gh); 410 return NULL; 411 } 412 return gh; 413 } 414 415 416 void 417 TALER_EXCHANGE_get_management_keys_cancel ( 418 struct TALER_EXCHANGE_ManagementGetKeysHandle *gh) 419 { 420 if (NULL != gh->job) 421 { 422 GNUNET_CURL_job_cancel (gh->job); 423 gh->job = NULL; 424 } 425 GNUNET_free (gh->url); 426 GNUNET_free (gh); 427 }