fix alignment issue, ensure we hash over packed structure to avoid non-determinism

This commit is contained in:
Christian Grothoff 2023-07-10 16:34:01 +02:00
parent 66f9a5b5e5
commit 6a483b51ec
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
5 changed files with 186 additions and 130 deletions

View File

@ -2110,8 +2110,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
{ {
struct TEH_DenominationKey *dk; struct TEH_DenominationKey *dk;
struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group; struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group;
/* 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 * The json blob with the group meta-data and list of denominations
@ -2215,7 +2215,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
*/ */
{ {
static const char *denoms_key = "denoms"; static const char *denoms_key = "denoms";
struct groupData *group; struct GroupData *group;
json_t *list; json_t *list;
json_t *entry; json_t *entry;
struct GNUNET_HashCode key; struct GNUNET_HashCode key;
@ -2226,28 +2226,19 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
.age_mask = dk->meta.age_mask, .age_mask = dk->meta.age_mask,
}; };
memset (&meta.hash,
0,
sizeof(meta.hash));
/* Search the group/JSON-blob for the key */ /* Search the group/JSON-blob for the key */
GNUNET_CRYPTO_hash (&meta, TALER_denomination_group_get_key (&meta,
sizeof(meta), &key);
&key); group = GNUNET_CONTAINER_multihashmap_get (
denominations_by_group,
group = &key);
(struct groupData *) GNUNET_CONTAINER_multihashmap_get (
denominations_by_group,
&key);
if (NULL == group) if (NULL == group)
{ {
/* There is no group for this meta-data yet, so we create a new group */ /* There is no group for this meta-data yet, so we create a new group */
bool age_restricted = meta.age_mask.bits != 0; bool age_restricted = meta.age_mask.bits != 0;
const char *cipher; const char *cipher;
group = GNUNET_new (struct groupData); group = GNUNET_new (struct GroupData);
memset (group, 0, sizeof(*group));
switch (meta.cipher) switch (meta.cipher)
{ {
case TALER_DENOMINATION_RSA: case TALER_DENOMINATION_RSA:
@ -2261,9 +2252,12 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
} }
group->json = GNUNET_JSON_PACK ( group->json = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("cipher", cipher), GNUNET_JSON_pack_string ("cipher",
TALER_JSON_PACK_DENOM_FEES ("fee", &meta.fees), cipher),
TALER_JSON_pack_amount ("value", &meta.value)); TALER_JSON_PACK_DENOM_FEES ("fee",
&meta.fees),
TALER_JSON_pack_amount ("value",
&meta.value));
GNUNET_assert (NULL != group->json); GNUNET_assert (NULL != group->json);
if (age_restricted) if (age_restricted)
@ -2354,7 +2348,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
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 groupData *group = NULL; struct GroupData *group = NULL;
iter = iter =
GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group); GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group);

View File

@ -6032,4 +6032,56 @@ TALER_age_restriction_from_secret (
struct TALER_AgeCommitmentProof *comm_proof); struct TALER_AgeCommitmentProof *comm_proof);
/**
* 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
{
/**
* XOR of all SHA-512 hashes of the public keys in this
* group.
*/
struct GNUNET_HashCode hash;
/**
* Value of coins in this denomination group.
*/
struct TALER_Amount value;
/**
* Fee structure for all coins in the group.
*/
struct TALER_DenomFeeSet fees;
/**
* Cipher used for the denomination.
*/
enum TALER_DenominationCipher cipher;
/**
* Age mask for the denomiation.
*/
struct TALER_AgeMask age_mask;
};
/**
* Compute a unique key for the meta data of a denomination group.
*
* @param dg denomination group to evaluate
* @param[out] key key to set
*/
void
TALER_denomination_group_get_key (
const struct TALER_DenominationGroup *dg,
struct GNUNET_HashCode *key);
#endif #endif

View File

