diff options
author | Christian Blättler <blatc2@bfh.ch> | 2024-04-30 22:28:08 +0200 |
---|---|---|
committer | Christian Blättler <blatc2@bfh.ch> | 2024-04-30 22:28:08 +0200 |
commit | 4a3145fca389fca1bbfc7ba61b7cafd1156ef656 (patch) | |
tree | f95de17dcb3e20036dc3b6c0046cf5a2b18cc935 /src/backend | |
parent | 5d9db0012144cbad9ba90b654fdfff17b55b6af9 (diff) |
work on tokens
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/taler-merchant-httpd_contract.c | 44 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_contract.h | 22 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_post-orders-ID-pay.c | 235 |
3 files changed, 195 insertions, 106 deletions
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index f05c97d5..e7c7ac49 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -308,8 +308,9 @@ parse_token_families (void *cls, struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_array_const ("keys", &keys), - GNUNET_JSON_spec_bool ("critical", - &family.critical), + GNUNET_JSON_spec_bool ("critical", + &family.critical), + GNUNET_JSON_spec_end () /* TODO: Figure out if these fields should be 'const' */ // GNUNET_JSON_spec_string ("description", // &family.description), @@ -415,4 +416,43 @@ TALER_JSON_spec_token_families (const char *name, }; return ret; +} + +enum GNUNET_GenericReturnValue +TMH_find_token_family_key (const char *slug, + struct GNUNET_TIME_Timestamp valid_after, + struct TALER_MerchantContractTokenFamily *families, + unsigned int families_len, + struct TALER_MerchantContractTokenFamily *family, + struct TALER_MerchantContractTokenFamilyKey *key) +{ + for (unsigned int i = 0; i < families_len; i++) + { + if (0 != strcmp (families[i].slug, slug)) + { + continue; + } + if (NULL != family) + { + *family = families[i]; + } + for (unsigned int k = 0; k < family->keys_len; k++) + { + if (GNUNET_TIME_timestamp_cmp(family->keys[k].valid_after, + ==, + valid_after)) + { + if (NULL != key) + { + *key = family->keys[k]; + } + return GNUNET_OK; + } + } + /* matching family found, but no key. */ + return GNUNET_NO; + } + + /* no matching family found */ + return GNUNET_NO; }
\ No newline at end of file diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h index 6c1841e9..006d0205 100644 --- a/src/backend/taler-merchant-httpd_contract.h +++ b/src/backend/taler-merchant-httpd_contract.h @@ -19,6 +19,7 @@ * @author Christian Blättler */ #include "taler-merchant-httpd.h" +#include <gnunet/gnunet_common.h> #include <gnunet/gnunet_time_lib.h> #include <jansson.h> @@ -614,3 +615,24 @@ struct GNUNET_JSON_Specification TALER_JSON_spec_token_families (const char *name, struct TALER_MerchantContractTokenFamily **families, unsigned int *families_len); + + +/** + * Find matching token family in @a families based on @a slug. Then use + * @a valid_after to find the matching public key within it. + * + * @param slug slug of the token family + * @param valid_after start time of the validity period of the key + * @param families array of token families to search in + * @param families_len length of the @a families array + * @param[out] family found family, set to NULL to only check for existence + * @param[out] key found key, set to NULL to only check for existence + * @return #GNUNET_OK on success #GNUNET_NO if no key was found + */ +enum GNUNET_GenericReturnValue +TMH_find_token_family_key (const char *slug, + struct GNUNET_TIME_Timestamp valid_after, + struct TALER_MerchantContractTokenFamily *families, + unsigned int families_len, + struct TALER_MerchantContractTokenFamily *family, + struct TALER_MerchantContractTokenFamilyKey *key);
\ No newline at end of file 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, |