diff options
| author | Özgür Kesim <oec-taler@kesim.org> | 2022-06-26 16:59:27 +0200 | 
|---|---|---|
| committer | Özgür Kesim <oec-taler@kesim.org> | 2022-06-26 16:59:27 +0200 | 
| commit | 31f74059e0d710254397688aabc201b230ef27da (patch) | |
| tree | 8a9717ae3d729c5916db15c7407d8d68b2828f85 | |
| parent | b39febe36fd66c8a36469cbedbc6197cc6c60135 (diff) | |
[new /keys response] create and parse denomination implemented
- /keys response now contains signed denomintations
	- hashes of denominations now XOR'ed per group into a single hash-code
	- final hash-code is now XOR of all group hash codes
	- final hash-code is signed
- lib/exchange_api_handle support for new "denominations" implemented
	- parses array of denomation groups
	- creates running xor of hashes
	- verifies signature at the end
	- previous diff/merge logic for keys remains intact.
| -rw-r--r-- | src/exchange/taler-exchange-httpd_keys.c | 119 | ||||
| -rw-r--r-- | src/include/taler_json_lib.h | 44 | ||||
| -rw-r--r-- | src/json/json_helper.c | 188 | ||||
| -rw-r--r-- | src/json/json_pack.c | 23 | ||||
| -rw-r--r-- | src/lib/exchange_api_handle.c | 257 | 
5 files changed, 424 insertions, 207 deletions
| diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 7580a8d7..de5f1fbc 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -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 */ @@ -1998,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); @@ -2043,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 */ @@ -2065,6 +2104,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)          GNUNET_CRYPTO_hash_context_finish (            GNUNET_CRYPTO_hash_context_copy (hash_context),            &hc); +          if (GNUNET_OK !=              create_krd (ksh,                          &hc, @@ -2072,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", @@ -2139,21 +2181,14 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)          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_Amount value; -          struct TALER_DenomFeeSet fees; -          struct TALER_AgeMask age_mask; -        } 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 = @@ -2168,15 +2203,15 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)            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); @@ -2190,10 +2225,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)            if (age_restricted)            { -            char *mask = TALER_age_mask_to_string (&meta.age_mask);              int r = json_object_set (group->json,                                       "age_mask", -                                     json_string (mask)); +                                     json_integer (meta.age_mask.bits));              GNUNET_assert (0 == r);            } @@ -2252,12 +2286,11 @@ 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 dependend?! -        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 @@ -2267,37 +2300,29 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)          GNUNET_assert (0 ==                         json_array_append_new (list, entry));        } -    } +    } /* loop over heap ends */      // 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 ( @@ -2317,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      }    } @@ -2333,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, @@ -2341,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/include/taler_json_lib.h b/src/include/taler_json_lib.h index a8ad6f24..f0b105e9 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 11aeceef..9752bb9f 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 090a8b96..bb52eeb0 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 0e76b289..be7bb3c3 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;  } | 
