Switch to Ed25519 for cryptographic binding of joinsplits to transactions.
This commit is contained in:
parent
b48122b57b
commit
320f2cc7e0
17
src/main.cpp
17
src/main.cpp
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "main.h"
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
#include "addrman.h"
|
||||
#include "alert.h"
|
||||
#include "arith_uint256.h"
|
||||
|
@ -967,19 +969,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
|||
REJECT_INVALID, "error-computing-signature-hash");
|
||||
}
|
||||
|
||||
// Verify the signature
|
||||
if (!tx.joinSplitPubKey.Verify(dataToBeSigned, tx.joinSplitSig)) {
|
||||
return state.DoS(100, error("CheckTransaction(): JoinSplit signature does not verify"),
|
||||
if (crypto_sign_verify_detached(&tx.joinSplitSig[0],
|
||||
dataToBeSigned.begin(), 32,
|
||||
tx.joinSplitPubKey.begin()
|
||||
) != 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"),
|
||||
REJECT_INVALID, "invalid-joinsplit-signature");
|
||||
}
|
||||
|
||||
// Ensure that zk-SNARKs verify
|
||||
uint256 pubKeyHash = tx.joinSplitPubKey.GetZcashHash();
|
||||
if (state.PerformPourVerification()) {
|
||||
// Ensure that zk-SNARKs verify
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
if (!pour.Verify(*pzcashParams, pubKeyHash)) {
|
||||
if (!pour.Verify(*pzcashParams, tx.joinSplitPubKey)) {
|
||||
return state.DoS(100, error("CheckTransaction(): pour does not verify"),
|
||||
REJECT_INVALID, "bad-txns-pour-verification-failed");
|
||||
REJECT_INVALID, "bad-txns-pour-verification-failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,8 +134,8 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
|||
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
|
||||
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
|
||||
*const_cast<std::vector<CPourTx>*>(&vpour) = tx.vpour;
|
||||
*const_cast<CCompressedPubKey*>(&joinSplitPubKey) = tx.joinSplitPubKey;
|
||||
*const_cast<std::vector<unsigned char>*>(&joinSplitSig) = tx.joinSplitSig;
|
||||
*const_cast<uint256*>(&joinSplitPubKey) = tx.joinSplitPubKey;
|
||||
*const_cast<joinsplit_sig_t*>(&joinSplitSig) = tx.joinSplitSig;
|
||||
*const_cast<uint256*>(&hash) = tx.hash;
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "script/script.h"
|
||||
#include "serialize.h"
|
||||
#include "uint256.h"
|
||||
#include "pubkey.h"
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
|
@ -291,6 +290,8 @@ private:
|
|||
void UpdateHash() const;
|
||||
|
||||
public:
|
||||
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
|
||||
|
||||
static const int32_t CURRENT_VERSION=1;
|
||||
|
||||
// The local variables are made const to prevent unintended modification
|
||||
|
@ -303,10 +304,8 @@ public:
|
|||
const std::vector<CTxOut> vout;
|
||||
const uint32_t nLockTime;
|
||||
const std::vector<CPourTx> vpour;
|
||||
// TODO: This should be an unsigned char[33] (or boost array)
|
||||
const CCompressedPubKey joinSplitPubKey;
|
||||
// TODO: This should be an unsigned char[64] (or boost array)
|
||||
const std::vector<unsigned char> joinSplitSig;
|
||||
const uint256 joinSplitPubKey;
|
||||
const joinsplit_sig_t joinSplitSig;
|
||||
|
||||
/** Construct a CTransaction that qualifies as IsNull() */
|
||||
CTransaction();
|
||||
|
@ -328,8 +327,8 @@ public:
|
|||
if (nVersion >= 2) {
|
||||
READWRITE(*const_cast<std::vector<CPourTx>*>(&vpour));
|
||||
if (vpour.size() > 0) {
|
||||
READWRITE(*const_cast<CCompressedPubKey*>(&joinSplitPubKey));
|
||||
READWRITE(*const_cast<std::vector<unsigned char>*>(&joinSplitSig));
|
||||
READWRITE(*const_cast<uint256*>(&joinSplitPubKey));
|
||||
READWRITE(*const_cast<joinsplit_sig_t*>(&joinSplitSig));
|
||||
}
|
||||
}
|
||||
if (ser_action.ForRead())
|
||||
|
@ -384,10 +383,8 @@ struct CMutableTransaction
|
|||
std::vector<CTxOut> vout;
|
||||
uint32_t nLockTime;
|
||||
std::vector<CPourTx> vpour;
|
||||
// TODO: This should be an unsigned char[33] (or boost array)
|
||||
CCompressedPubKey joinSplitPubKey;
|
||||
// TODO: This should be an unsigned char[64] (or boost array)
|
||||
std::vector<unsigned char> joinSplitSig;
|
||||
uint256 joinSplitPubKey;
|
||||
boost::array<unsigned char, 64> joinSplitSig;
|
||||
|
||||
CMutableTransaction();
|
||||
CMutableTransaction(const CTransaction& tx);
|
||||
|
|
72
src/pubkey.h
72
src/pubkey.h
|
@ -9,7 +9,6 @@
|
|||
#include "hash.h"
|
||||
#include "serialize.h"
|
||||
#include "uint256.h"
|
||||
#include "sodium.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
@ -188,77 +187,6 @@ public:
|
|||
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
|
||||
};
|
||||
|
||||
class CCompressedPubKey {
|
||||
private:
|
||||
CPubKey pubKey;
|
||||
public:
|
||||
|
||||
CCompressedPubKey()
|
||||
{
|
||||
// pubKey's 0-argument constructor invalidates it.
|
||||
}
|
||||
|
||||
CCompressedPubKey(const CPubKey &pubKey)
|
||||
{
|
||||
this->pubKey = pubKey;
|
||||
// TODO: check that it's compressed and valid and throw exception if
|
||||
// not.
|
||||
}
|
||||
|
||||
unsigned int GetSerializeSize(int nType, int nVersion) const
|
||||
{
|
||||
assert(pubKey.size() == 33);
|
||||
return pubKey.size();
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
void Serialize(Stream& s, int nType, int nVersion) const
|
||||
{
|
||||
unsigned int len = pubKey.size();
|
||||
s.write((char*)pubKey.begin(), len);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersion)
|
||||
{
|
||||
unsigned int len = 33;
|
||||
s.read((char*)pubKey.begin(), len);
|
||||
// TODO: check that it's compressed and valid.
|
||||
}
|
||||
|
||||
//! Get the 256-bit hash of this public key for the Zcash protocol.
|
||||
uint256 GetZcashHash() const
|
||||
{
|
||||
// TODO: is the thing in vch actually the right thing to hash/encode?
|
||||
|
||||
const unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES]
|
||||
= {'Z','c','a','s','h','E','C','D','S','A','P','u','b','K','e','y'};
|
||||
|
||||
uint256 hash;
|
||||
if (crypto_generichash_blake2b_salt_personal(hash.begin(), 32,
|
||||
pubKey.begin(), pubKey.size(),
|
||||
NULL, 0, // No key.
|
||||
NULL, // No salt.
|
||||
personalization
|
||||
) != 0)
|
||||
{
|
||||
throw std::logic_error("hash function failure");
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const
|
||||
{
|
||||
// TODO: make sure to check the s < 0xffff.... value thing etc.
|
||||
// TODO: use compact signatures (maybe just use the secp256k1 API
|
||||
// instead of these classes).
|
||||
return pubKey.Verify(hash, vchSig);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct CExtPubKey {
|
||||
unsigned char nDepth;
|
||||
unsigned char vchFingerprint[4];
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include "script/script.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef vector<unsigned char> valtype;
|
||||
|
@ -1085,7 +1083,8 @@ public:
|
|||
::Serialize(s, txTo.vpour, nType, nVersion);
|
||||
if (txTo.vpour.size() > 0) {
|
||||
::Serialize(s, txTo.joinSplitPubKey, nType, nVersion);
|
||||
std::vector<unsigned char> nullSig = {};
|
||||
|
||||
boost::array<unsigned char, 64> nullSig = {};
|
||||
::Serialize(s, nullSig, nType, nVersion);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -13,7 +13,6 @@
|
|||
#include "util.h"
|
||||
#include "version.h"
|
||||
#include "sodium.h"
|
||||
#include "key.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -82,7 +81,8 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
|
|||
txTmp.vin.resize(1);
|
||||
}
|
||||
|
||||
txTmp.joinSplitSig = {};
|
||||
// Blank out the joinsplit signature.
|
||||
memset(&txTmp.joinSplitSig[0], 0, 64);
|
||||
|
||||
// Serialize and hash
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
|
@ -143,20 +143,21 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
|||
tx.vpour.push_back(pourtx);
|
||||
}
|
||||
|
||||
CKey joinSplitPrivKey;
|
||||
joinSplitPrivKey.MakeNewKey(true);
|
||||
CCompressedPubKey joinSplitPubKey(joinSplitPrivKey.GetPubKey());
|
||||
tx.joinSplitPubKey = joinSplitPubKey;
|
||||
CTransaction signTx(tx);
|
||||
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
||||
crypto_sign_keypair(tx.joinSplitPubKey.begin(), joinSplitPrivKey);
|
||||
|
||||
// TODO: #966
|
||||
// TODO: #966.
|
||||
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||
// Empty output script.
|
||||
CScript scriptCode;
|
||||
CTransaction signTx(tx);
|
||||
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL);
|
||||
BOOST_CHECK(dataToBeSigned != one);
|
||||
|
||||
// Add the signature
|
||||
joinSplitPrivKey.Sign(dataToBeSigned, tx.joinSplitSig);
|
||||
assert(crypto_sign_detached(&tx.joinSplitSig[0], NULL,
|
||||
dataToBeSigned.begin(), 32,
|
||||
joinSplitPrivKey
|
||||
) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "script/script_error.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
@ -379,6 +381,9 @@ BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
|||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
||||
crypto_sign_keypair(newTx.joinSplitPubKey.begin(), joinSplitPrivKey);
|
||||
|
||||
state.SetPerformPourVerification(false); // don't verify the snark
|
||||
|
||||
// No pours, vin and vout, means it should be invalid.
|
||||
|
@ -399,22 +404,18 @@ BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
|||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "invalid-joinsplit-signature");
|
||||
|
||||
|
||||
CKey joinSplitPrivKey;
|
||||
joinSplitPrivKey.MakeNewKey(true);
|
||||
CCompressedPubKey joinSplitPubKey(joinSplitPrivKey.GetPubKey());
|
||||
|
||||
newTx.joinSplitPubKey = joinSplitPubKey;
|
||||
CTransaction signTx(newTx);
|
||||
|
||||
// TODO: #966
|
||||
// TODO: #966.
|
||||
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||
// Empty output script.
|
||||
CScript scriptCode;
|
||||
CTransaction signTx(newTx);
|
||||
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL);
|
||||
BOOST_CHECK(dataToBeSigned != one);
|
||||
|
||||
// Add the signature
|
||||
joinSplitPrivKey.Sign(dataToBeSigned, newTx.joinSplitSig);
|
||||
assert(crypto_sign_detached(&newTx.joinSplitSig[0], NULL,
|
||||
dataToBeSigned.begin(), 32,
|
||||
joinSplitPrivKey
|
||||
) == 0);
|
||||
|
||||
BOOST_CHECK(CheckTransaction(newTx, state));
|
||||
}
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
#include "walletdb.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "zcbenchmarks.h"
|
||||
#include "key.h"
|
||||
#include "script/interpreter.h"
|
||||
|
||||
#include "sodium.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
|
@ -2652,22 +2653,23 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
throw runtime_error("unsupported pour input/output counts");
|
||||
}
|
||||
|
||||
CKey joinSplitPrivKey;
|
||||
joinSplitPrivKey.MakeNewKey(true);
|
||||
CCompressedPubKey joinSplitPubKey(joinSplitPrivKey.GetPubKey());
|
||||
uint256 joinSplitPubKey;
|
||||
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
||||
crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey);
|
||||
|
||||
CMutableTransaction mtx(tx);
|
||||
mtx.nVersion = 2;
|
||||
mtx.joinSplitPubKey = joinSplitPubKey;
|
||||
|
||||
CPourTx pourtx(*pzcashParams,
|
||||
joinSplitPubKey.GetZcashHash(),
|
||||
joinSplitPubKey,
|
||||
anchor,
|
||||
{vpourin[0], vpourin[1]},
|
||||
{vpourout[0], vpourout[1]},
|
||||
vpub_old,
|
||||
vpub_new);
|
||||
assert(pourtx.Verify(*pzcashParams, joinSplitPubKey.GetZcashHash()));
|
||||
|
||||
assert(pourtx.Verify(*pzcashParams, joinSplitPubKey));
|
||||
|
||||
mtx.vpour.push_back(pourtx);
|
||||
|
||||
|
@ -2682,7 +2684,16 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
}
|
||||
|
||||
// Add the signature
|
||||
joinSplitPrivKey.Sign(dataToBeSigned, mtx.joinSplitSig);
|
||||
assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
|
||||
dataToBeSigned.begin(), 32,
|
||||
joinSplitPrivKey
|
||||
) == 0);
|
||||
|
||||
// Sanity check
|
||||
assert(crypto_sign_verify_detached(&mtx.joinSplitSig[0],
|
||||
dataToBeSigned.begin(), 32,
|
||||
mtx.joinSplitPubKey.begin()
|
||||
) == 0);
|
||||
|
||||
CTransaction rawTx(mtx);
|
||||
|
||||
|
@ -2696,7 +2707,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
ss2 << ((unsigned char) 0x00);
|
||||
ss2 << pourtx.ephemeralKey;
|
||||
ss2 << pourtx.ciphertexts[0];
|
||||
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey.GetZcashHash());
|
||||
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey);
|
||||
|
||||
encryptedBucket1 = HexStr(ss2.begin(), ss2.end());
|
||||
}
|
||||
|
@ -2705,7 +2716,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
ss2 << ((unsigned char) 0x01);
|
||||
ss2 << pourtx.ephemeralKey;
|
||||
ss2 << pourtx.ciphertexts[1];
|
||||
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey.GetZcashHash());
|
||||
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey);
|
||||
|
||||
encryptedBucket2 = HexStr(ss2.begin(), ss2.end());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue