exchange_api_get-management-keys.c (13425B)
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/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 "taler/taler-exchange/get-management-keys.h" 28 #include "exchange_api_curl_defaults.h" 29 #include "taler/taler_signatures.h" 30 #include "taler/taler_curl_lib.h" 31 #include "taler/taler_util.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_GetManagementKeysHandle 43 { 44 45 /** 46 * The base URL for this request. 47 */ 48 char *base_url; 49 50 /** 51 * The full URL for this request, set during _start. 52 */ 53 char *url; 54 55 /** 56 * Handle for the request. 57 */ 58 struct GNUNET_CURL_Job *job; 59 60 /** 61 * Function to call with the result. 62 */ 63 TALER_EXCHANGE_GetManagementKeysCallback cb; 64 65 /** 66 * Closure for @a cb. 67 */ 68 TALER_EXCHANGE_GET_MANAGEMENT_KEYS_RESULT_CLOSURE *cb_cls; 69 70 /** 71 * Reference to the execution context. 72 */ 73 struct GNUNET_CURL_Context *ctx; 74 75 }; 76 77 78 /** 79 * Handle the case that the response was of type #MHD_HTTP_OK. 80 * 81 * @param[in,out] gmkh request handle 82 * @param response the response 83 * @return #GNUNET_OK if the response was well-formed 84 */ 85 static enum GNUNET_GenericReturnValue 86 handle_ok (struct TALER_EXCHANGE_GetManagementKeysHandle *gmkh, 87 const json_t *response) 88 { 89 struct TALER_EXCHANGE_GetManagementKeysResponse gkr = { 90 .hr.http_status = MHD_HTTP_OK, 91 .hr.reply = response, 92 }; 93 struct TALER_EXCHANGE_FutureKeys *fk 94 = &gkr.details.ok.keys; 95 const json_t *sk; 96 const json_t *dk; 97 bool ok; 98 struct GNUNET_JSON_Specification spec[] = { 99 GNUNET_JSON_spec_array_const ("future_denoms", 100 &dk), 101 GNUNET_JSON_spec_array_const ("future_signkeys", 102 &sk), 103 GNUNET_JSON_spec_fixed_auto ("master_pub", 104 &fk->master_pub), 105 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 106 &fk->denom_secmod_public_key), 107 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 108 &fk->denom_secmod_cs_public_key), 109 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 110 &fk->signkey_secmod_public_key), 111 GNUNET_JSON_spec_end () 112 }; 113 114 if (GNUNET_OK != 115 GNUNET_JSON_parse (response, 116 spec, 117 NULL, NULL)) 118 { 119 GNUNET_break_op (0); 120 return GNUNET_SYSERR; 121 } 122 fk->num_sign_keys = json_array_size (sk); 123 fk->num_denom_keys = json_array_size (dk); 124 fk->sign_keys = GNUNET_new_array ( 125 fk->num_sign_keys, 126 struct TALER_EXCHANGE_FutureSigningPublicKey); 127 fk->denom_keys = GNUNET_new_array ( 128 fk->num_denom_keys, 129 struct TALER_EXCHANGE_FutureDenomPublicKey); 130 ok = true; 131 for (unsigned int i = 0; i < fk->num_sign_keys; i++) 132 { 133 json_t *j = json_array_get (sk, 134 i); 135 struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key 136 = &fk->sign_keys[i]; 137 struct GNUNET_JSON_Specification ispec[] = { 138 GNUNET_JSON_spec_fixed_auto ("key", 139 &sign_key->key), 140 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 141 &sign_key->signkey_secmod_sig), 142 GNUNET_JSON_spec_timestamp ("stamp_start", 143 &sign_key->valid_from), 144 GNUNET_JSON_spec_timestamp ("stamp_expire", 145 &sign_key->valid_until), 146 GNUNET_JSON_spec_timestamp ("stamp_end", 147 &sign_key->valid_legal), 148 GNUNET_JSON_spec_end () 149 }; 150 151 if (GNUNET_OK != 152 GNUNET_JSON_parse (j, 153 ispec, 154 NULL, NULL)) 155 { 156 GNUNET_break_op (0); 157 ok = false; 158 break; 159 } 160 { 161 struct GNUNET_TIME_Relative duration 162 = GNUNET_TIME_absolute_get_difference (sign_key->valid_from.abs_time, 163 sign_key->valid_until.abs_time); 164 165 if (GNUNET_OK != 166 TALER_exchange_secmod_eddsa_verify ( 167 &sign_key->key, 168 sign_key->valid_from, 169 duration, 170 &fk->signkey_secmod_public_key, 171 &sign_key->signkey_secmod_sig)) 172 { 173 GNUNET_break_op (0); 174 ok = false; 175 break; 176 } 177 } 178 } 179 for (unsigned int i = 0; i < fk->num_denom_keys; i++) 180 { 181 json_t *j = json_array_get (dk, 182 i); 183 struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key 184 = &fk->denom_keys[i]; 185 const char *section_name; 186 struct GNUNET_JSON_Specification ispec[] = { 187 TALER_JSON_spec_amount_any ("value", 188 &denom_key->value), 189 GNUNET_JSON_spec_timestamp ("stamp_start", 190 &denom_key->valid_from), 191 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 192 &denom_key->withdraw_valid_until), 193 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 194 &denom_key->expire_deposit), 195 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 196 &denom_key->expire_legal), 197 TALER_JSON_spec_denom_pub ("denom_pub", 198 &denom_key->key), 199 TALER_JSON_spec_amount_any ("fee_withdraw", 200 &denom_key->fee_withdraw), 201 TALER_JSON_spec_amount_any ("fee_deposit", 202 &denom_key->fee_deposit), 203 TALER_JSON_spec_amount_any ("fee_refresh", 204 &denom_key->fee_refresh), 205 TALER_JSON_spec_amount_any ("fee_refund", 206 &denom_key->fee_refund), 207 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 208 &denom_key->denom_secmod_sig), 209 GNUNET_JSON_spec_string ("section_name", 210 §ion_name), 211 GNUNET_JSON_spec_end () 212 }; 213 214 if (GNUNET_OK != 215 GNUNET_JSON_parse (j, 216 ispec, 217 NULL, NULL)) 218 { 219 GNUNET_break_op (0); 220 #if DEBUG 221 json_dumpf (j, 222 stderr, 223 JSON_INDENT (2)); 224 #endif 225 ok = false; 226 break; 227 } 228 229 { 230 struct TALER_DenominationHashP h_denom_pub; 231 struct GNUNET_TIME_Relative duration 232 = GNUNET_TIME_absolute_get_difference ( 233 denom_key->valid_from.abs_time, 234 denom_key->withdraw_valid_until.abs_time); 235 236 TALER_denom_pub_hash (&denom_key->key, 237 &h_denom_pub); 238 switch (denom_key->key.bsign_pub_key->cipher) 239 { 240 case GNUNET_CRYPTO_BSA_RSA: 241 { 242 struct TALER_RsaPubHashP h_rsa; 243 244 TALER_rsa_pub_hash ( 245 denom_key->key.bsign_pub_key->details.rsa_public_key, 246 &h_rsa); 247 if (GNUNET_OK != 248 TALER_exchange_secmod_rsa_verify (&h_rsa, 249 section_name, 250 denom_key->valid_from, 251 duration, 252 &fk->denom_secmod_public_key, 253 &denom_key->denom_secmod_sig)) 254 { 255 GNUNET_break_op (0); 256 ok = false; 257 break; 258 } 259 } 260 break; 261 case GNUNET_CRYPTO_BSA_CS: 262 { 263 struct TALER_CsPubHashP h_cs; 264 265 TALER_cs_pub_hash ( 266 &denom_key->key.bsign_pub_key->details.cs_public_key, 267 &h_cs); 268 if (GNUNET_OK != 269 TALER_exchange_secmod_cs_verify (&h_cs, 270 section_name, 271 denom_key->valid_from, 272 duration, 273 &fk->denom_secmod_cs_public_key, 274 &denom_key->denom_secmod_sig)) 275 { 276 GNUNET_break_op (0); 277 ok = false; 278 break; 279 } 280 } 281 break; 282 default: 283 GNUNET_break_op (0); 284 ok = false; 285 break; 286 } 287 } 288 if (! ok) 289 break; 290 } 291 if (ok) 292 { 293 gmkh->cb (gmkh->cb_cls, 294 &gkr); 295 } 296 for (unsigned int i = 0; i < fk->num_denom_keys; i++) 297 TALER_denom_pub_free (&fk->denom_keys[i].key); 298 GNUNET_free (fk->sign_keys); 299 GNUNET_free (fk->denom_keys); 300 return (ok) ? GNUNET_OK : GNUNET_SYSERR; 301 } 302 303 304 /** 305 * Function called when we're done processing the 306 * HTTP GET /management/keys request. 307 * 308 * @param cls the `struct TALER_EXCHANGE_GetManagementKeysHandle` 309 * @param response_code HTTP response code, 0 on error 310 * @param response response body, NULL if not in JSON 311 */ 312 static void 313 handle_get_keys_finished (void *cls, 314 long response_code, 315 const void *response) 316 { 317 struct TALER_EXCHANGE_GetManagementKeysHandle *gmkh = cls; 318 const json_t *json = response; 319 struct TALER_EXCHANGE_GetManagementKeysResponse gkr = { 320 .hr.http_status = (unsigned int) response_code, 321 .hr.reply = json 322 }; 323 324 gmkh->job = NULL; 325 switch (response_code) 326 { 327 case MHD_HTTP_OK: 328 if (GNUNET_OK == 329 handle_ok (gmkh, 330 response)) 331 { 332 gmkh->cb = NULL; 333 } 334 else 335 { 336 response_code = 0; 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 */