/* This file is part of TALER (C) 2020, 2021 Taler Systems SA 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 Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file util/test_helper_rsa.c * @brief Tests for RSA crypto helper * @author Christian Grothoff */ #include "platform.h" #include "taler_util.h" /** * Configuration has 1 minute duration and 5 minutes lookahead, but * we do not get 'revocations' for expired keys. So this must be * large enough to deal with key rotation during the runtime of * the benchmark. */ #define MAX_KEYS 1024 /** * How many random key revocations should we test? */ #define NUM_REVOKES 3 /** * How many iterations of the successful signing test should we run? */ #define NUM_SIGN_TESTS 5 /** * How many iterations of the successful signing test should we run * during the benchmark phase? */ #define NUM_SIGN_PERFS 100 /** * How many parallel clients should we use for the parallel * benchmark? (> 500 may cause problems with the max open FD number limit). */ #define NUM_CORES 8 /** * Number of keys currently in #keys. */ static unsigned int num_keys; /** * Keys currently managed by the helper. */ struct KeyData { /** * Validity start point. */ struct GNUNET_TIME_Timestamp start_time; /** * Key expires for signing at @e start_time plus this value. */ struct GNUNET_TIME_Relative validity_duration; /** * Hash of the public key. */ struct TALER_RsaPubHashP h_rsa; /** * Full public key. */ struct TALER_DenominationPublicKey denom_pub; /** * Is this key currently valid? */ bool valid; /** * Did the test driver revoke this key? */ bool revoked; }; /** * Array of all the keys we got from the helper. */ static struct KeyData keys[MAX_KEYS]; /** * Release memory occupied by #keys. */ static void free_keys (void) { for (unsigned int i = 0; i 0); num_keys--; } } /** * Function called with information about available keys for signing. Usually * only called once per key upon connect. Also called again in case a key is * being revoked, in that case with an @a end_time of zero. Stores the keys * status in #keys. * * @param cls closure, NULL * @param section_name name of the denomination type in the configuration; * NULL if the key has been revoked or purged * @param start_time when does the key become available for signing; * zero if the key has been revoked or purged * @param validity_duration how long does the key remain available for signing; * zero if the key has been revoked or purged * @param h_rsa hash of the @a denom_pub that is available (or was purged) * @param denom_pub the public key itself, NULL if the key was revoked or purged * @param sm_pub public key of the security module, NULL if the key was revoked or purged * @param sm_sig signature from the security module, NULL if the key was revoked or purged * The signature was already verified against @a sm_pub. */ static void key_cb (void *cls, const char *section_name, struct GNUNET_TIME_Timestamp start_time, struct GNUNET_TIME_Relative validity_duration, const struct TALER_RsaPubHashP *h_rsa, const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_SecurityModulePublicKeyP *sm_pub, const struct TALER_SecurityModuleSignatureP *sm_sig) { (void) cls; (void) sm_pub; (void) sm_sig; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Key notification about key %s in `%s'\n", GNUNET_h2s (&h_rsa->hash), section_name); if (0 == validity_duration.rel_value_us) { bool found = false; GNUNET_break (NULL == denom_pub); GNUNET_break (NULL == section_name); for (unsigned int i = 0; i 0); num_keys--; found = true; break; } if (! found) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: helper announced expiration of unknown key!\n"); return; } GNUNET_break (NULL != denom_pub); 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; } { struct TALER_DenominationSignature rs; if (GNUNET_OK != TALER_denom_sig_unblind (&rs, &ds, &ps.blinding_key, &keys[i].denom_pub)) { GNUNET_break (0); return 6; } TALER_blinded_denom_sig_free (&ds); if (GNUNET_OK != TALER_denom_pub_verify (&keys[i].denom_pub, &rs, &c_hash)) { /* signature invalid */ GNUNET_break (0); TALER_denom_sig_free (&rs); return 7; } TALER_denom_sig_free (&rs); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received valid signature for key %s\n", GNUNET_h2s (&keys[i].h_rsa.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 signing does not work if the key is unknown */ { struct TALER_RsaPubHashP rnd; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &rnd, sizeof (rnd)); ds = TALER_CRYPTO_helper_rsa_sign (dh, &rnd, "Hello", strlen ("Hello"), &ec); 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. * * @param dh handle to the helper * @return 0 on success */ static int perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh, const char *type) { struct TALER_BlindedDenominationSignature ds; enum TALER_ErrorCode ec; struct GNUNET_TIME_Relative duration; struct TALER_PlanchetSecretsP ps; struct TALER_ExchangeWithdrawValues alg_values; alg_values.cipher = TALER_DENOMINATION_RSA; TALER_planchet_setup_random (&ps, &alg_values); duration = GNUNET_TIME_UNIT_ZERO; TALER_CRYPTO_helper_rsa_poll (dh); for (unsigned int j = 0; j, GNUNET_TIME_UNIT_SECONDS)) continue; if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( keys[i].start_time.abs_time), >, keys[i].validity_duration)) continue; { struct TALER_CoinPubHash c_hash; struct TALER_PlanchetDetail pd; GNUNET_assert (GNUNET_YES == TALER_planchet_prepare (&keys[i].denom_pub, &alg_values, &ps, &c_hash, &pd)); /* use this key as long as it works */ while (1) { struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get (); struct GNUNET_TIME_Relative delay; ds = TALER_CRYPTO_helper_rsa_sign (dh, &keys[i].h_rsa, pd.blinded_planchet.details. rsa_blinded_planchet.blinded_msg, pd.blinded_planchet.details. rsa_blinded_planchet. blinded_msg_size, &ec); if (TALER_EC_NONE != ec) break; delay = GNUNET_TIME_absolute_get_duration (start); duration = GNUNET_TIME_relative_add (duration, delay); TALER_blinded_denom_sig_free (&ds); j++; if (NUM_SIGN_PERFS <= j) break; } TALER_blinded_planchet_free (&pd.blinded_planchet); } } /* for i */ } /* for j */ fprintf (stderr, "%u (%s) signature operations took %s\n", (unsigned int) NUM_SIGN_PERFS, type, GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES)); return 0; } /** * Parallel signing logic. * * @param esh handle to the helper * @return 0 on success */ static int par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_TIME_Absolute start; struct GNUNET_TIME_Relative duration; pid_t pids[NUM_CORES]; struct TALER_CRYPTO_RsaDenominationHelper *dh; start = GNUNET_TIME_absolute_get (); for (unsigned int i = 0; i