diff options
author | Christian Blättler <blatc2@bfh.ch> | 2024-04-18 18:24:52 +0200 |
---|---|---|
committer | Christian Blättler <blatc2@bfh.ch> | 2024-04-18 18:24:52 +0200 |
commit | 4f00d126a95b41ef157734e8e2b11100aab569a2 (patch) | |
tree | fea10173bc06240abe0cfa9149ef7d3776ca605e | |
parent | 4fdbc050d140cb8fa12e8a3e6cfc17afb721a514 (diff) |
adapt to changed spec and fix key handling
-rw-r--r-- | src/backend/taler-merchant-httpd_contract.h | 125 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders.c | 243 | ||||
-rw-r--r-- | src/backenddb/pg_lookup_token_family_key.c | 2 | ||||
-rw-r--r-- | src/backenddb/pg_lookup_token_family_key.h | 5 |
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 |