Compare commits

..

No commits in common. "31f74059e0d710254397688aabc201b230ef27da" and "140a54ed0811e73b027755761ffe39c328a15ca8" have entirely different histories.

6 changed files with 243 additions and 460 deletions

View File

@ -1726,13 +1726,12 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
* @a recoup and @a denoms. * @a recoup and @a denoms.
* *
* @param[in,out] ksh key state handle we build @a krd for * @param[in,out] ksh key state handle we build @a krd for
* @param[in] denom_keys_hash hash over all the denomination keys in @a denoms * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms
* @param last_cpd timestamp to use * @param last_cpd timestamp to use
* @param signkeys list of sign keys to return * @param signkeys list of sign keys to return
* @param recoup list of revoked keys to return * @param recoup list of revoked keys to return
* @param denoms list of denominations to return * @param denoms list of denominations to return
* @param grouped_denominations list of grouped denominations to return * @param grouped_denominations list of grouped denominations to return
* @param[in] h_grouped XOR of all hashes in @a grouped_demoninations
* @return #GNUNET_OK on success * @return #GNUNET_OK on success
*/ */
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
@ -1742,14 +1741,11 @@ create_krd (struct TEH_KeyStateHandle *ksh,
json_t *signkeys, json_t *signkeys,
json_t *recoup, json_t *recoup,
json_t *denoms, json_t *denoms,
json_t *grouped_denominations, json_t *grouped_denominations)
const struct GNUNET_HashCode *h_grouped)
{ {
struct KeysResponseData krd; struct KeysResponseData krd;
struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig; struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_ExchangePublicKeyP grouped_exchange_pub;
struct TALER_ExchangeSignatureP grouped_exchange_sig;
json_t *keys; json_t *keys;
GNUNET_assert (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time)); GNUNET_assert (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time));
@ -1757,13 +1753,11 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_assert (NULL != recoup); GNUNET_assert (NULL != recoup);
GNUNET_assert (NULL != denoms); GNUNET_assert (NULL != denoms);
GNUNET_assert (NULL != grouped_denominations); GNUNET_assert (NULL != grouped_denominations);
GNUNET_assert (NULL != h_grouped);
GNUNET_assert (NULL != ksh->auditors); GNUNET_assert (NULL != ksh->auditors);
GNUNET_assert (NULL != TEH_currency); GNUNET_assert (NULL != TEH_currency);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating /keys at cherry pick date %s\n", "Creating /keys at cherry pick date %s\n",
GNUNET_TIME_timestamp2s (last_cpd)); GNUNET_TIME_timestamp2s (last_cpd));
/* Sign hash over denomination keys */ /* Sign hash over denomination keys */
{ {
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
@ -1785,33 +1779,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
} }
} }
/* Sign grouped hash */
{
enum TALER_ErrorCode ec;
if (TALER_EC_NONE !=
(ec =
TALER_exchange_online_key_set_sign (
&TEH_keys_exchange_sign2_,
ksh,
last_cpd,
h_grouped,
&grouped_exchange_pub,
&grouped_exchange_sig)))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Could not create key response data: cannot sign grouped hash (%s)\n",
TALER_ErrorCode_get_hint (ec));
return GNUNET_SYSERR;
}
}
/* both public keys really must be the same */
GNUNET_assert (0 ==
memcmp (&grouped_exchange_pub,
&exchange_pub,
sizeof(exchange_pub)));
{ {
const struct SigningKey *sk; const struct SigningKey *sk;
@ -1848,9 +1815,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_JSON_pack_data_auto ("eddsa_pub", GNUNET_JSON_pack_data_auto ("eddsa_pub",
&exchange_pub), &exchange_pub),
GNUNET_JSON_pack_data_auto ("eddsa_sig", GNUNET_JSON_pack_data_auto ("eddsa_sig",
&exchange_sig), &exchange_sig));
GNUNET_JSON_pack_data_auto ("denominations_sig",
&grouped_exchange_sig));
GNUNET_assert (NULL != keys); GNUNET_assert (NULL != keys);
/* Set wallet limit if KYC is configured */ /* Set wallet limit if KYC is configured */
@ -2033,7 +1998,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
struct GNUNET_TIME_Timestamp last_cpd; struct GNUNET_TIME_Timestamp last_cpd;
struct GNUNET_CONTAINER_Heap *heap; struct GNUNET_CONTAINER_Heap *heap;
struct GNUNET_HashContext *hash_context = NULL; struct GNUNET_HashContext *hash_context = NULL;
struct GNUNET_HashCode grouped_hash_xor = {0};
sctx.signkeys = json_array (); sctx.signkeys = json_array ();
GNUNET_assert (NULL != sctx.signkeys); GNUNET_assert (NULL != sctx.signkeys);
@ -2079,11 +2043,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
/* groupData is the value we store for each group meta-data */ /* groupData is the value we store for each group meta-data */
struct groupData struct groupData
{ {
/* The json blob with the group meta-data and list of denominations */ json_t *json; /* The json blob with the group meta-data and list of denominations */
json_t *json; struct GNUNET_HashContext *hash_context; /* hash over all denominations in that group */
/* xor of all hashes of denominations in that group */
struct GNUNET_HashCode hash_xor;
}; };
/* heap = min heap, sorted by start time */ /* heap = min heap, sorted by start time */
@ -2104,7 +2065,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_CRYPTO_hash_context_finish ( GNUNET_CRYPTO_hash_context_finish (
GNUNET_CRYPTO_hash_context_copy (hash_context), GNUNET_CRYPTO_hash_context_copy (hash_context),
&hc); &hc);
if (GNUNET_OK != if (GNUNET_OK !=
create_krd (ksh, create_krd (ksh,
&hc, &hc,
@ -2112,9 +2072,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
sctx.signkeys, sctx.signkeys,
recoup, recoup,
denoms, denoms,
grouped_denominations, grouped_denominations))
&grouped_hash_xor))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n", "Failed to generate key response data for %s\n",
@ -2181,14 +2139,21 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
json_t *list; json_t *list;
json_t *entry; json_t *entry;
struct GNUNET_HashCode key; struct GNUNET_HashCode key;
struct TALER_DenominationGroup meta = {
/* Find the group/JSON-blob for the key */
struct
{
enum TALER_DenominationCipher cipher;
struct TALER_Amount value;
struct TALER_DenomFeeSet fees;
struct TALER_AgeMask age_mask;
} meta = {
.cipher = dk->denom_pub.cipher, .cipher = dk->denom_pub.cipher,
.value = dk->meta.value, .value = dk->meta.value,
.fees = dk->meta.fees, .fees = dk->meta.fees,
.age_mask = dk->meta.age_mask, .age_mask = dk->meta.age_mask,
}; };
/* Search the group/JSON-blob for the key */
GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key); GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key);
group = group =
@ -2203,15 +2168,15 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
char *cipher; char *cipher;
group = GNUNET_new (struct groupData); group = GNUNET_new (struct groupData);
memset (group, 0, sizeof(*group)); group->hash_context = GNUNET_CRYPTO_hash_context_start ();
switch (meta.cipher) switch (meta.cipher)
{ {
case TALER_DENOMINATION_RSA: case TALER_DENOMINATION_RSA:
cipher = age_restricted ? "RSA+age_restricted": "RSA"; cipher = age_restricted ? "RSA+age_restriction": "RSA";
break; break;
case TALER_DENOMINATION_CS: case TALER_DENOMINATION_CS:
cipher = age_restricted ? "CS+age_restricted": "CS"; cipher = age_restricted ? "CS+age_restriction": "CS";
break; break;
default: default:
GNUNET_assert (false); GNUNET_assert (false);
@ -2225,9 +2190,10 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
if (age_restricted) if (age_restricted)
{ {
char *mask = TALER_age_mask_to_string (&meta.age_mask);
int r = json_object_set (group->json, int r = json_object_set (group->json,
"age_mask", "age_mask",
json_integer (meta.age_mask.bits)); json_string (mask));
GNUNET_assert (0 == r); GNUNET_assert (0 == r);
} }
@ -2286,11 +2252,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_assert (NULL != entry); GNUNET_assert (NULL != entry);
} }
// Build up the running xor of all hashes of the denominations in this // Build up the running hash of all denominations in this group
// group //
GNUNET_CRYPTO_hash_xor (&dk->h_denom_pub.hash, // TODO: FIXME-oec: this is cipher and age_restriction dependend?!
&group->hash_xor, GNUNET_CRYPTO_hash_context_read (group->hash_context,
&group->hash_xor); &dk->h_denom_pub,
sizeof (struct GNUNET_HashCode));
// Finally, add the denomination to the list of denominations in this // Finally, add the denomination to the list of denominations in this
// group // group
@ -2300,29 +2267,37 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_assert (0 == GNUNET_assert (0 ==
json_array_append_new (list, entry)); json_array_append_new (list, entry));
} }
} /* loop over heap ends */ }
// Create the JSON-array of grouped denominations // Create the JSON-array of grouped denominations
if (0 < if (0 <
GNUNET_CONTAINER_multihashmap_size (denominations_by_group)) GNUNET_CONTAINER_multihashmap_size (denominations_by_group))
{ {
struct GNUNET_CONTAINER_MultiHashMapIterator *iter; struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
struct GNUNET_HashCode all_hashcode;
struct GNUNET_HashContext *all_hash_ctx;
struct groupData *group = NULL; struct groupData *group = NULL;
all_hash_ctx =
GNUNET_CRYPTO_hash_context_start ();
iter = iter =
GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group); GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group);
while (GNUNET_OK == while (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_iterator_next (iter, GNUNET_CONTAINER_multihashmap_iterator_next (iter, NULL, (const
NULL, void **)
(const &group))
void **) &group))
{ {
struct GNUNET_HashCode hc; struct GNUNET_HashCode hc;
GNUNET_CRYPTO_hash_xor (&group->hash_xor, GNUNET_CRYPTO_hash_context_finish (
&grouped_hash_xor, group->hash_context,
&grouped_hash_xor); &hc);
GNUNET_CRYPTO_hash_context_read (all_hash_ctx,
&hc,
sizeof (struct GNUNET_HashCode));
GNUNET_assert (0 == GNUNET_assert (0 ==
json_object_set ( json_object_set (
@ -2342,6 +2317,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter); GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group); GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
GNUNET_CRYPTO_hash_context_finish (
all_hash_ctx,
&all_hashcode);
// FIXME-oec: TODO:
// sign all_hashcode and add the signature to the /keys response
} }
} }
@ -2352,6 +2333,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_CRYPTO_hash_context_finish (hash_context, GNUNET_CRYPTO_hash_context_finish (hash_context,
&hc); &hc);
if (GNUNET_OK != if (GNUNET_OK !=
create_krd (ksh, create_krd (ksh,
&hc, &hc,
@ -2359,8 +2341,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
sctx.signkeys, sctx.signkeys,
recoup, recoup,
denoms, denoms,
grouped_denominations, grouped_denominations))
&grouped_hash_xor))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n", "Failed to generate key response data for %s\n",

