Browse Source

Add support for encrypting spending keys

metaverse
Jack Grigg 8 years ago
parent
commit
16d140f4a2
  1. 57
      src/gtest/test_keystore.cpp
  2. 1
      src/keystore.h
  3. 104
      src/wallet/crypter.cpp
  4. 32
      src/wallet/crypter.h
  5. 8
      src/zcash/Address.cpp
  6. 3
      src/zcash/Address.hpp

57
src/gtest/test_keystore.cpp

@ -1,6 +1,8 @@
#include <gtest/gtest.h>
#include "keystore.h"
#include "random.h"
#include "wallet/crypter.h"
#include "zcash/Address.hpp"
TEST(keystore_tests, store_and_retrieve_spending_key) {
@ -41,3 +43,58 @@ TEST(keystore_tests, store_and_retrieve_note_decryptor) {
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut);
}
class TestCCryptoKeyStore : public CCryptoKeyStore
{
public:
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::EncryptKeys(vMasterKeyIn); }
bool Unlock(const CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::Unlock(vMasterKeyIn); }
};
TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
TestCCryptoKeyStore keyStore;
uint256 r {GetRandHash()};
CKeyingMaterial vMasterKey (r.begin(), r.end());
libzcash::SpendingKey keyOut;
std::set<libzcash::PaymentAddress> addrs;
// 1) Test adding a key to an unencrypted key store, then encrypting it
auto sk = libzcash::SpendingKey::random();
auto addr = sk.address();
keyStore.AddSpendingKey(sk);
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
ASSERT_EQ(sk, keyOut);
ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey));
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
ASSERT_FALSE(keyStore.GetSpendingKey(addr, keyOut));
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
ASSERT_EQ(sk, keyOut);
keyStore.GetPaymentAddresses(addrs);
ASSERT_EQ(1, addrs.size());
ASSERT_EQ(1, addrs.count(addr));
// 2) Test adding a spending key to an already-encrypted key store
auto sk2 = libzcash::SpendingKey::random();
auto addr2 = sk2.address();
keyStore.AddSpendingKey(sk2);
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
ASSERT_EQ(sk2, keyOut);
ASSERT_TRUE(keyStore.Lock());
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
ASSERT_FALSE(keyStore.GetSpendingKey(addr2, keyOut));
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
ASSERT_EQ(sk2, keyOut);
keyStore.GetPaymentAddresses(addrs);
ASSERT_EQ(2, addrs.size());
ASSERT_EQ(1, addrs.count(addr2));
}

1
src/keystore.h

@ -172,5 +172,6 @@ public:
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
typedef std::map<libzcash::PaymentAddress, std::vector<unsigned char> > CryptedSpendingKeyMap;
#endif // BITCOIN_KEYSTORE_H

104
src/wallet/crypter.cpp

