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