View File

@ -146,17 +146,17 @@ TALER_age_mask_to_string (
*/ */
void void
age_restriction_disable ( age_restriction_disable (
struct TALER_Extension *ext) struct TALER_Extension *this)
{ {
if (NULL == ext) if (NULL == this)
return; return;
ext->config = NULL; this->config = NULL;
if (NULL != ext->config_json) if (NULL != this->config_json)
{ {
json_decref (ext->config_json); json_decref (this->config_json);
ext->config_json = NULL; this->config_json = NULL;
} }
TE_age_restriction_config.mask.bits = 0; TE_age_restriction_config.mask.bits = 0;
@ -174,7 +174,7 @@ age_restriction_disable (
*/ */
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
age_restriction_load_taler_config ( age_restriction_load_taler_config (
struct TALER_Extension *ext, struct TALER_Extension *this,
const struct GNUNET_CONFIGURATION_Handle *cfg) const struct GNUNET_CONFIGURATION_Handle *cfg)
{ {
char *groups = NULL; char *groups = NULL;
@ -192,8 +192,8 @@ age_restriction_load_taler_config (
"ENABLED"))) "ENABLED")))
{ {
/* Age restriction is not enabled */ /* Age restriction is not enabled */
ext->config = NULL; this->config = NULL;
ext->config_json = NULL; this->config_json = NULL;
return GNUNET_OK; return GNUNET_OK;
} }
@ -228,10 +228,10 @@ age_restriction_load_taler_config (
__builtin_popcount (mask.bits) - 1); __builtin_popcount (mask.bits) - 1);
TE_age_restriction_config.mask.bits = mask.bits; TE_age_restriction_config.mask.bits = mask.bits;
TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */ TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
ext->config = &TE_age_restriction_config; this->config = &TE_age_restriction_config;
/* Note: we do now have TE_age_restriction_config set, however /* Note: we do now have TE_age_restriction_config set, however
* ext->config_json is NOT set, i.e. the extension is not yet active! For * this->config_json is NOT set, i.e. the extension is not yet active! For
* age restriction to become active, load_json_config must have been * age restriction to become active, load_json_config must have been
* called. */ * called. */
} }
@ -244,12 +244,12 @@ age_restriction_load_taler_config (
/** /**
* @brief implements the TALER_Extension.load_json_config interface. * @brief implements the TALER_Extension.load_json_config interface.
* @param ext if NULL, only tests the configuration * @param this if NULL, only tests the configuration
* @param config the configuration as json * @param config the configuration as json
*/ */
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
age_restriction_load_json_config ( age_restriction_load_json_config (
struct TALER_Extension *ext, struct TALER_Extension *this,
json_t *jconfig) json_t *jconfig)
{ {
struct TALER_AgeMask mask = {0}; struct TALER_AgeMask mask = {0};
@ -260,10 +260,10 @@ age_restriction_load_json_config (
return ret; return ret;
/* only testing the parser */ /* only testing the parser */
if (ext == NULL) if (this == NULL)
return GNUNET_OK; return GNUNET_OK;
if (TALER_Extension_AgeRestriction != ext->type) if (TALER_Extension_AgeRestriction != this->type)
return GNUNET_SYSERR; return GNUNET_SYSERR;
TE_age_restriction_config.mask.bits = mask.bits; TE_age_restriction_config.mask.bits = mask.bits;
@ -278,12 +278,12 @@ age_restriction_load_json_config (
TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1;
} }
ext->config = &TE_age_restriction_config; this->config = &TE_age_restriction_config;
if (NULL != ext->config_json) if (NULL != this->config_json)
json_decref (ext->config_json); json_decref (this->config_json);
ext->config_json = jconfig; this->config_json = jconfig;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"loaded new age restriction config with age groups: %s\n", "loaded new age restriction config with age groups: %s\n",
@ -295,22 +295,22 @@ age_restriction_load_json_config (
/** /**
* @brief implements the TALER_Extension.load_json_config interface. * @brief implements the TALER_Extension.load_json_config interface.
* @param ext if NULL, only tests the configuration * @param this if NULL, only tests the configuration
* @param config the configuration as json * @param config the configuration as json
*/ */
json_t * json_t *
age_restriction_config_to_json ( age_restriction_config_to_json (
const struct TALER_Extension *ext) const struct TALER_Extension *this)
{ {
char *mask_str; char *mask_str;
json_t *conf; json_t *conf;
GNUNET_assert (NULL != ext); GNUNET_assert (NULL != this);
GNUNET_assert (NULL != ext->config); GNUNET_assert (NULL != this->config);
if (NULL != ext->config_json) if (NULL != this->config_json)
{ {
return json_copy (ext->config_json); return json_copy (this->config_json);
} }
mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask); mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask);
@ -319,8 +319,8 @@ age_restriction_config_to_json (
); );
return GNUNET_JSON_PACK ( return GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", ext->critical), GNUNET_JSON_pack_bool ("critical", this->critical),
GNUNET_JSON_pack_string ("version", ext->version), GNUNET_JSON_pack_string ("version", this->version),
GNUNET_JSON_pack_object_steal ("config", conf) GNUNET_JSON_pack_object_steal ("config", conf)
); );
} }