@ -6,6 +6,7 @@
#include "script/script.h"
#include "script/standard.h"
#include "streams.h"
#include "util.h"
#include <string>
@ -135,12 +136,31 @@ static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsi
return key.VerifyPubKey(vchPubKey);
}
static bool DecryptSpendingKey(const CKeyingMaterial& vMasterKey,
const std::vector<unsigned char>& vchCryptedSecret,
const libzcash::PaymentAddress& address,
libzcash::SpendingKey& sk)
{
CKeyingMaterial vchSecret;
if(!DecryptSecret(vMasterKey, vchCryptedSecret, address.GetHash(), vchSecret))
return false;
if (vchSecret.size() != 32)
return false;
// TODO does this undo the benefits of using CKeyingMaterial?
std::vector<unsigned char> serialized(vchSecret.begin(), vchSecret.end());
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
ss >> sk;
return sk.address() == address;
}
bool CCryptoKeyStore::SetCrypted()
{
LOCK(cs_KeyStore);
if (fUseCrypto)
return true;
if (!mapKeys.empty())
if (!(mapKeys.empty() && mapSpendingKeys.empty()))
return false;
fUseCrypto = true;
return true;
@ -184,6 +204,21 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
if (fDecryptionThoroughlyChecked)
break;
}
CryptedSpendingKeyMap::const_iterator skmi = mapCryptedSpendingKeys.begin();
for (; skmi != mapCryptedSpendingKeys.end(); ++skmi)
{
const libzcash::PaymentAddress &address = (*skmi).first;
const std::vector<unsigned char> &vchCryptedSecret = (*skmi).second;
libzcash::SpendingKey sk;
if (!DecryptSpendingKey(vMasterKeyIn, vchCryptedSecret, address, sk))
{
keyFail = true;
break;
}
keyPass = true;
if (fDecryptionThoroughlyChecked)
break;
}
if (keyPass && keyFail)
{
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
@ -267,6 +302,59 @@ bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) co
return false;
}
bool CCryptoKeyStore::AddSpendingKey(const libzcash::SpendingKey &sk)
{
{
LOCK(cs_KeyStore);
if (!IsCrypted())
return CBasicKeyStore::AddSpendingKey(sk);
if (IsLocked())
return false;
std::vector<unsigned char> vchCryptedSecret;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sk;
CKeyingMaterial vchSecret(ss.begin(), ss.end());
auto address = sk.address();
if (!EncryptSecret(vMasterKey, vchSecret, address.GetHash(), vchCryptedSecret))
return false;
if (!AddCryptedSpendingKey(address, vchCryptedSecret))
return false;
}
return true;
}
bool CCryptoKeyStore::AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const std::vector<unsigned char> &vchCryptedSecret)
{
{
LOCK(cs_KeyStore);
if (!SetCrypted())
return false;
mapCryptedSpendingKeys[address] = vchCryptedSecret;
}
return true;
}
bool CCryptoKeyStore::GetSpendingKey(const libzcash::PaymentAddress &address, libzcash::SpendingKey &skOut) const
{
{
LOCK(cs_KeyStore);
if (!IsCrypted())
return CBasicKeyStore::GetSpendingKey(address, skOut);
CryptedSpendingKeyMap::const_iterator mi = mapCryptedSpendingKeys.find(address);
if (mi != mapCryptedSpendingKeys.end())
{
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second;
return DecryptSpendingKey(vMasterKey, vchCryptedSecret, address, skOut);
}
}
return false;
}
bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
{
{
@ -287,6 +375,20 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
return false;
}
mapKeys.clear();
BOOST_FOREACH(SpendingKeyMap::value_type& mSpendingKey, mapSpendingKeys)
{
const libzcash::SpendingKey &sk = mSpendingKey.second;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << sk;
CKeyingMaterial vchSecret(ss.begin(), ss.end());
libzcash::PaymentAddress address = sk.address();
std::vector<unsigned char> vchCryptedSecret;
if (!EncryptSecret(vMasterKeyIn, vchSecret, address.GetHash(), vchCryptedSecret))
return false;
if (!AddCryptedSpendingKey(address, vchCryptedSecret))
return false;
}
mapSpendingKeys.clear();
}
return true;
}

32
src/wallet/crypter.h

@ -8,6 +8,7 @@
#include "keystore.h"
#include "serialize.h"
#include "support/allocators/secure.h"
#include "zcash/Address.hpp"
class uint256;
@ -114,10 +115,11 @@ class CCryptoKeyStore : public CBasicKeyStore
{
private:
CryptedKeyMap mapCryptedKeys;
CryptedSpendingKeyMap mapCryptedSpendingKeys;
CKeyingMaterial vMasterKey;
//! if fUseCrypto is true, mapKeys must be empty
//! if fUseCrypto is true, mapKeys and mapSpendingKeys must be empty
//! if fUseCrypto is false, vMasterKey must be empty
bool fUseCrypto;
@ -185,6 +187,34 @@ public:
mi++;
}
}
virtual bool AddCryptedSpendingKey(const libzcash::PaymentAddress &address, const std::vector<unsigned char> &vchCryptedSecret);
bool AddSpendingKey(const libzcash::SpendingKey &sk);
bool HaveSpendingKey(const libzcash::PaymentAddress &address) const
{
{
LOCK(cs_KeyStore);
if (!IsCrypted())
return CBasicKeyStore::HaveSpendingKey(address);
return mapCryptedSpendingKeys.count(address) > 0;
}
return false;
}
bool GetSpendingKey(const libzcash::PaymentAddress &address, libzcash::SpendingKey &skOut) const;
void GetPaymentAddresses(std::set<libzcash::PaymentAddress> &setAddress) const
{
if (!IsCrypted())
{
CBasicKeyStore::GetPaymentAddresses(setAddress);
return;
}
setAddress.clear();
CryptedSpendingKeyMap::const_iterator mi = mapCryptedSpendingKeys.begin();
while (mi != mapCryptedSpendingKeys.end())
{
setAddress.insert((*mi).first);
mi++;
}
}
/**
* Wallet status (encrypted, locked) changed.

8
src/zcash/Address.cpp

@ -1,9 +1,17 @@
#include "Address.hpp"
#include "NoteEncryption.hpp"
#include "hash.h"
#include "prf.h"
#include "streams.h"
namespace libzcash {
uint256 PaymentAddress::GetHash() const {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
return Hash(ss.begin(), ss.end());
}
uint256 ViewingKey::pk_enc() {
return ZCNoteEncryption::generate_pubkey(*this);
}

3
src/zcash/Address.hpp

@ -23,6 +23,9 @@ public:
READWRITE(pk_enc);
}
//! Get the 256-bit SHA256d hash of this payment address.
uint256 GetHash() const;
friend inline bool operator==(const PaymentAddress& a, const PaymentAddress& b) {
return a.a_pk == b.a_pk && a.pk_enc == b.pk_enc;
}

Loading…
Cancel
Save