exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 55e033da608a6bf60e0b08050cd8e548b23fd5ea
parent 17a5c6286d278a26527e3c21c4952e32661f764c
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 11 Oct 2026 14:46:23 +0200

make sure revocation reaches secmod

Diffstat:
Msrc/exchange/taler-exchange-httpd_post-management-signkeys-EXCHANGE_PUB-revoke.c | 5++++-
Msrc/include/taler/taler_testing_lib.h | 24++++++++++++++++++++++++
Msrc/testing/test_exchange_api_revocation.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testing/testing_api_cmd_revoke_sign_key.c | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 244 insertions(+), 1 deletion(-)

diff --git a/src/exchange/taler-exchange-httpd_post-management-signkeys-EXCHANGE_PUB-revoke.c b/src/exchange/taler-exchange-httpd_post-management-signkeys-EXCHANGE_PUB-revoke.c @@ -28,6 +28,7 @@ #include "taler-exchange-httpd_management.h" #include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_get-keys.h" +#include "taler-exchange-httpd_secmod-helpers.h" #include "exchange-database/insert_signkey_revocation.h" @@ -80,7 +81,9 @@ TEH_handler_management_signkeys_EP_revoke ( TALER_EC_GENERIC_DB_STORE_FAILED, "signkey revocation"); } - TEH_keys_update_states (); + /* Tell the security module to stop using the key (and to create a + replacement); this also updates our key state. */ + TEH_SECMOD_esign_revoke (exchange_pub); return TALER_MHD_reply_static ( connection, MHD_HTTP_NO_CONTENT, diff --git a/src/include/taler/taler_testing_lib.h b/src/include/taler/taler_testing_lib.h @@ -2306,6 +2306,30 @@ TALER_TESTING_cmd_revoke_sign_key ( /** + * Check that an online signing key was revoked: it was valid in the + * /keys of @a keys_before_ref, it is not listed in the /keys of + * @a keys_after_ref, and the client therefore rejects replies signed + * with it (TALER_EXCHANGE_test_signing_key()). + * + * @param label command label. + * @param signkey_ref reference to a command that identifies the + * revoked signing key (i.e. because it was used to sign a + * deposit confirmation). + * @param keys_before_ref "get exchange" command that downloaded /keys + * before the revocation + * @param keys_after_ref "get exchange" command that downloaded /keys + * after the revocation + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_signkey_revoked ( + const char *label, + const char *signkey_ref, + const char *keys_before_ref, + const char *keys_after_ref); + + +/** * Create a request for a wallet's KYC UUID. * * @param label command label. diff --git a/src/testing/test_exchange_api_revocation.c b/src/testing/test_exchange_api_revocation.c @@ -312,6 +312,55 @@ run (void *cls, "batch-withdraw-revocation", "0,2", "EUR:10"), + /* Revocation of an online signing key: get a coin to spend after + the revocation (age-restricted, as the plain denominations of + all these values were revoked above) ... */ + TALER_TESTING_cmd_admin_add_incoming ("create-reserve-3", + "EUR:1.01", + &cred.ba, + cred.user42_payto), + CMD_EXEC_WIREWATCH ("wirewatch-6"), + TALER_TESTING_cmd_withdraw_amount ("withdraw-signkey-revocation", + "create-reserve-3", + "EUR:1", + 13, /* age restriction */ + MHD_HTTP_OK), + /* ... revoke the key that signed "deposit-full", which the exchange + must refuse with a bad master signature ... */ + TALER_TESTING_cmd_revoke_sign_key ("revoke-signkey-bad-sig", + MHD_HTTP_FORBIDDEN, + true, + "deposit-full"), + TALER_TESTING_cmd_revoke_sign_key ("revoke-signkey", + MHD_HTTP_NO_CONTENT, + false, + "deposit-full"), + /* ... sign the replacement key the security module created ... */ + TALER_TESTING_cmd_exec_offline_sign_keys ("sign-replacement-signkey", + config_file), + /* ... then /keys must no longer offer the revoked key, so that + clients reject replies signed with it ... */ + TALER_TESTING_cmd_get_exchange ("get-exchange-after-signkey-revocation", + cred.cfg, + NULL, + true, + true), + TALER_TESTING_cmd_check_signkey_revoked ( + "check-signkey-revoked", + "deposit-full", + "get-exchange", + "get-exchange-after-signkey-revocation"), + /* ... and the exchange must sign with the replacement key, which + the client accepts. */ + TALER_TESTING_cmd_deposit ( + "deposit-after-signkey-revocation", + "withdraw-signkey-revocation", + 0, + cred.user42_payto, + "{\"items\":[{\"name\":\"ice cream\",\"value\":\"EUR:1\"}]}", + GNUNET_TIME_UNIT_ZERO, + "EUR:1", + MHD_HTTP_OK), TALER_TESTING_cmd_end () }; diff --git a/src/testing/testing_api_cmd_revoke_sign_key.c b/src/testing/testing_api_cmd_revoke_sign_key.c @@ -255,3 +255,170 @@ TALER_TESTING_cmd_revoke_sign_key ( return cmd; } } + + +/** + * State for a "check signkey revoked" CMD. + */ +struct CheckRevokedState +{ + /** + * Command that offers the revoked signing key. + */ + const char *signkey_ref; + + /** + * "get exchange" command that downloaded /keys before the revocation. + */ + const char *keys_before_ref; + + /** + * "get exchange" command that downloaded /keys after the revocation. + */ + const char *keys_after_ref; +}; + + +/** + * Obtain the /keys of the "get exchange" command @a label. + * + * @param is interpreter state + * @param label label of the command + * @return NULL on error + */ +static const struct TALER_EXCHANGE_Keys * +get_keys_of (struct TALER_TESTING_Interpreter *is, + const char *label) +{ + const struct TALER_TESTING_Command *cmd; + struct TALER_EXCHANGE_Keys *keys; + + cmd = TALER_TESTING_interpreter_lookup_command (is, + label); + if ( (NULL == cmd) || + (GNUNET_OK != + TALER_TESTING_get_trait_keys (cmd, + &keys)) ) + return NULL; + return keys; +} + + +/** + * Run the "check signkey revoked" command. + * + * @param cls a `struct CheckRevokedState` + * @param cmd the command currently being executed. + * @param is the interpreter state. + */ +static void +check_revoked_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct CheckRevokedState *crs = cls; + const struct TALER_TESTING_Command *sk_cmd; + const struct TALER_ExchangePublicKeyP *exchange_pub; + const struct TALER_EXCHANGE_Keys *before; + const struct TALER_EXCHANGE_Keys *after; + + (void) cmd; + sk_cmd = TALER_TESTING_interpreter_lookup_command (is, + crs->signkey_ref); + before = get_keys_of (is, + crs->keys_before_ref); + after = get_keys_of (is, + crs->keys_after_ref); + if ( (NULL == sk_cmd) || + (GNUNET_OK != + TALER_TESTING_get_trait_exchange_pub (sk_cmd, + 0, + &exchange_pub)) || + (NULL == before) || + (NULL == after) ) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + /* The key was valid before the revocation ... */ + if (GNUNET_OK != + TALER_EXCHANGE_test_signing_key (before, + exchange_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Signing key %s was not valid before its revocation\n", + TALER_B2S (exchange_pub)); + TALER_TESTING_interpreter_fail (is); + return; + } + /* ... /keys no longer lists it after the revocation ... */ + for (unsigned int i = 0; i<after->num_sign_keys; i++) + { + if (0 == + GNUNET_memcmp (exchange_pub, + &after->sign_keys[i].key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "/keys still lists revoked signing key %s\n", + TALER_B2S (exchange_pub)); + TALER_TESTING_interpreter_fail (is); + return; + } + } + /* ... so the client rejects replies signed with it. */ + if (GNUNET_SYSERR != + TALER_EXCHANGE_test_signing_key (after, + exchange_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Client still accepts revoked signing key %s\n", + TALER_B2S (exchange_pub)); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Free the state of a "check signkey revoked" CMD. + * + * @param cls a `struct CheckRevokedState` + * @param cmd the command which is being cleaned up. + */ +static void +check_revoked_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct CheckRevokedState *crs = cls; + + (void) cmd; + GNUNET_free (crs); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_signkey_revoked ( + const char *label, + const char *signkey_ref, + const char *keys_before_ref, + const char *keys_after_ref) +{ + struct CheckRevokedState *crs; + + crs = GNUNET_new (struct CheckRevokedState); + crs->signkey_ref = signkey_ref; + crs->keys_before_ref = keys_before_ref; + crs->keys_after_ref = keys_after_ref; + { + struct TALER_TESTING_Command cmd = { + .cls = crs, + .label = label, + .run = &check_revoked_run, + .cleanup = &check_revoked_cleanup + }; + + return cmd; + } +}