////////////////////////////////////////////////////////////////////////////////
// //
// Copyright (C) 2011-2015, Armory Technologies, Inc. //
// Distributed under the GNU Affero General Public License (AGPL v3) //
// See LICENSE-ATI or http://www.gnu.org/licenses/agpl.html //
// //
////////////////////////////////////////////////////////////////////////////////
#include "EncryptionUtils.h"
#include "log.h"
#include "cryptopp/integer.h"
#include "cryptopp/oids.h"
//#include
//#include
//#include
#define CRYPTO_DEBUG false
/////////////////////////////////////////////////////////////////////////////
// We have to explicitly re-define some of these methods...
SecureBinaryData & SecureBinaryData::append(SecureBinaryData & sbd2)
{
if(sbd2.getSize()==0)
return (*this);
if(getSize()==0)
BinaryData::copyFrom(sbd2.getPtr(), sbd2.getSize());
else
BinaryData::append(sbd2.getRawRef());
lockData();
return (*this);
}
/////////////////////////////////////////////////////////////////////////////
SecureBinaryData SecureBinaryData::operator+(SecureBinaryData & sbd2) const
{
SecureBinaryData out(getSize() + sbd2.getSize());
memcpy(out.getPtr(), getPtr(), getSize());
memcpy(out.getPtr()+getSize(), sbd2.getPtr(), sbd2.getSize());
out.lockData();
return out;
}
/////////////////////////////////////////////////////////////////////////////
SecureBinaryData & SecureBinaryData::operator=(SecureBinaryData const & sbd2)
{
copyFrom(sbd2.getPtr(), sbd2.getSize() );
lockData();
return (*this);
}
/////////////////////////////////////////////////////////////////////////////
bool SecureBinaryData::operator==(SecureBinaryData const & sbd2) const
{
if(getSize() != sbd2.getSize())
return false;
for(unsigned int i=0; i 0)
prng.IncorporateEntropy( (byte*)entropy.getPtr(), entropy.getSize());
SecureBinaryData randData(numBytes);
prng.GenerateBlock(randData.getPtr(), numBytes);
return randData;
}
/////////////////////////////////////////////////////////////////////////////
KdfRomix::KdfRomix(void) :
hashFunctionName_( "sha512" ),
hashOutputBytes_( 64 ),
kdfOutputBytes_( 32 ),
memoryReqtBytes_( 32 ),
numIterations_( 0 )
{
// Nothing to do here
}
/////////////////////////////////////////////////////////////////////////////
KdfRomix::KdfRomix(uint32_t memReqts, uint32_t numIter, SecureBinaryData salt) :
hashFunctionName_( "sha512" ),
hashOutputBytes_( 64 ),
kdfOutputBytes_( 32 )
{
usePrecomputedKdfParams(memReqts, numIter, salt);
}
/////////////////////////////////////////////////////////////////////////////
void KdfRomix::computeKdfParams(double targetComputeSec, uint32_t maxMemReqts)
{
// Create a random salt, even though this is probably unnecessary:
// the variation in numIter and memReqts is probably effective enough
salt_ = SecureBinaryData().GenerateRandom(32);
// If target compute is 0s, then this method really only generates
// a random salt, and sets the other params to default minimum.
if(targetComputeSec == 0)
{
numIterations_ = 1;
memoryReqtBytes_ = 1024;
return;
}
// Here, we pick the largest memory reqt that allows the executing system
// to compute the KDF is less than the target time. A maximum can be
// specified, in case the target system is likely to be memory-limited
// more than compute-speed limited
SecureBinaryData testKey("This is an example key to test KDF iteration speed");
// Start the search for a memory value at 1kB
memoryReqtBytes_ = 1024;
double approxSec = 0;
while(approxSec <= targetComputeSec/4 && memoryReqtBytes_ < maxMemReqts)
{
memoryReqtBytes_ *= 2;
sequenceCount_ = memoryReqtBytes_ / hashOutputBytes_;
lookupTable_.resize(memoryReqtBytes_);
TIMER_RESTART("KDF_Mem_Search");
testKey = DeriveKey_OneIter(testKey);
TIMER_STOP("KDF_Mem_Search");
approxSec = TIMER_READ_SEC("KDF_Mem_Search");
}
// Recompute here, in case we didn't enter the search above
sequenceCount_ = memoryReqtBytes_ / hashOutputBytes_;
lookupTable_.resize(memoryReqtBytes_);
// Depending on the search above (or if a low max memory was chosen,
// we may need to do multiple iterations to achieve the desired compute
// time on this system.
double allItersSec = 0;
uint32_t numTest = 1;
while(allItersSec < 0.02)
{
numTest *= 2;
TIMER_RESTART("KDF_Time_Search");
for(uint32_t i=0; i consecutive hashes of the passphrase
// Every iteration is stored in the next 64-bytes in the Lookup table
for(uint32_t nByte=0; nByte
V64ptr = (uint64_t*)(frontOfLUT + HSZ*newIndex);
// xor X with V, and store the result in X
for(uint32_t i=0; i::Encryption aes_enc( (byte*)key.getPtr(),
key.getSize(),
(byte*)iv.getPtr());
aes_enc.ProcessData( (byte*)encrData.getPtr(),
(byte*)data.getPtr(),
data.getSize());
return encrData;
}
/////////////////////////////////////////////////////////////////////////////
// Implement AES decryption using AES mode, CFB
SecureBinaryData CryptoAES::DecryptCFB(SecureBinaryData & data,
SecureBinaryData & key,
SecureBinaryData iv )
{
if(CRYPTO_DEBUG)
{
cout << "AES Decrypt" << endl;
cout << " BinData: " << data.toHexStr() << endl;
cout << " BinKey : " << key.toHexStr() << endl;
cout << " BinIV : " << iv.toHexStr() << endl;
}
if(data.getSize() == 0)
return SecureBinaryData(0);
SecureBinaryData unencrData(data.getSize());
BTC_CFB_MODE::Decryption aes_enc( (byte*)key.getPtr(),
key.getSize(),
(byte*)iv.getPtr());
aes_enc.ProcessData( (byte*)unencrData.getPtr(),
(byte*)data.getPtr(),
data.getSize());
return unencrData;
}
/////////////////////////////////////////////////////////////////////////////
// Same as above, but only changing the AES mode of operation (CBC, not CFB)
SecureBinaryData CryptoAES::EncryptCBC(const SecureBinaryData & data,
const SecureBinaryData & key,
SecureBinaryData & iv) const
{
if(CRYPTO_DEBUG)
{
cout << "AES Decrypt" << endl;
cout << " BinData: " << data.toHexStr() << endl;
cout << " BinKey : " << key.toHexStr() << endl;
cout << " BinIV : " << iv.toHexStr() << endl;
}
if(data.getSize() == 0)
return SecureBinaryData(0);
SecureBinaryData encrData(data.getSize());
// Caller can supply their own IV/entropy, or let it be generated here
// (variable "iv" is a reference, so check it on the way out)
if(iv.getSize() == 0)
iv = SecureBinaryData().GenerateRandom(BTC_AES::BLOCKSIZE);
BTC_CBC_MODE::Encryption aes_enc( (byte*)key.getPtr(),
key.getSize(),
(byte*)iv.getPtr());
aes_enc.ProcessData( (byte*)encrData.getPtr(),
(byte*)data.getPtr(),
data.getSize());
return encrData;
}
/////////////////////////////////////////////////////////////////////////////
// Same as above, but only changing the AES mode of operation (CBC, not CFB)
SecureBinaryData CryptoAES::DecryptCBC(const SecureBinaryData & data,
const SecureBinaryData & key,
const SecureBinaryData & iv ) const
{
if(CRYPTO_DEBUG)
{
cout << "AES Decrypt" << endl;
cout << " BinData: " << data.toHexStr() << endl;
cout << " BinKey : " << key.toHexStr() << endl;
cout << " BinIV : " << iv.toHexStr() << endl;
}
if(data.getSize() == 0)
return SecureBinaryData(0);
SecureBinaryData unencrData(data.getSize());
BTC_CBC_MODE::Decryption aes_enc( (byte*)key.getPtr(),
key.getSize(),
(byte*)iv.getPtr());
aes_enc.ProcessData( (byte*)unencrData.getPtr(),
(byte*)data.getPtr(),
data.getSize());
return unencrData;
}
/////////////////////////////////////////////////////////////////////////////
BTC_PRIVKEY CryptoECDSA::CreateNewPrivateKey(SecureBinaryData entropy)
{
return ParsePrivateKey(SecureBinaryData().GenerateRandom(32, entropy));
}
/////////////////////////////////////////////////////////////////////////////
BTC_PRIVKEY CryptoECDSA::ParsePrivateKey(SecureBinaryData const & privKeyData)
{
BTC_PRIVKEY cppPrivKey;
CryptoPP::Integer privateExp;
privateExp.Decode(privKeyData.getPtr(), privKeyData.getSize(), UNSIGNED);
cppPrivKey.Initialize(CryptoPP::ASN1::secp256k1(), privateExp);
return cppPrivKey;
}
/////////////////////////////////////////////////////////////////////////////
BTC_PUBKEY CryptoECDSA::ParsePublicKey(SecureBinaryData const & pubKey65B)
{
SecureBinaryData pubXbin(pubKey65B.getSliceRef( 1,32));
SecureBinaryData pubYbin(pubKey65B.getSliceRef(33,32));
return ParsePublicKey(pubXbin, pubYbin);
}
/////////////////////////////////////////////////////////////////////////////
BTC_PUBKEY CryptoECDSA::ParsePublicKey(SecureBinaryData const & pubKeyX32B,
SecureBinaryData const & pubKeyY32B)
{
BTC_PUBKEY cppPubKey;
CryptoPP::Integer pubX;
CryptoPP::Integer pubY;
pubX.Decode(pubKeyX32B.getPtr(), pubKeyX32B.getSize(), UNSIGNED);
pubY.Decode(pubKeyY32B.getPtr(), pubKeyY32B.getSize(), UNSIGNED);
BTC_ECPOINT publicPoint(pubX, pubY);
// Initialize the public key with the ECP point just created
cppPubKey.Initialize(CryptoPP::ASN1::secp256k1(), publicPoint);
// Validate the public key -- not sure why this needs a PRNG
BTC_PRNG prng;
assert(cppPubKey.Validate(prng, 3));
return cppPubKey;
}
/////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::SerializePrivateKey(BTC_PRIVKEY const & privKey)
{
CryptoPP::Integer privateExp = privKey.GetPrivateExponent();
SecureBinaryData privKeyData(32);
privateExp.Encode(privKeyData.getPtr(), privKeyData.getSize(), UNSIGNED);
return privKeyData;
}
/////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::SerializePublicKey(BTC_PUBKEY const & pubKey)
{
BTC_ECPOINT publicPoint = pubKey.GetPublicElement();
CryptoPP::Integer pubX = publicPoint.x;
CryptoPP::Integer pubY = publicPoint.y;
SecureBinaryData pubData(65);
pubData.fill(0x04); // we fill just to set the first byte...
pubX.Encode(pubData.getPtr()+1, 32, UNSIGNED);
pubY.Encode(pubData.getPtr()+33, 32, UNSIGNED);
return pubData;
}
/////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::ComputePublicKey(SecureBinaryData const & cppPrivKey)
{
BTC_PRIVKEY pk = ParsePrivateKey(cppPrivKey);
BTC_PUBKEY pub;
pk.MakePublicKey(pub);
return SerializePublicKey(pub);
}
/////////////////////////////////////////////////////////////////////////////
BTC_PUBKEY CryptoECDSA::ComputePublicKey(BTC_PRIVKEY const & cppPrivKey)
{
BTC_PUBKEY cppPubKey;
cppPrivKey.MakePublicKey(cppPubKey);
// Validate the public key -- not sure why this needs a prng...
BTC_PRNG prng;
assert(cppPubKey.Validate(prng, 3));
return cppPubKey;
}
////////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::GenerateNewPrivateKey(SecureBinaryData entropy)
{
return SecureBinaryData().GenerateRandom(32, entropy);
}
////////////////////////////////////////////////////////////////////////////////
bool CryptoECDSA::CheckPubPrivKeyMatch(BTC_PRIVKEY const & cppPrivKey,
BTC_PUBKEY const & cppPubKey)
{
BTC_PUBKEY computedPubKey;
cppPrivKey.MakePublicKey(computedPubKey);
BTC_ECPOINT ppA = cppPubKey.GetPublicElement();
BTC_ECPOINT ppB = computedPubKey.GetPublicElement();
return (ppA.x==ppB.x && ppA.y==ppB.y);
}
/////////////////////////////////////////////////////////////////////////////
bool CryptoECDSA::CheckPubPrivKeyMatch(SecureBinaryData const & privKey32,
SecureBinaryData const & pubKey65)
{
if(CRYPTO_DEBUG)
{
cout << "CheckPubPrivKeyMatch:" << endl;
cout << " BinPrv: " << privKey32.toHexStr() << endl;
cout << " BinPub: " << pubKey65.toHexStr() << endl;
}
BTC_PRIVKEY privKey = ParsePrivateKey(privKey32);
BTC_PUBKEY pubKey = ParsePublicKey(pubKey65);
return CheckPubPrivKeyMatch(privKey, pubKey);
}
bool CryptoECDSA::VerifyPublicKeyValid(SecureBinaryData const & pubKey)
{
if(CRYPTO_DEBUG)
{
cout << "BinPub: " << pubKey.toHexStr() << endl;
}
SecureBinaryData keyToCheck(65);
// To support compressed keys, we'll just check to see if a key is compressed
// and then decompress it.
if(pubKey.getSize() == 33) {
keyToCheck = UncompressPoint(pubKey);
}
else {
keyToCheck = pubKey;
}
// Basically just copying the ParsePublicKey method, but without
// the assert that would throw an error from C++
SecureBinaryData pubXbin(keyToCheck.getSliceRef( 1,32));
SecureBinaryData pubYbin(keyToCheck.getSliceRef(33,32));
CryptoPP::Integer pubX;
CryptoPP::Integer pubY;
pubX.Decode(pubXbin.getPtr(), pubXbin.getSize(), UNSIGNED);
pubY.Decode(pubYbin.getPtr(), pubYbin.getSize(), UNSIGNED);
BTC_ECPOINT publicPoint(pubX, pubY);
// Initialize the public key with the ECP point just created
BTC_PUBKEY cppPubKey;
cppPubKey.Initialize(CryptoPP::ASN1::secp256k1(), publicPoint);
// Validate the public key -- not sure why this needs a PRNG
BTC_PRNG prng;
return cppPubKey.Validate(prng, 3);
}
/////////////////////////////////////////////////////////////////////////////
// Use the secp256k1 curve to sign data of an arbitrary length.
// Input: Data to sign (const SecureBinaryData&)
// The private key used to sign the data (const SecureBinaryData&)
// A flag indicating if deterministic signing is used (const bool&)
// Output: None
// Return: The signature of the data (SecureBinaryData)
SecureBinaryData CryptoECDSA::SignData(SecureBinaryData const & binToSign,
SecureBinaryData const & binPrivKey,
const bool& detSign)
{
if(CRYPTO_DEBUG)
{
cout << "SignData:" << endl;
cout << " BinSgn: " << binToSign.getSize() << " " << binToSign.toHexStr() << endl;
cout << " BinPrv: " << binPrivKey.getSize() << " " << binPrivKey.toHexStr() << endl;
cout << " DetSign: " << detSign << endl;
}
BTC_PRIVKEY cppPrivKey = ParsePrivateKey(binPrivKey);
return SignData(binToSign, cppPrivKey, detSign);
}
/////////////////////////////////////////////////////////////////////////////
// Use the secp256k1 curve to sign data of an arbitrary length.
// Input: Data to sign (const SecureBinaryData&)
// The private key used to sign the data (const BTC_PRIVKEY&)
// A flag indicating if deterministic signing is used (const bool&)
// Output: None
// Return: The signature of the data (SecureBinaryData)
SecureBinaryData CryptoECDSA::SignData(SecureBinaryData const & binToSign,
BTC_PRIVKEY const & cppPrivKey,
const bool& detSign)
{
// We trick the Crypto++ ECDSA module by passing it a single-hashed
// message, it will do the second hash before it signs it. This is
// exactly what we need.
CryptoPP::SHA256 sha256;
BTC_PRNG prng;
// Execute the first sha256 op -- the signer will do the other one
SecureBinaryData hashVal(32);
sha256.CalculateDigest(hashVal.getPtr(),
binToSign.getPtr(),
binToSign.getSize());
// Do we want to use a PRNG or use deterministic signing (RFC 6979)?
string signature;
if(detSign)
{
BTC_DETSIGNER signer(cppPrivKey);
CryptoPP::StringSource(
hashVal.toBinStr(), true, new CryptoPP::SignerFilter(
prng, signer, new CryptoPP::StringSink(signature)));
}
else
{
BTC_SIGNER signer(cppPrivKey);
CryptoPP::StringSource(
hashVal.toBinStr(), true, new CryptoPP::SignerFilter(
prng, signer, new CryptoPP::StringSink(signature)));
}
return SecureBinaryData(signature);
}
/////////////////////////////////////////////////////////////////////////////
bool CryptoECDSA::VerifyData(SecureBinaryData const & binMessage,
SecureBinaryData const & binSignature,
SecureBinaryData const & pubkey65B)
{
if(CRYPTO_DEBUG)
{
cout << "VerifyData:" << endl;
cout << " BinMsg: " << binMessage.toHexStr() << endl;
cout << " BinSig: " << binSignature.toHexStr() << endl;
cout << " BinPub: " << pubkey65B.toHexStr() << endl;
}
BTC_PUBKEY cppPubKey = ParsePublicKey(pubkey65B);
return VerifyData(binMessage, binSignature, cppPubKey);
}
/////////////////////////////////////////////////////////////////////////////
bool CryptoECDSA::VerifyData(BinaryData const & binMessage,
const BinaryData& sig,
BTC_PUBKEY const & cppPubKey) const
{
/***
This is the faster sig verification, with less sanity checks and copies.
Meant for chain verifiation, use the SecureBinaryData versions for regular
verifications.
***/
CryptoPP::SHA256 sha256;
BTC_PRNG prng;
//pub keys are already validated by the script parser
// We execute the first SHA256 op, here. Next one is done by Verifier
BinaryData hashVal(32);
sha256.CalculateDigest(hashVal.getPtr(),
binMessage.getPtr(),
binMessage.getSize());
// Verifying message
BTC_VERIFIER verifier(cppPubKey);
return verifier.VerifyMessage((const byte*)hashVal.getPtr(),
hashVal.getSize(),
(const byte*)sig.getPtr(),
sig.getSize());
}
/////////////////////////////////////////////////////////////////////////////
bool CryptoECDSA::VerifyData(SecureBinaryData const & binMessage,
SecureBinaryData const & binSignature,
BTC_PUBKEY const & cppPubKey)
{
CryptoPP::SHA256 sha256;
BTC_PRNG prng;
assert(cppPubKey.Validate(prng, 3));
// We execute the first SHA256 op, here. Next one is done by Verifier
SecureBinaryData hashVal(32);
sha256.CalculateDigest(hashVal.getPtr(),
binMessage.getPtr(),
binMessage.getSize());
// Verifying message
BTC_VERIFIER verifier(cppPubKey);
return verifier.VerifyMessage((const byte*)hashVal.getPtr(),
hashVal.getSize(),
(const byte*)binSignature.getPtr(),
binSignature.getSize());
}
/////////////////////////////////////////////////////////////////////////////
// Deterministically generate new private key using a chaincode
// Changed: added using the hash of the public key to the mix
// b/c multiplying by the chaincode alone is too "linear"
// (there's no reason to believe it's insecure, but it doesn't
// hurt to add some extra entropy/non-linearity to the chain
// generation process)
SecureBinaryData CryptoECDSA::ComputeChainedPrivateKey(
SecureBinaryData const & binPrivKey,
SecureBinaryData const & chainCode,
SecureBinaryData* multiplierOut)
{
auto&& binPubKey = ComputePublicKey(binPrivKey);
if( binPrivKey.getSize() != 32 || chainCode.getSize() != 32)
{
LOGERR << "***ERROR: Invalid private key or chaincode (both must be 32B)";
LOGERR << "BinPrivKey: " << binPrivKey.getSize();
LOGERR << "BinPrivKey: (not logged for security)";
LOGERR << "BinChain : " << chainCode.getSize();
LOGERR << "BinChain : " << chainCode.toHexStr();
}
// Adding extra entropy to chaincode by xor'ing with hash256 of pubkey
BinaryData chainMod = binPubKey.getHash256();
BinaryData chainOrig = chainCode.getRawCopy();
BinaryData chainXor(32);
for(uint8_t i=0; i<8; i++)
{
uint8_t offset = 4*i;
*(uint32_t*)(chainXor.getPtr()+offset) =
*(uint32_t*)( chainMod.getPtr()+offset) ^
*(uint32_t*)(chainOrig.getPtr()+offset);
}
// Hard-code the order of the group
static SecureBinaryData SECP256K1_ORDER_BE = SecureBinaryData().CreateFromHex(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
CryptoPP::Integer mult, origPrivExp, ecOrder;
// A
mult.Decode(chainXor.getPtr(), chainXor.getSize(), UNSIGNED);
// B
origPrivExp.Decode(binPrivKey.getPtr(), binPrivKey.getSize(), UNSIGNED);
// C
ecOrder.Decode(SECP256K1_ORDER_BE.getPtr(), SECP256K1_ORDER_BE.getSize(), UNSIGNED);
// A*B mod C will get us a new private key exponent
CryptoPP::Integer newPrivExponent =
a_times_b_mod_c(mult, origPrivExp, ecOrder);
// Convert new private exponent to big-endian binary string
SecureBinaryData newPrivData(32);
newPrivExponent.Encode(newPrivData.getPtr(), newPrivData.getSize(), UNSIGNED);
if(multiplierOut != NULL)
(*multiplierOut) = SecureBinaryData(chainXor);
return newPrivData;
}
/////////////////////////////////////////////////////////////////////////////
// Deterministically generate new public key using a chaincode
SecureBinaryData CryptoECDSA::ComputeChainedPublicKey(
SecureBinaryData const & binPubKey,
SecureBinaryData const & chainCode,
SecureBinaryData* multiplierOut)
{
if(CRYPTO_DEBUG)
{
cout << "ComputeChainedPUBLICKey:" << endl;
cout << " BinPub: " << binPubKey.toHexStr() << endl;
cout << " BinChn: " << chainCode.toHexStr() << endl;
}
static SecureBinaryData SECP256K1_ORDER_BE = SecureBinaryData::CreateFromHex(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
// Added extra entropy to chaincode by xor'ing with hash256 of pubkey
BinaryData chainMod = binPubKey.getHash256();
BinaryData chainOrig = chainCode.getRawCopy();
BinaryData chainXor(32);
for(uint8_t i=0; i<8; i++)
{
uint8_t offset = 4*i;
*(uint32_t*)(chainXor.getPtr()+offset) =
*(uint32_t*)( chainMod.getPtr()+offset) ^
*(uint32_t*)(chainOrig.getPtr()+offset);
}
// Parse the chaincode as a big-endian integer
CryptoPP::Integer mult;
mult.Decode(chainXor.getPtr(), chainXor.getSize(), UNSIGNED);
// "new" init as "old", to make sure it's initialized on the correct curve
BTC_PUBKEY oldPubKey = ParsePublicKey(binPubKey);
BTC_PUBKEY newPubKey = ParsePublicKey(binPubKey);
// Let Crypto++ do the EC math for us, serialize the new public key
newPubKey.SetPublicElement( oldPubKey.ExponentiatePublicElement(mult) );
if(multiplierOut != NULL)
(*multiplierOut) = SecureBinaryData(chainXor);
//LOGINFO << "Computed new chained public key using:";
//LOGINFO << " Public key: " << binPubKey.toHexStr().c_str();
//LOGINFO << " PubKeyHash: " << chainMod.toHexStr().c_str();
//LOGINFO << " Chaincode: " << chainOrig.toHexStr().c_str();
//LOGINFO << " Multiplier: " << chainXor.toHexStr().c_str();
return CryptoECDSA::SerializePublicKey(newPubKey);
}
////////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::InvMod(const SecureBinaryData& m)
{
static BinaryData N = BinaryData::CreateFromHex(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
CryptoPP::Integer cppM;
CryptoPP::Integer cppModulo;
cppM.Decode(m.getPtr(), m.getSize(), UNSIGNED);
cppModulo.Decode(N.getPtr(), N.getSize(), UNSIGNED);
CryptoPP::Integer cppResult = cppM.InverseMod(cppModulo);
SecureBinaryData result(32);
cppResult.Encode(result.getPtr(), result.getSize(), UNSIGNED);
return result;
}
////////////////////////////////////////////////////////////////////////////////
bool CryptoECDSA::ECVerifyPoint(BinaryData const & x,
BinaryData const & y)
{
BTC_PUBKEY cppPubKey;
CryptoPP::Integer pubX;
CryptoPP::Integer pubY;
pubX.Decode(x.getPtr(), x.getSize(), UNSIGNED);
pubY.Decode(y.getPtr(), y.getSize(), UNSIGNED);
BTC_ECPOINT publicPoint(pubX, pubY);
// Initialize the public key with the ECP point just created
cppPubKey.Initialize(CryptoPP::ASN1::secp256k1(), publicPoint);
// Validate the public key -- not sure why this needs a PRNG
BTC_PRNG prng;
return cppPubKey.Validate(prng, 3);
}
////////////////////////////////////////////////////////////////////////////////
CryptoPP::ECP CryptoECDSA::Get_secp256k1_ECP(void)
{
static bool firstRun = true;
static CryptoPP::Integer intN;
static CryptoPP::Integer inta;
static CryptoPP::Integer intb;
static BinaryData N;
static BinaryData a;
static BinaryData b;
if(firstRun)
{
firstRun = false;
N = BinaryData::CreateFromHex(
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f");
a = BinaryData::CreateFromHex(
"0000000000000000000000000000000000000000000000000000000000000000");
b = BinaryData::CreateFromHex(
"0000000000000000000000000000000000000000000000000000000000000007");
intN.Decode( N.getPtr(), N.getSize(), UNSIGNED);
inta.Decode( a.getPtr(), a.getSize(), UNSIGNED);
intb.Decode( b.getPtr(), b.getSize(), UNSIGNED);
}
return CryptoPP::ECP(intN, inta, intb);
}
////////////////////////////////////////////////////////////////////////////////
BinaryData CryptoECDSA::ECMultiplyScalars(BinaryData const & A,
BinaryData const & B)
{
// Hardcode the order of the secp256k1 EC group
static BinaryData N = BinaryData::CreateFromHex(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
CryptoPP::Integer intA, intB, intC, intN;
intA.Decode(A.getPtr(), A.getSize(), UNSIGNED);
intB.Decode(B.getPtr(), B.getSize(), UNSIGNED);
intN.Decode(N.getPtr(), N.getSize(), UNSIGNED);
intC = a_times_b_mod_c(intA, intB, intN);
BinaryData C(32);
intC.Encode(C.getPtr(), 32, UNSIGNED);
return C;
}
////////////////////////////////////////////////////////////////////////////////
BinaryData CryptoECDSA::ECMultiplyPoint(BinaryData const & A,
BinaryData const & Bx,
BinaryData const & By)
{
CryptoPP::ECP ecp = Get_secp256k1_ECP();
CryptoPP::Integer intA, intBx, intBy, intCx, intCy;
intA.Decode( A.getPtr(), A.getSize(), UNSIGNED);
intBx.Decode(Bx.getPtr(), Bx.getSize(), UNSIGNED);
intBy.Decode(By.getPtr(), By.getSize(), UNSIGNED);
BTC_ECPOINT B(intBx, intBy);
BTC_ECPOINT C = ecp.ScalarMultiply(B, intA);
BinaryData Cbd(64);
C.x.Encode(Cbd.getPtr(), 32, UNSIGNED);
C.y.Encode(Cbd.getPtr()+32, 32, UNSIGNED);
return Cbd;
}
////////////////////////////////////////////////////////////////////////////////
BinaryData CryptoECDSA::ECAddPoints(BinaryData const & Ax,
BinaryData const & Ay,
BinaryData const & Bx,
BinaryData const & By)
{
CryptoPP::ECP ecp = Get_secp256k1_ECP();
CryptoPP::Integer intAx, intAy, intBx, intBy, intCx, intCy;
intAx.Decode(Ax.getPtr(), Ax.getSize(), UNSIGNED);
intAy.Decode(Ay.getPtr(), Ay.getSize(), UNSIGNED);
intBx.Decode(Bx.getPtr(), Bx.getSize(), UNSIGNED);
intBy.Decode(By.getPtr(), By.getSize(), UNSIGNED);
BTC_ECPOINT A(intAx, intAy);
BTC_ECPOINT B(intBx, intBy);
BTC_ECPOINT C = ecp.Add(A,B);
BinaryData Cbd(64);
C.x.Encode(Cbd.getPtr(), 32, UNSIGNED);
C.y.Encode(Cbd.getPtr()+32, 32, UNSIGNED);
return Cbd;
}
////////////////////////////////////////////////////////////////////////////////
BinaryData CryptoECDSA::ECInverse(BinaryData const & Ax,
BinaryData const & Ay)
{
CryptoPP::ECP ecp = Get_secp256k1_ECP();
CryptoPP::Integer intAx, intAy, intCx, intCy;
intAx.Decode(Ax.getPtr(), Ax.getSize(), UNSIGNED);
intAy.Decode(Ay.getPtr(), Ay.getSize(), UNSIGNED);
BTC_ECPOINT A(intAx, intAy);
BTC_ECPOINT C = ecp.Inverse(A);
BinaryData Cbd(64);
C.x.Encode(Cbd.getPtr(), 32, UNSIGNED);
C.y.Encode(Cbd.getPtr()+32, 32, UNSIGNED);
return Cbd;
}
////////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::CompressPoint(SecureBinaryData const & pubKey65)
{
CryptoPP::ECP ecp = Get_secp256k1_ECP();
BTC_ECPOINT ptPub;
ecp.DecodePoint(ptPub, (byte*)pubKey65.getPtr(), 65);
SecureBinaryData ptCompressed(33);
ecp.EncodePoint((byte*)ptCompressed.getPtr(), ptPub, true);
return ptCompressed;
}
////////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::UncompressPoint(SecureBinaryData const & pubKey33)
{
CryptoPP::ECP ecp = Get_secp256k1_ECP();
BTC_ECPOINT ptPub;
ecp.DecodePoint(ptPub, (byte*)pubKey33.getPtr(), 33);
SecureBinaryData ptUncompressed(65);
ecp.EncodePoint((byte*)ptUncompressed.getPtr(), ptPub, false);
return ptUncompressed;
}
////////////////////////////////////////////////////////////////////////////////
BinaryData CryptoECDSA::computeLowS(BinaryDataRef s)
{
static SecureBinaryData SECP256K1_ORDER_BE = SecureBinaryData().CreateFromHex(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
CryptoPP::Integer ecOrder, sInteger;
ecOrder.Decode(SECP256K1_ORDER_BE.getPtr(), SECP256K1_ORDER_BE.getSize(), UNSIGNED);
//divide by 2
auto&& halfOrder = ecOrder >> 1;
sInteger.Decode(s.getPtr(), s.getSize(), UNSIGNED);
if (sInteger > halfOrder)
sInteger = ecOrder - sInteger;
auto len = sInteger.ByteCount();
BinaryData lowS(len);
sInteger.Encode(lowS.getPtr(), len, UNSIGNED);
return lowS;
}
////////////////////////////////////////////////////////////////////////////////
SecureBinaryData CryptoECDSA::a_plus_b_mod_n(
const SecureBinaryData& a, const SecureBinaryData& b, const SecureBinaryData& n)
{
CryptoPP::Integer A, B, N;
A.Decode(a.getPtr(), a.getSize(), UNSIGNED);
B.Decode(b.getPtr(), b.getSize(), UNSIGNED);
N.Decode(n.getPtr(), n.getSize(), UNSIGNED);
auto&& result = (A + B) % N;
SecureBinaryData result_bd(n.getSize());
result.Encode(result_bd.getPtr(), result_bd.getSize(), UNSIGNED);
return result_bd;
}
////////////////////////////////////////////////////////////////////////////////
pair CryptoECDSA::bip32_derive_private_key(
const SecureBinaryData& privateKey,
const SecureBinaryData& chainCode,
unsigned index)
{
SecureBinaryData hmac_result(64);
if (index > 0x80000000)
{
//hard derivation:
//hmac512(chaincode, 0 | privatekey | index)
CryptoPP::HMAC<:sha512> hmac(
chainCode.getPtr(), chainCode.getSize());
BinaryWriter bw;
bw.put_uint8_t(0); //0x00
bw.put_BinaryData(privateKey);
bw.put_uint32_t(index, BE);
hmac.CalculateDigest(hmac_result.getPtr(),
bw.getData().getPtr(), bw.getSize());
}
else
{
//soft derivation:
//hmac512(chaincode, compressed pubkey | index)
CryptoPP::HMAC<:sha512> hmac(
chainCode.getPtr(), chainCode.getSize());
//pubkey
auto&& pubkey = CryptoECDSA().ComputePublicKey(privateKey);
auto&& compressed_pubkey = CryptoECDSA().CompressPoint(pubkey);
BinaryWriter bw;
bw.put_BinaryData(compressed_pubkey);
bw.put_uint32_t(index, BE);
hmac.CalculateDigest(hmac_result.getPtr(),
bw.getData().getPtr(), bw.getSize());
}
//privkey, chaincode
pair derived_key;
//privkey
static SecureBinaryData SECP256K1_ORDER_BE = SecureBinaryData().CreateFromHex(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
static SecureBinaryData zero = SecureBinaryData().CreateFromHex(
"0000000000000000000000000000000000000000000000000000000000000000");
auto&& precursor = hmac_result.getSliceCopy(0, 32);
derived_key.first = move(CryptoECDSA::a_plus_b_mod_n(
precursor, privateKey, SECP256K1_ORDER_BE));
//chaincode
derived_key.second = move(hmac_result.getSliceCopy(32, 32));
//sanity checks
if (derived_key.second > SECP256K1_ORDER_BE)
throw runtime_error("derived chaincode > curve order");
if (derived_key.first == zero)
throw runtime_error("derived private key == 0");
return derived_key;
}
////////////////////////////////////////////////////////////////////////////////
pair CryptoECDSA::bip32_derive_public_key(
const SecureBinaryData& pubKey,
const SecureBinaryData& chainCode,
unsigned index)
{
SecureBinaryData hmac_result(64);
if (index > 0x80000000)
{
//hard derivation
throw runtime_error("cannot hard derive from pubkey");
}
else
{
//soft derivation:
//hmac512(chaincode, compressed pubkey | index)
CryptoPP::HMAC<:sha512> hmac(
chainCode.getPtr(), chainCode.getSize());
//pubkey
SecureBinaryData compressedPubKey;
if (pubKey.getSize() == 65)
{
compressedPubKey = move(CryptoECDSA().CompressPoint(pubKey));
}
else if (pubKey.getSize() == 33)
{
compressedPubKey = pubKey;
}
else
{
throw runtime_error("pubkey has to be 65 or 33 bytes long");
}
BinaryWriter bw;
bw.put_BinaryData(compressedPubKey);
bw.put_uint32_t(index, BE);
hmac.CalculateDigest(hmac_result.getPtr(),
bw.getData().getPtr(), bw.getSize());
}
//pubkey, chaincode
pair derived_key;
//pubkey
auto&& precursor = hmac_result.getSliceCopy(0, 32);
auto&& precursor_point = CryptoECDSA().ComputePublicKey(precursor);
SecureBinaryData decompressedPubKey;
if (pubKey.getSize() == 65)
{
decompressedPubKey = pubKey;
}
else if (pubKey.getSize() == 33)
{
decompressedPubKey = move(CryptoECDSA().UncompressPoint(pubKey));
}
else
{
throw runtime_error("pubkey has to be 65 or 33 bytes long");
}
auto&& new_point = CryptoECDSA().ECAddPoints(
decompressedPubKey.getSliceRef(1, 32), decompressedPubKey.getSliceRef(33, 32),
precursor_point.getSliceRef(1, 32), precursor_point.getSliceRef(33, 32));
//TODO: check new point is not G
derived_key.first = move(CryptoECDSA().ComputePublicKey(new_point));
//chaincode
derived_key.second = move(hmac_result.getSliceCopy(32, 32));
//check chaincode is not superior to the curve's order
static SecureBinaryData SECP256K1_ORDER_BE = SecureBinaryData().CreateFromHex(
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
if (derived_key.second > SECP256K1_ORDER_BE)
throw runtime_error("derived chaincode > curve order");
return derived_key;
}
////////////////////////////////////////////////////////////////////////////////
pair CryptoECDSA::bip32_seed_to_master_root(
const SecureBinaryData& seed)
{
auto&& key_hmac = BtcUtils::getHMAC512("Bitcoin Seed", seed);
auto&& privkey = key_hmac.getSliceCopy(0, 32);
auto&& chaincode = key_hmac.getSliceCopy(32, 32);
return make_pair(privkey, chaincode);
}