aboutsummaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
diff options
context:
space:
mode:
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.c235
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,