Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ file(GLOB SCRIPT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/script/*.h)
file(GLOB RPC_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/rpc/*.h)
file(GLOB COMPAT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/compat/*.h)
file(GLOB CONSENSUS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/consensus/*.h)
file(GLOB CTAES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/ctaes/*.h)

source_group("BitcoinHeaders" FILES
${HEADERS}
Expand All @@ -281,6 +282,7 @@ source_group("BitcoinHeaders" FILES
${RPC_HEADERS}
${COMPAT_HEADERS}
${CONSENSUS_HEADERS}
${CTAES_HEADERS}
./src/support/cleanse.h
)

Expand Down Expand Up @@ -384,6 +386,7 @@ target_include_directories(WALLET_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src
)

set(BITCOIN_CRYPTO_SOURCES
./src/crypto/aes.cpp
./src/crypto/sha1.cpp
./src/crypto/sha256.cpp
./src/crypto/sha512.cpp
Expand Down
10 changes: 9 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ libbitcoin_wallet_a_SOURCES = \
crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(PIC_FLAGS)
crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIC_FLAGS)
crypto_libbitcoin_crypto_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
crypto/sha1.cpp \
crypto/sha256.cpp \
crypto/sha512.cpp \
Expand Down Expand Up @@ -565,7 +567,13 @@ CLEANFILES += zmq/*.gcda zmq/*.gcno
CLEANFILES += zpiv/*.gcda zpiv/*.gcno
CLEANFILES += obj/build.h

EXTRA_DIST =
CTAES_DIST = crypto/ctaes/bench.c
CTAES_DIST += crypto/ctaes/ctaes.c
CTAES_DIST += crypto/ctaes/ctaes.h
CTAES_DIST += crypto/ctaes/README.md
CTAES_DIST += crypto/ctaes/test.c

EXTRA_DIST = $(CTAES_DIST)


config/pivx-config.h: config/stamp-h1
Expand Down
3 changes: 2 additions & 1 deletion src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ BITCOIN_TESTS =\
if ENABLE_WALLET
BITCOIN_TESTS += \
test/accounting_tests.cpp \
wallet/test/wallet_tests.cpp
wallet/test/wallet_tests.cpp \
wallet/test/crypto_tests.cpp
endif

test_test_pivx_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
Expand Down
160 changes: 50 additions & 110 deletions src/crypter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,51 @@

#include "crypter.h"

#include "crypto/aes.h"
#include "crypto/sha512.h"
#include "script/script.h"
#include "script/standard.h"
#include "util.h"
#include "init.h"
#include "uint256.h"

#include <openssl/aes.h>
#include <openssl/evp.h>
#include "wallet/wallet.h"

int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const
{
// This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc
// cipher and sha512 message digest. Because sha512's output size (64b) is
// greater than the aes256 block size (16b) + aes256 key size (32b),
// there's no need to process more than once (D_0).

if(!count || !key || !iv)
return 0;

unsigned char buf[CSHA512::OUTPUT_SIZE];
CSHA512 di;

di.Write((const unsigned char*)strKeyData.c_str(), strKeyData.size());
if(chSalt.size())
di.Write(&chSalt[0], chSalt.size());
di.Finalize(buf);

for(int i = 0; i != count - 1; i++)
di.Reset().Write(buf, sizeof(buf)).Finalize(buf);

memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE);
memcpy(iv, buf + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE);
memory_cleanse(buf, sizeof(buf));
return WALLET_CRYPTO_KEY_SIZE;
}

bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
{
if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
return false;

int i = 0;
if (nDerivationMethod == 0)
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
(unsigned char*)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);
i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, chKey, chIV);

if (i != (int)WALLET_CRYPTO_KEY_SIZE) {
memory_cleanse(chKey, sizeof(chKey));
Expand All @@ -37,7 +63,7 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v

bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
{
if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE)
if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_IV_SIZE)
return false;

memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
Expand All @@ -47,145 +73,59 @@ bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigne
return true;
}

bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char>& vchCiphertext)
bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char>& vchCiphertext) const
{
if (!fKeySet)
return false;

// max ciphertext len for a n bytes of plaintext is
// n + AES_BLOCK_SIZE - 1 bytes
int nLen = vchPlaintext.size();
int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
vchCiphertext = std::vector<unsigned char>(nCLen);

bool fOk = true;

EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
if (fOk) fOk = EVP_EncryptUpdate(ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
EVP_CIPHER_CTX_free(ctx);
// n + AES_BLOCKSIZE bytes
vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);

if (!fOk) return false;

vchCiphertext.resize(nCLen + nFLen);
AES256CBCEncrypt enc(chKey, chIV, true);
size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]);
if(nLen < vchPlaintext.size())
return false;
vchCiphertext.resize(nLen);
return true;
}

bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) const
{
if (!fKeySet)
return false;

// plaintext will always be equal to or lesser than length of ciphertext
int nLen = vchCiphertext.size();
int nPLen = nLen, nFLen = 0;

vchPlaintext = CKeyingMaterial(nPLen);

bool fOk = true;

EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
if (fOk) fOk = EVP_DecryptUpdate(ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
EVP_CIPHER_CTX_free(ctx);

if (!fOk) return false;

vchPlaintext.resize(nPLen + nFLen);
vchPlaintext.resize(nLen);
AES256CBCDecrypt dec(chKey, chIV, true);
nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]);
if(nLen == 0)
return false;
vchPlaintext.resize(nLen);
return true;
}


bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial& vchPlaintext, const uint256& nIV, std::vector<unsigned char>& vchCiphertext)
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
if (!cKeyCrypter.SetKey(vMasterKey, chIV))
return false;
return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
}


// General secure AES 256 CBC encryption routine
bool EncryptAES256(const SecureString& sKey, const SecureString& sPlaintext, const std::string& sIV, std::string& sCiphertext)
{
// max ciphertext len for a n bytes of plaintext is
// n + AES_BLOCK_SIZE - 1 bytes
int nLen = sPlaintext.size();
int nCLen = nLen + AES_BLOCK_SIZE;
int nFLen = 0;

// Verify key sizes
if (sKey.size() != 32 || sIV.size() != AES_BLOCK_SIZE) {
LogPrintf("crypter EncryptAES256 - Invalid key or block size: Key: %d sIV:%d\n", sKey.size(), sIV.size());
return false;
}

// Prepare output buffer
sCiphertext.resize(nCLen);

// Perform the encryption
EVP_CIPHER_CTX* ctx;

bool fOk = true;

ctx = EVP_CIPHER_CTX_new();
if (fOk) fOk = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (const unsigned char*)&sKey[0], (const unsigned char*)&sIV[0]);
if (fOk) fOk = EVP_EncryptUpdate(ctx, (unsigned char*)&sCiphertext[0], &nCLen, (const unsigned char*)&sPlaintext[0], nLen);
if (fOk) fOk = EVP_EncryptFinal_ex(ctx, (unsigned char*)(&sCiphertext[0]) + nCLen, &nFLen);
EVP_CIPHER_CTX_free(ctx);

if (!fOk) return false;

sCiphertext.resize(nCLen + nFLen);
return true;
}


bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
{
CCrypter cKeyCrypter;
std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
if (!cKeyCrypter.SetKey(vMasterKey, chIV))
return false;
return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
}

bool DecryptAES256(const SecureString& sKey, const std::string& sCiphertext, const std::string& sIV, SecureString& sPlaintext)
{
// plaintext will always be equal to or lesser than length of ciphertext
int nLen = sCiphertext.size();
int nPLen = nLen, nFLen = 0;

// Verify key sizes
if (sKey.size() != 32 || sIV.size() != AES_BLOCK_SIZE) {
LogPrintf("crypter DecryptAES256 - Invalid key or block size\n");
return false;
}

sPlaintext.resize(nPLen);

EVP_CIPHER_CTX* ctx;

bool fOk = true;

ctx = EVP_CIPHER_CTX_new();
if (fOk) fOk = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (const unsigned char*)&sKey[0], (const unsigned char*)&sIV[0]);
if (fOk) fOk = EVP_DecryptUpdate(ctx, (unsigned char*)&sPlaintext[0], &nPLen, (const unsigned char*)&sCiphertext[0], nLen);
if (fOk) fOk = EVP_DecryptFinal_ex(ctx, (unsigned char*)(&sPlaintext[0]) + nPLen, &nFLen);
EVP_CIPHER_CTX_free(ctx);

if (!fOk) return false;

sPlaintext.resize(nPLen + nFLen);
return true;
}


bool CCryptoKeyStore::SetCrypted()
{
LOCK(cs_KeyStore);
Expand Down
18 changes: 12 additions & 6 deletions src/crypter.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class uint256;

const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
const unsigned int WALLET_CRYPTO_IV_SIZE = 16;

/**
* Private key encryption is done based on a CMasterKey,
Expand Down Expand Up @@ -68,18 +69,26 @@ class CMasterKey

typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;

namespace wallet_crypto
{
class TestCrypter;
}

/** Encryption/decryption context with key information */
class CCrypter
{
friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV
private:
unsigned char chKey[WALLET_CRYPTO_KEY_SIZE];
unsigned char chIV[WALLET_CRYPTO_KEY_SIZE];
unsigned char chIV[WALLET_CRYPTO_IV_SIZE];
bool fKeySet;

int BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const;

public:
bool SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod);
bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char>& vchCiphertext);
bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext);
bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char>& vchCiphertext) const;
bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) const;
bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV);

void CleanKey()
Expand Down Expand Up @@ -112,9 +121,6 @@ class CCrypter
bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial& vchPlaintext, const uint256& nIV, std::vector<unsigned char>& vchCiphertext);
bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext);

bool EncryptAES256(const SecureString& sKey, const SecureString& sPlaintext, const std::string& sIV, std::string& sCiphertext);
bool DecryptAES256(const SecureString& sKey, const std::string& sCiphertext, const std::string& sIV, SecureString& sPlaintext);


/** Keystore which keeps the private keys encrypted.
* It derives from the basic key store, which is used if no encryption is active.
Expand Down
Loading