aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Blättler <blatc2@bfh.ch>2024-04-18 18:24:52 +0200
committerChristian Blättler <blatc2@bfh.ch>2024-04-18 18:24:52 +0200
commit4f00d126a95b41ef157734e8e2b11100aab569a2 (patch)
treefea10173bc06240abe0cfa9149ef7d3776ca605e
parent4fdbc050d140cb8fa12e8a3e6cfc17afb721a514 (diff)
adapt to changed spec and fix key handling
-rw-r--r--src/backend/taler-merchant-httpd_contract.h125
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders.c243
-rw-r--r--src/backenddb/pg_lookup_token_family_key.c2
-rw-r--r--src/backenddb/pg_lookup_token_family_key.h5
4 files changed, 236 insertions, 139 deletions
diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h
index defe6479..3196b469 100644
--- a/src/backend/taler-merchant-httpd_contract.h
+++ b/src/backend/taler-merchant-httpd_contract.h
@@ -251,102 +251,97 @@ struct TALER_MerchantContractChoice
unsigned int outputs_len;
};
-struct TALER_MerchantContractLimits
+/**
+ * Public key and corresponding metadata for a token family.
+ */
+struct TALER_MerchantContractTokenFamilyKey
{
/**
- * Currency these limits are for.
- */
- char currency[TALER_CURRENCY_LEN];
-
- /**
- * The hash of the merchant instance's wire details.
+ * Public key.
*/
- struct TALER_MerchantWireHashP h_wire;
+ struct TALER_TokenFamilyPublicKey pub;
/**
- * Wire transfer method identifier for the wire method associated with ``h_wire``.
- * The wallet may only select exchanges via a matching auditor if the
- * exchange also supports this wire method.
- * The wire transfer fees must be added based on this wire transfer method.
+ * Tokens signed by this key will be valid after this time.
*/
- char *wire_method;
+ struct GNUNET_TIME_Timestamp valid_after;
/**
- * Maximum total deposit fee accepted by the merchant for this contract.
+ * Tokens signed by this key will be valid before this time.
*/
- struct TALER_Amount max_fee;
+ struct GNUNET_TIME_Timestamp valid_before;
};
-struct TALER_MerchantContractTokenAuthority
+struct TALER_MerchantContractTokenFamily
{
/**
- * Label of the token authority.
- */
- const char *label;
+ * Slug of the token family.
+ */
+ const char *slug;
/**
- * Human-readable description of the semantics of the tokens issued by
- * this authority.
- */
+ * Human-readable description of the semantics of the tokens issued by
+ * this token family.
+ */
char *description;
/**
- * Map from IETF BCP 47 language tags to localized description.
- */
+ * Map from IETF BCP 47 language tags to localized description.
+ */
json_t *description_i18n;
/**
- * Public key used to validate tokens signed by this authority.
- */
- struct TALER_TokenFamilyPublicKey *pub;
+ * Relevant public keys of this token family for the given contract.
+ */
+ struct TALER_MerchantContractTokenFamilyKey *keys;
/**
- * When will tokens signed by this key expire?
- */
- struct GNUNET_TIME_Timestamp token_expiration;
+ * Length of the @e keys array.
+ */
+ unsigned int keys_len;
/**
- * Must a wallet understand this token type to process contracts that
- * consume or yield it?
- */
+ * Must a wallet understand this token type to process contracts that
+ * consume or yield it?
+ */
bool critical;
/**
- * Kind of the token.
- */
+ * Kind of the token family.
+ */
enum TALER_MerchantContractTokenKind kind;
/**
- * Kind-specific information about the token.
- */
+ * Kind-specific information about the token.
+ */
union
{
/**
- * Subscription token.
- */
+ * Subscription token.
+ */
struct
{
- /**
- * When does the subscription period start?
- */
- struct GNUNET_TIME_Timestamp start_date;
+ // /**
+ // * When does the subscription period start?
+ // */
+ // struct GNUNET_TIME_Timestamp start_date;
- /**
- * When does the subscription period end?
- */
- struct GNUNET_TIME_Timestamp end_date;
+ // /**
+ // * When does the subscription period end?
+ // */
+ // struct GNUNET_TIME_Timestamp end_date;
/**
- * Array of domain names where this subscription can be safely used
- * (e.g. the issuer warrants that these sites will re-issue tokens of
- * this type if the respective contract says so). May contain "*" for
- * any domain or subdomain.
- */
+ * Array of domain names where this subscription can be safely used
+ * (e.g. the issuer warrants that these sites will re-issue tokens of
+ * this type if the respective contract says so). May contain "*" for
+ * any domain or subdomain.
+ */
const char **trusted_domains;
/**
- * Length of the @e trusted_domains array.
- */
+ * Length of the @e trusted_domains array.
+ */
unsigned int trusted_domains_len;
} subscription;
@@ -356,19 +351,19 @@ struct TALER_MerchantContractTokenAuthority
struct
{
/**
- * Array of domain names where this discount token is intended to be
- * used. May contain "*" for any domain or subdomain. Users should be
- * warned about sites proposing to consume discount tokens of this
- * type that are not in this list that the merchant is accepting a
- * coupon from a competitor and thus may be attaching different
- * semantics (like get 20% discount for my competitors 30% discount
- * token).
- */
+ * Array of domain names where this discount token is intended to be
+ * used. May contain "*" for any domain or subdomain. Users should be
+ * warned about sites proposing to consume discount tokens of this
+ * type that are not in this list that the merchant is accepting a
+ * coupon from a competitor and thus may be attaching different
+ * semantics (like get 20% discount for my competitors 30% discount
+ * token).
+ */
const char **expected_domains;
/**
- * Length of the @e expected_domains array.
- */
+ * Length of the @e expected_domains array.
+ */
unsigned int expected_domains_len;
} discount;
@@ -540,7 +535,7 @@ struct TALER_MerchantContract
/**
* Array of token authorities.
*/
- struct TALER_MerchantContractTokenAuthority *token_authorities;
+ struct TALER_MerchantContractTokenFamily *token_authorities;
/**
* Length of the @e token_authorities array.
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
index fa71f6da..6974e367 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -26,6 +26,7 @@
*/
#include "platform.h"
#include <gnunet/gnunet_common.h>
+#include <gnunet/gnunet_db_lib.h>
#include <gnunet/gnunet_json_lib.h>
#include <gnunet/gnunet_time_lib.h>
#include <jansson.h>
@@ -376,14 +377,14 @@ struct OrderContext
unsigned int choices_len;
/**
- * Array of token types referenced in the contract.
+ * Array of token families referenced in the contract.
*/
- struct TALER_MerchantContractTokenAuthority *authorities;
+ struct TALER_MerchantContractTokenFamily *token_families;
/**
- * Length of the @e authorities array.
+ * Length of the @e token_families array.
*/
- unsigned int authorities_len;
+ unsigned int token_families_len;
} parse_choices;
/**
@@ -1318,35 +1319,69 @@ get_exchange_keys (void *cls,
}
/**
- * Fetch details about the token family with the given @a slug
- * and add them to the list of token authorities. Check if the
- * token family already has a valid key configured and if not,
- * create a new one.
+ * Check if the token family with the given @a slug is already present in
+ * the list of token families for this order. If not, fetch its details and
+ * add it to the list. Then check if there is a public key with a matching
+ * @a valid_after field. If not, generate a new key pair and store it in the
+ * database.
*
* @param[in,out] oc order context
* @param slug slug of the token family
- * @param start_date validity start date of the token
+ * @param valid_after validity start date of the token, subject to rounding
*/
static MHD_RESULT
-set_token_authority (struct OrderContext *oc,
- const char *slug,
- struct GNUNET_TIME_Timestamp start_date)
+set_token_family (struct OrderContext *oc,
+ const char *slug,
+ struct GNUNET_TIME_Timestamp valid_after)
{
struct TALER_MERCHANTDB_TokenFamilyKeyDetails key_details;
- struct TALER_MerchantContractTokenAuthority authority;
+ struct TALER_MerchantContractTokenFamily *family = NULL;
enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Absolute min_start_date = GNUNET_TIME_absolute_subtract (
- start_date.abs_time,
- // TODO: make this configurable. This is the granularity of token
- // expiration dates.
- GNUNET_TIME_UNIT_DAYS
- );
+ // TODO: make this configurable. This is the granularity of token
+ // expiration dates.
+ struct GNUNET_TIME_Relative rounding = GNUNET_TIME_UNIT_YEARS;
+
+ struct GNUNET_TIME_Timestamp min_valid_after = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_round_down (
+ valid_after.abs_time,
+ rounding));
+ struct GNUNET_TIME_Timestamp max_valid_after = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add(
+ min_valid_after.abs_time,
+ rounding));
+
+ for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
+ {
+ if (0 == strcmp (oc->parse_choices.token_families[i].slug,
+ slug))
+ {
+ family = &oc->parse_choices.token_families[i];
+ break;
+ }
+ }
+
+ if (NULL != family)
+ {
+ for (unsigned int i = 0; i<family->keys_len; i++)
+ {
+ if (family->keys[i].valid_after.abs_time.abs_value_us >=
+ min_valid_after.abs_time.abs_value_us
+ && family->keys[i].valid_after.abs_time.abs_value_us <
+ max_valid_after.abs_time.abs_value_us)
+ {
+ /* The token family and a matching key is already added. */
+ return MHD_YES;
+ }
+ }
+ }
+
+ family = GNUNET_new (struct TALER_MerchantContractTokenFamily);
qs = TMH_db->lookup_token_family_key (TMH_db->cls,
oc->hc->instance->settings.id,
slug,
- GNUNET_TIME_absolute_to_timestamp (min_start_date),
- start_date,
+ min_valid_after,
+ max_valid_after,
&key_details);
if (qs <= 0)
@@ -1384,15 +1419,18 @@ set_token_authority (struct OrderContext *oc,
if (NULL == key_details.pub)
{
+ /* There is no matching key for this token family yet. */
+ /* We have to generate one. */
/* If public key is NULL, private key must also be NULL */
GNUNET_assert (NULL == key_details.priv);
+ enum GNUNET_DB_QueryStatus iqs;
struct GNUNET_CRYPTO_BlindSignPrivateKey *priv;
struct GNUNET_CRYPTO_BlindSignPublicKey *pub;
- struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
- struct GNUNET_TIME_Timestamp valid_before = GNUNET_TIME_absolute_to_timestamp(
- GNUNET_TIME_absolute_add (now,
- key_details.token_family.duration));
+ struct GNUNET_TIME_Timestamp valid_before =
+ GNUNET_TIME_absolute_to_timestamp(
+ GNUNET_TIME_absolute_add (min_valid_after.abs_time,
+ key_details.token_family.duration));
GNUNET_CRYPTO_blind_sign_keys_create (&priv,
&pub,
@@ -1407,24 +1445,75 @@ set_token_authority (struct OrderContext *oc,
.private_key = *priv,
};
- qs = TMH_db->insert_token_family_key (TMH_db->cls,
+ // TODO: Check if I have to decref pub and priv here.
+
+ iqs = TMH_db->insert_token_family_key (TMH_db->cls,
slug,
&token_pub,
&token_priv,
- GNUNET_TIME_absolute_to_timestamp (now),
+ min_valid_after,
valid_before);
- authority.token_expiration = valid_before;
- authority.pub = &token_pub;
+ /* private key is no longer needed */
// GNUNET_CRYPTO_blind_sign_priv_decref (&token_priv.private_key);
+
+ if (iqs < 0)
+ {
+ enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ unsigned int http_status = 0;
+
+ switch (iqs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ ec = TALER_EC_GENERIC_DB_STORE_FAILED;
+ break;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* case listed to make compilers happy */
+ GNUNET_assert (0);
+ }
+
+ // GNUNET_CRYPTO_blind_sign_pub_decref (&token_pub.public_key);
+
+ GNUNET_break (0);
+ reply_with_error (oc,
+ http_status,
+ ec,
+ "token_family_slug");
+ return MHD_NO;
+ }
+
+ {
+ struct TALER_MerchantContractTokenFamilyKey key = {
+ .pub = token_pub,
+ .valid_after = min_valid_after,
+ .valid_before = valid_before,
+ };
+
+ GNUNET_array_append (family->keys,
+ family->keys_len,
+ key);
+ }
} else {
- authority.token_expiration = key_details.valid_before;
- authority.pub = key_details.pub;
+ struct TALER_MerchantContractTokenFamilyKey key = {
+ .pub = *key_details.pub,
+ .valid_after = key_details.valid_before,
+ .valid_before = key_details.valid_before,
+ };
+
+ GNUNET_array_append (family->keys,
+ family->keys_len,
+ key);
}
- authority.label = slug;
- authority.description = key_details.token_family.description;
- authority.description_i18n = key_details.token_family.description_i18n;
+ family->slug = slug;
+ family->description = key_details.token_family.description;
+ family->description_i18n = key_details.token_family.description_i18n;
GNUNET_free (key_details.token_family.slug);
GNUNET_free (key_details.token_family.name);
@@ -1434,22 +1523,20 @@ set_token_authority (struct OrderContext *oc,
switch (key_details.token_family.kind) {
case TALER_MERCHANTDB_TFK_Subscription:
- authority.kind = TALER_MCTK_SUBSCRIPTION;
- authority.details.subscription.start_date = key_details.valid_after;
- authority.details.subscription.end_date = key_details.valid_before;
- authority.critical = true;
+ family->kind = TALER_MCTK_SUBSCRIPTION;
+ family->critical = true;
// TODO: Set trusted domains
break;
case TALER_MERCHANTDB_TFK_Discount:
- authority.kind = TALER_MCTK_DISCOUNT;
- authority.critical = false;
+ family->kind = TALER_MCTK_DISCOUNT;
+ family->critical = false;
// TODO: Set expected domains
break;
}
- GNUNET_array_append (oc->parse_choices.authorities,
- oc->parse_choices.authorities_len,
- authority);
+ GNUNET_array_append (oc->parse_choices.token_families,
+ oc->parse_choices.token_families_len,
+ *family);
return MHD_YES;
}
@@ -1514,37 +1601,51 @@ serialize_order (struct OrderContext *oc)
}
}
- for (unsigned int i = 0; i<oc->parse_choices.authorities_len; i++)
+ for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
{
- struct TALER_MerchantContractTokenAuthority *authority = &oc->parse_choices.authorities[i];
+ json_t *keys = json_array();
+ enum GNUNET_CRYPTO_BlindSignatureAlgorithm cipher = GNUNET_CRYPTO_BSA_INVALID;
+ struct TALER_MerchantContractTokenFamily *family = &oc->parse_choices.token_families[i];
+
+ for (unsigned int j = 0; j<family->keys_len; j++)
+ {
+ struct TALER_MerchantContractTokenFamilyKey key = family->keys[j];
+ cipher = key.pub.public_key.cipher;
+
+ json_t *jkey = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_pub",
+ &key.pub.public_key.pub_key_hash),
+ GNUNET_JSON_pack_allow_null(
+ GNUNET_JSON_pack_rsa_public_key ("rsa_pub",
+ key.pub.public_key.details.rsa_public_key)),
+ // GNUNET_JSON_pack_allow_null(
+ // GNUNET_JSON_pack_data_auto ("cs_pub",
+ // &key.pub.public_key.details.cs_public_key)),
+ GNUNET_JSON_pack_timestamp ("valid_after",
+ key.valid_after),
+ GNUNET_JSON_pack_timestamp ("valid_before",
+ key.valid_before)
+ );
+
+ GNUNET_assert (0 == json_array_append_new (keys, jkey));
+ }
- // TODO: Finish spec to clearly define how token families are stored in
- // ContractTerms.
- // Here are some thoughts:
- // - Multiple keys of the same token family can be referrenced in
- // one contract. E.g. exchanging old subscription for new.
- // - TokenAuthority should be renamed to TokenFamily for consistency.
- // - TokenFamilySlug can be used instead of TokenAuthorityLabel, but
- // every token-based in- or ouput needs to have a valid_after date,
- // so it's clear with key is referenced.
- json_t *jauthority = GNUNET_JSON_PACK (
+ json_t *jfamily = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("description",
- authority->description),
+ family->description),
+ GNUNET_JSON_pack_int64 ("cipher",
+ cipher),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_object_incref ("description_i18n",
- authority->description_i18n)),
- GNUNET_JSON_pack_data_auto ("h_pub",
- &authority->pub->public_key.pub_key_hash),
- GNUNET_JSON_pack_data_auto ("pub",
- &authority->pub->public_key.pub_key_hash),
- GNUNET_JSON_pack_timestamp ("token_expiration",
- authority->token_expiration)
+ family->description_i18n)),
+ GNUNET_JSON_pack_array_steal ("keys",
+ keys)
);
GNUNET_assert (0 ==
json_object_set_new (token_types,
- authority->label,
- jauthority));
+ family->slug,
+ jfamily));
}
for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
@@ -2397,9 +2498,9 @@ parse_choices (struct OrderContext *oc)
bool found = false;
- for (unsigned int i = 0; i<oc->parse_choices.authorities_len; i++)
+ for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
{
- if (0 == strcmp (oc->parse_choices.authorities[i].label,
+ if (0 == strcmp (oc->parse_choices.token_families[i].slug,
input.details.token.token_family_slug))
{
found = true;
@@ -2410,7 +2511,7 @@ parse_choices (struct OrderContext *oc)
if (! found)
{
MHD_RESULT res;
- res = set_token_authority (oc,
+ res = set_token_family (oc,
input.details.token.token_family_slug,
input.details.token.valid_after);
@@ -2493,9 +2594,9 @@ parse_choices (struct OrderContext *oc)
bool found = false;
- for (unsigned int i = 0; i<oc->parse_choices.authorities_len; i++)
+ for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
{
- if (0 == strcmp (oc->parse_choices.authorities[i].label,
+ if (0 == strcmp (oc->parse_choices.token_families[i].slug,
output.details.token.token_family_slug))
{
found = true;
@@ -2506,7 +2607,7 @@ parse_choices (struct OrderContext *oc)
if (! found)
{
MHD_RESULT res;
- res = set_token_authority (oc,
+ res = set_token_family (oc,
output.details.token.token_family_slug,
output.details.token.valid_after);
diff --git a/src/backenddb/pg_lookup_token_family_key.c b/src/backenddb/pg_lookup_token_family_key.c
index f1fa75f3..035e26b0 100644
--- a/src/backenddb/pg_lookup_token_family_key.c
+++ b/src/backenddb/pg_lookup_token_family_key.c
@@ -129,7 +129,7 @@ TMH_PG_lookup_token_family_key (void *cls,
" LEFT JOIN merchant_token_family_keys"
" ON merchant_token_families.token_family_serial = merchant_token_family_keys.token_family_serial"
" AND merchant_token_family_keys.valid_after >= $3"
- " AND merchant_token_family_keys.valid_after <= $4"
+ " AND merchant_token_family_keys.valid_after < $4"
" JOIN merchant_instances"
" USING (merchant_serial)"
" WHERE merchant_instances.merchant_id=$1"
diff --git a/src/backenddb/pg_lookup_token_family_key.h b/src/backenddb/pg_lookup_token_family_key.h
index aa7335cf..fd88887c 100644
--- a/src/backenddb/pg_lookup_token_family_key.h
+++ b/src/backenddb/pg_lookup_token_family_key.h
@@ -27,13 +27,14 @@
#include "taler_merchantdb_plugin.h"
/**
- * Lookup details about a particular token family key.
+ * Lookup details about a particular token family key. The valid_after field
+ * of the key has to be >= @e min_valid_after and < @e max_valid_after.
*
* @param cls closure
* @param instance_id instance to lookup token family key for
* @param token_family_slug slug of token family to lookup
* @param min_valid_after lower bound of the start of the key validation period
- * @param max_valid_after upper bound of the start of the key validation period
+ * @param max_valid_after strict upper bound of the start of the key validation period
* @param[out] details set to the token family key details on success, can be NULL
* (in that case we only want to check if the token family key exists)
* @return database result code