aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorChristian Blättler <blatc2@bfh.ch>2024-04-30 22:28:08 +0200
committerChristian Blättler <blatc2@bfh.ch>2024-04-30 22:28:08 +0200
commit4a3145fca389fca1bbfc7ba61b7cafd1156ef656 (patch)
treef95de17dcb3e20036dc3b6c0046cf5a2b18cc935 /src/backend
parent5d9db0012144cbad9ba90b654fdfff17b55b6af9 (diff)
work on tokens
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/taler-merchant-httpd_contract.c44
-rw-r--r--src/backend/taler-merchant-httpd_contract.h22
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-pay.c235
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,