@ -380,26 +380,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
{
enum TALER_DenominationCipher cipher;
struct TALER_Amount value;
struct TALER_DenomFeeSet fees;
struct TALER_AgeMask age_mask;
// hash is/should be the XOR of all SHA-512 hashes of the public keys in this
// group
struct GNUNET_HashCode hash;
};
/** /**
* Generate a parser for a group of denominations. * Generate a parser for a group of denominations.

View File

@ -763,7 +763,7 @@ decode_keys_json (const json_t *resp_obj,
key_data->age_mask = TALER_extensions_get_age_restriction_mask (); key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
} }
/** /*
* 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).
* *
@ -793,6 +793,9 @@ decode_keys_json (const json_t *resp_obj,
&denom_keys_array), &denom_keys_array),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
json_t *denom_key_obj;
unsigned int index;
EXITIF (GNUNET_SYSERR == EXITIF (GNUNET_SYSERR ==
GNUNET_JSON_parse (group_obj, GNUNET_JSON_parse (group_obj,
group_spec, group_spec,
@ -800,78 +803,73 @@ decode_keys_json (const json_t *resp_obj,
NULL)); NULL));
/* Now, parse the individual denominations */ /* Now, parse the individual denominations */
json_array_foreach (denom_keys_array, index, denom_key_obj)
{ {
json_t *denom_key_obj; /* Set the common fields from the group for this particular
unsigned int index; denomination. Required to make the validity check inside
parse_json_denomkey_partially pass */
struct TALER_EXCHANGE_DenomPublicKey dk = {
.key.cipher = group.cipher,
.value = group.value,
.fees = group.fees,
.key.age_mask = group.age_mask
};
bool found = false;
json_array_foreach (denom_keys_array, index, denom_key_obj) EXITIF (GNUNET_SYSERR ==
parse_json_denomkey_partially (&dk,
group.cipher,
check_sig,
denom_key_obj,
&key_data->master_pub,
check_sig ? &hash_xor : NULL));
/* Build the running xor of the SHA512-hash of the public keys for the group */
GNUNET_CRYPTO_hash_xor (&dk.h_key.hash,
&group_hash_xor,
&group_hash_xor);
for (unsigned int j = 0;
j<key_data->num_denom_keys;
j++)
{ {
/* Set the common fields from the group for this particular if (0 == denoms_cmp (&dk,
denomination. Required to make the validity check inside &key_data->denom_keys[j]))
parse_json_denomkey_partially pass */
struct TALER_EXCHANGE_DenomPublicKey dk = {
.key.cipher = group.cipher,
.value = group.value,
.fees = group.fees,
.key.age_mask = group.age_mask
};
bool found = false;
EXITIF (GNUNET_SYSERR ==
parse_json_denomkey_partially (&dk,
group.cipher,
check_sig,
denom_key_obj,
&key_data->master_pub,
check_sig ? &hash_xor : NULL));
/* Build the running xor of the SHA512-hash of the public keys for the group */
GNUNET_CRYPTO_hash_xor (&dk.h_key.hash,
&group_hash_xor,
&group_hash_xor);
for (unsigned int j = 0;
j<key_data->num_denom_keys;
j++)
{ {
if (0 == denoms_cmp (&dk, found = true;
&key_data->denom_keys[j])) break;
{
found = true;
break;
}
} }
}
if (found) if (found)
{ {
/* 0:0:0 did not support /keys cherry picking */ /* 0:0:0 did not support /keys cherry picking */
TALER_LOG_DEBUG ("Skipping denomination key: already know it\n"); TALER_LOG_DEBUG ("Skipping denomination key: already know it\n");
TALER_denom_pub_free (&dk.key); TALER_denom_pub_free (&dk.key);
continue; continue;
} }
if (key_data->denom_keys_size == key_data->num_denom_keys) if (key_data->denom_keys_size == key_data->num_denom_keys)
GNUNET_array_grow (key_data->denom_keys, GNUNET_array_grow (key_data->denom_keys,
key_data->denom_keys_size, key_data->denom_keys_size,
key_data->denom_keys_size * 2 + 2); key_data->denom_keys_size * 2 + 2);
key_data->denom_keys[key_data->num_denom_keys++] = dk; key_data->denom_keys[key_data->num_denom_keys++] = dk;
/* Update "last_denom_issue_date" */ /* Update "last_denom_issue_date" */
TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n", TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n",
GNUNET_TIME_timestamp2s (dk.valid_from)); GNUNET_TIME_timestamp2s (dk.valid_from));
key_data->last_denom_issue_date key_data->last_denom_issue_date
= GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date, = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date,
dk.valid_from); dk.valid_from);
}; /* end of json_array_foreach over denominations */ }; /* end of json_array_foreach over denominations */
/* The calculated group_hash_xor must be the same as group.hash from /* The calculated group_hash_xor must be the same as group.hash from
the JSON. */ the JSON. */
EXITIF (0 != EXITIF (0 !=
GNUNET_CRYPTO_hash_cmp (&group_hash_xor, GNUNET_CRYPTO_hash_cmp (&group_hash_xor,
&group.hash)); &group.hash));
} /* end of block for parsing individual denominations */
} /* end of json_array_foreach over groups of denominations */ } /* end of json_array_foreach over groups of denominations */
} } /* end of scope for group_ojb/group_idx */
/* parse the auditor information */ /* parse the auditor information */
{ {
@ -1620,11 +1618,6 @@ struct GroupData
*/ */
json_t *json; json_t *json;
/**
* xor of all hashes of denominations in that group
*/
struct GNUNET_HashCode hash_xor;
/** /**
* Meta data for this group. * Meta data for this group.
*/ */
@ -1668,7 +1661,7 @@ add_grp (void *cls,
ge = GNUNET_JSON_PACK ( ge = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("hash", GNUNET_JSON_pack_data_auto ("hash",
&gd->hash_xor), &gd->meta.hash),
GNUNET_JSON_pack_string ("cipher", GNUNET_JSON_pack_string ("cipher",
cipher), cipher),
GNUNET_JSON_pack_array_steal ("denoms", GNUNET_JSON_pack_array_steal ("denoms",
@ -1751,8 +1744,7 @@ TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd)
json_decref (signkeys); json_decref (signkeys);
return NULL; return NULL;
} }
// FIXME: construct denominations_by_group analogous
// to taler-exchange-httpd_keys!
{ {
struct GNUNET_CONTAINER_MultiHashMap *dbg; struct GNUNET_CONTAINER_MultiHashMap *dbg;
@ -1776,9 +1768,8 @@ TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd)
>, >,
dk->expire_deposit)) dk->expire_deposit))
continue; /* skip keys that have expired */ continue; /* skip keys that have expired */
GNUNET_CRYPTO_hash (&meta, TALER_denomination_group_get_key (&meta,
sizeof(meta), &key);
&key);
gd = GNUNET_CONTAINER_multihashmap_get (dbg, gd = GNUNET_CONTAINER_multihashmap_get (dbg,
&key); &key);
if (NULL == gd) if (NULL == gd)
@ -1794,18 +1785,11 @@ TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd)
gd, gd,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
/* Build the running xor of the SHA512-hash of the public keys */
} }
{ /* Build the running xor of the SHA512-hash of the public keys */
struct TALER_DenominationHashP hc; GNUNET_CRYPTO_hash_xor (&dk->h_key.hash,
&gd->meta.hash,
TALER_denom_pub_hash (&dk->key, &gd->meta.hash);
&hc);
GNUNET_CRYPTO_hash_xor (&hc.hash,
&gd->hash_xor,
&gd->hash_xor);
}
switch (meta.cipher) switch (meta.cipher)
{ {
case TALER_DENOMINATION_RSA: case TALER_DENOMINATION_RSA:
@ -1833,10 +1817,6 @@ TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd)
dk->valid_from), dk->valid_from),
GNUNET_JSON_pack_timestamp ("stamp_expire_legal", GNUNET_JSON_pack_timestamp ("stamp_expire_legal",
dk->expire_legal), dk->expire_legal),
TALER_JSON_pack_amount ("value",
&dk->value),
TALER_JSON_PACK_DENOM_FEES ("fee",
&dk->fees),
GNUNET_JSON_pack_data_auto ("master_sig", GNUNET_JSON_pack_data_auto ("master_sig",
&dk->master_sig), &dk->master_sig),
key_spec key_spec

View File

@ -461,4 +461,54 @@ TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet,
} }
GNUNET_NETWORK_STRUCT_BEGIN
/**
* Structure we hash to compute the group key for
* a denomination group.
*/
struct DenominationGroupP
{
/**
* Value of coins in this denomination group.
*/
struct TALER_AmountNBO value;
/**
* Fee structure for all coins in the group.
*/
struct TALER_DenomFeeSetNBOP fees;
/**
* Age mask for the denomiation, in NBO.
*/
uint32_t age_mask GNUNET_PACKED;
/**
* Cipher used for the denomination, in NBO.
*/
uint32_t cipher GNUNET_PACKED;
};
GNUNET_NETWORK_STRUCT_END
void
TALER_denomination_group_get_key (
const struct TALER_DenominationGroup *dg,
struct GNUNET_HashCode *key)
{
struct DenominationGroupP dgp = {
.age_mask = htonl (dg->age_mask.bits),
.cipher = htonl (dg->cipher)
};
TALER_amount_hton (&dgp.value,
&dg->value);
TALER_denom_fee_set_hton (&dgp.fees,
&dg->fees);
GNUNET_CRYPTO_hash (&dgp,
sizeof (dgp),
key);
}
/* end of crypto.c */ /* end of crypto.c */