diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd_post-orders-ID-pay.c')
-rw-r--r-- | src/backend/taler-merchant-httpd_post-orders-ID-pay.c | 235 |
1 files changed, 131 insertions, 104 deletions
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c index 1808989a..0dad53db 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -60,13 +60,13 @@ * TODO: What is a good value for this? * Maximum number of tokens that we allow as inputs per transaction */ -#define MAX_TOKEN_ALLOWED_INPUT_TOKENS 128 +#define MAX_TOKEN_ALLOWED_INPUTs 128 /** * TODO: What is a good value for this? * Maximum number of tokens that we allow as outputs per transaction */ -#define MAX_TOKEN_ALLOWED_OUTPUT_TOKENS 128 +#define MAX_TOKEN_ALLOWED_OUTPUTs 128 /** * How often do we ask the exchange again about our @@ -287,6 +287,25 @@ struct TokenEnvelope /** + * (Blindly) signed token to be returned to the wallet. + */ +struct SignedOutputToken +{ + + /** + * Blinded token use public keys waiting to be signed. + */ + struct TALER_TokenIssueBlindSignatureP sig; + + /** + * Hash of token issue public key. + */ + struct TALER_TokenIssuePublicKeyHashP h_issue; + +}; + + +/** * Information kept during a pay request for each exchange. */ struct ExchangeGroup @@ -365,7 +384,7 @@ struct PayContext * Array with @e output_tokens_cnt signed tokens returned in * the response to the wallet. */ - struct TALER_TokenIssueBlindSignatureP *output_tokens; + struct SignedOutputToken *output_tokens; /** * Array with @e token_envelopes_cnt (blinded) token envelopes. @@ -1484,6 +1503,28 @@ phase_batch_deposits (struct PayContext *pc) /** + * Build JSON array of blindly signed token envelopes, + * to be used in the response to the wallet. + * + * @param[in,out] pc payment context to use + */ +static json_t * +build_token_sigs (struct PayContext *pc) +{ + json_t *token_sigs = json_array (); + for (unsigned int i = 0; i < pc->output_tokens_len; i++) + { + json_array_append_new (token_sigs, GNUNET_JSON_PACK ( + GNUNET_JSON_pack_blinded_sig ("blind_sig", pc->output_tokens[i].sig.signature), + GNUNET_JSON_pack_data_auto ("h_issue", &pc->output_tokens[i].h_issue) + )); + } + + return token_sigs; +} + + +/** * Generate response (payment successful) * * @param[in,out] pc payment context where the payment was successful @@ -1493,6 +1534,7 @@ phase_success_response (struct PayContext *pc) { struct TALER_MerchantSignatureP sig; char *pos_confirmation; + json_t *token_sigs; /* Sign on our end (as the payment did go through, even if it may have been refunded already) */ @@ -1506,6 +1548,9 @@ phase_success_response (struct PayContext *pc) pc->pos_alg, &pc->amount, pc->timestamp); + token_sigs = (0 >= pc->output_tokens_len) + ? NULL + : build_token_sigs (pc); pay_end (pc, TALER_MHD_REPLY_JSON_PACK ( pc->connection, @@ -1513,6 +1558,9 @@ phase_success_response (struct PayContext *pc) GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("pos_confirmation", pos_confirmation)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("token_sigs", + token_sigs)), GNUNET_JSON_pack_data_auto ("sig", &sig))); GNUNET_free (pos_confirmation); @@ -2223,13 +2271,13 @@ find_valid_input_tokens (struct PayContext *pc, return GNUNET_NO; } - if (GNUNET_OK != TALER_merchant_token_issue_verify (&tuc->pub, - &key->pub, - &tuc->unblinded_sig)) + if (GNUNET_OK != TALER_token_issue_verify (&tuc->pub, + &key->pub, + &tuc->unblinded_sig)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Input token for public key with valid_after " - "`%s' is invalid\n", + "`%s' has invalid issue signature\n", GNUNET_TIME_timestamp2s (key->valid_after)); GNUNET_break (0); pay_end (pc, @@ -2299,9 +2347,11 @@ sign_token_envelopes (struct PayContext *pc, continue; } - TALER_merchant_token_issue_sign (&pc->token_envelopes[i].blinded_token, - priv, - &pc->output_tokens[i]); + TALER_token_issue_sign (priv, + &pc->token_envelopes[i].blinded_token, + &pc->output_tokens[i].sig); + + pc->output_tokens[i].h_issue.hash = pc->token_envelopes[i].h_issue.hash; num_signed++; } @@ -2310,7 +2360,7 @@ sign_token_envelopes (struct PayContext *pc, { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Expected %d token envelopes for public key with valid_after " - "`%s', but found %d\n", + "'%s', but found %d\n", expected_num, GNUNET_TIME_timestamp2s (key->valid_after), num_signed); @@ -2328,45 +2378,6 @@ sign_token_envelopes (struct PayContext *pc, /** - * Find metching token family based on @a slug. Then use @a valid_after to - * find the matching public key. - * - * @param pc payment context - * @param slug token family slug - * @param valid_after valid_after timestamp - * @param[out] family matching token family - * @param[out] key matching public key - */ -static void -lookup_token_family_key (struct PayContext *pc, - const char *slug, - struct GNUNET_TIME_Timestamp valid_after, - struct TALER_MerchantContractTokenFamily *family, - struct TALER_MerchantContractTokenFamilyKey *key) -{ - for (unsigned int i = 0; i < pc->token_families_len; i++) - { - if (0 != strcmp (pc->token_families[i].slug, slug)) - { - continue; - } - *family = pc->token_families[i]; - for (unsigned int k = 0; k < family->keys_len; k++) - { - if (GNUNET_TIME_timestamp_cmp(family->keys[k].valid_after, - ==, - valid_after)) - { - *key = family->keys[k]; - break; - } - } - break; - } -} - - -/** * Validate tokens and token envelopes. First, we check if all tokens listed in * the 'inputs' array of the selected choice are present in the 'tokens' array * of the request. Then, we validate the signatures of each provided token. @@ -2428,8 +2439,8 @@ phase_validate_tokens (struct PayContext *pc) for (unsigned int i = 0; i<selected.inputs_len; i++) { struct TALER_MerchantContractInput input = selected.inputs[i]; - struct TALER_MerchantContractTokenFamily *family = NULL; - struct TALER_MerchantContractTokenFamilyKey *key = NULL; + struct TALER_MerchantContractTokenFamily family; + struct TALER_MerchantContractTokenFamilyKey key; if (input.type != TALER_MCIT_TOKEN) { @@ -2437,13 +2448,12 @@ phase_validate_tokens (struct PayContext *pc) continue; } - lookup_token_family_key (pc, - input.details.token.token_family_slug, - input.details.token.valid_after, - family, - key); - - if (NULL == family || NULL == key) + if (GNUNET_OK != TMH_find_token_family_key (input.details.token.token_family_slug, + input.details.token.valid_after, + pc->token_families, + pc->token_families_len, + &family, + &key)) { /* this should never happen, since the choices and token families are validated on insert. */ @@ -2457,8 +2467,8 @@ phase_validate_tokens (struct PayContext *pc) } if (GNUNET_NO == find_valid_input_tokens (pc, - key, - input.details.token.count)) + &key, + input.details.token.count)) { /* Error is already scheduled from find_valid_input_token. */ return; @@ -2474,8 +2484,8 @@ phase_validate_tokens (struct PayContext *pc) enum GNUNET_DB_QueryStatus qs; struct TALER_MERCHANTDB_TokenFamilyKeyDetails details; struct TALER_MerchantContractOutput output = selected.outputs[i]; - struct TALER_MerchantContractTokenFamily *family = NULL; - struct TALER_MerchantContractTokenFamilyKey *key = NULL; + struct TALER_MerchantContractTokenFamily family; + struct TALER_MerchantContractTokenFamilyKey key; if (output.type != TALER_MCOT_TOKEN) { @@ -2483,13 +2493,12 @@ phase_validate_tokens (struct PayContext *pc) continue; } - lookup_token_family_key (pc, - output.details.token.token_family_slug, - output.details.token.valid_after, - family, - key); - - if (NULL == family || NULL == key) + if (GNUNET_OK != TMH_find_token_family_key (output.details.token.token_family_slug, + output.details.token.valid_after, + pc->token_families, + pc->token_families_len, + &family, + &key)) { /* this should never happen, since the choices and token families are validated on insert. */ @@ -2504,25 +2513,27 @@ phase_validate_tokens (struct PayContext *pc) qs = TMH_db->lookup_token_family_key (TMH_db->cls, pc->hc->instance->settings.id, - family->slug, - key->valid_after, - key->valid_after, + family.slug, + key.valid_after, + key.valid_after, &details); if (qs <= 0) { GNUNET_break (0); pay_end (pc, - TALER_MHD_reply_with_error (pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL)); + TALER_MHD_reply_with_error (pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL)); return; } + GNUNET_assert (NULL != details.priv.private_key); + if (GNUNET_OK != sign_token_envelopes (pc, - family->slug, - key, + family.slug, + &key, &details.priv, output.details.token.count)) { @@ -2940,10 +2951,11 @@ phase_parse_wallet_data (struct PayContext *pc) GNUNET_JSON_spec_int64 ("choice_index", &pc->choice_index), NULL), - GNUNET_JSON_spec_mark_optional( - GNUNET_JSON_spec_fixed_auto ("h_outputs", - &pc->choice_index), - NULL), + /* TODO: Add h_outputs to wallet_data */ + // GNUNET_JSON_spec_mark_optional( + // GNUNET_JSON_spec_fixed_auto ("h_outputs", + // &pc->h_outputs), + // NULL), GNUNET_JSON_spec_end () }; @@ -3032,6 +3044,7 @@ phase_parse_pay (struct PayContext *pc) /* use empty string as default if client didn't specify it */ pc->session_id = GNUNET_strdup (""); } + pc->coins_cnt = json_array_size (coins); if (pc->coins_cnt > MAX_COIN_ALLOWED_COINS) { @@ -3044,30 +3057,10 @@ phase_parse_pay (struct PayContext *pc) "'coins' array too long")); return; } - - pc->tokens_cnt = json_array_size (tokens); - if (pc->tokens_cnt > MAX_TOKEN_ALLOWED_INPUT_TOKENS) - { - GNUNET_break_op (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "'tokens' array too long")); - return; - } - /* note: 1 coin = 1 deposit confirmation expected */ pc->dc = GNUNET_new_array (pc->coins_cnt, struct DepositConfirmation); - pc->tokens = GNUNET_new_array (pc->tokens_cnt, - struct TokenUseConfirmation); - - pc->token_envelopes = GNUNET_new_array (pc->token_envelopes_cnt, - struct TokenEnvelope); - /* This loop populates the array 'dc' in 'pc' */ { unsigned int coins_index; @@ -3183,6 +3176,22 @@ phase_parse_pay (struct PayContext *pc) } } + pc->tokens_cnt = json_array_size (tokens); + if (pc->tokens_cnt > MAX_TOKEN_ALLOWED_INPUTs) + { + GNUNET_break_op (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "'tokens' array too long")); + return; + } + + pc->tokens = GNUNET_new_array (pc->tokens_cnt, + struct TokenUseConfirmation); + /* This look populates the array 'tokens' in 'pc' */ { unsigned int tokens_index; @@ -3235,6 +3244,22 @@ phase_parse_pay (struct PayContext *pc) } } + pc->token_envelopes_cnt = json_array_size (tokens_evs); + if (pc->token_envelopes_cnt > MAX_TOKEN_ALLOWED_OUTPUTs) + { + GNUNET_break_op (0); + pay_end (pc, + TALER_MHD_reply_with_error ( + pc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "'token_evs' array too long")); + return; + } + + pc->token_envelopes = GNUNET_new_array (pc->token_envelopes_cnt, + struct TokenEnvelope); + { unsigned int tokens_ev_index; json_t *token_ev; @@ -3246,7 +3271,9 @@ phase_parse_pay (struct PayContext *pc) TALER_JSON_spec_token_envelope ("token_ev", &ev->blinded_token), GNUNET_JSON_spec_fixed_auto ("h_issue", - &ev->h_issue.hash)}; + &ev->h_issue.hash), + GNUNET_JSON_spec_end () + }; enum GNUNET_GenericReturnValue res; res = TALER_MHD_parse_json_data (pc->connection, |