From 106664ed0c50621bd20568c948ad30fccd0689ea Mon Sep 17 00:00:00 2001 From: Lucien Heuzeveldt Date: Tue, 4 Jan 2022 17:26:01 +0100 Subject: implement TALER_CRYPTO_helper_cs_r_derive and related tests --- src/include/taler_crypto_lib.h | 28 +++++- src/util/crypto_helper_cs.c | 195 ++++++++++++++++++++++++++++++++++++ src/util/taler-exchange-secmod-cs.c | 3 +- src/util/test_helper_cs.c | 132 +++++++++++++++++++++++- 4 files changed, 353 insertions(+), 5 deletions(-) diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index bd889b354..a20e51204 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -1800,7 +1800,7 @@ TALER_CRYPTO_helper_cs_sign ( /** - * Ask the helper to revoke the public key associated with @param h_denom_pub . + * Ask the helper to revoke the public key associated with @param h_cs . * Will cause the helper to tell all clients that the key is now unavailable, * and to create a replacement key. * @@ -1812,7 +1812,7 @@ TALER_CRYPTO_helper_cs_sign ( * callback. * * @param dh helper to process connection - * @param h_rsa hash of the RSA public key to revoke + * @param h_cs hash of the CS public key to revoke */ void TALER_CRYPTO_helper_cs_revoke ( @@ -1820,6 +1820,30 @@ TALER_CRYPTO_helper_cs_revoke ( const struct TALER_CsPubHashP *h_cs); +/** + * Ask the helper to derive R using the @param nonce and denomination key + * associated with @param h_cs. + * + * This operation will block until the R has been obtained. Should + * this process receive a signal (that is not ignored) while the operation is + * pending, the operation will fail. Note that the helper may still believe + * that it created the signature. Thus, signals may result in a small + * differences in the signature counters. Retrying in this case may work. + * + * @param dh helper to process connection + * @param h_cs hash of the CS public key to revoke + * @param nonce witdhraw nonce + * @param[out] ec set to the error code (or #TALER_EC_NONE on success) + * @return R, the value inside the structure will be NULL on failure, + * see @a ec for details about the failure + */ +struct TALER_DenominationCsPublicR +TALER_CRYPTO_helper_cs_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh, + const struct TALER_CsPubHashP *h_cs, + const struct TALER_WithdrawNonce *nonce, + enum TALER_ErrorCode *ec); + + /** * Close connection to @a dh. * diff --git a/src/util/crypto_helper_cs.c b/src/util/crypto_helper_cs.c index 95050a1f5..5b2999348 100644 --- a/src/util/crypto_helper_cs.c +++ b/src/util/crypto_helper_cs.c @@ -633,6 +633,201 @@ TALER_CRYPTO_helper_cs_revoke ( } +struct TALER_DenominationCsPublicR +TALER_CRYPTO_helper_cs_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh, + const struct TALER_CsPubHashP *h_cs, + const struct TALER_WithdrawNonce *nonce, + enum TALER_ErrorCode *ec) +{ + struct TALER_DenominationCsPublicR r_pub; + + memset (&r_pub, + 0, + sizeof (r_pub)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting R derivation process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + return r_pub; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting R\n"); + { + struct TALER_CRYPTO_CsRDeriveRequest rdr; + + rdr.header.size = htons (sizeof (rdr)); + rdr.header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE); + rdr.reserved = htonl (0); + rdr.h_cs = *h_cs; + rdr.nonce = *nonce; + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + &rdr, + sizeof (rdr))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + return r_pub; + } + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply\n"); + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + *ec = TALER_EC_INVALID; + while (1) + { + uint16_t msize; + ssize_t ret; + + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + return r_pub; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + break; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + *ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + return r_pub; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_RES_RDERIVE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + { + const struct TALER_CRYPTO_RDeriveResponse *rdr = + (const struct TALER_CRYPTO_RDeriveResponse *) buf; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received R\n"); + *ec = TALER_EC_NONE; + finished = true; + r_pub = rdr->r_pub; + break; + } + case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + { + const struct TALER_CRYPTO_RDeriveFailure *rdf = + (const struct TALER_CRYPTO_RDeriveFailure *) buf; + + *ec = (enum TALER_ErrorCode) ntohl (rdf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "R derivation failed!\n"); + finished = true; + break; + } + case TALER_HELPER_CS_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with CS helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + goto end; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ +end: + return r_pub; + } +} + + void TALER_CRYPTO_helper_cs_disconnect ( struct TALER_CRYPTO_CsDenominationHelper *dh) diff --git a/src/util/taler-exchange-secmod-cs.c b/src/util/taler-exchange-secmod-cs.c index 14f0a5d10..0df7c3ddf 100644 --- a/src/util/taler-exchange-secmod-cs.c +++ b/src/util/taler-exchange-secmod-cs.c @@ -618,7 +618,6 @@ handle_r_derive_request (struct TES_Client *client, &rdf.header); } - // TODO: print nonce too? GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received request to derive R with key %s\n", GNUNET_h2s (&rdr->h_cs.hash)); @@ -692,7 +691,7 @@ cs_work_dispatch (struct TES_Client *client, return handle_revoke_request ( client, (const struct TALER_CRYPTO_CsRevokeRequest *) hdr); - case TALER_HELPER_CS_MT_RES_RDERIVE: + case TALER_HELPER_CS_MT_REQ_RDERIVE: if (msize != sizeof (struct TALER_CRYPTO_CsRDeriveRequest)) { GNUNET_break_op (0); diff --git a/src/util/test_helper_cs.c b/src/util/test_helper_cs.c index 41d363fdc..d59c21637 100644 --- a/src/util/test_helper_cs.c +++ b/src/util/test_helper_cs.c @@ -255,6 +255,133 @@ test_revocation (struct TALER_CRYPTO_CsDenominationHelper *dh) } +/** + * Test R derivation logic. + * + * @param dh handle to the helper + * @return 0 on success + */ +static int +test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) +{ + struct TALER_DenominationCsPublicR r_pub; + enum TALER_ErrorCode ec; + bool success = false; + struct TALER_PlanchetSecretsP ps; + struct TALER_CoinPubHash c_hash; + + TALER_planchet_setup_random (&ps, TALER_DENOMINATION_RSA); + for (unsigned int i = 0; i, + GNUNET_TIME_UNIT_SECONDS)) + { + /* key worked too early */ + GNUNET_break (0); + return 4; + } + if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( + keys[i].start_time.abs_time), + >, + keys[i].validity_duration)) + { + /* key worked too later */ + GNUNET_break (0); + return 5; + } + + // since R is part of the signature creation process, it can't be tested fully here + // instead it will be further tested in the signature creation process + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received valid R for key %s\n", + GNUNET_h2s (&keys[i].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[i].start_time.abs_time))) && + (GNUNET_TIME_relative_cmp ( + GNUNET_TIME_absolute_get_duration ( + keys[i].start_time.abs_time), + <, + keys[i].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 R derivation does not work if the key is unknown */ + { + struct TALER_CsPubHashP rnd; + struct TALER_WithdrawNonce nonce; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &rnd, + sizeof (rnd)); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &nonce, + sizeof (nonce)); + r_pub = TALER_CRYPTO_helper_cs_r_derive (dh, + &rnd, + &nonce, + &ec); + if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) + { + GNUNET_break (0); + return 17; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "R derivation with invalid key %s failed as desired\n", + GNUNET_h2s (&rnd.hash)); + } + return 0; +} + + /** * Test signing logic. * @@ -600,9 +727,11 @@ run_test (void) " Done (%u keys)\n", num_keys); ret = 0; - // TODO: implement other tests if (0 == ret) ret = test_revocation (dh); + if (0 == ret) + ret = test_r_derive (dh); + // TODO: implement other tests // if (0 == ret) // ret = test_signing (dh); // if (0 == ret) @@ -685,6 +814,7 @@ main (int argc, (int) code); ret = 5; } + // TODO: remove GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "I am here"); GNUNET_OS_process_destroy (helper); -- cgit v1.2.3