View File

@ -359,36 +359,6 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
TALER_JSON_pack_amount ("account_fee", &(gfs)->account), \ TALER_JSON_pack_amount ("account_fee", &(gfs)->account), \
TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse) TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse)
/**
* Group of Denominations. These are the common fields of an array of
* denominations.
*
* The corresponding JSON-blob will also contain an array of particular
* denominations with only the timestamps, cipher-specific public key and the
* master signature.
*
**/
struct TALER_DenominationGroup
{
/* currency must be set prior to calling TALER_JSON_spec_denomination_group */
const char *currency;
enum TALER_DenominationCipher cipher;
struct TALER_Amount value;
struct TALER_DenomFeeSet fees;
struct TALER_AgeMask age_mask;
};
/**
* Generate a parser for a group of denominations.
* NOTE: group.currency MUST have been set prior to calling this function.
*
* @param field name of the field, maybe NULL
* @param[out] group denomination group information
* @return corresponding field spec
*/
struct GNUNET_JSON_Specification
TALER_JSON_spec_denomination_group (const char *field,
struct TALER_DenominationGroup *group);
/** /**
* Generate line in parser specification for denomination public key. * Generate line in parser specification for denomination public key.
@ -401,20 +371,6 @@ struct GNUNET_JSON_Specification
TALER_JSON_spec_denom_pub (const char *field, TALER_JSON_spec_denom_pub (const char *field,
struct TALER_DenominationPublicKey *pk); struct TALER_DenominationPublicKey *pk);
/**
* Generate a parser specification for a denomination public key of a given
* cipher.
*
* @param field name of the field
* @parm cipher which cipher type to parse for
* @param[out] pk key to fill
* @return corresponding field spec
*/
struct GNUNET_JSON_Specification
TALER_JSON_spec_denom_pub_cipher (const char *field,
const enum TALER_DenominationCipher cipher,
struct TALER_DenominationPublicKey *pk);
/** /**
* Generate line in parser specification for denomination signature. * Generate line in parser specification for denomination signature.

View File

@ -35,15 +35,11 @@
static enum TALER_DenominationCipher static enum TALER_DenominationCipher
string_to_cipher (const char *cipher_s) string_to_cipher (const char *cipher_s)
{ {
if ((0 == strcasecmp (cipher_s, if (0 == strcasecmp (cipher_s,
"RSA")) || "RSA"))
(0 == strcasecmp (cipher_s,
"RSA+age_restricted")))
return TALER_DENOMINATION_RSA; return TALER_DENOMINATION_RSA;
if ((0 == strcasecmp (cipher_s, if (0 == strcasecmp (cipher_s,
"CS")) || "CS"))
(0 == strcasecmp (cipher_s,
"CS+age_restricted")))
return TALER_DENOMINATION_CS; return TALER_DENOMINATION_CS;
return TALER_DENOMINATION_INVALID; return TALER_DENOMINATION_INVALID;
} }
@ -243,84 +239,6 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
} }
static enum GNUNET_GenericReturnValue
parse_denomination_group (void *cls,
json_t *root,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_DenominationGroup *group = spec->ptr;
const char *cipher;
bool age_mask_missing = false;
bool has_age_restricted_suffix = false;
struct GNUNET_JSON_Specification gspec[] = {
GNUNET_JSON_spec_string ("cipher",
&cipher),
TALER_JSON_spec_amount ("value",
group->currency,
&group->value),
TALER_JSON_SPEC_DENOM_FEES ("fee",
group->currency,
&group->fees),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_uint32 ("age_mask",
&group->age_mask.bits),
&age_mask_missing),
GNUNET_JSON_spec_end ()
};
const char *emsg;
unsigned int eline;
if (GNUNET_OK !=
GNUNET_JSON_parse (root,
gspec,
&emsg,
&eline))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
group->cipher = string_to_cipher (cipher);
if (TALER_DENOMINATION_INVALID == group->cipher)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
/* age_mask and suffix must be consistent */
has_age_restricted_suffix =
(NULL != strstr (cipher, "+age_restricted"));
if (has_age_restricted_suffix && age_mask_missing)
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (age_mask_missing)
group->age_mask.bits = 0;
return GNUNET_OK;
}
struct GNUNET_JSON_Specification
TALER_JSON_spec_denomination_group (const char *name,
struct TALER_DenominationGroup *group)
{
struct GNUNET_JSON_Specification ret = {
.parser = &parse_denomination_group,
.cleaner = NULL,
.field = name,
.ptr = group,
.ptr_size = sizeof(*group),
.size_ptr = NULL,
};
return ret;
}
/** /**
* Parse given JSON object to an encrypted contract. * Parse given JSON object to an encrypted contract.
* *
@ -412,14 +330,11 @@ parse_denom_pub (void *cls,
{ {
struct TALER_DenominationPublicKey *denom_pub = spec->ptr; struct TALER_DenominationPublicKey *denom_pub = spec->ptr;
const char *cipher; const char *cipher;
bool age_mask_missing = false;
struct GNUNET_JSON_Specification dspec[] = { struct GNUNET_JSON_Specification dspec[] = {
GNUNET_JSON_spec_string ("cipher", GNUNET_JSON_spec_string ("cipher",
&cipher), &cipher),
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_uint32 ("age_mask",
GNUNET_JSON_spec_uint32 ("age_mask", &denom_pub->age_mask.bits),
&denom_pub->age_mask.bits),
&age_mask_missing),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
const char *emsg; const char *emsg;
@ -435,10 +350,6 @@ parse_denom_pub (void *cls,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (age_mask_missing)
denom_pub->age_mask.bits = 0;
denom_pub->cipher = string_to_cipher (cipher); denom_pub->cipher = string_to_cipher (cipher);
switch (denom_pub->cipher) switch (denom_pub->cipher)
{ {
@ -522,93 +433,6 @@ TALER_JSON_spec_denom_pub (const char *field,
} }
/**
* Parse given JSON object partially into a denomination public key.
*
* Depending on the cipher in cls, it parses the corresponding public key type.
*
* @param cls closure, enum TALER_DenominationCipher
* @param cipher cipher to parse for
* @param root the json object representing data
* @param[out] spec where to write the data
* @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
*/
static enum GNUNET_GenericReturnValue
parse_denom_pub_cipher (void *cls,
json_t *root,
struct GNUNET_JSON_Specification *spec)
{
struct TALER_DenominationPublicKey *denom_pub = spec->ptr;
enum TALER_DenominationCipher cipher = (enum TALER_DenominationCipher) cls;
const char *emsg;
unsigned int eline;
switch (cipher)
{
case TALER_DENOMINATION_RSA:
{
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_rsa_public_key (
"rsa_pub",
&denom_pub->details.rsa_public_key),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (root,
ispec,
&emsg,
&eline))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
case TALER_DENOMINATION_CS:
{
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_fixed ("cs_pub",
&denom_pub->details.cs_public_key,
sizeof (denom_pub->details.cs_public_key)),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (root,
ispec,
&emsg,
&eline))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
default:
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
}
struct GNUNET_JSON_Specification
TALER_JSON_spec_denom_pub_cipher (const char *field,
enum TALER_DenominationCipher cipher,
struct TALER_DenominationPublicKey *pk)
{
struct GNUNET_JSON_Specification ret = {
.parser = &parse_denom_pub_cipher,
.cleaner = &clean_denom_pub,
.field = field,
.cls = (void *) cipher,
.ptr = pk
};
return ret;
}
/** /**
* Parse given JSON object to denomination signature. * Parse given JSON object to denomination signature.
* *

View File

@ -79,38 +79,35 @@ TALER_JSON_pack_denom_pub (
struct GNUNET_JSON_PackSpec ps = { struct GNUNET_JSON_PackSpec ps = {
.field_name = name, .field_name = name,
}; };
struct GNUNET_JSON_PackSpec mask_or_end;
if (NULL == pk) if (NULL == pk)
return ps; return ps;
mask_or_end = (0 != pk->age_mask.bits) ?
GNUNET_JSON_pack_uint64 ("age_mask", pk->age_mask.bits) :
GNUNET_JSON_pack_end_ ();
switch (pk->cipher) switch (pk->cipher)
{ {
case TALER_DENOMINATION_RSA: case TALER_DENOMINATION_RSA:
ps.object ps.object
= GNUNET_JSON_PACK ( = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("cipher", "RSA"), GNUNET_JSON_pack_string ("cipher",
"RSA"),
GNUNET_JSON_pack_uint64 ("age_mask",
pk->age_mask.bits),
GNUNET_JSON_pack_rsa_public_key ("rsa_public_key", GNUNET_JSON_pack_rsa_public_key ("rsa_public_key",
pk->details.rsa_public_key), pk->details.rsa_public_key));
mask_or_end);
break; break;
case TALER_DENOMINATION_CS: case TALER_DENOMINATION_CS:
ps.object ps.object
= GNUNET_JSON_PACK ( = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("cipher", "CS"), GNUNET_JSON_pack_string ("cipher",
"CS"),
GNUNET_JSON_pack_uint64 ("age_mask",
pk->age_mask.bits),
GNUNET_JSON_pack_data_varsize ("cs_public_key", GNUNET_JSON_pack_data_varsize ("cs_public_key",
&pk->details.cs_public_key, &pk->details.cs_public_key,
sizeof (pk->details.cs_public_key)), sizeof (pk->details.cs_public_key)));
mask_or_end);
break; break;
default: default:
GNUNET_assert (0); GNUNET_assert (0);
} }
return ps; return ps;
} }

View File

@ -303,31 +303,24 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
/** /**
* Parse a exchange's denomination key encoded in JSON partially. * Parse a exchange's denomination key encoded in JSON.
*
* Only the values for master_sig, timestamps and the cipher-specific public
* key are parsed. All other fields (fees, age_mask, value) MUST have been set
* prior to calling this function, otherwise the signature verification
* performed within this function will fail.
* *
* @param currency expected currency of all fees * @param currency expected currency of all fees
* @param[out] denom_key where to return the result * @param[out] denom_key where to return the result
* @param cipher cipher type to parse
* @param check_sigs should we check signatures? * @param check_sigs should we check signatures?
* @param[in] denom_key_obj json to parse * @param[in] denom_key_obj json to parse
* @param master_key master key to use to verify signature * @param master_key master key to use to verify signature
* @param hash_xor where to accumulate data for signature verification via XOR * @param hash_context where to accumulate data for signature verification
* @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
* invalid or the json malformed. * invalid or the json malformed.
*/ */
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
parse_json_denomkey_partially (const char *currency, parse_json_denomkey (const char *currency,
struct TALER_EXCHANGE_DenomPublicKey *denom_key, struct TALER_EXCHANGE_DenomPublicKey *denom_key,
enum TALER_DenominationCipher cipher, bool check_sigs,
bool check_sigs, json_t *denom_key_obj,
json_t *denom_key_obj, struct TALER_MasterPublicKeyP *master_key,
struct TALER_MasterPublicKeyP *master_key, struct GNUNET_HashContext *hash_context)
struct GNUNET_HashCode *hash_xor)
{ {
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig", GNUNET_JSON_spec_fixed_auto ("master_sig",
@ -340,9 +333,14 @@ parse_json_denomkey_partially (const char *currency,
&denom_key->valid_from), &denom_key->valid_from),
GNUNET_JSON_spec_timestamp ("stamp_expire_legal", GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
&denom_key->expire_legal), &denom_key->expire_legal),
TALER_JSON_spec_denom_pub_cipher (NULL, TALER_JSON_spec_amount ("value",
cipher, currency,
&denom_key->key), &denom_key->value),
TALER_JSON_SPEC_DENOM_FEES ("fee",
currency,
&denom_key->fees),
TALER_JSON_spec_denom_pub ("denom_pub",
&denom_key->key),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -356,11 +354,10 @@ parse_json_denomkey_partially (const char *currency,
} }
TALER_denom_pub_hash (&denom_key->key, TALER_denom_pub_hash (&denom_key->key,
&denom_key->h_key); &denom_key->h_key);
if (NULL != hash_xor) if (NULL != hash_context)
GNUNET_CRYPTO_hash_xor (&denom_key->h_key.hash, GNUNET_CRYPTO_hash_context_read (hash_context,
hash_xor, &denom_key->h_key,
hash_xor); sizeof (struct GNUNET_HashCode));
if (! check_sigs) if (! check_sigs)
return GNUNET_OK; return GNUNET_OK;
EXITIF (GNUNET_SYSERR == EXITIF (GNUNET_SYSERR ==
@ -732,13 +729,15 @@ decode_keys_json (const json_t *resp_obj,
struct TALER_EXCHANGE_Keys *key_data, struct TALER_EXCHANGE_Keys *key_data,
enum TALER_EXCHANGE_VersionCompatibility *vc) enum TALER_EXCHANGE_VersionCompatibility *vc)
{ {
struct TALER_ExchangeSignatureP denominations_sig; struct TALER_ExchangeSignatureP sig;
struct GNUNET_HashCode hash_xor = {0}; struct GNUNET_HashContext *hash_context = NULL;
struct GNUNET_HashContext *hash_context_restricted = NULL;
bool have_age_restricted_denom = false;
struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangePublicKeyP pub;
const char *currency; const char *currency;
struct GNUNET_JSON_Specification mspec[] = { struct GNUNET_JSON_Specification mspec[] = {
GNUNET_JSON_spec_fixed_auto ("denominations_sig", GNUNET_JSON_spec_fixed_auto ("eddsa_sig",
&denominations_sig), &sig),
GNUNET_JSON_spec_fixed_auto ("eddsa_pub", GNUNET_JSON_spec_fixed_auto ("eddsa_pub",
&pub), &pub),
GNUNET_JSON_spec_fixed_auto ("master_public_key", GNUNET_JSON_spec_fixed_auto ("master_public_key",
@ -761,7 +760,7 @@ decode_keys_json (const json_t *resp_obj,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
#if 1 /* DEBUG */ #if DEBUG
json_dumpf (resp_obj, json_dumpf (resp_obj,
stderr, stderr,
JSON_INDENT (2)); JSON_INDENT (2));
@ -830,6 +829,13 @@ decode_keys_json (const json_t *resp_obj,
} }
} }
/* parse the master public key and issue date of the response */
if (check_sig)
{
hash_context = GNUNET_CRYPTO_hash_context_start ();
hash_context_restricted = GNUNET_CRYPTO_hash_context_start ();
}
/* parse the global fees */ /* parse the global fees */
{ {
json_t *global_fees; json_t *global_fees;
@ -927,101 +933,93 @@ decode_keys_json (const json_t *resp_obj,
/* 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 *denominations_by_group; /* The denominations can be in "denoms" and (optionally) in
json_t *group_obj; * "age_restricted_denoms"
unsigned int group_idx; */
struct
denominations_by_group = {
json_object_get ( char *name;
resp_obj, struct GNUNET_HashContext *hc;
"denominations"); bool is_optional_age_restriction;
}
EXITIF (JSON_ARRAY != hive[2] = {
json_typeof (denominations_by_group));
json_array_foreach (denominations_by_group, group_idx, group_obj) {
/* First, parse { cipher, fees, value, age_mask } of the current group */
struct TALER_DenominationGroup group = {
.currency = currency
};
struct GNUNET_JSON_Specification group_spec[] = {
TALER_JSON_spec_denomination_group (NULL, &group),
GNUNET_JSON_spec_end ()
};
EXITIF (GNUNET_SYSERR ==
GNUNET_JSON_parse (group_obj,
group_spec,
NULL,
NULL));
/* Now, parse the individual denominations */
{ {
json_t *denom_keys_array; "denoms",
json_t *denom_key_obj; hash_context,
unsigned int index; false
denom_keys_array = json_object_get (group_obj, "denoms"); },
EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); {
"age_restricted_denoms",
json_array_foreach (denom_keys_array, index, denom_key_obj) { hash_context_restricted,
struct TALER_EXCHANGE_DenomPublicKey dk = {0}; true
bool found = false; }
memset (&dk, 0, sizeof (dk));
/* Set the common fields from the group for this particular
* denomination. Required to make the validity check inside
* parse_json_denomkey_partially pass */
dk.key.cipher = group.cipher;
dk.value = group.value;
dk.fees = group.fees;
dk.key.age_mask = group.age_mask;
EXITIF (GNUNET_SYSERR ==
parse_json_denomkey_partially (key_data->currency,
&dk,
group.cipher,
check_sig,
denom_key_obj,
&key_data->master_pub,
check_sig ? &hash_xor: NULL));
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);
if (NULL == denom_keys_array)
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;
/* mark that we have at least one age restricted denomination, needed
* for the hash calculation and signature verification below. */
have_age_restricted_denom |= hive[s].is_optional_age_restriction;
memset (&dk,
0,
sizeof (dk));
EXITIF (GNUNET_SYSERR ==
parse_json_denomkey (key_data->currency,
&dk,
check_sig,
denom_key_obj,
&key_data->master_pub,
hive[s].hc));
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 */
@ -1141,6 +1139,30 @@ decode_keys_json (const json_t *resp_obj,
if (check_sig) if (check_sig)
{ {
struct GNUNET_HashCode hc;
/* If we had any age restricted denominations, add their hash to the end of
* the normal denominations. */
if (have_age_restricted_denom)
{
struct GNUNET_HashCode hcr;
GNUNET_CRYPTO_hash_context_finish (hash_context_restricted,
&hcr);
hash_context_restricted = NULL;
GNUNET_CRYPTO_hash_context_read (hash_context,
&hcr,
sizeof(struct GNUNET_HashCode));
}
else
{
GNUNET_CRYPTO_hash_context_abort (hash_context_restricted);
hash_context_restricted = NULL;
}
GNUNET_CRYPTO_hash_context_finish (hash_context,
&hc);
hash_context = NULL;
EXITIF (GNUNET_OK != EXITIF (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_data, TALER_EXCHANGE_test_signing_key (key_data,
&pub)); &pub));
@ -1148,15 +1170,18 @@ decode_keys_json (const json_t *resp_obj,
EXITIF (GNUNET_OK != EXITIF (GNUNET_OK !=
TALER_exchange_online_key_set_verify ( TALER_exchange_online_key_set_verify (
key_data->list_issue_date, key_data->list_issue_date,
&hash_xor, &hc,
&pub, &pub,
&denominations_sig)); &sig));
} }
return GNUNET_OK; return GNUNET_OK;
EXITIF_exit: EXITIF_exit:
*vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR; *vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
if (NULL != hash_context)
GNUNET_CRYPTO_hash_context_abort (hash_context);
if (NULL != hash_context_restricted)
GNUNET_CRYPTO_hash_context_abort (hash_context_restricted);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }