aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c161
-rw-r--r--src/extensions/extension_age_restriction.c54
-rw-r--r--src/include/taler_json_lib.h44
-rw-r--r--src/json/json_helper.c188
-rw-r--r--src/json/json_pack.c23
-rw-r--r--src/lib/exchange_api_handle.c257
6 files changed, 468 insertions, 259 deletions
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 6eadb0d70..de5f1fbc9 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -1351,7 +1351,7 @@ denomination_info_cb (
dk->meta = *meta;
dk->master_sig = *master_sig;
dk->recoup_possible = recoup_possible;
- dk->denom_pub.age_mask = meta->age_mask; /* FIXME-oec: age_mask -> reserved_field */
+ dk->denom_pub.age_mask = meta->age_mask;
GNUNET_assert (
GNUNET_OK ==
@@ -1726,12 +1726,13 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
* @a recoup and @a denoms.
*
* @param[in,out] ksh key state handle we build @a krd for
- * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms
+ * @param[in] denom_keys_hash hash over all the denomination keys in @a denoms
* @param last_cpd timestamp to use
* @param signkeys list of sign keys to return
* @param recoup list of revoked keys to return
* @param denoms list of 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
*/
static enum GNUNET_GenericReturnValue
@@ -1741,11 +1742,14 @@ create_krd (struct TEH_KeyStateHandle *ksh,
json_t *signkeys,
json_t *recoup,
json_t *denoms,
- json_t *grouped_denominations)
+ json_t *grouped_denominations,
+ const struct GNUNET_HashCode *h_grouped)
{
struct KeysResponseData krd;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
+ struct TALER_ExchangePublicKeyP grouped_exchange_pub;
+ struct TALER_ExchangeSignatureP grouped_exchange_sig;
json_t *keys;
GNUNET_assert (! GNUNET_TIME_absolute_is_zero (last_cpd.abs_time));
@@ -1753,11 +1757,13 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_assert (NULL != recoup);
GNUNET_assert (NULL != denoms);
GNUNET_assert (NULL != grouped_denominations);
+ GNUNET_assert (NULL != h_grouped);
GNUNET_assert (NULL != ksh->auditors);
GNUNET_assert (NULL != TEH_currency);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating /keys at cherry pick date %s\n",
GNUNET_TIME_timestamp2s (last_cpd));
+
/* Sign hash over denomination keys */
{
enum TALER_ErrorCode ec;
@@ -1779,6 +1785,33 @@ 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;
@@ -1815,7 +1848,9 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_JSON_pack_data_auto ("eddsa_pub",
&exchange_pub),
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);
/* Set wallet limit if KYC is configured */
@@ -1876,7 +1911,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_assert (0 == r);
}
- /* Update the keys object with the extensions */
+ /* Update the keys object with the extensions and its signature */
if (has_extensions)
{
json_t *sig;
@@ -1888,12 +1923,10 @@ create_krd (struct TEH_KeyStateHandle *ksh,
extensions);
GNUNET_assert (0 == r);
- /* add extensions_sig */
sig = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("extensions_sig",
&TEH_extensions_sig));
- /* update the keys object with extensions_sig */
r = json_object_update (keys, sig);
GNUNET_assert (0 == r);
}
@@ -2000,6 +2033,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
struct GNUNET_TIME_Timestamp last_cpd;
struct GNUNET_CONTAINER_Heap *heap;
struct GNUNET_HashContext *hash_context = NULL;
+ struct GNUNET_HashCode grouped_hash_xor = {0};
sctx.signkeys = json_array ();
GNUNET_assert (NULL != sctx.signkeys);
@@ -2045,8 +2079,11 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
/* groupData is the value we store for each group meta-data */
struct groupData
{
- json_t *json; /* The json blob with the group meta-data and list of denominations */
- struct GNUNET_HashContext *hash_context; /* hash over all denominations in that group */
+ /* The json blob with the group meta-data and list of denominations */
+ json_t *json;
+
+ /* xor of all hashes of denominations in that group */
+ struct GNUNET_HashCode hash_xor;
};
/* heap = min heap, sorted by start time */
@@ -2060,13 +2097,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
/*
* This is not the first entry in the heap (because last_cpd !=
* GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different
- * start time. Therefore, we create an entry in the ksh.
+ * start time. Therefore, we create a new entry in ksh.
*/
struct GNUNET_HashCode hc;
GNUNET_CRYPTO_hash_context_finish (
GNUNET_CRYPTO_hash_context_copy (hash_context),
&hc);
+
if (GNUNET_OK !=
create_krd (ksh,
&hc,
@@ -2074,7 +2112,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
sctx.signkeys,
recoup,
denoms,
- grouped_denominations))
+ grouped_denominations,
+
+ &grouped_hash_xor))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
@@ -2135,54 +2175,43 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
* denominations in this group as a json-blob in the multihashmap
* denominations_by_group.
**/
-
{
static const char *denoms_key = "denoms";
struct groupData *group;
json_t *list;
json_t *entry;
struct GNUNET_HashCode key;
-
- /* Find the group/JSON-blob for the key */
- struct
- {
- enum TALER_DenominationCipher cipher;
- struct TALER_AgeMask age_mask;
- struct TALER_Amount value;
- struct TALER_DenomFeeSet fees;
- } meta = {
+ struct TALER_DenominationGroup meta = {
.cipher = dk->denom_pub.cipher,
.value = dk->meta.value,
.fees = dk->meta.fees,
.age_mask = dk->meta.age_mask,
};
+ /* Search the group/JSON-blob for the key */
GNUNET_CRYPTO_hash (&meta, sizeof(meta), &key);
- group = (struct groupData *) GNUNET_CONTAINER_multihashmap_get (
- denominations_by_group,
- &key);
+ group =
+ (struct groupData *) GNUNET_CONTAINER_multihashmap_get (
+ denominations_by_group,
+ &key);
if (NULL == group)
{
- /*
- * There is no group for this meta-data yet, so let's create a new
- * group entry.
- */
-
+ // There is no group for this meta-data yet, so we create a new group
bool age_restricted = meta.age_mask.bits != 0;
char *cipher;
group = GNUNET_new (struct groupData);
- group->hash_context = GNUNET_CRYPTO_hash_context_start ();
+ memset (group, 0, sizeof(*group));
switch (meta.cipher)
{
case TALER_DENOMINATION_RSA:
- cipher = age_restricted ? "RSA+age_restriction": "RSA";
+ cipher = age_restricted ? "RSA+age_restricted": "RSA";
break;
case TALER_DENOMINATION_CS:
- cipher = age_restricted ? "CS+age_restriction": "CS";
+ cipher = age_restricted ? "CS+age_restricted": "CS";
break;
default:
GNUNET_assert (false);
@@ -2196,13 +2225,13 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
if (age_restricted)
{
- GNUNET_assert (0 ==
- json_object_set (group->json,
- "age_mask",
- json_integer (meta.age_mask.bits)));
+ int r = json_object_set (group->json,
+ "age_mask",
+ json_integer (meta.age_mask.bits));
+ GNUNET_assert (0 == r);
}
- /* Create a new array for the denominations in this group */
+ // Create a new array for the denominations in this group
list = json_array ();
GNUNET_assert (NULL != list);
GNUNET_assert (0 ==
@@ -2216,10 +2245,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
- /*
- * Now that we have found/created the right group, add the denomination
- * to the list
- */
+ // Now that we have found/created the right group, add the
+ // denomination to the list
{
struct GNUNET_JSON_PackSpec key_spec;
@@ -2259,53 +2286,43 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_assert (NULL != entry);
}
- /*
- * Build up the running hash of all denominations in this group
- * TODO: FIXME-oec: this is cipher and age_restriction dependent?!
- */
- GNUNET_CRYPTO_hash_context_read (group->hash_context,
- &dk->h_denom_pub,
- sizeof (struct GNUNET_HashCode));
+ // Build up the running xor of all hashes of the denominations in this
+ // group
+ GNUNET_CRYPTO_hash_xor (&dk->h_denom_pub.hash,
+ &group->hash_xor,
+ &group->hash_xor);
- /* Finally, add the denomination to the list of denominations in this
- * group */
+ // Finally, add the denomination to the list of denominations in this
+ // group
list = json_object_get (group->json, denoms_key);
GNUNET_assert (NULL != list);
GNUNET_assert (true == json_is_array (list));
GNUNET_assert (0 ==
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 <
GNUNET_CONTAINER_multihashmap_size (denominations_by_group))
{
struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
- struct GNUNET_HashCode all_hashcode;
- struct GNUNET_HashContext *all_hash_ctx;
struct groupData *group = NULL;
- all_hash_ctx =
- GNUNET_CRYPTO_hash_context_start ();
-
iter =
GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group);
while (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_iterator_next (iter, NULL, (const
- void **)
- &group))
+ GNUNET_CONTAINER_multihashmap_iterator_next (iter,
+ NULL,
+ (const
+ void **) &group))
{
struct GNUNET_HashCode hc;
- GNUNET_CRYPTO_hash_context_finish (
- group->hash_context,
- &hc);
-
- GNUNET_CRYPTO_hash_context_read (all_hash_ctx,
- &hc,
- sizeof (struct GNUNET_HashCode));
+ GNUNET_CRYPTO_hash_xor (&group->hash_xor,
+ &grouped_hash_xor,
+ &grouped_hash_xor);
GNUNET_assert (0 ==
json_object_set (
@@ -2325,12 +2342,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
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 */
}
}
@@ -2341,7 +2352,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
GNUNET_CRYPTO_hash_context_finish (hash_context,
&hc);
-
if (GNUNET_OK !=
create_krd (ksh,
&hc,
@@ -2349,7 +2359,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
sctx.signkeys,
recoup,
denoms,
- grouped_denominations))
+ grouped_denominations,
+ &grouped_hash_xor))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
diff --git a/src/extensions/extension_age_restriction.c b/src/extensions/extension_age_restriction.c
index e55c85202..9acc5dcbf 100644
--- a/src/extensions/extension_age_restriction.c
+++ b/src/extensions/extension_age_restriction.c
@@ -146,17 +146,17 @@ TALER_age_mask_to_string (
*/
void
age_restriction_disable (
- struct TALER_Extension *this)
+ struct TALER_Extension *ext)
{
- if (NULL == this)
+ if (NULL == ext)
return;
- this->config = NULL;
+ ext->config = NULL;
- if (NULL != this->config_json)
+ if (NULL != ext->config_json)
{
- json_decref (this->config_json);
- this->config_json = NULL;
+ json_decref (ext->config_json);
+ ext->config_json = NULL;
}
TE_age_restriction_config.mask.bits = 0;
@@ -174,7 +174,7 @@ age_restriction_disable (
*/
static enum GNUNET_GenericReturnValue
age_restriction_load_taler_config (
- struct TALER_Extension *this,
+ struct TALER_Extension *ext,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
char *groups = NULL;
@@ -192,8 +192,8 @@ age_restriction_load_taler_config (
"ENABLED")))
{
/* Age restriction is not enabled */
- this->config = NULL;
- this->config_json = NULL;
+ ext->config = NULL;
+ ext->config_json = NULL;
return GNUNET_OK;
}
@@ -228,10 +228,10 @@ age_restriction_load_taler_config (
__builtin_popcount (mask.bits) - 1);
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 */
- this->config = &TE_age_restriction_config;
+ ext->config = &TE_age_restriction_config;
/* Note: we do now have TE_age_restriction_config set, however
- * this->config_json is NOT set, i.e. the extension is not yet active! For
+ * ext->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
* called. */
}
@@ -244,12 +244,12 @@ age_restriction_load_taler_config (
/**
* @brief implements the TALER_Extension.load_json_config interface.
- * @param this if NULL, only tests the configuration
+ * @param ext if NULL, only tests the configuration
* @param config the configuration as json
*/
static enum GNUNET_GenericReturnValue
age_restriction_load_json_config (
- struct TALER_Extension *this,
+ struct TALER_Extension *ext,
json_t *jconfig)
{
struct TALER_AgeMask mask = {0};
@@ -260,10 +260,10 @@ age_restriction_load_json_config (
return ret;
/* only testing the parser */
- if (this == NULL)
+ if (ext == NULL)
return GNUNET_OK;
- if (TALER_Extension_AgeRestriction != this->type)
+ if (TALER_Extension_AgeRestriction != ext->type)
return GNUNET_SYSERR;
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;
}
- this->config = &TE_age_restriction_config;
+ ext->config = &TE_age_restriction_config;
- if (NULL != this->config_json)
- json_decref (this->config_json);
+ if (NULL != ext->config_json)
+ json_decref (ext->config_json);
- this->config_json = jconfig;
+ ext->config_json = jconfig;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"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.
- * @param this if NULL, only tests the configuration
+ * @param ext if NULL, only tests the configuration
* @param config the configuration as json
*/
json_t *
age_restriction_config_to_json (
- const struct TALER_Extension *this)
+ const struct TALER_Extension *ext)
{
char *mask_str;
json_t *conf;
- GNUNET_assert (NULL != this);
- GNUNET_assert (NULL != this->config);
+ GNUNET_assert (NULL != ext);
+ GNUNET_assert (NULL != ext->config);
- if (NULL != this->config_json)
+ if (NULL != ext->config_json)
{
- return json_copy (this->config_json);
+ return json_copy (ext->config_json);
}
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 (
- GNUNET_JSON_pack_bool ("critical", this->critical),
- GNUNET_JSON_pack_string ("version", this->version),
+ GNUNET_JSON_pack_bool ("critical", ext->critical),
+ GNUNET_JSON_pack_string ("version", ext->version),
GNUNET_JSON_pack_object_steal ("config", conf)
);
}
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index a8ad6f243..f0b105e98 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -359,6 +359,36 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
TALER_JSON_pack_amount ("account_fee", &(gfs)->account), \
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.
@@ -371,6 +401,20 @@ struct GNUNET_JSON_Specification
TALER_JSON_spec_denom_pub (const char *field,
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.
diff --git a/src/json/json_helper.c b/src/json/json_helper.c
index 11aeceefb..9752bb9f8 100644
--- a/src/json/json_helper.c
+++ b/src/json/json_helper.c
@@ -35,11 +35,15 @@
static enum TALER_DenominationCipher
string_to_cipher (const char *cipher_s)
{
- if (0 == strcasecmp (cipher_s,
- "RSA"))
+ if ((0 == strcasecmp (cipher_s,
+ "RSA")) ||
+ (0 == strcasecmp (cipher_s,
+ "RSA+age_restricted")))
return TALER_DENOMINATION_RSA;
- if (0 == strcasecmp (cipher_s,
- "CS"))
+ if ((0 == strcasecmp (cipher_s,
+ "CS")) ||
+ (0 == strcasecmp (cipher_s,
+ "CS+age_restricted")))
return TALER_DENOMINATION_CS;
return TALER_DENOMINATION_INVALID;
}
@@ -239,6 +243,84 @@ 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.
*
@@ -330,11 +412,14 @@ parse_denom_pub (void *cls,
{
struct TALER_DenominationPublicKey *denom_pub = spec->ptr;
const char *cipher;
+ bool age_mask_missing = false;
struct GNUNET_JSON_Specification dspec[] = {
GNUNET_JSON_spec_string ("cipher",
&cipher),
- GNUNET_JSON_spec_uint32 ("age_mask",
- &denom_pub->age_mask.bits),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("age_mask",
+ &denom_pub->age_mask.bits),
+ &age_mask_missing),
GNUNET_JSON_spec_end ()
};
const char *emsg;
@@ -350,6 +435,10 @@ parse_denom_pub (void *cls,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
+
+ if (age_mask_missing)
+ denom_pub->age_mask.bits = 0;
+
denom_pub->cipher = string_to_cipher (cipher);
switch (denom_pub->cipher)
{
@@ -434,6 +523,93 @@ 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.
*
* @param cls closure, NULL
diff --git a/src/json/json_pack.c b/src/json/json_pack.c
index 090a8b96b..bb52eeb05 100644
--- a/src/json/json_pack.c
+++ b/src/json/json_pack.c
@@ -79,35 +79,38 @@ TALER_JSON_pack_denom_pub (
struct GNUNET_JSON_PackSpec ps = {
.field_name = name,
};
+ struct GNUNET_JSON_PackSpec mask_or_end;
if (NULL == pk)
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)
{
case TALER_DENOMINATION_RSA:
ps.object
= GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("cipher",
- "RSA"),
- GNUNET_JSON_pack_uint64 ("age_mask",
- pk->age_mask.bits),
+ GNUNET_JSON_pack_string ("cipher", "RSA"),
GNUNET_JSON_pack_rsa_public_key ("rsa_public_key",
- pk->details.rsa_public_key));
+ pk->details.rsa_public_key),
+ mask_or_end);
break;
case TALER_DENOMINATION_CS:
ps.object
= GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("cipher",
- "CS"),
- GNUNET_JSON_pack_uint64 ("age_mask",
- pk->age_mask.bits),
+ GNUNET_JSON_pack_string ("cipher", "CS"),
GNUNET_JSON_pack_data_varsize ("cs_public_key",
&pk->details.cs_public_key,
- sizeof (pk->details.cs_public_key)));
+ sizeof (pk->details.cs_public_key)),
+ mask_or_end);
break;
default:
GNUNET_assert (0);
}
+
return ps;
}
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index 0e76b289d..be7bb3c39 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -303,24 +303,31 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
/**
- * Parse a exchange's denomination key encoded in JSON.
+ * Parse a exchange's denomination key encoded in JSON partially.
+ *
+ * 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[out] denom_key where to return the result
+ * @param cipher cipher type to parse
* @param check_sigs should we check signatures?
* @param[in] denom_key_obj json to parse
* @param master_key master key to use to verify signature
- * @param hash_context where to accumulate data for signature verification
+ * @param hash_xor where to accumulate data for signature verification via XOR
* @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
* invalid or the json malformed.
*/
static enum GNUNET_GenericReturnValue
-parse_json_denomkey (const char *currency,
- struct TALER_EXCHANGE_DenomPublicKey *denom_key,
- bool check_sigs,
- json_t *denom_key_obj,
- struct TALER_MasterPublicKeyP *master_key,
- struct GNUNET_HashContext *hash_context)
+parse_json_denomkey_partially (const char *currency,
+ struct TALER_EXCHANGE_DenomPublicKey *denom_key,
+ enum TALER_DenominationCipher cipher,
+ bool check_sigs,
+ json_t *denom_key_obj,
+ struct TALER_MasterPublicKeyP *master_key,
+ struct GNUNET_HashCode *hash_xor)
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig",
@@ -333,14 +340,9 @@ parse_json_denomkey (const char *currency,
&denom_key->valid_from),
GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
&denom_key->expire_legal),
- TALER_JSON_spec_amount ("value",
- currency,
- &denom_key->value),
- TALER_JSON_SPEC_DENOM_FEES ("fee",
- currency,
- &denom_key->fees),
- TALER_JSON_spec_denom_pub ("denom_pub",
- &denom_key->key),
+ TALER_JSON_spec_denom_pub_cipher (NULL,
+ cipher,
+ &denom_key->key),
GNUNET_JSON_spec_end ()
};
@@ -354,10 +356,11 @@ parse_json_denomkey (const char *currency,
}
TALER_denom_pub_hash (&denom_key->key,
&denom_key->h_key);
- if (NULL != hash_context)
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &denom_key->h_key,
- sizeof (struct GNUNET_HashCode));
+ if (NULL != hash_xor)
+ GNUNET_CRYPTO_hash_xor (&denom_key->h_key.hash,
+ hash_xor,
+ hash_xor);
+
if (! check_sigs)
return GNUNET_OK;
EXITIF (GNUNET_SYSERR ==
@@ -729,15 +732,13 @@ decode_keys_json (const json_t *resp_obj,
struct TALER_EXCHANGE_Keys *key_data,
enum TALER_EXCHANGE_VersionCompatibility *vc)
{
- struct TALER_ExchangeSignatureP sig;
- struct GNUNET_HashContext *hash_context = NULL;
- struct GNUNET_HashContext *hash_context_restricted = NULL;
- bool have_age_restricted_denom = false;
+ struct TALER_ExchangeSignatureP denominations_sig;
+ struct GNUNET_HashCode hash_xor = {0};
struct TALER_ExchangePublicKeyP pub;
const char *currency;
struct GNUNET_JSON_Specification mspec[] = {
- GNUNET_JSON_spec_fixed_auto ("eddsa_sig",
- &sig),
+ GNUNET_JSON_spec_fixed_auto ("denominations_sig",
+ &denominations_sig),
GNUNET_JSON_spec_fixed_auto ("eddsa_pub",
&pub),
GNUNET_JSON_spec_fixed_auto ("master_public_key",
@@ -760,7 +761,7 @@ decode_keys_json (const json_t *resp_obj,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
-#if DEBUG
+#if 1 /* DEBUG */
json_dumpf (resp_obj,
stderr,
JSON_INDENT (2));
@@ -829,13 +830,6 @@ 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 */
{
json_t *global_fees;
@@ -933,93 +927,101 @@ decode_keys_json (const json_t *resp_obj,
/* 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;
- struct GNUNET_HashContext *hc;
- bool is_optional_age_restriction;
- }
- hive[2] = {
- {
- "denoms",
- hash_context,
- false
- },
- {
- "age_restricted_denoms",
- hash_context_restricted,
- true
- }
- };
-
- 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;
+ json_t *denominations_by_group;
+ json_t *group_obj;
+ unsigned int group_idx;
- denom_keys_array = json_object_get (resp_obj,
- hive[s].name);
+ denominations_by_group =
+ json_object_get (
+ resp_obj,
+ "denominations");
- if (NULL == denom_keys_array)
- continue;
+ EXITIF (JSON_ARRAY !=
+ json_typeof (denominations_by_group));
- EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
+ json_array_foreach (denominations_by_group, group_idx, group_obj) {
+ /* First, parse { cipher, fees, value, age_mask } of the current group */
- json_array_foreach (denom_keys_array, index, denom_key_obj) {
- struct TALER_EXCHANGE_DenomPublicKey dk;
- bool found = false;
+ struct TALER_DenominationGroup group = {
+ .currency = currency
+ };
+ struct GNUNET_JSON_Specification group_spec[] = {
+ TALER_JSON_spec_denomination_group (NULL, &group),
+ GNUNET_JSON_spec_end ()
+ };
- /* 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;
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_JSON_parse (group_obj,
+ group_spec,
+ NULL,
+ NULL));
- 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));
+ /* Now, parse the individual denominations */
+ {
+ json_t *denom_keys_array;
+ json_t *denom_key_obj;
+ unsigned int index;
+ denom_keys_array = json_object_get (group_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 = {0};
+ 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;
+ }
+ }
- for (unsigned int j = 0;
- j<key_data->num_denom_keys;
- j++)
- {
- if (0 == denoms_cmp (&dk,
- &key_data->denom_keys[j]))
+ if (found)
{
- found = true;
- break;
+ /* 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);
}
- 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 */
@@ -1139,30 +1141,6 @@ decode_keys_json (const json_t *resp_obj,
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 !=
TALER_EXCHANGE_test_signing_key (key_data,
&pub));
@@ -1170,18 +1148,15 @@ decode_keys_json (const json_t *resp_obj,
EXITIF (GNUNET_OK !=
TALER_exchange_online_key_set_verify (
key_data->list_issue_date,
- &hc,
+ &hash_xor,
&pub,
- &sig));
+ &denominations_sig));
}
+
return GNUNET_OK;
-EXITIF_exit:
+EXITIF_exit:
*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;
}