/* This file is part of TALER 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 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/crypto.c * @brief Cryptographic utility functions * @author Sree Harsha Totakura * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff * @author Özgür Kesim */ #include "platform.h" #include "taler_util.h" #include /** * Used in TALER_AgeCommitmentHash_isNullOrZero for comparison */ const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash = {0}; /** * Function called by libgcrypt on serious errors. * Prints an error message and aborts the process. * * @param cls NULL * @param wtf unknown * @param msg error message */ static void fatal_error_handler (void *cls, int wtf, const char *msg) { (void) cls; (void) wtf; fprintf (stderr, "Fatal error in libgcrypt: %s\n", msg); abort (); } /** * Initialize libgcrypt. */ void __attribute__ ((constructor)) TALER_gcrypt_init () { gcry_set_fatalerror_handler (&fatal_error_handler, NULL); if (! gcry_check_version (NEED_LIBGCRYPT_VERSION)) { fprintf (stderr, "libgcrypt version mismatch\n"); abort (); } /* Disable secure memory (we should never run on a system that even uses swap space for memory). */ gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); } enum GNUNET_GenericReturnValue TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info, const struct TALER_DenominationPublicKey *denom_pub) { struct TALER_CoinPubHash c_hash; #if ENABLE_SANITY_CHECKS struct TALER_DenominationHash d_hash; TALER_denom_pub_hash (denom_pub, &d_hash); GNUNET_assert (0 == GNUNET_memcmp (&d_hash, &coin_public_info->denom_pub_hash)); #endif TALER_coin_pub_hash (&coin_public_info->coin_pub, &coin_public_info->h_age_commitment, &c_hash); if (GNUNET_OK != TALER_denom_pub_verify (denom_pub, &coin_public_info->denom_sig, &c_hash)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "coin signature is invalid\n"); return GNUNET_NO; } return GNUNET_YES; } void TALER_link_derive_transfer_secret ( const struct TALER_CoinSpendPrivateKeyP *coin_priv, const struct TALER_TransferPrivateKeyP *trans_priv, struct TALER_TransferSecretP *ts) { struct TALER_CoinSpendPublicKeyP coin_pub; GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_eddsa (&trans_priv->ecdhe_priv, &coin_pub.eddsa_pub, &ts->key)); } void TALER_link_reveal_transfer_secret ( const struct TALER_TransferPrivateKeyP *trans_priv, const struct TALER_CoinSpendPublicKeyP *coin_pub, struct TALER_TransferSecretP *transfer_secret) { GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_eddsa (&trans_priv->ecdhe_priv, &coin_pub->eddsa_pub, &transfer_secret->key)); } void TALER_link_recover_transfer_secret ( const struct TALER_TransferPublicKeyP *trans_pub, const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_TransferSecretP *transfer_secret) { GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_ecdh (&coin_priv->eddsa_priv, &trans_pub->ecdhe_pub, &transfer_secret->key)); } void 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); GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_kdf (ps, sizeof (*ps), &be_salt, sizeof (be_salt), secret_seed, sizeof (*secret_seed), "taler-coin-derivation", strlen ("taler-coin-derivation"), NULL, 0)); } void 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) { 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)); } void TALER_cs_withdraw_nonce_derive ( const struct TALER_PlanchetMasterSecretP *ps, struct TALER_CsNonce *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 TALER_CsNonce *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", // FIXME: value used in spec? strlen ("refresh-n"), rms, sizeof(*rms), NULL, 0)); } enum GNUNET_GenericReturnValue TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, const struct TALER_ExchangeWithdrawValues *alg_values, const union TALER_DenominationBlindingKeyP *bks, const struct TALER_CoinSpendPrivateKeyP *coin_priv, const struct TALER_AgeCommitmentHash *ach, struct TALER_CoinPubHash *c_hash, struct TALER_PlanchetDetail *pd ) { struct TALER_CoinSpendPublicKeyP coin_pub; GNUNET_assert (alg_values->cipher == dk->cipher); GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); if (GNUNET_OK != TALER_denom_blind (dk, bks, ach, &coin_pub, alg_values, c_hash, &pd->blinded_planchet)) { GNUNET_break (0); return GNUNET_SYSERR; } TALER_denom_pub_hash (dk, &pd->denom_pub_hash); return GNUNET_OK; } void TALER_planchet_detail_free (struct TALER_PlanchetDetail *pd) { TALER_blinded_planchet_free (&pd->blinded_planchet); } enum GNUNET_GenericReturnValue TALER_planchet_to_coin ( const struct TALER_DenominationPublicKey *dk, const struct TALER_BlindedDenominationSignature *blind_sig, const union TALER_DenominationBlindingKeyP *bks, const struct TALER_CoinSpendPrivateKeyP *coin_priv, const struct TALER_AgeCommitmentHash *ach, const struct TALER_CoinPubHash *c_hash, const struct TALER_ExchangeWithdrawValues *alg_values, struct TALER_FreshCoin *coin) { if ( (dk->cipher != blind_sig->cipher) || (dk->cipher != alg_values->cipher) ) { GNUNET_break_op (0); return GNUNET_SYSERR; } if (GNUNET_OK != TALER_denom_sig_unblind (&coin->sig, blind_sig, bks, c_hash, alg_values, dk)) { GNUNET_break_op (0); return GNUNET_SYSERR; } 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; } 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, const struct TALER_Amount *amount_with_fee) { 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 0); /* sanity check */ TALER_denom_pub_hash (rcs[0].new_coins[i].dk, &denom_hash); GNUNET_CRYPTO_hash_context_read (hash_context, &denom_hash, sizeof (denom_hash)); } /* next, add public key of coin and amount being refreshed */ { struct TALER_AmountNBO melt_amountn; GNUNET_CRYPTO_hash_context_read (hash_context, coin_pub, sizeof (struct TALER_CoinSpendPublicKeyP)); TALER_amount_hton (&melt_amountn, amount_with_fee); GNUNET_CRYPTO_hash_context_read (hash_context, &melt_amountn, sizeof (struct TALER_AmountNBO)); } /* finally, add all the envelopes */ for (unsigned int i = 0; inew_coins[j]; TALER_blinded_planchet_hash_ (&rcd->blinded_planchet, hash_context); } } /* Conclude */ GNUNET_CRYPTO_hash_context_finish (hash_context, &rc->session_hash); } void TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_AgeCommitmentHash *ach, struct TALER_CoinPubHash *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 */ const size_t key_s = sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey); const size_t age_s = sizeof(struct TALER_AgeCommitmentHash); char data[key_s + age_s]; GNUNET_memcpy (&data[0], &coin_pub->eddsa_pub, key_s); GNUNET_memcpy (&data[key_s], ach, age_s); GNUNET_CRYPTO_hash (&data, key_s + age_s, &coin_h->hash); } } void TALER_age_commitment_hash ( const struct TALER_AgeCommitment *commitment, struct TALER_AgeCommitmentHash *ahash) { struct GNUNET_HashContext *hash_context; struct GNUNET_HashCode hash; GNUNET_assert (NULL != ahash); if (NULL == commitment) { memset (ahash, 0, sizeof(struct TALER_AgeCommitmentHash)); return; } GNUNET_assert (__builtin_popcount (commitment->mask.mask) - 1 == commitment->num_pub); hash_context = GNUNET_CRYPTO_hash_context_start (); for (size_t i = 0; i < commitment->num_pub; i++) { GNUNET_CRYPTO_hash_context_read (hash_context, &commitment->pub[i], sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)); } GNUNET_CRYPTO_hash_context_finish (hash_context, &hash); GNUNET_memcpy (&ahash->shash.bits, &hash.bits, sizeof(ahash->shash.bits)); } /* To a given age value between 0 and 31, returns the index of the age group * defined by the given mask. */ static uint8_t get_age_group ( const struct TALER_AgeMask *mask, uint8_t age) { uint32_t m = mask->mask; uint8_t i = 0; while (m > 0) { if (0 >= age) break; m = m >> 1; i += m & 1; age--; } return i; } enum GNUNET_GenericReturnValue TALER_age_restriction_commit ( const struct TALER_AgeMask *mask, const uint8_t age, const uint32_t seed, struct TALER_AgeCommitment *new) { uint8_t num_pub = __builtin_popcount (mask->mask) - 1; uint8_t num_priv = get_age_group (mask, age) - 1; size_t i; GNUNET_assert (NULL != new); GNUNET_assert (mask->mask & 1); /* fist bit must have been set */ GNUNET_assert (0 <= num_priv); GNUNET_assert (31 > num_priv); new->mask.mask = mask->mask; new->num_pub = num_pub; new->num_priv = num_priv; new->pub = GNUNET_new_array ( num_pub, struct TALER_AgeCommitmentPublicKeyP); new->priv = GNUNET_new_array ( num_priv, struct TALER_AgeCommitmentPrivateKeyP); /* Create as many private keys as we need */ for (i = 0; i < num_priv; i++) { uint32_t seedBE = htonl (seed + i); if (GNUNET_OK != GNUNET_CRYPTO_kdf (&new->priv[i], sizeof (new->priv[i]), &seedBE, sizeof (seedBE), "taler-age-commitment-derivation", strlen ( "taler-age-commitment-derivation"), NULL, 0)) goto FAIL; GNUNET_CRYPTO_eddsa_key_get_public (&new->priv[i].eddsa_priv, &new->pub[i].eddsa_pub); } /* Fill the rest of the public keys with random values */ for (; ipub[i], sizeof(new->pub[i])); return GNUNET_OK; FAIL: GNUNET_free (new->pub); GNUNET_free (new->priv); return GNUNET_SYSERR; } enum GNUNET_GenericReturnValue TALER_age_commitment_derive ( const struct TALER_AgeCommitment *orig, const uint32_t seed, struct TALER_AgeCommitment *new) { struct GNUNET_CRYPTO_EccScalar val; /* * age commitment consists of GNUNET_CRYPTO_Eddsa{Private,Public}Key * * GNUNET_CRYPTO_EddsaPrivateKey is a * unsigned char d[256 / 8]; * * GNUNET_CRYPTO_EddsaPublicKey is a * unsigned char q_y[256 / 8]; * * We want to multiply, both, the Private Key by an integer factor and the * public key (point on curve) with the equivalent scalar. * * From the seed we will derive * 1. a scalar to multiply the public keys with * 2. a factor to multiply the private key with * * Invariants: * point*scalar == public(private*factor) * * A point on a curve is GNUNET_CRYPTO_EccPoint which is * unsigned char v[256 / 8]; * * A ECC scaler for use in point multiplications is a * GNUNET_CRYPTO_EccScalar which is a * unsigned car v[256 / 8]; * */ GNUNET_assert (NULL != new); GNUNET_assert (orig->num_pub == __builtin_popcount (orig->mask.mask) - 1); GNUNET_assert (orig->num_priv <= orig->num_pub); new->mask = orig->mask; new->num_pub = orig->num_pub; new->num_priv = orig->num_priv; new->pub = GNUNET_new_array ( new->num_pub, struct TALER_AgeCommitmentPublicKeyP); new->priv = GNUNET_new_array ( new->num_priv, struct TALER_AgeCommitmentPrivateKeyP); GNUNET_CRYPTO_ecc_scalar_from_int (seed, &val); /* scalar multiply the public keys on the curve */ for (size_t i = 0; i < orig->num_pub; i++) { /* We shift all keys by the same scalar */ struct GNUNET_CRYPTO_EccPoint *p = (struct GNUNET_CRYPTO_EccPoint *) &orig->pub[i]; struct GNUNET_CRYPTO_EccPoint *np = (struct GNUNET_CRYPTO_EccPoint *) &new->pub[i]; if (GNUNET_OK != GNUNET_CRYPTO_ecc_pmul_mpi ( p, &val, np)) goto FAIL; } /* multiply the private keys */ /* we borough ideas from GNUNET_CRYPTO_ecdsa_private_key_derive */ { uint32_t seedBE; uint8_t dc[32]; gcry_mpi_t f, x, d, n; gcry_ctx_t ctx; GNUNET_assert (0==gcry_mpi_ec_new (&ctx,NULL, "Ed25519")); n = gcry_mpi_ec_get_mpi ("n", ctx, 1); /* make the seed big endian */ seedBE = GNUNET_htonll (seed); GNUNET_CRYPTO_mpi_scan_unsigned (&f, &seedBE, sizeof(seedBE)); for (size_t i = 0; i < orig->num_priv; i++) { /* convert to big endian for libgrypt */ for (size_t j = 0; j < 32; j++) dc[i] = orig->priv[i].eddsa_priv.d[31 - j]; GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc)); d = gcry_mpi_new (256); gcry_mpi_mulm (d, f, x, n); gcry_mpi_release (x); gcry_mpi_release (d); gcry_mpi_release (n); gcry_mpi_release (d); GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d); for (size_t j = 0; j <32; j++) new->priv[i].eddsa_priv.d[j] = dc[31 - 1]; sodium_memzero (dc, sizeof(dc)); /* TODO: * make sure that the calculated private key generate the same public * keys */ } gcry_mpi_release (f); gcry_ctx_release (ctx); } return GNUNET_OK; FAIL: GNUNET_free (new->pub); GNUNET_free (new->priv); return GNUNET_SYSERR; } void TALER_age_commitment_free ( struct TALER_AgeCommitment *commitment) { if (NULL == commitment) return; if (NULL != commitment->priv) { GNUNET_CRYPTO_zero_keys ( commitment->priv, sizeof(*commitment->priv) * commitment->num_priv); GNUNET_free (commitment->priv); commitment->priv = NULL; } if (NULL != commitment->pub) { GNUNET_free (commitment->pub); commitment->priv = NULL; } GNUNET_free (commitment); } enum GNUNET_GenericReturnValue TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet, const struct TALER_DenominationHash *denom_hash, struct TALER_BlindedCoinHash *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); return GNUNET_OK; } /* end of crypto.c */