From a6260049a0ccbbe01b2970b52727b5f25dad5415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sun, 9 Jan 2022 15:16:18 +0100 Subject: [PATCH 1/2] parse the "age_restricted_denoms" in "/keys" --- src/include/taler_exchange_service.h | 5 + src/lib/exchange_api_handle.c | 133 ++++++++++++++++----------- 2 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 04b731b32..f1fccd70a 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -159,6 +159,11 @@ struct TALER_EXCHANGE_DenomPublicKey * revoked by the exchange. */ bool revoked; + + /** + * Is the denomination age-restricted? + */ + bool age_restricted; }; diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index df501f0b7..8919ae601 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -345,6 +345,7 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key, GNUNET_JSON_spec_end () }; + if (GNUNET_OK != GNUNET_JSON_parse (denom_key_obj, spec, @@ -794,60 +795,86 @@ decode_keys_json (const json_t *resp_obj, /* parse the denomination keys, merging with the possibly EXISTING array as required (/keys cherry picking) */ { - json_t *denom_keys_array; - json_t *denom_key_obj; - unsigned int index; - - EXITIF (NULL == (denom_keys_array = - json_object_get (resp_obj, - "denoms"))); - EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); - - json_array_foreach (denom_keys_array, index, denom_key_obj) { - struct TALER_EXCHANGE_DenomPublicKey dk; - bool found = false; - - memset (&dk, - 0, - sizeof (dk)); - EXITIF (GNUNET_SYSERR == - parse_json_denomkey (&dk, - check_sig, - denom_key_obj, - &key_data->master_pub, - hash_context)); - - for (unsigned int j = 0; - jnum_denom_keys; - j++) - { - if (0 == denoms_cmp (&dk, - &key_data->denom_keys[j])) - { - found = true; - break; - } - } - if (found) - { - /* 0:0:0 did not support /keys cherry picking */ - TALER_LOG_DEBUG ("Skipping denomination key: already know it\n"); - TALER_denom_pub_free (&dk.key); - continue; - } - if (key_data->denom_keys_size == key_data->num_denom_keys) - GNUNET_array_grow (key_data->denom_keys, - key_data->denom_keys_size, - key_data->denom_keys_size * 2 + 2); - key_data->denom_keys[key_data->num_denom_keys++] = dk; - - /* Update "last_denom_issue_date" */ - TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n", - GNUNET_TIME_timestamp2s (dk.valid_from)); - key_data->last_denom_issue_date - = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date, - dk.valid_from); + struct + { + char *name; + bool is_optional_age_restriction; + } section[2] = { + /* The denominations can be in "denoms" or in + * "age_restricted_denoms", the later being optional */ + { "denoms", false }, + { "age_restricted_denoms", true}, }; + + for (size_t s = 0; s < sizeof(section) / sizeof(section[0]); s++) + { + json_t *denom_keys_array; + json_t *denom_key_obj; + unsigned int index; + + denom_keys_array = json_object_get (resp_obj, + section[s].name); + + EXITIF (NULL == denom_keys_array && + ! section[s].is_optional_age_restriction); + + if (NULL == denom_keys_array && + section[s].is_optional_age_restriction) + continue; + + EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); + + json_array_foreach (denom_keys_array, index, denom_key_obj) { + struct TALER_EXCHANGE_DenomPublicKey dk; + bool found = false; + + memset (&dk, + 0, + sizeof (dk)); + EXITIF (GNUNET_SYSERR == + parse_json_denomkey (&dk, + check_sig, + denom_key_obj, + &key_data->master_pub, + hash_context)); + + /* Mark age restriction according where we got this denomination from, + * "denoms" or "age_restricted_denoms" */ + if (section[s].is_optional_age_restriction) + dk.age_restricted = true; + + for (unsigned int j = 0; + jnum_denom_keys; + j++) + { + if (0 == denoms_cmp (&dk, + &key_data->denom_keys[j])) + { + found = true; + break; + } + } + if (found) + { + /* 0:0:0 did not support /keys cherry picking */ + TALER_LOG_DEBUG ("Skipping denomination key: already know it\n"); + TALER_denom_pub_free (&dk.key); + continue; + } + if (key_data->denom_keys_size == key_data->num_denom_keys) + GNUNET_array_grow (key_data->denom_keys, + key_data->denom_keys_size, + key_data->denom_keys_size * 2 + 2); + key_data->denom_keys[key_data->num_denom_keys++] = dk; + + /* Update "last_denom_issue_date" */ + TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n", + GNUNET_TIME_timestamp2s (dk.valid_from)); + key_data->last_denom_issue_date + = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date, + dk.valid_from); + }; + } } /* parse the auditor information */ From 836b692ead326ea7ff69cfadeaca51b92b3e2001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sun, 9 Jan 2022 23:48:09 +0100 Subject: [PATCH 2/2] parse age-restriction info from /keys - parse "age_restriction" extension, extract mask for age groups - parse denoms from "age_restricted_denoms", too, if available --- src/include/taler_exchange_service.h | 5 ++ src/include/taler_extensions.h | 2 +- src/lib/exchange_api_handle.c | 81 +++++++++++++++++++++++----- src/util/extension_age_restriction.c | 2 +- 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index f1fccd70a..6976293cb 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -287,6 +287,11 @@ struct TALER_EXCHANGE_Keys */ struct GNUNET_TIME_Timestamp last_denom_issue_date; + /** + * If age restriction is enabled on the exchange, we get an non-zero age_mask + */ + struct TALER_AgeMask age_mask; + /** * Length of the @e sign_keys array (number of valid entries). */ diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index 199776eb7..243811eb5 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -110,7 +110,7 @@ TALER_extension_get_by_name (const char *name, * @return Error, if age groups were invalid, OK otherwise. */ enum TALER_Extension_ReturnValue -TALER_parse_age_group_string (char *groups, +TALER_parse_age_group_string (const char *groups, struct TALER_AgeMask *mask); /** diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index 8919ae601..ac0e0584f 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -30,6 +30,7 @@ #include "taler_exchange_service.h" #include "taler_auditor_service.h" #include "taler_signatures.h" +#include "taler_extensions.h" #include "exchange_api_handle.h" #include "exchange_api_curl_defaults.h" #include "backoff.h" @@ -762,6 +763,7 @@ decode_keys_json (const json_t *resp_obj, return GNUNET_SYSERR; } } + /* parse the master public key and issue date of the response */ if (check_sig) hash_context = GNUNET_CRYPTO_hash_context_start (); @@ -792,36 +794,89 @@ decode_keys_json (const json_t *resp_obj, } } + /* Parse the supported extension(s): age-restriction. */ + /* TODO: maybe lift this into a FP in TALER_Extension ? */ + { + json_t *age_restriction = json_object_get (resp_obj, + "age_restriction"); + + if (NULL != age_restriction) + { + bool critical; + const char *version; + const char *age_groups; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_bool ("critical", + &critical), + GNUNET_JSON_spec_string ("version", + &version), + GNUNET_JSON_spec_string ("age_groups", + &age_groups), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (age_restriction, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (critical || // do we care? + 0 != strncmp (version, "1", 1) ) /* TODO: better compatibility check */ + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (TALER_Extension_OK != + TALER_parse_age_group_string (age_groups, + &key_data->age_mask)) + { + // TODO: print more specific error? + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } + /* parse the denomination keys, merging with the possibly EXISTING array as required (/keys cherry picking) */ { + /* The denominations can be in "denoms" and (optionally) in + * "age_restricted_denoms" + */ struct - { - char *name; - bool is_optional_age_restriction; - } section[2] = { - /* The denominations can be in "denoms" or in - * "age_restricted_denoms", the later being optional */ - { "denoms", false }, - { "age_restricted_denoms", true}, + { char *name; + bool is_optional_age_restriction;} hive[2] = { + { "denoms", false }, + { "age_restricted_denoms", true }, }; - for (size_t s = 0; s < sizeof(section) / sizeof(section[0]); s++) + for (size_t s = 0; s < sizeof(hive) / sizeof(hive[0]); s++) { json_t *denom_keys_array; json_t *denom_key_obj; unsigned int index; denom_keys_array = json_object_get (resp_obj, - section[s].name); + hive[s].name); EXITIF (NULL == denom_keys_array && - ! section[s].is_optional_age_restriction); + ! hive[s].is_optional_age_restriction); if (NULL == denom_keys_array && - section[s].is_optional_age_restriction) + hive[s].is_optional_age_restriction) continue; + /* if "age_restricted_denoms" exists, age-restriction better be enabled + * (that is: mask non-zero) */ + EXITIF (NULL != denom_keys_array && + hive[s].is_optional_age_restriction && + 0 == key_data->age_mask.mask); + EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); json_array_foreach (denom_keys_array, index, denom_key_obj) { @@ -840,7 +895,7 @@ decode_keys_json (const json_t *resp_obj, /* Mark age restriction according where we got this denomination from, * "denoms" or "age_restricted_denoms" */ - if (section[s].is_optional_age_restriction) + if (hive[s].is_optional_age_restriction) dk.age_restricted = true; for (unsigned int j = 0; diff --git a/src/util/extension_age_restriction.c b/src/util/extension_age_restriction.c index 42a58b2e9..b29a8ca88 100644 --- a/src/util/extension_age_restriction.c +++ b/src/util/extension_age_restriction.c @@ -80,7 +80,7 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg, * @return Error if string was invalid, OK otherwise. */ enum TALER_Extension_ReturnValue -TALER_parse_age_group_string (char *groups, +TALER_parse_age_group_string (const char *groups, struct TALER_AgeMask *mask) { enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS;