diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/exchange/taler-exchange-httpd_age-withdraw_reveal.c | 75 | ||||
-rw-r--r-- | src/exchangedb/0003-age_withdraw.sql | 2 | ||||
-rw-r--r-- | src/exchangedb/exchange_do_age_withdraw.sql | 4 | ||||
-rw-r--r-- | src/exchangedb/pg_do_age_withdraw.c | 12 | ||||
-rw-r--r-- | src/exchangedb/pg_get_age_withdraw.c | 29 | ||||
-rw-r--r-- | src/include/taler_exchange_service.h | 2 | ||||
-rw-r--r-- | src/include/taler_pq_lib.h | 75 | ||||
-rw-r--r-- | src/lib/exchange_api_age_withdraw_reveal.c | 14 | ||||
-rw-r--r-- | src/pq/pq_query_helper.c | 385 | ||||
-rw-r--r-- | src/pq/pq_result_helper.c | 302 | ||||
-rw-r--r-- | src/testing/test_exchange_api_age_restriction.c | 6 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_age_withdraw.c | 5 |
12 files changed, 860 insertions, 51 deletions
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c index 4aa238df..e1f4fdb7 100644 --- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c +++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c @@ -71,7 +71,7 @@ struct AgeRevealContext * The data from the original age-withdraw. Will be retrieved from * the DB via @a ach and @a reserve_pub. */ - struct TALER_EXCHANGEDB_AgeWithdraw *commitment; + struct TALER_EXCHANGEDB_AgeWithdraw commitment; }; @@ -106,11 +106,8 @@ parse_age_withdraw_reveal_json ( error = "disclosed_coin_secrets must be an array"; else if (num_entries == 0) error = "disclosed_coin_secrets must not be empty"; - else if (num_entries > TALER_MAX_FRESH_COINS * (TALER_CNC_KAPPA - 1)) + else if (num_entries > TALER_MAX_FRESH_COINS) error = "maximum number of coins that can be withdrawn has been exceeded"; - else if (0 != num_entries % (TALER_CNC_KAPPA - 1)) - error = "the size of disclosed_coin_secrets must be a multiple of " - TALER_CNC_KAPPA_MINUS_ONE_STR; if (NULL != error) { @@ -120,29 +117,26 @@ parse_age_withdraw_reveal_json ( return GNUNET_SYSERR; } - actx->num_secrets = num_entries; - actx->num_coins = num_entries / (TALER_CNC_KAPPA - 1); + actx->num_secrets = num_entries * (TALER_CNC_KAPPA - 1); + actx->num_coins = num_entries; } /* Continue parsing the parts */ { unsigned int idx = 0; + unsigned int k = 0; + json_t *array = NULL; json_t *value = NULL; /* Parse diclosed keys */ actx->disclosed_coin_secrets = - GNUNET_new_array (num_entries, + GNUNET_new_array (actx->num_secrets, struct TALER_PlanchetMasterSecretP); - json_array_foreach (j_disclosed_coin_secrets, idx, value) { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, &actx->disclosed_coin_secrets[idx]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, spec, NULL, NULL)) + json_array_foreach (j_disclosed_coin_secrets, idx, array) { + if (! json_is_array (array) || + (TALER_CNC_KAPPA - 1 != json_array_size (array))) { char msg[256] = {0}; GNUNET_snprintf (msg, @@ -153,6 +147,32 @@ parse_age_withdraw_reveal_json ( TALER_EC_GENERIC_PARAMETER_MALFORMED, msg); goto EXIT; + + } + + json_array_foreach (array, k, value) + { + struct TALER_PlanchetMasterSecretP *sec = + &actx->disclosed_coin_secrets[2 * idx + k]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, sec), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, spec, NULL, NULL)) + { + char msg[256] = {0}; + GNUNET_snprintf (msg, + sizeof(msg), + "couldn't parse entry no. %d in array disclosed_coin_secrets[%d]", + k + 1, + idx + 1); + *mhd_ret = TALER_MHD_reply_with_ec (connection, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + msg); + goto EXIT; + } } }; } @@ -182,7 +202,7 @@ find_original_commitment ( struct MHD_Connection *connection, const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, const struct TALER_ReservePublicKeyP *reserve_pub, - struct TALER_EXCHANGEDB_AgeWithdraw **commitment, + struct TALER_EXCHANGEDB_AgeWithdraw *commitment, MHD_RESULT *result) { enum GNUNET_DB_QueryStatus qs; @@ -192,7 +212,7 @@ find_original_commitment ( qs = TEH_plugin->get_age_withdraw (TEH_plugin->cls, reserve_pub, h_commitment, - *commitment); + commitment); switch (qs) { case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: @@ -262,7 +282,13 @@ calculate_blinded_hash ( connection, result); if (NULL == denom_key) + { + GNUNET_break_op (0); + *result = TALER_MHD_reply_with_ec (connection, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + NULL); return GNUNET_SYSERR; + } /* calculate age commitment hash */ { @@ -351,7 +377,7 @@ calculate_blinded_hash ( GNUNET_assert (GNUNET_OK == ret); } - return GNUNET_SYSERR; + return ret; } @@ -417,9 +443,9 @@ verify_commitment_and_max_age ( { size_t i = 0; /* either 0 or 1, to index into coin_evs */ - for (size_t gamma = 0; gamma<TALER_CNC_KAPPA; gamma++) + for (size_t k = 0; k<TALER_CNC_KAPPA; k++) { - if (gamma == (size_t) commitment->noreveal_index) + if (k == (size_t) commitment->noreveal_index) { GNUNET_CRYPTO_hash_context_read (hash_context, &commitment->h_coin_evs[coin_idx], @@ -432,7 +458,7 @@ verify_commitment_and_max_age ( const struct TALER_PlanchetMasterSecretP *secret; struct TALER_BlindedCoinHashP bch; - GNUNET_assert (i<2); + GNUNET_assert (2>i); GNUNET_assert ((TALER_CNC_KAPPA - 1) * num_coins > j); secret = &disclosed_coin_secrets[j]; @@ -478,7 +504,6 @@ verify_commitment_and_max_age ( } } - return ret; } @@ -572,7 +597,7 @@ TEH_handler_age_withdraw_reveal ( if (GNUNET_OK != verify_commitment_and_max_age ( rc->connection, - actx.commitment, + &actx.commitment, actx.disclosed_coin_secrets, actx.num_coins, &result)) @@ -580,7 +605,7 @@ TEH_handler_age_withdraw_reveal ( /* Finally, return the signatures */ result = reply_age_withdraw_reveal_success (rc->connection, - actx.commitment); + &actx.commitment); } while(0); diff --git a/src/exchangedb/0003-age_withdraw.sql b/src/exchangedb/0003-age_withdraw.sql index 1d296b05..9816e466 100644 --- a/src/exchangedb/0003-age_withdraw.sql +++ b/src/exchangedb/0003-age_withdraw.sql @@ -29,6 +29,8 @@ BEGIN '(age_withdraw_id BIGINT GENERATED BY DEFAULT AS IDENTITY' ',h_commitment BYTEA NOT NULL CONSTRAINT h_commitment_length CHECK(LENGTH(h_commitment)=64)' ',max_age SMALLINT NOT NULL CONSTRAINT max_age_positive CHECK(max_age>=0)' + ',amount_with_fee_val INT8 NOT NULL' + ',amount_with_fee_frac INT4 NOT NULL' ',reserve_pub BYTEA NOT NULL CONSTRAINT reserve_pub_length CHECK(LENGTH(reserve_pub)=32)' ',reserve_sig BYTEA NOT NULL CONSTRAINT reserve_sig_length CHECK(LENGTH(reserve_sig)=64)' ',noreveal_index SMALLINT NOT NULL CONSTRAINT noreveal_index_positive CHECK(noreveal_index>=0)' diff --git a/src/exchangedb/exchange_do_age_withdraw.sql b/src/exchangedb/exchange_do_age_withdraw.sql index 2230d4bf..49a1433f 100644 --- a/src/exchangedb/exchange_do_age_withdraw.sql +++ b/src/exchangedb/exchange_do_age_withdraw.sql @@ -143,6 +143,8 @@ WHERE INSERT INTO exchange.age_withdraw (h_commitment ,max_age + ,amount_with_fee_val + ,amount_with_fee_frac ,reserve_pub ,reserve_sig ,noreveal_index @@ -152,6 +154,8 @@ INSERT INTO exchange.age_withdraw VALUES (h_commitment ,maximum_age_committed + ,amount_val + ,amount_frac ,rpub ,rsig ,noreveal_index diff --git a/src/exchangedb/pg_do_age_withdraw.c b/src/exchangedb/pg_do_age_withdraw.c index c79b2b3d..4fb52d46 100644 --- a/src/exchangedb/pg_do_age_withdraw.c +++ b/src/exchangedb/pg_do_age_withdraw.c @@ -52,15 +52,15 @@ TEH_PG_do_age_withdraw ( GNUNET_PQ_query_param_auto_from_type (&commitment->h_commitment), GNUNET_PQ_query_param_uint16 (&commitment->max_age), GNUNET_PQ_query_param_uint16 (&commitment->noreveal_index), - GNUNET_PQ_query_param_array_auto_from_type (commitment->num_coins, - commitment->h_coin_evs, - pg->conn), + TALER_PQ_query_param_array_blinded_coin_hash (commitment->num_coins, + commitment->h_coin_evs, + pg->conn), GNUNET_PQ_query_param_array_uint64 (commitment->num_coins, commitment->denom_serials, pg->conn), - GNUNET_PQ_query_param_array_auto_from_type (commitment->num_coins, - commitment->denom_sigs, - pg->conn), + TALER_PQ_query_param_array_blinded_denom_sig (commitment->num_coins, + commitment->denom_sigs, + pg->conn), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { diff --git a/src/exchangedb/pg_get_age_withdraw.c b/src/exchangedb/pg_get_age_withdraw.c index 5e123ca9..a66051c7 100644 --- a/src/exchangedb/pg_get_age_withdraw.c +++ b/src/exchangedb/pg_get_age_withdraw.c @@ -52,27 +52,26 @@ TEH_PG_get_age_withdraw ( &aw->amount_with_fee), GNUNET_PQ_result_spec_uint16 ("noreveal_index", &aw->noreveal_index), - GNUNET_PQ_result_spec_array_fixed_size ( + TALER_PQ_result_spec_array_blinded_coin_hash ( pg->conn, - "h_coin_evs", - sizeof(struct TALER_BlindedPlanchet), + "h_blind_evs", &aw->num_coins, - (void **) &aw->h_coin_evs), - GNUNET_PQ_result_spec_array_fixed_size ( + &aw->h_coin_evs), + TALER_PQ_result_spec_array_blinded_denom_sig ( pg->conn, "denom_sigs", - sizeof(struct TALER_DenominationSignature), - NULL, - (void **) &aw->denom_sigs), - GNUNET_PQ_result_spec_array_fixed_size ( + NULL, /* we assume that this is the same size as h_coin_evs */ + &aw->denom_sigs), + TALER_PQ_result_spec_array_denom_hash ( pg->conn, "denom_pub_hashes", - sizeof(struct TALER_DenominationHashP), - NULL, - (void **) &aw->denom_pub_hashes), + NULL, /* we assume that this is the same size as h_coin_evs */ + &aw->denom_pub_hashes), GNUNET_PQ_result_spec_end }; + GNUNET_assert (NULL != aw); + /* Used in #postgres_get_age_withdraw() to locate the response for a /reserve/$RESERVE_PUB/age-withdraw request using the hash of the blinded message. Also needed to ensure @@ -87,12 +86,12 @@ TEH_PG_get_age_withdraw ( ",amount_with_fee_val" ",amount_with_fee_frac" ",noreveal_index" - ",h_coin_evs" + ",h_blind_evs" ",denom_sigs" ",ARRAY(" " SELECT denominations.denom_pub_hash FROM (" - " SELECT UNNEST(denomination_serials) AS id," - " generate_subscripts(denominations_serials, 1) AS nr" /* for order */ + " SELECT UNNEST(denom_serials) AS id," + " generate_subscripts(denom_serials, 1) AS nr" /* for order */ " ) AS denoms" " LEFT JOIN denominations ON denominations.denominations_serial=denoms.id" ") AS denom_pub_hashes" diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index e8d78916..29fc005e 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -3066,6 +3066,7 @@ typedef void * @param coin_inputs The input for the coins to withdraw, same as in the previous call to /age-withdraw * @param noreveal_index The index into each of the kappa coin candidates, that should not be revealed to the exchange * @param h_commitment The commmitment from the previous call to /age-withdraw + * @param reserve_pub The public key of the reserve the original call to /age-withdraw was made to * @param res_cb A callback for the result, maybe NULL * @param res_cb_cls A closure for @e res_cb, maybe NULL * @return a handle for this request; NULL if the argument was invalid. @@ -3080,6 +3081,7 @@ TALER_EXCHANGE_age_withdraw_reveal ( num_coins], uint8_t noreveal_index, const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_ReservePublicKeyP *reserve_pub, TALER_EXCHANGE_AgeWithdrawRevealCallback res_cb, void *res_cb_cls); diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index fdc17ca5..7ea6bef3 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -127,6 +127,33 @@ TALER_PQ_query_param_json (const json_t *x); /** + * Generate query parameter for an array of blinded denomination signatures + * + * @param num number of elements in @e denom_sigs + * @param denom_sigs array of blinded denomination signatures + * @param db context for the db-connection + */ +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_blinded_denom_sig ( + size_t num, + const struct TALER_BlindedDenominationSignature *denom_sigs, + const struct GNUNET_PQ_Context *db + ); + +/** + * Generate query parameter for an array of blinded hashes of coin envelopes + * + * @param num number of elements in @e denom_sigs + * @param coin_evs array of blinded hashes of coin envelopes + * @param db context for the db-connection + */ +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_blinded_coin_hash ( + size_t num, + const struct TALER_BlindedCoinHashP *coin_evs, + const struct GNUNET_PQ_Context *db); + +/** * Currency amount expected. * * @param name name of the field in the table @@ -229,6 +256,54 @@ TALER_PQ_result_spec_json (const char *name, json_t **jp); +/** + * Array of blinded denomination signature expected + * + * @parma db context of the database connection + * @param name name of the field in the table + * @param[out] num number of elements in @e denom_sigs + * @param[out] denom_sigs where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_blinded_denom_sig ( + const struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_BlindedDenominationSignature **denom_sigs); + +/** + * Array of blinded hashes of coin envelopes + * + * @parma db context of the database connection + * @param name name of the field in the table + * @param[out] num number of elements in @e denom_sigs + * @param[out] h_coin_evs where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_blinded_coin_hash ( + const struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_BlindedCoinHashP **h_coin_evs); + +/** + * Array of hashes of denominations + * + * @parma db context of the database connection + * @param name name of the field in the table + * @param[out] num number of elements in @e denom_sigs + * @param[out] denom_hs where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_denom_hash ( + const struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_DenominationHashP **denom_hs); + #endif /* TALER_PQ_LIB_H_ */ /* end of include/taler_pq_lib.h */ diff --git a/src/lib/exchange_api_age_withdraw_reveal.c b/src/lib/exchange_api_age_withdraw_reveal.c index 75707a4e..1e804bc8 100644 --- a/src/lib/exchange_api_age_withdraw_reveal.c +++ b/src/lib/exchange_api_age_withdraw_reveal.c @@ -47,6 +47,9 @@ struct TALER_EXCHANGE_AgeWithdrawRevealHandle /* The age-withdraw commitment */ struct TALER_AgeWithdrawCommitmentHashP h_commitment; + /* The reserve's public key */ + const struct TALER_ReservePublicKeyP *reserve_pub; + /* Number of coins */ size_t num_coins; @@ -231,7 +234,7 @@ handle_age_withdraw_reveal_finished ( break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, the exchange basically just says - that it doesn't know this age-withraw commitment. */ + that it doesn't know this age-withdraw commitment. */ awr.hr.ec = TALER_JSON_get_error_code (j_response); awr.hr.hint = TALER_JSON_get_error_hint (j_response); break; @@ -299,7 +302,7 @@ prepare_url ( *end = '\0'; GNUNET_snprintf (arg_str, sizeof (arg_str), - "age-withraw/%s/reveal", + "age-withdraw/%s/reveal", pub_str); awrh->request_url = TALER_url_join (exchange_url, @@ -343,6 +346,9 @@ perform_protocol ( } \ } while(0) + j_array_of_secrets = json_array (); + FAIL_IF (NULL == j_array_of_secrets); + for (size_t n = 0; n < awrh->num_coins; n++) { const struct TALER_PlanchetMasterSecretP *secrets = @@ -369,6 +375,8 @@ perform_protocol ( j_secrets)); } j_request_body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("reserve_pub", + awrh->reserve_pub), GNUNET_JSON_pack_array_steal ("disclosed_coin_secrets", j_array_of_secrets)); FAIL_IF (NULL == j_request_body); @@ -418,6 +426,7 @@ TALER_EXCHANGE_age_withdraw_reveal ( num_coins], uint8_t noreveal_index, const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_ReservePublicKeyP *reserve_pub, TALER_EXCHANGE_AgeWithdrawRevealCallback reveal_cb, void *reveal_cb_cls) { @@ -429,6 +438,7 @@ TALER_EXCHANGE_age_withdraw_reveal ( awrh->coins_input = coins_input; awrh->callback = reveal_cb; awrh->callback_cls = reveal_cb_cls; + awrh->reserve_pub = reserve_pub; if (GNUNET_OK != prepare_url (exchange_url, diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c index 2904b63f..9a02cdda 100644 --- a/src/pq/pq_query_helper.c +++ b/src/pq/pq_query_helper.c @@ -24,6 +24,7 @@ #include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_pq_lib.h> #include "taler_pq_lib.h" +#include "pq_common.h" /** @@ -671,4 +672,388 @@ TALER_PQ_query_param_json (const json_t *x) } +/** ------------------- Array support -----------------------------------**/ + +/** + * Closure for the array type handlers. + * + * May contain sizes information for the data, given (and handled) by the + * caller. + */ +struct qconv_array_cls +{ + /** + * If not null, contains the array of sizes (the size of the array is the + * .size field in the ambient GNUNET_PQ_QueryParam struct). We do not free + * this memory. + * + * If not null, this value has precedence over @a sizes, which MUST be NULL */ + const size_t *sizes; + + /** + * If @a size and @a c_sizes are NULL, this field defines the same size + * for each element in the array. + */ + size_t same_size; + + /** + * If true, the array parameter to the data pointer to the qconv_array is a + * continuous byte array of data, either with @a same_size each or sizes + * provided bytes by @a sizes; + */ + bool continuous; + + /** + * Type of the array elements + */ + enum TALER_PQ_ArrayType typ; + + /** + * Oid of the array elements + */ + Oid oid; +}; + +/** + * Callback to cleanup a qconv_array_cls to be used during + * GNUNET_PQ_cleanup_query_params_closures + */ +static void +qconv_array_cls_cleanup (void *cls) +{ + GNUNET_free (cls); +} + + +/** + * Function called to convert input argument into SQL parameters for arrays + * + * Note: the format for the encoding of arrays for libpq is not very well + * documented. We peeked into various sources (postgresql and libpqtypes) for + * guidance. + * + * @param cls Closure of type struct qconv_array_cls* + * @param data Pointer to first element in the array + * @param data_len Number of _elements_ in array @a data (if applicable) + * @param[out] param_values SQL data to set + * @param[out] param_lengths SQL length data to set + * @param[out] param_formats SQL format data to set + * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays + * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() + * @param scratch_length number of entries left in @a scratch + * @return -1 on error, number of offsets used in @a scratch otherwise + */ +static int +qconv_array ( + void *cls, + const void *data, + size_t data_len, + void *param_values[], + int param_lengths[], + int param_formats[], + unsigned int param_length, + void *scratch[], + unsigned int scratch_length) +{ + struct qconv_array_cls *meta = cls; + size_t num = data_len; + size_t total_size; + const size_t *sizes; + bool same_sized; + void *elements = NULL; + bool noerror = true; + /* needed to capture the encoded rsa signatures */ + void **buffers = NULL; + size_t *buffer_lengths = NULL; + + (void) (param_length); + (void) (scratch_length); + + GNUNET_assert (NULL != meta); + GNUNET_assert (num < INT_MAX); + + sizes = meta->sizes; + same_sized = (0 != meta->same_size); + +#define RETURN_UNLESS(cond) \ + do { \ + if (! (cond)) \ + { \ + GNUNET_break ((cond)); \ + noerror = false; \ + goto DONE; \ + } \ + } while(0) + + /* Calculate sizes and check bounds */ + { + /* num * length-field */ + size_t x = sizeof(uint32_t); + size_t y = x * num; + RETURN_UNLESS ((0 == num) || (y / num == x)); + + /* size of header */ + total_size = x = sizeof(struct TALER_PQ_ArrayHeader); + total_size += y; + RETURN_UNLESS (total_size >= x); + + /* sizes of elements */ + if (same_sized) + { + x = num * meta->same_size; + RETURN_UNLESS ((0 == num) || (x / num == meta->same_size)); + + y = total_size; + total_size += x; + RETURN_UNLESS (total_size >= y); + } + else /* sizes are different per element */ + { + + switch (meta->typ) + { + case TALER_PQ_array_of_blinded_denom_sig: + { + const struct TALER_BlindedDenominationSignature *denom_sigs = data; + size_t len; + + buffers = GNUNET_new_array (num, void *); + buffer_lengths = GNUNET_new_array (num, size_t); + + for (size_t i = 0; i<num; i++) + { + switch (denom_sigs[i].cipher) + { + case TALER_DENOMINATION_RSA: + len = GNUNET_CRYPTO_rsa_signature_encode ( + denom_sigs[i].details.blinded_rsa_signature, + &buffers[i]); + RETURN_UNLESS (len != 0); + break; + case TALER_DENOMINATION_CS: + len = sizeof (denom_sigs[i].details.blinded_cs_answer); + break; + default: + GNUNET_assert (0); + } + + /* for the cipher and marker */ + len += 2 * sizeof(uint32_t); + buffer_lengths[i] = len; + + y = total_size; + total_size += len; + RETURN_UNLESS (total_size >= y); + } + sizes = buffer_lengths; + break; + } + default: + GNUNET_assert (0); + } + } + + RETURN_UNLESS (INT_MAX > total_size); + RETURN_UNLESS (0 != total_size); + + elements = GNUNET_malloc (total_size); + } + + /* Write data */ + { + char *out = elements; + struct TALER_PQ_ArrayHeader h = { + .ndim = htonl (1), /* We only support one-dimensional arrays */ + .has_null = htonl (0), /* We do not support NULL entries in arrays */ + .lbound = htonl (1), /* Default start index value */ + .dim = htonl (num), + .oid = htonl (meta->oid), + }; + + /* Write header */ + GNUNET_memcpy (out, &h, sizeof(h)); + out += sizeof(h); + + + /* Write elements */ + for (size_t i = 0; i < num; i++) + { + size_t sz = same_sized ? meta->same_size : sizes[i]; + + *(uint32_t *) out = htonl (sz); + out += sizeof(uint32_t); + + switch (meta->typ) + { + case TALER_PQ_array_of_blinded_denom_sig: + { + const struct TALER_BlindedDenominationSignature *denom_sigs = data; + + uint32_t be[2]; + be[0] = htonl ((uint32_t) denom_sigs[i].cipher); + be[1] = htonl (0x01); /* magic margker: blinded */ + GNUNET_memcpy (out, + &be, + sizeof(be)); + out += sizeof(be); + sz -= sizeof(be); + + switch (denom_sigs[i].cipher) + { + case TALER_DENOMINATION_RSA: + { + void *buf = buffers[i]; + + GNUNET_memcpy (out, + buf, + sz); + break; + } + case TALER_DENOMINATION_CS: + GNUNET_memcpy (out, + &denom_sigs[i].details.blinded_cs_answer, + sz); + break; + default: + GNUNET_assert (0); + } + break; + } + case TALER_PQ_array_of_blinded_coin_hash: + { + const struct TALER_BlindedCoinHashP *coin_hs = data; + GNUNET_memcpy (out, + &coin_hs[i], + sizeof(struct TALER_BlindedCoinHashP)); + + break; + } + case TALER_PQ_array_of_denom_hash: + { + const struct TALER_DenominationHashP *denom_hs = data; + GNUNET_memcpy (out, + &denom_hs[i], + sizeof(struct TALER_DenominationHashP)); + break; + } + default: + { + GNUNET_assert (0); + break; + } + } + out += sz; + } + } + + param_values[0] = elements; + param_lengths[0] = total_size; + param_formats[0] = 1; + scratch[0] = elements; + +DONE: + if (NULL != buffers) + { + for (size_t i = 0; i<num; i++) + GNUNET_free (buffers[i]); + GNUNET_free (buffers); + } + GNUNET_free (buffer_lengths); + + if (noerror) + return 1; + + return -1; +} + + +/** + * Function to genreate a typ specific query parameter and corresponding closure + * + * @param num Number of elements in @a elements + * @param continuous If true, @a elements is an continuous array of data + * @param elements Array of @a num elements, either continuous or pointers + * @param sizes Array of @a num sizes, one per element, may be NULL + * @param same_size If not 0, all elements in @a elements have this size + * @param typ Supported internal type of each element in @a elements + * @param oid Oid of the type to be used in Postgres + * @return Query parameter + */ +static struct GNUNET_PQ_QueryParam +query_param_array_generic ( + unsigned int num, + bool continuous, + const void *elements, + const size_t *sizes, + size_t same_size, + enum TALER_PQ_ArrayType typ, + Oid oid) +{ + struct qconv_array_cls *meta = GNUNET_new (struct qconv_array_cls); + meta->typ = typ; + meta->oid = oid; + meta->sizes = sizes; + meta->same_size = same_size; + meta->continuous = continuous; + + struct GNUNET_PQ_QueryParam res = { + .conv = qconv_array, + .conv_cls = meta, + .conv_cls_cleanup = qconv_array_cls_cleanup, + .data = elements, + .size = num, + .num_params = 1, + }; + + return res; +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_blinded_denom_sig ( + size_t num, + const struct TALER_BlindedDenominationSignature *denom_sigs, + const struct GNUNET_PQ_Context *db) +{ + return query_param_array_generic (num, + true, + denom_sigs, + NULL, + 0, + TALER_PQ_array_of_blinded_denom_sig, + GNUNET_PQ_get_oid (db, + GNUNET_PQ_DATATYPE_BYTEA)); +}; + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_blinded_coin_hash ( + size_t num, + const struct TALER_BlindedCoinHashP *coin_hs, + const struct GNUNET_PQ_Context *db) +{ + return query_param_array_generic (num, + true, + coin_hs, + NULL, + sizeof(struct TALER_BlindedCoinHashP), + TALER_PQ_array_of_blinded_coin_hash, + GNUNET_PQ_get_oid (db, + GNUNET_PQ_DATATYPE_BYTEA)); +}; + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_denom_hash ( + size_t num, + const struct TALER_DenominationHashP *denom_hs, + const struct GNUNET_PQ_Context *db) +{ + return query_param_array_generic (num, + true, + denom_hs, + NULL, + sizeof(struct TALER_DenominationHashP), + TALER_PQ_array_of_denom_hash, + GNUNET_PQ_get_oid (db, + GNUNET_PQ_DATATYPE_BYTEA)); +}; /* end of pq/pq_query_helper.c */ diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c index 9441412d..0a734bbc 100644 --- a/src/pq/pq_result_helper.c +++ b/src/pq/pq_result_helper.c @@ -20,6 +20,7 @@ */ #include "platform.h" #include <gnunet/gnunet_util_lib.h> +#include "pq_common.h" #include "taler_pq_lib.h" @@ -975,4 +976,305 @@ TALER_PQ_result_spec_exchange_withdraw_values ( } +/** + * Closure for the array result specifications. Contains type information + * for the generic parser extract_array_generic and out-pointers for the results. + */ +struct ArrayResultCls +{ + /* Oid of the expected type, must match the oid in the header of the PQResult struct */ + Oid oid; + + /* Target type */ + enum TALER_PQ_ArrayType typ; + + /* If not 0, defines the expected size of each entry */ + size_t same_size; + + /* Out-pointer to write the number of elements in the array */ + size_t *num; + + /* Out-pointer. If @a typ is TALER_PQ_array_of_byte and @a same_size is 0, + * allocate and put the array of @a num sizes here. NULL otherwise */ + size_t **sizes; +}; + +/** + * Extract data from a Postgres database @a result as array of a specific type + * from row @a row. The type information and optionally additional + * out-parameters are given in @a cls which is of type array_result_cls. + * + * @param cls closure of type array_result_cls + * @param result where to extract data from + * @param row row to extract data from + * @param fname name (or prefix) of the fields to extract from + * @param[in,out] dst_size where to store size of result, may be NULL + * @param[out] dst where to store the result + * @return + * #GNUNET_YES if all results could be extracted + * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) + */ +static enum GNUNET_GenericReturnValue +extract_array_generic ( + void *cls, + PGresult *result, + int row, + const char *fname, + size_t *dst_size, + void *dst) +{ + const struct ArrayResultCls *info = cls; + int data_sz; + char *data; + void *out = NULL; + struct TALER_PQ_ArrayHeader header; + int col_num; + + GNUNET_assert (NULL != dst); + *((void **) dst) = NULL; + + #define FAIL_IF(cond) \ + do { \ + if ((cond)) \ + { \ + GNUNET_break (! (cond)); \ + goto FAIL; \ + } \ + } while(0) + + col_num = PQfnumber (result, fname); + FAIL_IF (0 > col_num); + + data_sz = PQgetlength (result, row, col_num); + FAIL_IF (0 > data_sz); + FAIL_IF (sizeof(header) > (size_t) data_sz); + + data = PQgetvalue (result, row, col_num); + FAIL_IF (NULL == data); + + { + struct TALER_PQ_ArrayHeader *h = + (struct TALER_PQ_ArrayHeader *) data; + + header.ndim = ntohl (h->ndim); + header.has_null = ntohl (h->has_null); + header.oid = ntohl (h->oid); + header.dim = ntohl (h->dim); + header.lbound = ntohl (h->lbound); + + FAIL_IF (1 != header.ndim); + FAIL_IF (INT_MAX <= header.dim); + FAIL_IF (0 != header.has_null); + FAIL_IF (1 != header.lbound); + FAIL_IF (info->oid != header.oid); + } + + if (NULL != info->num) + *info->num = header.dim; + + { + char *in = data + sizeof(header); + + switch (info->typ) + { + case TALER_PQ_array_of_denom_hash: + if (NULL != dst_size) + *dst_size = sizeof(struct TALER_DenominationHashP) * (header.dim); + out = GNUNET_new_array (header.dim, struct TALER_DenominationHashP); + *((void **) dst) = out; + for (uint32_t i = 0; i < header.dim; i++) + { + size_t sz = ntohl (*(uint32_t *) in); + FAIL_IF (sz != sizeof(struct TALER_DenominationHashP)); + in += sizeof(uint32_t); + *(struct TALER_DenominationHashP *) out = + *(struct TALER_DenominationHashP *) in; + in += sz; + out += sz; + } + return GNUNET_OK; + + case TALER_PQ_array_of_blinded_coin_hash: + if (NULL != dst_size) + *dst_size = sizeof(struct TALER_BlindedCoinHashP) * (header.dim); + out = GNUNET_new_array (header.dim, struct TALER_BlindedCoinHashP); + *((void **) dst) = out; + for (uint32_t i = 0; i < header.dim; i++) + { + size_t sz = ntohl (*(uint32_t *) in); + FAIL_IF (sz != sizeof(struct TALER_BlindedCoinHashP)); + in += sizeof(uint32_t); + *(struct TALER_BlindedCoinHashP *) out = + *(struct TALER_BlindedCoinHashP *) in; + in += sz; + out += sz; + } + return GNUNET_OK; + + case TALER_PQ_array_of_blinded_denom_sig: + { + struct TALER_BlindedDenominationSignature *denom_sigs; + if (0 == header.dim) + { + if (NULL != dst_size) + *dst_size = 0; + break; + } + + denom_sigs = GNUNET_new_array (header.dim, + struct TALER_BlindedDenominationSignature); + *((void **) dst) = denom_sigs; + + /* copy data */ + for (uint32_t i = 0; i < header.dim; i++) + { + struct TALER_BlindedDenominationSignature *denom_sig = &denom_sigs[i]; + uint32_t be[2]; + size_t sz = ntohl (*(uint32_t *) in); + in += sizeof(uint32_t); + + FAIL_IF (sizeof(be) > sz); + GNUNET_memcpy (&be, + in, + sizeof(be)); + FAIL_IF (0x01 != ntohl (be[1])); /* magic marker: blinded */ + + in += sizeof(be); + sz -= sizeof(be); + + denom_sig->cipher = ntohl (be[0]); + switch (denom_sig->cipher) + { + case TALER_DENOMINATION_RSA: + denom_sig->details.blinded_rsa_signature = + GNUNET_CRYPTO_rsa_signature_decode (in, + sz); + FAIL_IF (NULL == denom_sig->details.blinded_rsa_signature); + break; + + case TALER_DENOMINATION_CS: + FAIL_IF (sizeof(denom_sig->details.blinded_cs_answer) != sz); + GNUNET_memcpy (&denom_sig->details.blinded_cs_answer, + in, + sz); + break; + + default: + FAIL_IF (true); + } + + in += sz; + } + return GNUNET_OK; + } + default: + FAIL_IF (true); + } + } + +FAIL: + GNUNET_free (*(void **) dst); + return GNUNET_SYSERR; + #undef FAIL_IF + +} + + +/** + * Cleanup of the data and closure of an array spec. + */ +static void +array_cleanup (void *cls, + void *rd) +{ + + struct ArrayResultCls *info = cls; + void **dst = rd; + + if ((0 == info->same_size) && + (NULL != info->sizes)) + GNUNET_free (*(info->sizes)); + + GNUNET_free (cls); + GNUNET_free (*dst); + *dst = NULL; +} + + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_blinded_denom_sig ( + const struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_BlindedDenominationSignature **denom_sigs) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_blinded_denom_sig; + info->oid = GNUNET_PQ_get_oid (db, + GNUNET_PQ_DATATYPE_BYTEA); + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) denom_sigs, + .fname = name, + .cls = info + }; + return res; + +}; + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_blinded_coin_hash ( + const struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_BlindedCoinHashP **h_coin_evs) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_blinded_coin_hash; + info->oid = GNUNET_PQ_get_oid (db, + GNUNET_PQ_DATATYPE_BYTEA); + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) h_coin_evs, + .fname = name, + .cls = info + }; + return res; + +}; + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_denom_hash ( + const struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_DenominationHashP **denom_hs) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_denom_hash; + info->oid = GNUNET_PQ_get_oid (db, + GNUNET_PQ_DATATYPE_BYTEA); + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) denom_hs, + .fname = name, + .cls = info + }; + return res; + +}; + + /* end of pq_result_helper.c */ diff --git a/src/testing/test_exchange_api_age_restriction.c b/src/testing/test_exchange_api_age_restriction.c index 56e46b22..cdfb58e2 100644 --- a/src/testing/test_exchange_api_age_restriction.c +++ b/src/testing/test_exchange_api_age_restriction.c @@ -290,7 +290,7 @@ run (void *cls, MHD_HTTP_CONFLICT, "EUR:10", NULL), - TALER_TESTING_cmd_age_withdraw ("age-withdraw-coin-1", + TALER_TESTING_cmd_age_withdraw ("age-withdraw-coins-1", "create-reserve-kyc-1", 8, MHD_HTTP_OK, @@ -298,6 +298,10 @@ run (void *cls, "EUR:5", "EUR:5", NULL), + /* FIXME[oec]: failing */ + TALER_TESTING_cmd_age_withdraw_reveal ("age-withdraw-coins-reveal-1", + "age-withdraw-coins-1", + MHD_HTTP_OK), TALER_TESTING_cmd_end (), }; diff --git a/src/testing/testing_api_cmd_age_withdraw.c b/src/testing/testing_api_cmd_age_withdraw.c index 98a5e1d8..8849cd31 100644 --- a/src/testing/testing_api_cmd_age_withdraw.c +++ b/src/testing/testing_api_cmd_age_withdraw.c @@ -629,8 +629,8 @@ age_withdraw_reveal_run ( * Get the command and state for the previous call to "age witdraw" */ age_withdraw_cmd = - TALER_TESTING_interpreter_get_command (is, - awrs->age_withdraw_reference); + TALER_TESTING_interpreter_lookup_command (is, + awrs->age_withdraw_reference); if (NULL == age_withdraw_cmd) { GNUNET_break (0); @@ -649,6 +649,7 @@ age_withdraw_reveal_run ( aws->coin_inputs, aws->noreveal_index, &aws->h_commitment, + &aws->reserve_pub, age_withdraw_reveal_cb, awrs); } |