aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Blättler <blatc2@bfh.ch>2024-06-06 14:03:39 +0200
committerChristian Blättler <blatc2@bfh.ch>2024-06-06 14:03:39 +0200
commit6f4486ffac5db888832e125ab4a9fbd673b69369 (patch)
tree9668b2f721f3a4cc3803dc35bf87f86769fd6ba0
parent3fa00294bee477ca58c9195baf39e0397c9b1b6e (diff)
check validity period of token families and keys
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-pay.c5
-rw-r--r--src/backend/taler-merchant-httpd_private-get-token-families-SLUG.c7
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders.c84
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 <gnunet/gnunet_json_lib.h>
#include <taler/taler_json_lib.h>
@@ -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 (