From 22c9321e67c54c581eca7ad8428267b0b14b9425 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 6 Dec 2020 21:48:31 +0100 Subject: expand exchangedb plugin for new /keys logic --- src/exchange/taler-exchange-httpd_keys.c | 61 +++- src/exchangedb/exchange-0002.sql | 15 +- src/exchangedb/plugin_exchangedb_postgres.c | 507 ++++++++++++++++++++++++++++ src/include/taler_exchangedb_plugin.h | 166 +++++++++ 4 files changed, 726 insertions(+), 23 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 2bc23de16..67a5a3fbe 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -229,6 +229,9 @@ struct TEH_KeyStateHandle */ struct GNUNET_CONTAINER_MultiPeerMap *signkey_map; + // FIXME: need list of auditors here! + // FIXME: need list of auditor-denominations here! + /** * Sorted array of responses to /keys (MUST be sorted by cherry-picking date) of * length @e krd_array_length; @@ -780,15 +783,35 @@ signkey_info_cb ( * @param auditor_pub the public key of the auditor * @param auditor_url URL of the REST API of the auditor * @param auditor_name human readable official name of the auditor - * @param ... MORE */ static void auditor_info_cb ( void *cls, const struct TALER_AuditorPublicKeyP *auditor_pub, const char *auditor_url, - const char *auditor_name, - ...) + const char *auditor_name) +{ + struct TEH_KeyStateHandle *ksh = cls; + + // FIXME: remember... +} + + +/** + * Function called with information about the denominations + * audited by the exchange's auditors. + * + * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param auditor_pub the public key of an auditor + * @param h_denom_pub hash of a denomination key audited by this auditor + * @param auditor_sig signature from the auditor affirming this + */ +static void +auditor_denom_cb ( + void *cls, + const struct TALER_AuditorPublicKeyP *auditor_pub, + const struct GNUNET_HashCode *h_denom_pub, + const struct TALER_AuditorSignatureP *auditor_sig) { struct TEH_KeyStateHandle *ksh = cls; @@ -828,8 +851,7 @@ build_key_state (struct HelperState *hs) GNUNET_YES); ksh->signkey_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO /* MUST be NO! */); -#if TBD - // NOTE: should ONLY fetch master-signed signkeys, but ALSO those that were revoked! + /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ qs = TEH_plugin->iterate_denominations (TEH_plugin->cls, &denomination_info_cb, ksh); @@ -840,12 +862,10 @@ build_key_state (struct HelperState *hs) true); return NULL; } -#endif -#if TBD - // NOTE: should ONLY fetch non-revoked AND master-signed signkeys! - qs = TEH_plugin->iterate_signkeys (TEH_plugin->cls, - &signkey_info_cb, - ksh); + /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ + qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls, + &signkey_info_cb, + ksh); if (qs < 0) { GNUNET_break (0); @@ -853,11 +873,19 @@ build_key_state (struct HelperState *hs) true); return NULL; } -#endif -#if TBD - qs = TEH_plugin->iterate_auditor_info (TEH_plugin->cls, - &auditor_info_cb, - ksh); + qs = TEH_plugin->iterate_active_auditors (TEH_plugin->cls, + &auditor_info_cb, + ksh); + if (qs < 0) + { + GNUNET_break (0); + destroy_key_state (ksh, + true); + return NULL; + } + qs = TEH_plugin->iterate_auditor_denominations (TEH_plugin->cls, + &auditor_denom_cb, + ksh); if (qs < 0) { GNUNET_break (0); @@ -865,7 +893,6 @@ build_key_state (struct HelperState *hs) true); return NULL; } -#endif return ksh; } diff --git a/src/exchangedb/exchange-0002.sql b/src/exchangedb/exchange-0002.sql index c7a315282..601935bf6 100644 --- a/src/exchangedb/exchange-0002.sql +++ b/src/exchangedb/exchange-0002.sql @@ -114,20 +114,23 @@ COMMENT ON COLUMN auditor_denom_sigs.auditor_sig CREATE TABLE IF NOT EXISTS exchange_sign_keys - (exchange_pub BYTEA PRIMARY KEY - ,master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32) + (exchange_pub BYTEA PRIMARY KEY CHECK (LENGTH(exchange_pub)=32) ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64) - ,legal_end INT8 NOT NULL + ,valid_from INT8 NOT NULL + ,expire_sign INT8 NOT NULL + ,expire_legal INT8 NOT NULL ); COMMENT ON TABLE exchange_sign_keys IS 'Table with master public key signatures on exchange online signing keys.'; COMMENT ON COLUMN exchange_sign_keys.exchange_pub IS 'Public online signing key of the exchange.'; -COMMENT ON COLUMN exchange_sign_keys.master_pub - IS 'Master public key of the exchange that was used for master_sig.'; COMMENT ON COLUMN exchange_sign_keys.master_sig IS 'Signature affirming the validity of the signing key of purpose TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY.'; -COMMENT ON COLUMN exchange_sign_keys.legal_end +COMMENT ON COLUMN exchange_sign_keys.valid_from + IS 'Time when this online signing key will first be used to sign messages.'; +COMMENT ON COLUMN exchange_sign_keys.expire_sign + IS 'Time when this online signing key will no longer be used to sign.'; +COMMENT ON COLUMN exchange_sign_keys.expire_legal IS 'Time when this online signing key legally expires.'; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 7918f540b..2fcb0d01d 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -314,6 +314,66 @@ postgres_get_session (void *cls) ",denom_pub" " FROM denominations;", 0), + /* Used in #postgres_iterate_denominations() */ + GNUNET_PQ_make_prepare ("select_denominations", + "SELECT" + " denominations.master_sig" + ",denom_revocations_serial_id IS NOT NULL AS revoked" + ",valid_from" + ",expire_withdraw" + ",expire_deposit" + ",expire_legal" + ",coin_val" /* value of this denom */ + ",coin_frac" /* fractional value of this denom */ + ",fee_withdraw_val" + ",fee_withdraw_frac" + ",fee_deposit_val" + ",fee_deposit_frac" + ",fee_refresh_val" + ",fee_refresh_frac" + ",fee_refund_val" + ",fee_refund_frac" + ",denom_pub" + " FROM denominations" + " LEFT JOIN " + " denomination_revocations USING (denom_pub_hash);", + 0), + /* Used in #postgres_iterate_active_signkeys() */ + GNUNET_PQ_make_prepare ("select_signkeys", + "SELECT" + " master_sig" + ",exchange_pub" + ",valid_from" + ",expire_sign" + ",expire_legal" + " FROM exchange_sign_keys esk" + " WHERE" + " expire_sign > $1" + " AND NOT EXISTS " + " (SELECT exchange_pub " + " FROM signkey_revocations skr" + " WHERE esk.exchange_pub = skr.exchange_pub);", + 1), + /* Used in #postgres_iterate_auditor_denominations() */ + GNUNET_PQ_make_prepare ("select_auditor_denoms", + "SELECT" + " auditor_denom_sigs.auditor_pub" + ",auditor_denom_sigs.denom_pub_hash" + ",auditor_denom_sigs.auditor_sig" + " FROM auditor_denom_sigs" + " JOIN auditors USING (auditor_pub)" + " WHERE auditors.is_active;", + 0), + /* Used in #postgres_iterate_active_auditors() */ + GNUNET_PQ_make_prepare ("select_auditors", + "SELECT" + " auditor_pub" + ",auditor_url" + ",auditor_name" + " FROM auditors" + " WHERE" + " is_active;", + 0), /* Used in #postgres_get_denomination_info() */ GNUNET_PQ_make_prepare ("denomination_get", "SELECT" @@ -1981,6 +2041,448 @@ postgres_iterate_denomination_info (void *cls, } +/** + * Closure for #dominations_cb_helper() + */ +struct DenomsIteratorContext +{ + /** + * Function to call with the results. + */ + TALER_EXCHANGEDB_DenominationsCallback cb; + + /** + * Closure to pass to @e cb + */ + void *cb_cls; + + /** + * Plugin context. + */ + struct PostgresClosure *pg; +}; + + +/** + * Helper function for #postgres_iterate_denominations(). + * Calls the callback with each denomination key. + * + * @param cls a `struct DenomsIteratorContext` + * @param result db results + * @param num_results number of results in @a result + */ +static void +dominations_cb_helper (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct DenomsIteratorContext *dic = cls; + struct PostgresClosure *pg = dic->pg; + + for (unsigned int i = 0; icb (dic->cb_cls, + &denom_pub, + &h_denom_pub, + &meta, + &master_sig, + (0 != revoked)); + GNUNET_PQ_cleanup_result (rs); + } +} + + +/** +* Function called to invoke @a cb on every known denomination key (revoked +* and non-revoked) that has been signed by the master key. Runs in its own +* read-only transaction (hence no session provided). +* +* + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each denomination key + * @param cb_cls closure for @a cb + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_iterate_denominations (void *cls, + TALER_EXCHANGEDB_DenominationsCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pc = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_end + }; + struct DenomsIteratorContext dic = { + .cb = cb, + .cb_cls = cb_cls, + .pg = pc + }; + struct TALER_EXCHANGEDB_Session *session; + + session = postgres_get_session (pc); + if (NULL == session) + return GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_PQ_eval_prepared_multi_select (session->conn, + "select_denominations", + params, + &dominations_cb_helper, + &dic); +} + + +/** + * Closure for #signkeys_cb_helper() + */ +struct SignkeysIteratorContext +{ + /** + * Function to call with the results. + */ + TALER_EXCHANGEDB_ActiveSignkeysCallback cb; + + /** + * Closure to pass to @e cb + */ + void *cb_cls; + +}; + + +/** + * Helper function for #postgres_active_signkeys(). + * Calls the callback with each signkey. + * + * @param cls a `struct SignkeysIteratorContext` + * @param result db results + * @param num_results number of results in @a result + */ +static void +signkeys_cb_helper (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct SignkeysIteratorContext *dic = cls; + + for (unsigned int i = 0; icb (dic->cb_cls, + &exchange_pub, + &meta, + &master_sig); + } +} + + +/** + * Function called to invoke @a cb on every non-revoked exchange signing key + * that has been signed by the master key. Revoked and (for signing!) + * expired keys are skipped. Runs in its own read-only transaction (hence no + * session provided). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each signing key + * @param cb_cls closure for @a cb + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_iterate_active_signkeys (void *cls, + TALER_EXCHANGEDB_ActiveSignkeysCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pc = cls; + struct GNUNET_TIME_Absolute now; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_absolute_time (&now), + GNUNET_PQ_query_param_end + }; + struct SignkeysIteratorContext dic = { + .cb = cb, + .cb_cls = cb_cls, + }; + struct TALER_EXCHANGEDB_Session *session; + + now = GNUNET_TIME_absolute_get (); + session = postgres_get_session (pc); + if (NULL == session) + return GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_PQ_eval_prepared_multi_select (session->conn, + "select_signkeys", + params, + &signkeys_cb_helper, + &dic); +} + + +/** + * Closure for #auditors_cb_helper() + */ +struct AuditorsIteratorContext +{ + /** + * Function to call with the results. + */ + TALER_EXCHANGEDB_AuditorsCallback cb; + + /** + * Closure to pass to @e cb + */ + void *cb_cls; + +}; + + +/** + * Helper function for #postgres_active_auditors(). + * Calls the callback with each auditor. + * + * @param cls a `struct SignkeysIteratorContext` + * @param result db results + * @param num_results number of results in @a result + */ +static void +auditors_cb_helper (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct AuditorsIteratorContext *dic = cls; + + for (unsigned int i = 0; icb (dic->cb_cls, + &auditor_pub, + auditor_url, + auditor_name); + GNUNET_PQ_cleanup_result (rs); + } +} + + +/** + * Function called to invoke @a cb on every active auditor. Disabled + * auditors are skipped. Runs in its own read-only transaction (hence no + * session provided). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each active auditor + * @param cb_cls closure for @a cb + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_iterate_active_auditors (void *cls, + TALER_EXCHANGEDB_AuditorsCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pc = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_end + }; + struct AuditorsIteratorContext dic = { + .cb = cb, + .cb_cls = cb_cls, + }; + struct TALER_EXCHANGEDB_Session *session; + + session = postgres_get_session (pc); + if (NULL == session) + return GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_PQ_eval_prepared_multi_select (session->conn, + "select_auditors", + params, + &auditors_cb_helper, + &dic); +} + + +/** + * Closure for #auditor_denoms_cb_helper() + */ +struct AuditorDenomsIteratorContext +{ + /** + * Function to call with the results. + */ + TALER_EXCHANGEDB_AuditorDenominationsCallback cb; + + /** + * Closure to pass to @e cb + */ + void *cb_cls; +}; + + +/** + * Helper function for #postgres_iterate_auditor_denominations(). + * Calls the callback with each auditor and denomination pair. + * + * @param cls a `struct AuditorDenomsIteratorContext` + * @param result db results + * @param num_results number of results in @a result + */ +static void +auditor_denoms_cb_helper (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct AuditorDenomsIteratorContext *dic = cls; + + for (unsigned int i = 0; icb (dic->cb_cls, + &auditor_pub, + &h_denom_pub, + &auditor_sig); + } +} + + +/** + * Function called to invoke @a cb on every denomination with an active + * auditor. Disabled auditors and denominations without auditor are + * skipped. Runs in its own read-only transaction (hence no session + * provided). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each active auditor + * @param cb_cls closure for @a cb + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_iterate_auditor_denominations ( + void *cls, + TALER_EXCHANGEDB_AuditorDenominationsCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pc = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_end + }; + struct AuditorDenomsIteratorContext dic = { + .cb = cb, + .cb_cls = cb_cls, + }; + struct TALER_EXCHANGEDB_Session *session; + + session = postgres_get_session (pc); + if (NULL == session) + return GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_PQ_eval_prepared_multi_select (session->conn, + "select_auditor_denoms", + params, + &auditor_denoms_cb_helper, + &dic); +} + + /** * Get the summary of a reserve. * @@ -8208,6 +8710,11 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->insert_denomination_info = &postgres_insert_denomination_info; plugin->get_denomination_info = &postgres_get_denomination_info; plugin->iterate_denomination_info = &postgres_iterate_denomination_info; + plugin->iterate_denominations = &postgres_iterate_denominations; + plugin->iterate_active_signkeys = &postgres_iterate_active_signkeys; + plugin->iterate_active_auditors = &postgres_iterate_active_auditors; + plugin->iterate_auditor_denominations = + &postgres_iterate_auditor_denominations; plugin->reserves_get = &postgres_reserves_get; plugin->reserves_in_insert = &postgres_reserves_in_insert; plugin->get_latest_reserve_in_reference = diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index b1686ab65..57254787c 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -293,6 +293,105 @@ struct TALER_EXCHANGEDB_DenominationKeyMetaData }; +/** + * Signature of a function called with information about the exchange's + * denomination keys. + * + * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param denom_pub public key of the denomination + * @param h_denom_pub hash of @a denom_pub + * @param meta meta data information about the denomination type (value, expirations, fees) + * @param master_sig master signature affirming the validity of this denomination + * @param recoup_possible true if the key was revoked and clients can currently recoup + * coins of this denomination + */ +typedef void +(*TALER_EXCHANGEDB_DenominationsCallback)( + void *cls, + const struct TALER_DenominationPublicKey *denom_pub, + const struct GNUNET_HashCode *h_denom_pub, + const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta, + const struct TALER_MasterSignatureP *master_sig, + bool recoup_possible); + + +/** + * Meta data about an exchange online signing key. + */ +struct TALER_EXCHANGEDB_SignkeyMetaData +{ + /** + * Start time of the validity period for this key. + */ + struct GNUNET_TIME_Absolute start; + + /** + * The exchange will sign messages with this key between @e start and this time. + */ + struct GNUNET_TIME_Absolute expire_sign; + + /** + * When do signatures with this sign key become invalid? + * After this point, these signatures cannot be used in (legal) + * disputes anymore, as the Exchange is then allowed to destroy its side + * of the evidence. @e expire_legal is expected to be significantly + * larger than @e expire_sign (by a year or more). + */ + struct GNUNET_TIME_Absolute expire_legal; + +}; + + +/** + * Signature of a function called with information about the exchange's + * online signing keys. + * + * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param exchange_pub public key of the exchange + * @param meta meta data information about the signing type (expirations) + * @param master_sig master signature affirming the validity of this denomination + */ +typedef void +(*TALER_EXCHANGEDB_ActiveSignkeysCallback)( + void *cls, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct TALER_EXCHANGEDB_SignkeyMetaData *meta, + const struct TALER_MasterSignatureP *master_sig); + + +/** + * Function called with information about the exchange's auditors. + * + * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param auditor_pub the public key of the auditor + * @param auditor_url URL of the REST API of the auditor + * @param auditor_name human readable official name of the auditor + */ +typedef void +(*TALER_EXCHANGEDB_AuditorsCallback)( + void *cls, + const struct TALER_AuditorPublicKeyP *auditor_pub, + const char *auditor_url, + const char *auditor_name); + + +/** + * Function called with information about the denominations + * audited by the exchange's auditors. + * + * @param cls closure with a `struct TEH_KeyStateHandle *` + * @param auditor_pub the public key of an auditor + * @param h_denom_pub hash of a denomination key audited by this auditor + * @param auditor_sig signature from the auditor affirming this + */ +typedef void +(*TALER_EXCHANGEDB_AuditorDenominationsCallback)( + void *cls, + const struct TALER_AuditorPublicKeyP *auditor_pub, + const struct GNUNET_HashCode *h_denom_pub, + const struct TALER_AuditorSignatureP *auditor_sig); + + /** * @brief Information we keep for a withdrawn coin to reproduce * the /withdraw operation if needed, and to have proof @@ -1778,6 +1877,73 @@ struct TALER_EXCHANGEDB_Plugin TALER_EXCHANGEDB_DenominationCallback cb, void *cb_cls); + + /** + * Function called to invoke @a cb on every known denomination key (revoked + * and non-revoked) that has been signed by the master key. Runs in its own + * read-only transaction (hence no session provided). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each denomination key + * @param cb_cls closure for @a cb + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*iterate_denominations)(void *cls, + TALER_EXCHANGEDB_DenominationsCallback cb, + void *cb_cls); + + /** + * Function called to invoke @a cb on every non-revoked exchange signing key + * that has been signed by the master key. Revoked and (for signing!) + * expired keys are skipped. Runs in its own read-only transaction (hence no + * session provided). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each signing key + * @param cb_cls closure for @a cb + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*iterate_active_signkeys)(void *cls, + TALER_EXCHANGEDB_ActiveSignkeysCallback cb, + void *cb_cls); + + + /** + * Function called to invoke @a cb on every active auditor. Disabled + * auditors are skipped. Runs in its own read-only transaction (hence no + * session provided). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each active auditor + * @param cb_cls closure for @a cb + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*iterate_active_auditors)(void *cls, + TALER_EXCHANGEDB_AuditorsCallback cb, + void *cb_cls); + + + /** + * Function called to invoke @a cb on every denomination with an active + * auditor. Disabled auditors and denominations without auditor are + * skipped. Runs in its own read-only transaction (hence no session + * provided). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param cb function to call on each active auditor-denomination pair + * @param cb_cls closure for @a cb + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*iterate_auditor_denominations)( + void *cls, + TALER_EXCHANGEDB_AuditorDenominationsCallback cb, + void *cb_cls); + + /** * Get the summary of a reserve. * -- cgit v1.2.3