implement denomination key revocation logic in exchangedb and taler-exchange-keyup (part of #3887)

This commit is contained in:
Christian Grothoff 2017-04-07 22:37:00 +02:00
parent ef6d9cc56d
commit 209076ebd3
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
8 changed files with 329 additions and 17 deletions

View File

@ -1,4 +1,4 @@
.TH TALER\-EXCHANGE\-KEYUP 1 "Apr 22, 2015" "GNU Taler" .TH TALER\-EXCHANGE\-KEYUP 1 "Apr 7, 2017" "GNU Taler"
.SH NAME .SH NAME
taler\-exchange\-keyup \- Setup Taler exchange denomination and signing keys. taler\-exchange\-keyup \- Setup Taler exchange denomination and signing keys.
@ -25,6 +25,9 @@ Location of the private EdDSA offline master key of the exchange.
.IP "\-o FILE, \-\-ouptut=FILE" .IP "\-o FILE, \-\-ouptut=FILE"
Where to write a denomination key signing request file to be given to the auditor. Where to write a denomination key signing request file to be given to the auditor.
.B .B
.IP "\-r DKH, \-\-revoke=DKH"
Revoke the denomination key where the denomination public key's hash is DKH.
.B
.IP "\-t TIMESTAMP, \-\-time=TIMESTAMP" .IP "\-t TIMESTAMP, \-\-time=TIMESTAMP"
Operate as if the current time was TIMESTAMP. Operate as if the current time was TIMESTAMP.
.B .B

View File

@ -122,6 +122,7 @@ exchange_signkeys_check ()
* @param cls closure (NULL) * @param cls closure (NULL)
* @param dki the denomination key * @param dki the denomination key
* @param alias coin alias * @param alias coin alias
* @param was_revoked #GNUNET_YES if the @a dki was revoked and wallets should trigger /payback
* @return #GNUNET_OK to continue to iterate, * @return #GNUNET_OK to continue to iterate,
* #GNUNET_NO to stop iteration with no error, * #GNUNET_NO to stop iteration with no error,
* #GNUNET_SYSERR to abort iteration with error! * #GNUNET_SYSERR to abort iteration with error!
@ -129,7 +130,8 @@ exchange_signkeys_check ()
static int static int
denomkeys_iter (void *cls, denomkeys_iter (void *cls,
const char *alias, const char *alias,
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki) const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
int was_revoked)
{ {
struct GNUNET_HashCode hc; struct GNUNET_HashCode hc;
@ -190,7 +192,23 @@ denomkeys_iter (void *cls,
static int static int
exchange_denomkeys_check () exchange_denomkeys_check ()
{ {
struct TALER_MasterPublicKeyP master_public_key_from_cfg;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_data (kcfg,
"exchange",
"master_public_key",
&master_public_key_from_cfg,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"master_public_key");
global_ret = 1;
return GNUNET_NO;
}
if (0 > TALER_EXCHANGEDB_denomination_keys_iterate (exchange_directory, if (0 > TALER_EXCHANGEDB_denomination_keys_iterate (exchange_directory,
&master_public_key_from_cfg,
&denomkeys_iter, &denomkeys_iter,
NULL)) NULL))
return GNUNET_NO; return GNUNET_NO;

View File

@ -220,6 +220,11 @@ static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
*/ */
static struct GNUNET_TIME_Relative max_duration_spend; static struct GNUNET_TIME_Relative max_duration_spend;
/**
* Revoke denomination key identified by this hash (if non-zero).
*/
static struct GNUNET_HashCode revoke_dkh;
/** /**
* Return value from main(). * Return value from main().
*/ */
@ -1018,6 +1023,106 @@ create_wire_fees ()
} }
/**
* Closure for functions processing a request to revoke a denomination
* public key and request the wallets to initiate /payback.
*/
struct RevokeClosure
{
/**
* Hash of the denomination public key to revoke.
*/
const struct GNUNET_HashCode *hc;
/**
* Base directory for keys.
*/
char *basedir;
/**
* Set to #GNUNET_OK if we found a matching key,
* Set to #GNUNET_SYSERR on error.
*/
int ok;
};
/**
* Revoke denomination keys matching the given hash.
*
* @param cls a `struct RevokeClosure` with information about what to revoke
* @param dki the denomination key
* @param alias coin alias
* @param was_revoked #GNUNET_YES if the @a dki was revoked and wallets should trigger /payback
* @return #GNUNET_OK to continue to iterate,
* #GNUNET_NO to stop iteration with no error,
* #GNUNET_SYSERR to abort iteration with error!
*/
static int
exchange_keys_revoke_by_dki (void *cls,
const char *alias,
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
int was_revoked)
{
struct RevokeClosure *rc = cls;
if (GNUNET_YES == was_revoked)
return GNUNET_OK; /* refuse to do it twice */
if (0 != memcmp (rc->hc,
&dki->issue.properties.denom_hash,
sizeof (struct GNUNET_HashCode)))
return GNUNET_OK;
rc->ok = GNUNET_OK;
if (GNUNET_OK !=
TALER_EXCHANGEDB_denomination_key_revoke (rc->basedir,
alias,
dki,
&master_priv))
{
rc->ok = GNUNET_SYSERR;
return GNUNET_SYSERR;
}
return GNUNET_NO;
}
/**
* Revoke the denomination key matching @a hc and request /payback to be
* initiated.
*
* @param hc denomination key hash to revoke
* @return #GNUNET_OK on success,
* #GNUNET_NO if @a hc was not found
* #GNUNET_SYSERR on error
*/
static int
revoke_denomination (const struct GNUNET_HashCode *hc)
{
struct RevokeClosure rc;
rc.hc = hc;
rc.ok = GNUNET_NO;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (kcfg,
"exchange",
"KEYDIR",
&rc.basedir))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"KEYDIR");
return GNUNET_SYSERR;
}
TALER_EXCHANGEDB_denomination_keys_iterate (rc.basedir,
&master_public_key,
&exchange_keys_revoke_by_dki,
&rc);
GNUNET_free (rc.basedir);
return rc.ok;
}
/** /**
* Main function that will be run. * Main function that will be run.
* *
@ -1032,6 +1137,7 @@ run (void *cls,
const char *cfgfile, const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *cfg) const struct GNUNET_CONFIGURATION_Handle *cfg)
{ {
static struct GNUNET_HashCode zero;
struct GNUNET_TIME_Relative lookahead_sign; struct GNUNET_TIME_Relative lookahead_sign;
struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv;
@ -1119,7 +1225,7 @@ run (void *cls,
/* check if key from file matches the one from the configuration */ /* check if key from file matches the one from the configuration */
{ {
struct GNUNET_CRYPTO_EddsaPublicKey master_public_key_from_cfg; struct TALER_MasterPublicKeyP master_public_key_from_cfg;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_data (kcfg, GNUNET_CONFIGURATION_get_data (kcfg,
@ -1137,7 +1243,7 @@ run (void *cls,
if (0 != if (0 !=
memcmp (&master_public_key, memcmp (&master_public_key,
&master_public_key_from_cfg, &master_public_key_from_cfg,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) sizeof (struct TALER_MasterPublicKeyP)))
{ {
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
"exchange", "exchange",
@ -1190,6 +1296,15 @@ run (void *cls,
global_ret = 1; global_ret = 1;
return; return;
} }
if ( (0 != memcmp (&zero,
&revoke_dkh,
sizeof (zero))) &&
(GNUNET_OK !=
revoke_denomination (&revoke_dkh)) )
{
global_ret = 1;
return;
}
} }
@ -1226,6 +1341,11 @@ main (int argc,
"FILENAME", "FILENAME",
"auditor denomination key signing request file to create", "auditor denomination key signing request file to create",
&auditorrequestfile), &auditorrequestfile),
GNUNET_GETOPT_option_base32_auto ('r',
"revoke",
"DKH",
"revoke denomination key hash (DKH) and request wallets to initiate /payback",
&revoke_dkh),
GNUNET_GETOPT_option_absolute_time ('t', GNUNET_GETOPT_option_absolute_time ('t',
"time", "time",
"TIMESTAMP", "TIMESTAMP",

View File

@ -210,6 +210,7 @@ TALER_EXCHANGE_conf_duration_provide ()
* @param cls closure * @param cls closure
* @param dki the denomination key issue * @param dki the denomination key issue
* @param alias coin alias * @param alias coin alias
* @param was_revoked #GNUNET_YES if @a dki has been revoked
* @return #GNUNET_OK to continue to iterate, * @return #GNUNET_OK to continue to iterate,
* #GNUNET_NO to stop iteration with no error, * #GNUNET_NO to stop iteration with no error,
* #GNUNET_SYSERR to abort iteration with error! * #GNUNET_SYSERR to abort iteration with error!
@ -217,7 +218,8 @@ TALER_EXCHANGE_conf_duration_provide ()
static int static int
reload_keys_denom_iter (void *cls, reload_keys_denom_iter (void *cls,
const char *alias, const char *alias,
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki) const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
int was_revoked)
{ {
struct TEH_KS_StateHandle *ctx = cls; struct TEH_KS_StateHandle *ctx = cls;
struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute now;
@ -674,6 +676,7 @@ TEH_KS_acquire_ (const char *location)
"Loading keys from `%s'\n", "Loading keys from `%s'\n",
TEH_exchange_directory); TEH_exchange_directory);
if (-1 == TALER_EXCHANGEDB_denomination_keys_iterate (TEH_exchange_directory, if (-1 == TALER_EXCHANGEDB_denomination_keys_iterate (TEH_exchange_directory,
&TEH_master_public_key,
&reload_keys_denom_iter, &reload_keys_denom_iter,
key_state)) key_state))
{ {

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015, 2016 Inria & GNUnet e.V. Copyright (C) 2014-2017 Inria & GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -25,6 +25,68 @@
#include "taler_exchangedb_lib.h" #include "taler_exchangedb_lib.h"
/**
* Mark the given denomination key as revoked and request the wallets
* to initiate /payback.
*
* @param exchange_base_dir base directory for the exchange,
* the signing keys must be in the #TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS
* subdirectory
* @param alias coin alias
* @param dki the denomination key to revoke
* @param mpriv master private key to sign
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
*/
int
TALER_EXCHANGEDB_denomination_key_revoke (const char *exchange_base_dir,
const char *alias,
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
const struct TALER_MasterPrivateKeyP *mpriv)
{
struct GNUNET_TIME_Absolute start;
struct TALER_MasterDenominationKeyRevocation rm;
struct TALER_MasterSignatureP msig;
char *fn;
struct GNUNET_DISK_FileHandle *fh;
ssize_t wrote;
int ret;
ret = GNUNET_SYSERR;
start = GNUNET_TIME_absolute_ntoh (dki->issue.properties.start);
GNUNET_asprintf (&fn,
"%s" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "%llu.rev",
exchange_base_dir,
alias,
(unsigned long long) start.abs_value_us);
rm.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED);
rm.purpose.size = htonl (sizeof (rm));
rm.h_denom_pub = dki->issue.properties.denom_hash;
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_eddsa_sign (&mpriv->eddsa_priv,
&rm.purpose,
&msig.eddsa_signature));
if (NULL == (fh = GNUNET_DISK_file_open
(fn,
GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_TRUNCATE,
GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE)))
goto cleanup;
if (GNUNET_SYSERR ==
(wrote = GNUNET_DISK_file_write (fh,
&msig,
sizeof (msig))))
goto cleanup;
if (wrote != sizeof (msig))
goto cleanup;
ret = GNUNET_OK;
cleanup:
if (NULL != fh)
(void) GNUNET_DISK_file_close (fh);
GNUNET_free (fn);
return ret;
}
/** /**
* Import a denomination key from the given file. * Import a denomination key from the given file.
* *
@ -157,6 +219,11 @@ struct DenomkeysIterateContext
*/ */
const char *alias; const char *alias;
/**
* Master public key to use to validate revocations.
*/
const struct TALER_MasterPublicKeyP *master_pub;
/** /**
* Function to call on each denomination key. * Function to call on each denomination key.
*/ */
@ -187,6 +254,15 @@ denomkeys_iterate_keydir_iter (void *cls,
struct DenomkeysIterateContext *dic = cls; struct DenomkeysIterateContext *dic = cls;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation issue; struct TALER_EXCHANGEDB_DenominationKeyIssueInformation issue;
int ret; int ret;
char *rev;
struct TALER_MasterSignatureP msig;
struct TALER_MasterDenominationKeyRevocation rm;
int revoked;
if ( (strlen(filename) > strlen (".rev")) &&
(0 == strcmp (&filename[strlen(filename) - strlen (".rev")],
".rev")) )
return GNUNET_OK; /* ignore revocation files _here_; we'll try for them just below */
memset (&issue, 0, sizeof (issue)); memset (&issue, 0, sizeof (issue));
if (GNUNET_OK != if (GNUNET_OK !=
@ -198,9 +274,53 @@ denomkeys_iterate_keydir_iter (void *cls,
filename); filename);
return GNUNET_OK; return GNUNET_OK;
} }
/* check for revocation file */
GNUNET_asprintf (&rev,
"%s.rev",
filename);
revoked = GNUNET_NO;
if (GNUNET_YES == GNUNET_DISK_file_test (rev))
{
/* Check if revocation is valid... */
if (sizeof (msig) !=
GNUNET_DISK_fn_read (rev,
&msig,
sizeof (msig)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Invalid revocation file `%s' found and ignored (bad size)\n"),
rev);
}
else
{
rm.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED);
rm.purpose.size = htonl (sizeof (rm));
rm.h_denom_pub = issue.issue.properties.denom_hash;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED,
&rm.purpose,
&msig.eddsa_signature,
&dic->master_pub->eddsa_pub))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Invalid revocation file `%s' found and ignored (bad signature)\n"),
rev);
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Denomination key `%s' was revoked!\n",
filename);
revoked = GNUNET_YES;
}
}
}
GNUNET_free (rev);
ret = dic->it (dic->it_cls, ret = dic->it (dic->it_cls,
dic->alias, dic->alias,
&issue); &issue,
revoked);
GNUNET_CRYPTO_rsa_private_key_free (issue.denom_priv.rsa_private_key); GNUNET_CRYPTO_rsa_private_key_free (issue.denom_priv.rsa_private_key);
GNUNET_CRYPTO_rsa_public_key_free (issue.denom_pub.rsa_public_key); GNUNET_CRYPTO_rsa_public_key_free (issue.denom_pub.rsa_public_key);
return ret; return ret;
@ -238,6 +358,7 @@ denomkeys_iterate_topdir_iter (void *cls,
* @param exchange_base_dir base directory for the exchange, * @param exchange_base_dir base directory for the exchange,
* the signing keys must be in the #TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS * the signing keys must be in the #TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS
* subdirectory * subdirectory
* @param master_pub master public key (used to check revocations)
* @param it function to call on each denomination key found * @param it function to call on each denomination key found
* @param it_cls closure for @a it * @param it_cls closure for @a it
* @return -1 on error, 0 if no files were found, otherwise * @return -1 on error, 0 if no files were found, otherwise
@ -247,6 +368,7 @@ denomkeys_iterate_topdir_iter (void *cls,
*/ */
int int
TALER_EXCHANGEDB_denomination_keys_iterate (const char *exchange_base_dir, TALER_EXCHANGEDB_denomination_keys_iterate (const char *exchange_base_dir,
const struct TALER_MasterPublicKeyP *master_pub,
TALER_EXCHANGEDB_DenominationKeyIterator it, TALER_EXCHANGEDB_DenominationKeyIterator it,
void *it_cls) void *it_cls)
{ {
@ -257,6 +379,7 @@ TALER_EXCHANGEDB_denomination_keys_iterate (const char *exchange_base_dir,
GNUNET_asprintf (&dir, GNUNET_asprintf (&dir,
"%s" DIR_SEPARATOR_STR TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS, "%s" DIR_SEPARATOR_STR TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS,
exchange_base_dir); exchange_base_dir);
dic.master_pub = master_pub;
dic.it = it; dic.it = it;
dic.it_cls = it_cls; dic.it_cls = it_cls;
ret = GNUNET_DISK_directory_scan (dir, ret = GNUNET_DISK_directory_scan (dir,

View File

@ -160,8 +160,9 @@ TALER_EXCHANGEDB_signing_key_write (const char *exchange_base_dir,
* @brief Iterator over denomination keys. * @brief Iterator over denomination keys.
* *
* @param cls closure * @param cls closure
* @param dki the denomination key
* @param alias coin alias * @param alias coin alias
* @param dki the denomination key
* @param was_revoked #GNUNET_YES if the @a dki was revoked and wallets should trigger /payback
* @return #GNUNET_OK to continue to iterate, * @return #GNUNET_OK to continue to iterate,
* #GNUNET_NO to stop iteration with no error, * #GNUNET_NO to stop iteration with no error,
* #GNUNET_SYSERR to abort iteration with error! * #GNUNET_SYSERR to abort iteration with error!
@ -169,7 +170,8 @@ TALER_EXCHANGEDB_signing_key_write (const char *exchange_base_dir,
typedef int typedef int
(*TALER_EXCHANGEDB_DenominationKeyIterator)(void *cls, (*TALER_EXCHANGEDB_DenominationKeyIterator)(void *cls,
const char *alias, const char *alias,
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki); const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
int was_revoked);
/** /**
@ -178,6 +180,7 @@ typedef int
* @param exchange_base_dir base directory for the exchange, * @param exchange_base_dir base directory for the exchange,
* the signing keys must be in the #TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS * the signing keys must be in the #TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS
* subdirectory * subdirectory
* @param master_pub master public key (used to check revocations)
* @param it function to call on each denomination key found * @param it function to call on each denomination key found
* @param it_cls closure for @a it * @param it_cls closure for @a it
* @return -1 on error, 0 if no files were found, otherwise * @return -1 on error, 0 if no files were found, otherwise
@ -187,10 +190,30 @@ typedef int
*/ */
int int
TALER_EXCHANGEDB_denomination_keys_iterate (const char *exchange_base_dir, TALER_EXCHANGEDB_denomination_keys_iterate (const char *exchange_base_dir,
const struct TALER_MasterPublicKeyP *master_pub,
TALER_EXCHANGEDB_DenominationKeyIterator it, TALER_EXCHANGEDB_DenominationKeyIterator it,
void *it_cls); void *it_cls);
/**
* Mark the given denomination key as revoked and request the wallets
* to initiate /payback.
*
* @param exchange_base_dir base directory for the exchange,
* the signing keys must be in the #TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS
* subdirectory
* @param alias coin alias
* @param dki the denomination key to revoke
* @param mpriv master private key to sign
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
*/
int
TALER_EXCHANGEDB_denomination_key_revoke (const char *exchange_base_dir,
const char *alias,
const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
const struct TALER_MasterPrivateKeyP *mpriv);
/** /**
* Exports a denomination key to the given file. * Exports a denomination key to the given file.
* *

View File

@ -83,6 +83,10 @@
*/ */
#define TALER_SIGNATURE_MASTER_WIRE_FEES 1028 #define TALER_SIGNATURE_MASTER_WIRE_FEES 1028
/**
* The given revocation key was revoked and must no longer be used.
*/
#define TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED 1029
/*********************************************/ /*********************************************/
/* Exchange online signatures (with signing key) */ /* Exchange online signatures (with signing key) */
@ -937,6 +941,24 @@ struct TALER_MasterWireFeePS
}; };
/**
* @brief Message confirming that a denomination key was revoked.
*/
struct TALER_MasterDenominationKeyRevocation
{
/**
* Purpose is #TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Hash of the denomination key.
*/
struct GNUNET_HashCode h_denom_pub;
};
/** /**
* @brief Format used to generate the signature on a request to obtain * @brief Format used to generate the signature on a request to obtain
* the wire transfer identifier associated with a deposit. * the wire transfer identifier associated with a deposit.