aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-10-31 14:02:54 +0100
committerChristian Grothoff <christian@grothoff.org>2017-10-31 14:02:54 +0100
commit2f2930f1ba0f1708fc4455c66173fd61188a3369 (patch)
treed09c8fa13d7bbd90fd94ceaa2d642e6f2693a9d3 /src/util
parentec8146de92d92db2cb0e9453d5ef1b538b852354 (diff)
major API refactoring, adding planchet generation and coin extraction APIs to the Taler crypto library, thereby simplifying code in withdraw, refresh, tipping, payback and testcases; slight API incompatibilities to previous versions are introduced
Diffstat (limited to 'src/util')
-rw-r--r--src/util/crypto.c137
-rw-r--r--src/util/test_crypto.c51
2 files changed, 165 insertions, 23 deletions
diff --git a/src/util/crypto.c b/src/util/crypto.c
index 378b73dc4..efc74850d 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -171,23 +171,49 @@ TALER_link_recover_transfer_secret (const struct TALER_TransferPublicKeyP *trans
/**
+ * Set the bits in the private EdDSA key so that they match
+ * the specification.
+ *
+ * @param[in,out] pk private key to patch
+ */
+static void
+patch_private_key (struct GNUNET_CRYPTO_EddsaPrivateKey *pk)
+{
+ uint8_t *p = (uint8_t *) pk;
+
+ /* Taken from like 170-172 of libgcrypt/cipher/ecc.c
+ * We note that libgcrypt stores the private key in the reverse order
+ * from many Ed25519 implementatons. */
+ p[0] &= 0x7f; /* Clear bit 255. */
+ p[0] |= 0x40; /* Set bit 254. */
+ p[31] &= 0xf8; /* Clear bits 2..0 so that d mod 8 == 0 */
+
+ /* FIXME: Run GNUNET_CRYPTO_ecdhe_key_create several times and inspect
+ * the output to verify that the same bits are set and cleared.
+ * Is it worth also adding a test case that runs gcry_pk_testkey on
+ * this key after first parsing it into libgcrypt's s-expression mess
+ * ala decode_private_eddsa_key from gnunet/src/util/crypto_ecc.c?
+ * It'd run check_secret_key but not test_keys from libgcrypt/cipher/ecc.c */
+}
+
+
+/**
* Setup information for a fresh coin.
*
* @param secret_seed seed to use for KDF to derive coin keys
* @param coin_num_salt number of the coin to include in KDF
- * @param[out] fc value to initialize
+ * @param[out] ps value to initialize
*/
void
TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed,
- unsigned int coin_num_salt,
- struct TALER_PlanchetSecretsP *fc)
+ unsigned int coin_num_salt,
+ struct TALER_PlanchetSecretsP *ps)
{
uint32_t be_salt = htonl (coin_num_salt);
- uint8_t *p;
GNUNET_assert (GNUNET_OK ==
- GNUNET_CRYPTO_kdf (fc,
- sizeof (*fc),
+ GNUNET_CRYPTO_kdf (ps,
+ sizeof (*ps),
&be_salt,
sizeof (be_salt),
secret_seed,
@@ -195,24 +221,97 @@ TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed,
"taler-coin-derivation",
strlen ("taler-coin-derivation"),
NULL, 0));
+ patch_private_key (&ps->coin_priv.eddsa_priv);
+}
- /* Taken from like 170-172 of libgcrypt/cipher/ecc.c
- * We note that libgcrypt stores the private key in the reverse order
- * from many Ed25519 implementatons. */
- p = (uint8_t *) &(fc->coin_priv);
- p[0] &= 0x7f; /* Clear bit 255. */
- p[0] |= 0x40; /* Set bit 254. */
- p[31] &= 0xf8; /* Clear bits 2..0 so that d mod 8 == 0 */
- /* FIXME: Run GNUNET_CRYPTO_ecdhe_key_create several times and inspect
- * the output to verify that the same bits are set and cleared.
- * Is it worth also adding a test case that runs gcry_pk_testkey on
- * this key after first parsing it into libgcrypt's s-expression mess
- * ala decode_private_eddsa_key from gnunet/src/util/crypto_ecc.c?
- * It'd run check_secret_key but not test_keys from libgcrypt/cipher/ecc.c */
+/**
+ * Setup information for a fresh coin.
+ *
+ * @param[out] ps value to initialize
+ */
+void
+TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps)
+{
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
+ ps,
+ sizeof (*ps));
+ patch_private_key (&ps->coin_priv.eddsa_priv);
+}
+
+
+/**
+ * Prepare a planchet for tipping. Creates and blinds a coin.
+ *
+ * @param dk denomination key for the coin to be created
+ * @param ps secret planchet internals (for #TALER_planchet_to_coin)
+ * @param[out] pd set to the planchet detail for TALER_MERCHANT_tip_pickup() and
+ * other withdraw operations
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk,
+ const struct TALER_PlanchetSecretsP *ps,
+ struct TALER_PlanchetDetail *pd)
+{
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&ps->coin_priv.eddsa_priv,
+ &coin_pub.eddsa_pub);
+ GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub,
+ sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+ &pd->c_hash);
+ if (GNUNET_YES !=
+ GNUNET_CRYPTO_rsa_blind (&pd->c_hash,
+ &ps->blinding_key.bks,
+ dk->rsa_public_key,
+ &pd->coin_ev,
+ &pd->coin_ev_size))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_CRYPTO_rsa_public_key_hash (dk->rsa_public_key,
+ &pd->denom_pub_hash);
+ return GNUNET_OK;
}
+/**
+ * Obtain a coin from the planchet's secrets and the blind signature
+ * of the exchange.
+ *
+ * @param dk denomination key, must match what was given to #TALER_planchet_prepare()
+ * @param blind_sig blind signature from the exchange
+ * @param ps secrets from #TALER_planchet_prepare()
+ * @param c_hash hash of the coin's public key for verification of the signature
+ * @param[out] coin set to the details of the fresh coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_planchet_to_coin (const struct TALER_DenominationPublicKey *dk,
+ const struct GNUNET_CRYPTO_RsaSignature *blind_sig,
+ const struct TALER_PlanchetSecretsP *ps,
+ const struct GNUNET_HashCode *c_hash,
+ struct TALER_FreshCoin *coin)
+{
+ struct GNUNET_CRYPTO_RsaSignature *sig;
+ sig = GNUNET_CRYPTO_rsa_unblind (blind_sig,
+ &ps->blinding_key.bks,
+ dk->rsa_public_key);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_rsa_verify (c_hash,
+ sig,
+ dk->rsa_public_key))
+ {
+ GNUNET_break_op (0);
+ GNUNET_CRYPTO_rsa_signature_free (sig);
+ return GNUNET_SYSERR;
+ }
+ coin->sig.rsa_signature = sig;
+ coin->coin_priv = ps->coin_priv;
+ return GNUNET_OK;
+}
/* end of crypto.c */
diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c
index 953ad94df..4713b3a35 100644
--- a/src/util/test_crypto.c
+++ b/src/util/test_crypto.c
@@ -71,11 +71,11 @@ test_high_level ()
&secret2,
sizeof (secret)));
TALER_planchet_setup_refresh (&secret,
- 0,
- &fc1);
+ 0,
+ &fc1);
TALER_planchet_setup_refresh (&secret,
- 1,
- &fc2);
+ 1,
+ &fc2);
GNUNET_assert (0 !=
memcmp (&fc1,
&fc2,
@@ -84,12 +84,55 @@ test_high_level ()
}
+/**
+ * Test the basic planchet functionality of creating a fresh planchet
+ * and extracting the respective signature.
+ *
+ * @return 0 on success
+ */
+static int
+test_planchets ()
+{
+ struct TALER_PlanchetSecretsP ps;
+ struct TALER_DenominationPrivateKey dk_priv;
+ struct TALER_DenominationPublicKey dk_pub;
+ struct TALER_PlanchetDetail pd;
+ struct GNUNET_CRYPTO_RsaSignature *blind_sig;
+ struct TALER_FreshCoin coin;
+
+ dk_priv.rsa_private_key = GNUNET_CRYPTO_rsa_private_key_create (1024);
+ dk_pub.rsa_public_key = GNUNET_CRYPTO_rsa_private_key_get_public (dk_priv.rsa_private_key);
+ TALER_planchet_setup_random (&ps);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_planchet_prepare (&dk_pub,
+ &ps,
+ &pd));
+ blind_sig = GNUNET_CRYPTO_rsa_sign_blinded (dk_priv.rsa_private_key,
+ pd.coin_ev,
+ pd.coin_ev_size);
+ GNUNET_assert (NULL != blind_sig);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_planchet_to_coin (&dk_pub,
+ blind_sig,
+ &ps,
+ &pd.c_hash,
+ &coin));
+ GNUNET_CRYPTO_rsa_signature_free (blind_sig);
+ GNUNET_CRYPTO_rsa_signature_free (coin.sig.rsa_signature);
+ GNUNET_CRYPTO_rsa_private_key_free (dk_priv.rsa_private_key);
+ GNUNET_CRYPTO_rsa_public_key_free (dk_pub.rsa_public_key);
+ return 0;
+}
+
+
int
main(int argc,
const char *const argv[])
{
if (0 != test_high_level ())
return 1;
+ if (0 != test_planchets ())
+ return 1;
return 0;
}