[age restriction] progress 11/n

Parse age restriction information from "/keys"
- parse "age_restriction" extension, extract mask for age groups
- parse denominations from "age_restricted_denoms", too, if available
This commit is contained in:
Özgür Kesim 2022-01-10 00:04:23 +01:00
parent d91750ca0f
commit e30989c930
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
4 changed files with 147 additions and 55 deletions

View File

@ -159,6 +159,11 @@ struct TALER_EXCHANGE_DenomPublicKey
* revoked by the exchange. * revoked by the exchange.
*/ */
bool revoked; bool revoked;
/**
* Is the denomination age-restricted?
*/
bool age_restricted;
}; };
@ -282,6 +287,11 @@ struct TALER_EXCHANGE_Keys
*/ */
struct GNUNET_TIME_Timestamp last_denom_issue_date; 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). * Length of the @e sign_keys array (number of valid entries).
*/ */

View File

@ -110,7 +110,7 @@ TALER_extension_get_by_name (const char *name,
* @return Error, if age groups were invalid, OK otherwise. * @return Error, if age groups were invalid, OK otherwise.
*/ */
enum TALER_Extension_ReturnValue enum TALER_Extension_ReturnValue
TALER_parse_age_group_string (char *groups, TALER_parse_age_group_string (const char *groups,
struct TALER_AgeMask *mask); struct TALER_AgeMask *mask);
/** /**

View File

@ -30,6 +30,7 @@
#include "taler_exchange_service.h" #include "taler_exchange_service.h"
#include "taler_auditor_service.h" #include "taler_auditor_service.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "taler_extensions.h"
#include "exchange_api_handle.h" #include "exchange_api_handle.h"
#include "exchange_api_curl_defaults.h" #include "exchange_api_curl_defaults.h"
#include "backoff.h" #include "backoff.h"
@ -345,6 +346,7 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (denom_key_obj, GNUNET_JSON_parse (denom_key_obj,
spec, spec,
@ -761,6 +763,7 @@ decode_keys_json (const json_t *resp_obj,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
} }
/* parse the master public key and issue date of the response */ /* parse the master public key and issue date of the response */
if (check_sig) if (check_sig)
hash_context = GNUNET_CRYPTO_hash_context_start (); hash_context = GNUNET_CRYPTO_hash_context_start ();
@ -791,63 +794,142 @@ 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 /* parse the denomination keys, merging with the
possibly EXISTING array as required (/keys cherry picking) */ possibly EXISTING array as required (/keys cherry picking) */
{ {
json_t *denom_keys_array; /* The denominations can be in "denoms" and (optionally) in
json_t *denom_key_obj; * "age_restricted_denoms"
unsigned int index; */
struct
EXITIF (NULL == (denom_keys_array = { char *name;
json_object_get (resp_obj, bool is_optional_age_restriction;} hive[2] = {
"denoms"))); { "denoms", false },
EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); { "age_restricted_denoms", true },
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;
j<key_data->num_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);
}; };
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,
hive[s].name);
EXITIF (NULL == denom_keys_array &&
! hive[s].is_optional_age_restriction);
if (NULL == denom_keys_array &&
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) {
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 (hive[s].is_optional_age_restriction)
dk.age_restricted = true;
for (unsigned int j = 0;
j<key_data->num_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 */ /* parse the auditor information */

View File

@ -80,7 +80,7 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
* @return Error if string was invalid, OK otherwise. * @return Error if string was invalid, OK otherwise.
*/ */
enum TALER_Extension_ReturnValue enum TALER_Extension_ReturnValue
TALER_parse_age_group_string (char *groups, TALER_parse_age_group_string (const char *groups,
struct TALER_AgeMask *mask) struct TALER_AgeMask *mask)
{ {
enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS; enum TALER_Extension_ReturnValue ret = TALER_Extension_ERROR_SYS;