diff options
Diffstat (limited to 'src/util/crypto.c')
-rw-r--r-- | src/util/crypto.c | 450 |
1 files changed, 306 insertions, 144 deletions
diff --git a/src/util/crypto.c b/src/util/crypto.c index 99171ebc7..4735af3b0 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2017 Taler Systems SA + Copyright (C) 2014-2022 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 @@ -20,12 +20,12 @@ * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff + * @author Özgür Kesim */ #include "platform.h" #include "taler_util.h" #include <gcrypt.h> - /** * Function called by libgcrypt on serious errors. * Prints an error message and aborts the process. @@ -69,37 +69,31 @@ TALER_gcrypt_init () } -/** - * Check if a coin is valid; that is, whether the denomination key exists, - * is not expired, and the signature is correct. - * - * @param coin_public_info the coin public info to check for validity - * @param denom_pub denomination key, must match @a coin_public_info's `denom_pub_hash` - * @return #GNUNET_YES if the coin is valid, - * #GNUNET_NO if it is invalid - * #GNUNET_SYSERR if an internal error occurred - */ -int +enum GNUNET_GenericReturnValue TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info, const struct TALER_DenominationPublicKey *denom_pub) { - struct GNUNET_HashCode c_hash; + struct TALER_CoinPubHashP c_hash; #if ENABLE_SANITY_CHECKS - struct GNUNET_HashCode d_hash; + struct TALER_DenominationHashP d_hash; - GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key, - &d_hash); + TALER_denom_pub_hash (denom_pub, + &d_hash); GNUNET_assert (0 == GNUNET_memcmp (&d_hash, &coin_public_info->denom_pub_hash)); #endif - GNUNET_CRYPTO_hash (&coin_public_info->coin_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &c_hash); + + TALER_coin_pub_hash (&coin_public_info->coin_pub, + coin_public_info->no_age_commitment + ? NULL + : &coin_public_info->h_age_commitment, + &c_hash); + if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (&c_hash, - coin_public_info->denom_sig.rsa_signature, - denom_pub->rsa_public_key)) + TALER_denom_pub_verify (denom_pub, + &coin_public_info->denom_sig, + &c_hash)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "coin signature is invalid\n"); @@ -109,17 +103,6 @@ TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info, } -/** - * Given the coin and the transfer private keys, compute the - * transfer secret. (Technically, we only need one of the two - * private keys, but the caller currently trivially only has - * the two private keys, so we derive one of the public keys - * internally to this function.) - * - * @param coin_priv coin key - * @param trans_priv transfer private key - * @param[out] ts computed transfer secret - */ void TALER_link_derive_transfer_secret ( const struct TALER_CoinSpendPrivateKeyP *coin_priv, @@ -134,18 +117,9 @@ TALER_link_derive_transfer_secret ( GNUNET_CRYPTO_ecdh_eddsa (&trans_priv->ecdhe_priv, &coin_pub.eddsa_pub, &ts->key)); - } -/** - * Decrypt the shared @a secret from the information in the - * @a trans_priv and @a coin_pub. - * - * @param trans_priv transfer private key - * @param coin_pub coin public key - * @param[out] transfer_secret set to the shared secret - */ void TALER_link_reveal_transfer_secret ( const struct TALER_TransferPrivateKeyP *trans_priv, @@ -159,14 +133,6 @@ TALER_link_reveal_transfer_secret ( } -/** - * Decrypt the shared @a secret from the information in the - * @a trans_priv and @a coin_pub. - * - * @param trans_pub transfer private key - * @param coin_priv coin public key - * @param[out] transfer_secret set to the shared secret - */ void TALER_link_recover_transfer_secret ( const struct TALER_TransferPublicKeyP *trans_pub, @@ -180,17 +146,31 @@ TALER_link_recover_transfer_secret ( } -/** - * 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] ps value to initialize - */ void -TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, - uint32_t coin_num_salt, - struct TALER_PlanchetSecretsP *ps) +TALER_planchet_master_setup_random ( + struct TALER_PlanchetMasterSecretP *ps) +{ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, + ps, + sizeof (*ps)); +} + + +void +TALER_refresh_master_setup_random ( + struct TALER_RefreshMasterSecretP *rms) +{ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, + rms, + sizeof (*rms)); +} + + +void +TALER_transfer_secret_to_planchet_secret ( + const struct TALER_TransferSecretP *secret_seed, + uint32_t coin_num_salt, + struct TALER_PlanchetMasterSecretP *ps) { uint32_t be_salt = htonl (coin_num_salt); @@ -207,109 +187,186 @@ TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, } -/** - * Setup information for a fresh coin. - * - * @param[out] ps value to initialize - */ void -TALER_planchet_setup_random (struct TALER_PlanchetSecretsP *ps) +TALER_planchet_secret_to_transfer_priv ( + const struct TALER_RefreshMasterSecretP *rms, + const struct TALER_CoinSpendPrivateKeyP *old_coin_priv, + uint32_t cnc_num, + struct TALER_TransferPrivateKeyP *tpriv) { - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, - ps, - sizeof (*ps)); + uint32_t be_salt = htonl (cnc_num); + + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_kdf (tpriv, + sizeof (*tpriv), + &be_salt, + sizeof (be_salt), + old_coin_priv, + sizeof (*old_coin_priv), + rms, + sizeof (*rms), + "taler-transfer-priv-derivation", + strlen ("taler-transfer-priv-derivation"), + NULL, 0)); } -/** - * 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 +void +TALER_cs_withdraw_nonce_derive ( + const struct TALER_PlanchetMasterSecretP *ps, + struct GNUNET_CRYPTO_CsSessionNonce *nonce) +{ + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (nonce, + sizeof (*nonce), + "n", + strlen ("n"), + ps, + sizeof(*ps), + NULL, + 0)); +} + + +void +TALER_cs_refresh_nonce_derive ( + const struct TALER_RefreshMasterSecretP *rms, + uint32_t coin_num_salt, + struct GNUNET_CRYPTO_CsSessionNonce *nonce) +{ + uint32_t be_salt = htonl (coin_num_salt); + + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (nonce, + sizeof (*nonce), + &be_salt, + sizeof (be_salt), + "refresh-n", + strlen ("refresh-n"), + rms, + sizeof(*rms), + NULL, + 0)); +} + + +void +TALER_rsa_pub_hash (const struct GNUNET_CRYPTO_RsaPublicKey *rsa, + struct TALER_RsaPubHashP *h_rsa) +{ + GNUNET_CRYPTO_rsa_public_key_hash (rsa, + &h_rsa->hash); + +} + + +void +TALER_cs_pub_hash (const struct GNUNET_CRYPTO_CsPublicKey *cs, + struct TALER_CsPubHashP *h_cs) +{ + GNUNET_CRYPTO_hash (cs, + sizeof(*cs), + &h_cs->hash); +} + + +enum GNUNET_GenericReturnValue TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, - const struct TALER_PlanchetSecretsP *ps, - struct TALER_PlanchetDetail *pd) + const struct TALER_ExchangeWithdrawValues *alg_values, + const union GNUNET_CRYPTO_BlindingSecretP *bks, + const union GNUNET_CRYPTO_BlindSessionNonce *nonce, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + const struct TALER_AgeCommitmentHash *ach, + struct TALER_CoinPubHashP *c_hash, + struct TALER_PlanchetDetail *pd + ) { struct TALER_CoinSpendPublicKeyP coin_pub; - GNUNET_CRYPTO_eddsa_key_get_public (&ps->coin_priv.eddsa_priv, + GNUNET_assert (alg_values->blinding_inputs->cipher == + dk->bsign_pub_key->cipher); + GNUNET_CRYPTO_eddsa_key_get_public (&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)) + if (GNUNET_OK != + TALER_denom_blind (dk, + bks, + nonce, + ach, + &coin_pub, + alg_values, + c_hash, + &pd->blinded_planchet)) { - GNUNET_break_op (0); + GNUNET_break (0); return GNUNET_SYSERR; } - GNUNET_CRYPTO_rsa_public_key_hash (dk->rsa_public_key, - &pd->denom_pub_hash); + TALER_denom_pub_hash (dk, + &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) +void +TALER_planchet_detail_free (struct TALER_PlanchetDetail *pd) { - struct GNUNET_CRYPTO_RsaSignature *sig; + TALER_blinded_planchet_free (&pd->blinded_planchet); +} + - sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, - &ps->blinding_key.bks, - dk->rsa_public_key); +enum GNUNET_GenericReturnValue +TALER_planchet_to_coin ( + const struct TALER_DenominationPublicKey *dk, + const struct TALER_BlindedDenominationSignature *blind_sig, + const union GNUNET_CRYPTO_BlindingSecretP *bks, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + const struct TALER_AgeCommitmentHash *ach, + const struct TALER_CoinPubHashP *c_hash, + const struct TALER_ExchangeWithdrawValues *alg_values, + struct TALER_FreshCoin *coin) +{ + if (dk->bsign_pub_key->cipher != + blind_sig->blinded_sig->cipher) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (dk->bsign_pub_key->cipher != + alg_values->blinding_inputs->cipher) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (c_hash, - sig, - dk->rsa_public_key)) + TALER_denom_sig_unblind (&coin->sig, + blind_sig, + bks, + c_hash, + alg_values, + dk)) { GNUNET_break_op (0); - GNUNET_CRYPTO_rsa_signature_free (sig); return GNUNET_SYSERR; } - coin->sig.rsa_signature = sig; - coin->coin_priv = ps->coin_priv; + if (GNUNET_OK != + TALER_denom_pub_verify (dk, + &coin->sig, + c_hash)) + { + GNUNET_break_op (0); + TALER_denom_sig_free (&coin->sig); + return GNUNET_SYSERR; + } + + coin->coin_priv = *coin_priv; + coin->h_age_commitment = ach; return GNUNET_OK; } -/** - * Compute the commitment for a /refresh/melt operation from - * the respective public inputs. - * - * @param[out] rc set to the value the wallet must commit to - * @param kappa number of transfer public keys involved (must be #TALER_CNC_KAPPA) - * @param num_new_coins number of new coins to be created - * @param rcs commitments array of @a kappa commitments - * @param coin_pub public key of the coin to be melted - * @param amount_with_fee amount to be melted, including fee - */ void TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, uint32_t kappa, + const struct TALER_RefreshMasterSecretP *rms, uint32_t num_new_coins, const struct TALER_RefreshCommitmentEntry *rcs, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -318,6 +375,10 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, struct GNUNET_HashContext *hash_context; hash_context = GNUNET_CRYPTO_hash_context_start (); + if (NULL != rms) + GNUNET_CRYPTO_hash_context_read (hash_context, + rms, + sizeof (*rms)); /* first, iterate over transfer public keys for hash_context */ for (unsigned int i = 0; i<kappa; i++) { @@ -329,19 +390,16 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, hash_context */ for (unsigned int i = 0; i<num_new_coins; i++) { - void *buf; - size_t buf_size; + struct TALER_DenominationHashP denom_hash; /* The denomination keys should / must all be identical regardless of what offset we use, so we use [0]. */ GNUNET_assert (kappa > 0); /* sanity check */ - buf_size = GNUNET_CRYPTO_rsa_public_key_encode ( - rcs[0].new_coins[i].dk->rsa_public_key, - &buf); + TALER_denom_pub_hash (rcs[0].new_coins[i].dk, + &denom_hash); GNUNET_CRYPTO_hash_context_read (hash_context, - buf, - buf_size); - GNUNET_free (buf); + &denom_hash, + sizeof (denom_hash)); } /* next, add public key of coin and amount being refreshed */ @@ -367,9 +425,8 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, { const struct TALER_RefreshCoinData *rcd = &rce->new_coins[j]; - GNUNET_CRYPTO_hash_context_read (hash_context, - rcd->coin_ev, - rcd->coin_ev_size); + TALER_blinded_planchet_hash_ (&rcd->blinded_planchet, + hash_context); } } @@ -379,4 +436,109 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, } +void +TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_AgeCommitmentHash *ach, + struct TALER_CoinPubHashP *coin_h) +{ + if (TALER_AgeCommitmentHash_isNullOrZero (ach)) + { + /* No age commitment was set */ + GNUNET_CRYPTO_hash (&coin_pub->eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &coin_h->hash); + } + else + { + /* Coin comes with age commitment. Take the hash of the age commitment + * into account */ + struct GNUNET_HashContext *hash_context; + + hash_context = GNUNET_CRYPTO_hash_context_start (); + + GNUNET_CRYPTO_hash_context_read ( + hash_context, + &coin_pub->eddsa_pub, + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)); + + GNUNET_CRYPTO_hash_context_read ( + hash_context, + ach, + sizeof(struct TALER_AgeCommitmentHash)); + + GNUNET_CRYPTO_hash_context_finish ( + hash_context, + &coin_h->hash); + } +} + + +void +TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet, + const struct TALER_DenominationHashP *denom_hash, + struct TALER_BlindedCoinHashP *bch) +{ + struct GNUNET_HashContext *hash_context; + + hash_context = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (hash_context, + denom_hash, + sizeof(*denom_hash)); + TALER_blinded_planchet_hash_ (blinded_planchet, + hash_context); + GNUNET_CRYPTO_hash_context_finish (hash_context, + &bch->hash); +} + + +GNUNET_NETWORK_STRUCT_BEGIN +/** + * Structure we hash to compute the group key for + * a denomination group. + */ +struct DenominationGroupP +{ + /** + * Value of coins in this denomination group. + */ + struct TALER_AmountNBO value; + + /** + * Fee structure for all coins in the group. + */ + struct TALER_DenomFeeSetNBOP fees; + + /** + * Age mask for the denomiation, in NBO. + */ + uint32_t age_mask GNUNET_PACKED; + + /** + * Cipher used for the denomination, in NBO. + */ + uint32_t cipher GNUNET_PACKED; +}; +GNUNET_NETWORK_STRUCT_END + + +void +TALER_denomination_group_get_key ( + const struct TALER_DenominationGroup *dg, + struct GNUNET_HashCode *key) +{ + struct DenominationGroupP dgp = { + .age_mask = htonl (dg->age_mask.bits), + .cipher = htonl (dg->cipher) + }; + + TALER_amount_hton (&dgp.value, + &dg->value); + TALER_denom_fee_set_hton (&dgp.fees, + &dg->fees); + GNUNET_CRYPTO_hash (&dgp, + sizeof (dgp), + key); +} + + /* end of crypto.c */ |