From 6f4486ffac5db888832e125ab4a9fbd673b69369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Bl=C3=A4ttler?= Date: Thu, 6 Jun 2024 14:03:39 +0200 Subject: check validity period of token families and keys --- .../taler-merchant-httpd_post-orders-ID-pay.c | 5 +- ...erchant-httpd_private-get-token-families-SLUG.c | 7 +- .../taler-merchant-httpd_private-post-orders.c | 84 ++++++++++++++++++---- 3 files changed, 78 insertions(+), 18 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 2407625b..4160230d 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -2088,9 +2088,8 @@ phase_execute_pay_transaction (struct PayContext *pc) pay_end (pc, TALER_MHD_reply_with_error (pc->connection, MHD_HTTP_CONFLICT, - /* TODO: Maybe use a token-specific error code here? */ - TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS, - "token already used")); + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_INVALID, + "tokens")); return; } } diff --git a/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c index 06dbbdf9..12e57a99 100644 --- a/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c +++ b/src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c @@ -20,6 +20,7 @@ */ #include "platform.h" #include "taler-merchant-httpd_private-get-token-families-SLUG.h" +#include #include @@ -84,6 +85,7 @@ TMH_private_get_tokenfamilies_SLUG (const struct TMH_RequestHandler *rh, result = TALER_MHD_REPLY_JSON_PACK ( connection, MHD_HTTP_OK, + GNUNET_JSON_pack_string ("slug", details.slug), GNUNET_JSON_pack_string ("name", details.name), GNUNET_JSON_pack_string ("description", details.description), GNUNET_JSON_pack_object_steal ("description_i18n", @@ -91,7 +93,10 @@ TMH_private_get_tokenfamilies_SLUG (const struct TMH_RequestHandler *rh, GNUNET_JSON_pack_timestamp ("valid_after", details.valid_after), GNUNET_JSON_pack_timestamp ("valid_before", details.valid_before), GNUNET_JSON_pack_time_rel ("duration", details.duration), - GNUNET_JSON_pack_string ("kind", kind) + GNUNET_JSON_pack_string ("kind", kind), + // TODO: Implement + GNUNET_JSON_pack_int64 ("issued", 0), + GNUNET_JSON_pack_int64 ("redeemed", 0) ); GNUNET_free (details.name); diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 9eb63516..e8332c70 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -1537,6 +1537,23 @@ set_token_family (struct OrderContext *oc, return GNUNET_SYSERR; } + struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); + + /* Verify that the token family is valid right now. */ + if (GNUNET_TIME_timestamp_cmp (key_details.token_family.valid_after, >, now) || + GNUNET_TIME_timestamp_cmp (key_details.token_family.valid_before, <=, now)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Token family expired or not yet valid\n"); + reply_with_error (oc, + /* TODO: HTTP Status Code GONE would be more elegant, + but that is already used to indicate that a product is out of stock. */ + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_NOT_VALID, + key_details.token_family.slug); + return GNUNET_SYSERR; + } + /* slug is not needed */ GNUNET_free (key_details.token_family.slug); @@ -1577,7 +1594,7 @@ set_token_family (struct OrderContext *oc, if (NULL == key_details.pub.public_key) { /* There is no matching key for this token family yet. */ - /* We have to generate one. */ + /* We have to generate a fresh key pair. */ /* If public key is NULL, private key must also be NULL */ GNUNET_assert (NULL == key_details.priv.private_key); @@ -1585,10 +1602,28 @@ set_token_family (struct OrderContext *oc, struct GNUNET_CRYPTO_BlindSignPrivateKey *priv; struct GNUNET_CRYPTO_BlindSignPublicKey *pub; struct GNUNET_TIME_Timestamp valid_before = - GNUNET_TIME_absolute_to_timestamp( + GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_absolute_add (min_valid_after.abs_time, key_details.token_family.duration)); + if (GNUNET_TIME_timestamp_cmp (min_valid_after, + <, + key_details.token_family.valid_after)) + { + /* If key would start before validity of token family, + set valid_after of key to the value of the token family. */ + min_valid_after = key_details.token_family.valid_after; + } + + if (GNUNET_TIME_timestamp_cmp (valid_before, + >, + key_details.token_family.valid_before)) + { + /* If key would end after validity of token family, + set valid_before of key to the value of the token family. */ + valid_before = key_details.token_family.valid_before; + } + GNUNET_CRYPTO_blind_sign_keys_create (&priv, &pub, /* TODO: Make cipher and key length configurable */ @@ -1603,11 +1638,11 @@ set_token_family (struct OrderContext *oc, }; iqs = TMH_db->insert_token_family_key (TMH_db->cls, - slug, - &token_pub, - &token_priv, - min_valid_after, - valid_before); + slug, + &token_pub, + &token_priv, + min_valid_after, + valid_before); GNUNET_CRYPTO_blind_sign_priv_decref (priv); @@ -1729,6 +1764,7 @@ serialize_order (struct OrderContext *oc) struct TALER_MerchantContractTokenFamilyKey key = family->keys[j]; json_t *jkey = GNUNET_JSON_PACK ( + /* TODO: Remove h_pub. */ GNUNET_JSON_pack_data_auto ("h_pub", &key.pub.public_key->pub_key_hash), GNUNET_JSON_pack_allow_null( @@ -1970,8 +2006,8 @@ set_max_fee (struct OrderContext *oc) static bool set_exchanges (struct OrderContext *oc) { - /* Note: re-building 'oc->exchanges' every time here might be a tad - expensive; could likely consider caching the result if it starts to + /* Note: re-building 'oc->set_exchanges.exchanges' every time here might be a + tad expensive; could likely consider caching the result if it starts to matter. */ if (NULL == oc->set_exchanges.exchanges) { @@ -2042,7 +2078,8 @@ parse_order (struct OrderContext *oc) * mostly because in GNUnet relative times can't * be negative. */ bool no_fee; - /* TODO: Check if the parsing of the nonce field is missing here. */ + /* TODO: Move "amount" field to choices. This entails moving the + stefan-base fee calculation to the parse_choices phase. */ struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("version", @@ -2504,10 +2541,14 @@ parse_choices (struct OrderContext *oc) const json_t *jinputs; const json_t *joutputs; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("inputs", - &jinputs), - GNUNET_JSON_spec_array_const ("outputs", - &joutputs), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("inputs", + &jinputs), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("outputs", + &joutputs), + NULL), GNUNET_JSON_spec_end () }; enum GNUNET_GenericReturnValue ret; @@ -2529,6 +2570,18 @@ parse_choices (struct OrderContext *oc) return; } + if (0 == json_array_size(jinputs) && 0 == json_array_size(joutputs)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Choice #%u must have at least one input or output\n", + i); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "choice"); + return; + } + { // TODO: Maybe move to a separate function const json_t *jinput; @@ -2545,6 +2598,8 @@ parse_choices (struct OrderContext *oc) &kind), GNUNET_JSON_spec_string ("token_family_slug", &input.details.token.token_family_slug), + /* TODO: Remove valid_after field for inputs, + use current system time instead. */ GNUNET_JSON_spec_timestamp ("valid_after", &input.details.token.valid_after), GNUNET_JSON_spec_mark_optional ( @@ -2620,6 +2675,7 @@ parse_choices (struct OrderContext *oc) &kind), GNUNET_JSON_spec_string ("token_family_slug", &output.details.token.token_family_slug), + /* TODO: Make valid_after optional, default to current system time. */ GNUNET_JSON_spec_timestamp ("valid_after", &output.details.token.valid_after), GNUNET_JSON_spec_mark_optional ( -- cgit v1.2.3