add test for batch CS derive/sign logic

This commit is contained in:
Christian Grothoff 2022-11-13 21:45:43 +01:00
parent 390d241019
commit 231cdaf4f7
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
3 changed files with 290 additions and 29 deletions

View File

@ -512,6 +512,28 @@ fail_sign (struct TES_Client *client,
} }
/**
* Generate error response that deriving failed.
*
* @param client client to send response to
* @param ec error code to include
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
fail_derive (struct TES_Client *client,
enum TALER_ErrorCode ec)
{
struct TALER_CRYPTO_RDeriveFailure sf = {
.header.size = htons (sizeof (sf)),
.header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
.ec = htonl (ec)
};
return TES_transmit (client->csock,
&sf.header);
}
/** /**
* Generate signature response. * Generate signature response.
* *
@ -842,19 +864,25 @@ finish_job (struct TES_Client *client,
{ {
sem_down (&bj->sem); sem_down (&bj->sem);
sem_done (&bj->sem); sem_done (&bj->sem);
switch (bj->type)
{
case TYPE_SIGN:
if (TALER_EC_NONE != bj->ec) if (TALER_EC_NONE != bj->ec)
{ {
fail_sign (client, fail_sign (client,
bj->ec); bj->ec);
return; return;
} }
switch (bj->type)
{
case TYPE_SIGN:
send_signature (client, send_signature (client,
&bj->details.sign.cs_answer); &bj->details.sign.cs_answer);
break; break;
case TYPE_RDERIVE: case TYPE_RDERIVE:
if (TALER_EC_NONE != bj->ec)
{
fail_derive (client,
bj->ec);
return;
}
send_derivation (client, send_derivation (client,
&bj->details.rderive.rpairp); &bj->details.rderive.rpairp);
break; break;
@ -878,16 +906,19 @@ handle_batch_sign_request (struct TES_Client *client,
uint16_t size = ntohs (bsr->header.size) - sizeof (*bsr); uint16_t size = ntohs (bsr->header.size) - sizeof (*bsr);
const void *off = (const void *) &bsr[1]; const void *off = (const void *) &bsr[1];
unsigned int idx = 0; unsigned int idx = 0;
struct BatchJob jobs[bs]; struct BatchJob jobs[GNUNET_NZL (bs)];
bool failure = false; bool failure = false;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Handling batch sign request of size %u\n",
(unsigned int) bs);
if (bs > TALER_MAX_FRESH_COINS) if (bs > TALER_MAX_FRESH_COINS)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
while ( (bs > 0) && while ( (bs > 0) &&
(size > sizeof (struct TALER_CRYPTO_CsSignRequestMessage)) ) (size >= sizeof (struct TALER_CRYPTO_CsSignRequestMessage)) )
{ {
const struct TALER_CRYPTO_CsSignRequestMessage *sr = off; const struct TALER_CRYPTO_CsSignRequestMessage *sr = off;
uint16_t s = ntohs (sr->header.size); uint16_t s = ntohs (sr->header.size);
@ -903,6 +934,9 @@ handle_batch_sign_request (struct TES_Client *client,
off += s; off += s;
size -= s; size -= s;
} }
GNUNET_break_op (0 == size);
bs = GNUNET_MIN (bs,
idx);
for (unsigned int i = 0; i<bs; i++) for (unsigned int i = 0; i<bs; i++)
finish_job (client, finish_job (client,
&jobs[i]); &jobs[i]);
@ -941,13 +975,16 @@ handle_batch_derive_request (struct TES_Client *client,
struct BatchJob jobs[bs]; struct BatchJob jobs[bs];
bool failure = false; bool failure = false;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Handling batch derivation request of size %u\n",
(unsigned int) bs);
if (bs > TALER_MAX_FRESH_COINS) if (bs > TALER_MAX_FRESH_COINS)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
while ( (bs > 0) && while ( (bs > 0) &&
(size > sizeof (struct TALER_CRYPTO_CsRDeriveRequest)) ) (size >= sizeof (struct TALER_CRYPTO_CsRDeriveRequest)) )
{ {
const struct TALER_CRYPTO_CsRDeriveRequest *rdr = off; const struct TALER_CRYPTO_CsRDeriveRequest *rdr = off;
uint16_t s = ntohs (rdr->header.size); uint16_t s = ntohs (rdr->header.size);
@ -964,20 +1001,17 @@ handle_batch_derive_request (struct TES_Client *client,
off += s; off += s;
size -= s; size -= s;
} }
GNUNET_break_op (0 == size);
bs = GNUNET_MIN (bs,
idx);
for (unsigned int i = 0; i<bs; i++) for (unsigned int i = 0; i<bs; i++)
finish_job (client, finish_job (client,
&jobs[i]); &jobs[i]);
if (failure) if (failure)
{ {
struct TALER_CRYPTO_SignFailure sf = {
.header.size = htons (sizeof (sf)),
.header.type = htons (TALER_HELPER_CS_MT_RES_BATCH_RDERIVE_FAILURE),
.ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
};
GNUNET_break (0); GNUNET_break (0);
return TES_transmit (client->csock, return fail_derive (client,
&sf.header); TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE);
} }
return GNUNET_OK; return GNUNET_OK;
} }
@ -1219,14 +1253,8 @@ handle_r_derive_request (struct TES_Client *client,
&r_pub); &r_pub);
if (TALER_EC_NONE != ec) if (TALER_EC_NONE != ec)
{ {
struct TALER_CRYPTO_RDeriveFailure rdf = { return fail_derive (client,
.header.size = htons (sizeof (rdf)), ec);
.header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
.ec = htonl (ec)
};
return TES_transmit (client->csock,
&rdf.header);
} }
ret = send_derivation (client, ret = send_derivation (client,

View File

@ -766,6 +766,9 @@ handle_batch_sign_request (struct TES_Client *client,
off += s; off += s;
size -= s; size -= s;
} }
GNUNET_break_op (0 == size);
bs = GNUNET_MIN (bs,
idx);
for (unsigned int i = 0; i<bs; i++) for (unsigned int i = 0; i<bs; i++)
finish_job (client, finish_job (client,
&jobs[i]); &jobs[i]);

View File

@ -439,8 +439,6 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh)
}; };
pd.blinded_planchet.cipher = TALER_DENOMINATION_CS; pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
// keys[i].denom_pub.cipher = TALER_DENOMINATION_CS;
TALER_cs_withdraw_nonce_derive (&ps, TALER_cs_withdraw_nonce_derive (&ps,
&pd.blinded_planchet.details. &pd.blinded_planchet.details.
cs_blinded_planchet.nonce); cs_blinded_planchet.nonce);
@ -592,6 +590,209 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh)
} }
/**
* Test batch signing logic.
*
* @param dh handle to the helper
* @param batch_size how large should the batch be
* @param check_sigs also check unknown key and signatures
* @return 0 on success
*/
static int
test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
unsigned int batch_size,
bool check_sigs)
{
struct TALER_BlindedDenominationSignature ds[batch_size];
enum TALER_ErrorCode ec;
bool success = false;
struct TALER_PlanchetMasterSecretP ps[batch_size];
struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size];
union TALER_DenominationBlindingKeyP bks[batch_size];
struct TALER_CoinPubHashP c_hash[batch_size];
struct TALER_ExchangeWithdrawValues alg_values[batch_size];
for (unsigned int i = 0; i<batch_size; i++)
TALER_planchet_master_setup_random (&ps[i]);
for (unsigned int k = 0; k<MAX_KEYS; k++)
{
if (! keys[k].valid)
continue;
{
struct TALER_PlanchetDetail pd[batch_size];
struct TALER_CRYPTO_CsSignRequest csr[batch_size];
struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size];
struct TALER_DenominationCSPublicRPairP crps[batch_size];
for (unsigned int i = 0; i<batch_size; i++)
{
cdr[i].h_cs = &keys[k].h_cs;
cdr[i].nonce =
&pd[i].blinded_planchet.details.cs_blinded_planchet.nonce;
pd[i].blinded_planchet.cipher = TALER_DENOMINATION_CS;
TALER_cs_withdraw_nonce_derive (
&ps[i],
&pd[i].blinded_planchet.details.cs_blinded_planchet.nonce);
alg_values[i].cipher = TALER_DENOMINATION_CS;
}
ec = TALER_CRYPTO_helper_cs_r_batch_derive_withdraw (
dh,
cdr,
batch_size,
crps);
if (TALER_EC_NONE != ec)
continue;
for (unsigned int i = 0; i<batch_size; i++)
{
alg_values[i].details.cs_values = crps[i];
TALER_planchet_setup_coin_priv (&ps[i],
&alg_values[i],
&coin_priv[i]);
TALER_planchet_blinding_secret_create (&ps[i],
&alg_values[i],
&bks[i]);
GNUNET_assert (GNUNET_YES ==
TALER_planchet_prepare (&keys[k].denom_pub,
&alg_values[i],
&bks[i],
&coin_priv[i],
NULL, /* no age commitment */
&c_hash[i],
&pd[i]));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Requesting signature with key %s\n",
GNUNET_h2s (&keys[k].h_cs.hash));
csr[i].h_cs = &keys[k].h_cs;
csr[i].blinded_planchet
= &pd[i].blinded_planchet.details.cs_blinded_planchet;
}
ec = TALER_CRYPTO_helper_cs_batch_sign_withdraw (
dh,
csr,
batch_size,
ds);
}
switch (ec)
{
case TALER_EC_NONE:
if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
keys[k].start_time.abs_time),
>,
GNUNET_TIME_UNIT_SECONDS))
{
/* key worked too early */
GNUNET_break (0);
return 4;
}
if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
keys[k].start_time.abs_time),
>,
keys[k].validity_duration))
{
/* key worked too later */
GNUNET_break (0);
return 5;
}
if (check_sigs)
{
for (unsigned int i = 0; i<batch_size; i++)
{
struct TALER_FreshCoin coin;
if (GNUNET_OK !=
TALER_planchet_to_coin (&keys[k].denom_pub,
&ds[i],
&bks[i],
&coin_priv[i],
NULL, /* no age commitment */
&c_hash[i],
&alg_values[i],
&coin))
{
GNUNET_break (0);
return 6;
}
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received valid signature for key %s\n",
GNUNET_h2s (&keys[k].h_cs.hash));
}
success = true;
break;
case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
/* This 'failure' is expected, we're testing also for the
error handling! */
if ( (GNUNET_TIME_relative_is_zero (
GNUNET_TIME_absolute_get_remaining (
keys[k].start_time.abs_time))) &&
(GNUNET_TIME_relative_cmp (
GNUNET_TIME_absolute_get_duration (
keys[k].start_time.abs_time),
<,
keys[k].validity_duration)) )
{
/* key should have worked! */
GNUNET_break (0);
return 6;
}
break;
default:
/* unexpected error */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected error %d\n",
ec);
return 7;
}
}
if (! success)
{
/* no valid key for signing found, also bad */
GNUNET_break (0);
return 16;
}
/* check signing does not work if the key is unknown */
if (check_sigs)
{
struct TALER_PlanchetDetail pd;
struct TALER_CsPubHashP rnd;
struct TALER_CRYPTO_CsSignRequest csr;
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&rnd,
sizeof (rnd));
pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
GNUNET_assert (GNUNET_YES ==
TALER_planchet_prepare (&keys[0].denom_pub,
&alg_values[0],
&bks[0],
&coin_priv[0],
NULL, /* no age commitment */
&c_hash[0],
&pd));
csr.h_cs = &rnd;
csr.blinded_planchet
= &pd.blinded_planchet.details.cs_blinded_planchet;
ec = TALER_CRYPTO_helper_cs_batch_sign_withdraw (
dh,
&csr,
1,
&ds[0]);
if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
{
if (TALER_EC_NONE == ec)
TALER_blinded_denom_sig_free (ds);
GNUNET_break (0);
return 17;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Signing with invalid key %s failed as desired\n",
GNUNET_h2s (&rnd.hash));
}
return 0;
}
/** /**
* Benchmark signing logic. * Benchmark signing logic.
* *
@ -812,6 +1013,34 @@ run_test (void)
ret = test_r_derive (dh); ret = test_r_derive (dh);
if (0 == ret) if (0 == ret)
ret = test_signing (dh); ret = test_signing (dh);
if (0 == ret)
ret = test_batch_signing (dh,
2,
true);
if (0 == ret)
ret = test_batch_signing (dh,
256,
true);
for (unsigned int i = 0; i<5; i++)
{
static unsigned int batches[] = { 1, 4, 16, 64, 256 };
unsigned int batch_size = batches[i];
struct GNUNET_TIME_Absolute start;
struct GNUNET_TIME_Relative duration;
start = GNUNET_TIME_absolute_get ();
if (0 != ret)
break;
ret = test_batch_signing (dh,
batch_size,
false);
duration = GNUNET_TIME_absolute_get_duration (start);
fprintf (stderr,
"%4u (batch) signature operations took %s (total real time)\n",
(unsigned int) batch_size,
GNUNET_STRINGS_relative_time_to_string (duration,
GNUNET_YES));
}
if (0 == ret) if (0 == ret)
ret = perf_signing (dh, ret = perf_signing (dh,
"sequential"); "sequential");
@ -835,13 +1064,14 @@ main (int argc,
int ret; int ret;
enum GNUNET_OS_ProcessStatusType type; enum GNUNET_OS_ProcessStatusType type;
unsigned long code; unsigned long code;
const char *loglev = "WARNING";
(void) argc; (void) argc;
(void) argv; (void) argv;
unsetenv ("XDG_DATA_HOME"); unsetenv ("XDG_DATA_HOME");
unsetenv ("XDG_CONFIG_HOME"); unsetenv ("XDG_CONFIG_HOME");
GNUNET_log_setup ("test-helper-cs", GNUNET_log_setup ("test-helper-cs",
"WARNING", loglev,
NULL); NULL);
GNUNET_OS_init (TALER_project_data_default ()); GNUNET_OS_init (TALER_project_data_default ());
libexec_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR); libexec_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
@ -857,7 +1087,7 @@ main (int argc,
"-c", "-c",
"test_helper_cs.conf", "test_helper_cs.conf",
"-L", "-L",
"WARNING", loglev,
NULL); NULL);
if (NULL == helper) if (NULL == helper)
{ {