bool CryptoECDSA::VerifyPublicKeyValid(SecureBinaryData const & pubKey65) { if(CRYPTO_DEBUG) { cout << "BinPub: " << pubKey65.toHexStr() << endl; } // Basically just copying the ParsePublicKey method, but without // the assert that would throw an error from C++ SecureBinaryData pubXbin(pubKey65.getSliceRef( 1,32)); SecureBinaryData pubYbin(pubKey65.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 static BTC_PRNG prng; return cppPubKey.Validate(prng, 3); }
///////////////////////////////////////////////////////////////////////////// // Same as above, but only changing the AES mode of operation (CBC, not CFB) SecureBinaryData CryptoAES::DecryptCBC(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_CBC_MODE<BTC_AES>::Decryption aes_enc( (byte*)key.getPtr(), key.getSize(), (byte*)iv.getPtr()); aes_enc.ProcessData( (byte*)unencrData.getPtr(), (byte*)data.getPtr(), data.getSize()); return unencrData; }
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()); }
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; }
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; }
SecureBinaryData CryptoECDSA::SignData(SecureBinaryData const & binToSign, SecureBinaryData const & binPrivKey) { if(CRYPTO_DEBUG) { cout << "SignData:" << endl; cout << " BinSgn: " << binToSign.getSize() << " " << binToSign.toHexStr() << endl; cout << " BinPrv: " << binPrivKey.getSize() << " " << binPrivKey.toHexStr() << endl; } BTC_PRIVKEY cppPrivKey = ParsePrivateKey(binPrivKey); return SignData(binToSign, cppPrivKey); }
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::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); }
///////////////////////////////////////////////////////////////////////////// // 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 SecureBinaryData::GenerateRandom(uint32_t numBytes, SecureBinaryData entropy) { BTC_PRNG prng; // Entropy here refers to *EXTRA* entropy. Crypto++ has it's own mechanism // for generating entropy which is sufficient, but it doesn't hurt to add // more if you have it. if(entropy.getSize() > 0) prng.IncorporateEntropy( (byte*)entropy.getPtr(), entropy.getSize()); SecureBinaryData randData(numBytes); prng.GenerateBlock(randData.getPtr(), numBytes); return randData; }
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 SecureBinaryData::operator==(SecureBinaryData const & sbd2) const { if(getSize() != sbd2.getSize()) return false; for(unsigned int i=0; i<getSize(); i++) if( (*this)[i] != sbd2[i] ) return false; return true; }
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; }
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::SignData(SecureBinaryData const & binToSign, BTC_PRIVKEY const & cppPrivKey) { // 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. static CryptoPP::SHA256 sha256; static 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()); string signature; BTC_SIGNER signer(cppPrivKey); CryptoPP::StringSource( hashVal.toBinStr(), true, new CryptoPP::SignerFilter( prng, signer, new CryptoPP::StringSink(signature))); return SecureBinaryData(signature); }
///////////////////////////////////////////////////////////////////////////// // 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::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); }
///////////////////////////////////////////////////////////////////////////// // 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 binPubKey, SecureBinaryData* multiplierOut) { if(CRYPTO_DEBUG) { cout << "ComputeChainedPrivateKey:" << endl; cout << " BinPrv: " << binPrivKey.toHexStr() << endl; cout << " BinChn: " << chainCode.toHexStr() << endl; cout << " BinPub: " << binPubKey.toHexStr() << endl; } if( binPubKey.getSize()==0 ) 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 << "BinPrivKey: " << binPrivKey.toHexStr(); 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); //LOGINFO << "Computed new chained private 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 newPrivData; }
void TestCrypto(void) { SecureBinaryData a("aaaaaaaaaa"); SecureBinaryData b; b.resize(5); SecureBinaryData c; c.resize(0); a.copyFrom(b); b.copyFrom(c); c.copyFrom(a); a.resize(0); b = a; SecureBinaryData d(a); cout << "a=" << a.toHexStr() << endl; cout << "b=" << b.toHexStr() << endl; cout << "c=" << c.toHexStr() << endl; cout << "d=" << d.toHexStr() << endl; SecureBinaryData e("eeeeeeeeeeeeeeee"); SecureBinaryData f("ffffffff"); SecureBinaryData g(0); e = g.copy(); e = f.copy(); cout << "e=" << e.toHexStr() << endl; cout << "f=" << f.toHexStr() << endl; cout << "g=" << g.toHexStr() << endl; ///////////////////////////////////////////////////////////////////////////// // Start Key-Derivation-Function (KDF) Tests. // ROMIX is the provably memory-hard (GPU-resistent) algorithm proposed by // Colin Percival, who is the creator of Scrypt. cout << endl << endl; cout << "Executing Key-Derivation-Function (KDF) tests" << endl; KdfRomix kdf; kdf.computeKdfParams(); kdf.printKdfParams(); SecureBinaryData passwd1("This is my first password"); SecureBinaryData passwd2("This is my first password."); SecureBinaryData passwd3("This is my first password"); SecureBinaryData key; cout << " Password1: '" << passwd1.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd1); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password2: '" << passwd2.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd2); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password1: '" << passwd3.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd3); cout << " MasterKey: '" << key.toHexStr() << endl << endl; ///////////////////////////////////////////////////////////////////////////// cout << "Executing KDF tests with longer compute time" << endl; kdf.computeKdfParams(1.0); kdf.printKdfParams(); cout << " Password1: '" << passwd1.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd1); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password2: '" << passwd2.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd2); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password1: '" << passwd3.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd3); cout << " MasterKey: '" << key.toHexStr() << endl << endl; ///////////////////////////////////////////////////////////////////////////// cout << "Executing KDF tests with limited memory target" << endl; kdf.computeKdfParams(1.0, 256*1024); kdf.printKdfParams(); cout << " Password1: '" << passwd1.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd1); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password2: '" << passwd2.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd2); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password1: '" << passwd3.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd3); cout << " MasterKey: '" << key.toHexStr() << endl << endl; ///////////////////////////////////////////////////////////////////////////// cout << "KDF with min memory requirement (1 kB)" << endl; kdf.computeKdfParams(1.0, 0); kdf.printKdfParams(); cout << " Password1: '" << passwd1.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd1); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password2: '" << passwd2.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd2); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password1: '" << passwd3.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd3); cout << " MasterKey: '" << key.toHexStr() << endl << endl; ///////////////////////////////////////////////////////////////////////////// cout << "KDF with 0 compute time" << endl; kdf.computeKdfParams(0, 0); kdf.printKdfParams(); cout << " Password1: '" << passwd1.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd1); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password2: '" << passwd2.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd2); cout << " MasterKey: '" << key.toHexStr() << endl << endl; cout << " Password1: '" << passwd3.toBinStr() << "'" << endl; key = kdf.DeriveKey(passwd3); cout << " MasterKey: '" << key.toHexStr() << endl << endl; // Test AES code using NIST test vectors /// *** Test 1 *** /// cout << endl << endl; SecureBinaryData testIV, plaintext, cipherTarg, cipherComp, testKey, rtPlain; testKey.createFromHex ("0000000000000000000000000000000000000000000000000000000000000000"); testIV.createFromHex ("80000000000000000000000000000000"); plaintext.createFromHex ("00000000000000000000000000000000"); cipherTarg.createFromHex("ddc6bf790c15760d8d9aeb6f9a75fd4e"); cout << " Plain : " << plaintext.toHexStr() << endl; cipherComp = CryptoAES().EncryptCFB(plaintext, testKey, testIV); cout << " CipherTarget : " << cipherComp.toHexStr() << endl; cout << " CipherCompute: " << cipherComp.toHexStr() << endl; rtPlain = CryptoAES().DecryptCFB(cipherComp, testKey, testIV); cout << " Plain : " << rtPlain.toHexStr() << endl; /// *** Test 2 *** /// cout << endl << endl; testKey.createFromHex ("0000000000000000000000000000000000000000000000000000000000000000"); testIV.createFromHex ("014730f80ac625fe84f026c60bfd547d"); plaintext.createFromHex ("00000000000000000000000000000000"); cipherTarg.createFromHex("5c9d844ed46f9885085e5d6a4f94c7d7"); cout << " Plain : " << plaintext.toHexStr() << endl; cipherComp = CryptoAES().EncryptCFB(plaintext, testKey, testIV); cout << " CipherTarget : " << cipherComp.toHexStr() << endl; cout << " CipherCompute: " << cipherComp.toHexStr() << endl; rtPlain = CryptoAES().DecryptCFB(cipherComp, testKey, testIV); cout << " Plain : " << rtPlain.toHexStr() << endl; /// *** Test 3 *** /// cout << endl << endl; testKey.createFromHex ("ffffffffffff0000000000000000000000000000000000000000000000000000"); testIV.createFromHex ("00000000000000000000000000000000"); plaintext.createFromHex ("00000000000000000000000000000000"); cipherTarg.createFromHex("225f068c28476605735ad671bb8f39f3"); cout << " Plain : " << plaintext.toHexStr() << endl; cipherComp = CryptoAES().EncryptCFB(plaintext, testKey, testIV); cout << " CipherTarget : " << cipherComp.toHexStr() << endl; cout << " CipherCompute: " << cipherComp.toHexStr() << endl; rtPlain = CryptoAES().DecryptCFB(cipherComp, testKey, testIV); cout << " Plain : " << rtPlain.toHexStr() << endl; /// My own test, for sanity (can only check the roundtrip values) // This test is a lot more exciting with the couts uncommented in Encrypt/Decrypt cout << endl << endl; cout << "Starting some kdf-aes-combined tests..." << endl; kdf.printKdfParams(); testKey = kdf.DeriveKey(SecureBinaryData("This passphrase is tough to guess")); SecureBinaryData secret, cipher; secret.createFromHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); SecureBinaryData randIV(0); // tell the crypto to generate a random IV for me. cout << "Encrypting:" << endl; cipher = CryptoAES().EncryptCFB(secret, testKey, randIV); cout << endl << endl; cout << "Decrypting:" << endl; secret = CryptoAES().DecryptCFB(cipher, testKey, randIV); cout << endl << endl; // Now encrypting so I can store the encrypted data in file cout << "Encrypting again:" << endl; cipher = CryptoAES().EncryptCFB(secret, testKey, randIV); ofstream testfile("safefile.txt", ios::out); testfile << "KdfParams " << endl; testfile << " MemReqts " << kdf.getMemoryReqtBytes() << endl; testfile << " NumIters " << kdf.getNumIterations() << endl; testfile << " HexSalt " << kdf.getSalt().toHexStr() << endl; testfile << "EncryptedData" << endl; testfile << " HexIV " << randIV.toHexStr() << endl; testfile << " Cipher " << cipher.toHexStr() << endl; testfile.close(); ifstream infile("safefile.txt", ios::in); uint32_t mem, nIters; SecureBinaryData salt, iv; char deadstr[256]; char hexstr[256]; infile >> deadstr; infile >> deadstr >> mem; infile >> deadstr >> nIters; infile >> deadstr >> hexstr; salt.copyFrom( SecureBinaryData::CreateFromHex(string(hexstr, 64))); infile >> deadstr; infile >> deadstr >> hexstr; iv.copyFrom( SecureBinaryData::CreateFromHex(string(hexstr, 64))); infile >> deadstr >> hexstr; cipher.copyFrom( SecureBinaryData::CreateFromHex(string(hexstr, 64))); infile.close(); cout << endl << endl; // Will try this twice, once with correct passphrase, once without SecureBinaryData cipherTry1 = cipher; SecureBinaryData cipherTry2 = cipher; SecureBinaryData newKey; KdfRomix newKdf(mem, nIters, salt); newKdf.printKdfParams(); // First test with the wrong passphrase cout << "Attempting to decrypt with wrong passphrase" << endl; SecureBinaryData passphrase = SecureBinaryData("This is the wrong passphrase"); newKey = newKdf.DeriveKey( passphrase ); CryptoAES().DecryptCFB(cipherTry1, newKey, iv); // Now try correct passphrase cout << "Attempting to decrypt with CORRECT passphrase" << endl; passphrase = SecureBinaryData("This passphrase is tough to guess"); newKey = newKdf.DeriveKey( passphrase ); CryptoAES().DecryptCFB(cipherTry2, newKey, iv); }
BTC_PUBKEY CryptoECDSA::ParsePublicKey(SecureBinaryData const & pubKey65B) { SecureBinaryData pubXbin(pubKey65B.getSliceRef( 1,32)); SecureBinaryData pubYbin(pubKey65B.getSliceRef(33,32)); return ParsePublicKey(pubXbin, pubYbin); }
SecureBinaryData KdfRomix::DeriveKey_OneIter(SecureBinaryData const & password) { CryptoPP::SHA512 sha512; // Concatenate the salt/IV to the password SecureBinaryData saltedPassword = password + salt_; // Prepare the lookup table lookupTable_.resize(memoryReqtBytes_); lookupTable_.fill(0); uint32_t const HSZ = hashOutputBytes_; uint8_t* frontOfLUT = lookupTable_.getPtr(); uint8_t* nextRead = NULL; uint8_t* nextWrite = NULL; // First hash to seed the lookup table, input is variable length anyway sha512.CalculateDigest(frontOfLUT, saltedPassword.getPtr(), saltedPassword.getSize()); // Compute <sequenceCount_> consecutive hashes of the passphrase // Every iteration is stored in the next 64-bytes in the Lookup table for(uint32_t nByte=0; nByte<memoryReqtBytes_-HSZ; nByte+=HSZ) { // Compute hash of slot i, put result in slot i+1 nextRead = frontOfLUT + nByte; nextWrite = nextRead + hashOutputBytes_; sha512.CalculateDigest(nextWrite, nextRead, hashOutputBytes_); } // LookupTable should be complete, now start lookup sequence. // Start with the last hash from the previous step SecureBinaryData X(frontOfLUT + memoryReqtBytes_ - HSZ, HSZ); SecureBinaryData Y(HSZ); // We "integerize" a hash value by taking the last 4 bytes of // as a uint32_t, and take modulo sequenceCount uint64_t* X64ptr = (uint64_t*)(X.getPtr()); uint64_t* Y64ptr = (uint64_t*)(Y.getPtr()); uint64_t* V64ptr = NULL; uint32_t newIndex; uint32_t const nXorOps = HSZ/sizeof(uint64_t); // Pure ROMix would use sequenceCount_ for the number of lookups. // We divide by 2 to reduce computation time RELATIVE to the memory usage // This still provides suffient LUT operations, but allows us to use more // memory in the same amount of time (and this is the justification for // the scrypt algorithm -- it is basically ROMix, modified for more // flexibility in controlling compute-time vs memory-usage). uint32_t const nLookups = sequenceCount_ / 2; for(uint32_t nSeq=0; nSeq<nLookups; nSeq++) { // Interpret last 4 bytes of last result (mod seqCt) as next LUT index newIndex = *(uint32_t*)(X.getPtr()+HSZ-4) % sequenceCount_; // V represents the hash result at <newIndex> V64ptr = (uint64_t*)(frontOfLUT + HSZ*newIndex); // xor X with V, and store the result in X for(uint32_t i=0; i<nXorOps; i++) *(Y64ptr+i) = *(X64ptr+i) ^ *(V64ptr+i); // Hash the xor'd data to get the next index for lookup sha512.CalculateDigest(X.getPtr(), Y.getPtr(), HSZ); } // Truncate the final result to get the final key lookupTable_.destroy(); return X.getSliceCopy(0,kdfOutputBytes_); }
///////////////////////////////////////////////////////////////////////////// // Same as above, but only changing the AES mode of operation (CBC, not CFB) SecureBinaryData CryptoAES::EncryptCBC(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 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<BTC_AES>::Encryption aes_enc( (byte*)key.getPtr(), key.getSize(), (byte*)iv.getPtr()); aes_enc.ProcessData( (byte*)encrData.getPtr(), (byte*)data.getPtr(), data.getSize()); return encrData; }
void TestECDSA(void) { SecureBinaryData msgToSign("This message came from me!"); SecureBinaryData privData = SecureBinaryData().GenerateRandom(32); BTC_PRIVKEY privKey = CryptoECDSA().ParsePrivateKey(privData); BTC_PUBKEY pubKey = CryptoECDSA().ComputePublicKey(privKey); // Test key-match check cout << "Do the pub-priv keypair we just created match? "; cout << (CryptoECDSA().CheckPubPrivKeyMatch(privKey, pubKey) ? 1 : 0) << endl; cout << endl; SecureBinaryData signature = CryptoECDSA().SignData(msgToSign, privKey); cout << "Signature = " << signature.toHexStr() << endl; cout << endl; bool isValid = CryptoECDSA().VerifyData(msgToSign, signature, pubKey); cout << "SigValid? = " << (isValid ? 1 : 0) << endl; cout << endl; // Test signature from blockchain: SecureBinaryData msg = SecureBinaryData::CreateFromHex("0100000001bb664ff716b9dfc831bcc666c1767f362ad467fcfbaf4961de92e45547daab870100000062537a7652a269537a829178a91480677c5392220db736455533477d0bc2fba65502879b69537a829178a91402d7aa2e76d9066fb2b3c41ff8839a5c81bdca19879b69537a829178a91410039ce4fdb5d4ee56148fe3935b9bfbbe4ecc89879b6953aeffffffff0280969800000000001976a9140817482d2e97e4be877efe59f4bae108564549f188ac7015a7000000000062537a7652a269537a829178a91480677c5392220db736455533477d0bc2fba65502879b69537a829178a91402d7aa2e76d9066fb2b3c41ff8839a5c81bdca19879b69537a829178a91410039ce4fdb5d4ee56148fe3935b9bfbbe4ecc89879b6953ae0000000001000000"); SecureBinaryData px = SecureBinaryData::CreateFromHex("8c006ff0d2cfde86455086af5a25b88c2b81858aab67f6a3132c885a2cb9ec38"); SecureBinaryData py = SecureBinaryData::CreateFromHex("e700576fd46c7d72d7d22555eee3a14e2876c643cd70b1b0a77fbf46e62331ac"); SecureBinaryData pub65 = SecureBinaryData::CreateFromHex("048c006ff0d2cfde86455086af5a25b88c2b81858aab67f6a3132c885a2cb9ec38e700576fd46c7d72d7d22555eee3a14e2876c643cd70b1b0a77fbf46e62331ac"); //SecureBinaryData sig = SecureBinaryData::CreateFromHex("3046022100d73f633f114e0e0b324d87d38d34f22966a03b072803afa99c9408201f6d6dc6022100900e85be52ad2278d24e7edbb7269367f5f2d6f1bd338d017ca4600087766144"); SecureBinaryData sig = SecureBinaryData::CreateFromHex("d73f633f114e0e0b324d87d38d34f22966a03b072803afa99c9408201f6d6dc6900e85be52ad2278d24e7edbb7269367f5f2d6f1bd338d017ca4600087766144"); pubKey = CryptoECDSA().ParsePublicKey(px,py); isValid = CryptoECDSA().VerifyData(msg, sig, pubKey); cout << "SigValid? = " << (isValid ? 1 : 0) << endl; // Test speed on signature: uint32_t nTest = 50; cout << "Test signature and verification speeds" << endl; cout << "\nTiming Signing"; TIMER_START("SigningTime"); for(uint32_t i=0; i<nTest; i++) { // This timing includes key parsing CryptoECDSA().SignData(msgToSign, privData); } TIMER_STOP("SigningTime"); // Test speed of verification TIMER_START("VerifyTime"); cout << "\nTiming Verify"; for(uint32_t i=0; i<nTest; i++) { // This timing includes key parsing CryptoECDSA().VerifyData(msg, sig, pub65); } TIMER_STOP("VerifyTime"); cout << endl; cout << "Timing (Signing): " << 1/(TIMER_READ_SEC("SigningTime")/nTest) << " signatures/sec" << endl; cout << "Timing (Verify): " << 1/(TIMER_READ_SEC("VerifyTime")/nTest) << " verifies/sec" << endl; // Test deterministic key generation SecureBinaryData privDataOrig = SecureBinaryData().GenerateRandom(32); BTC_PRIVKEY privOrig = CryptoECDSA().ParsePrivateKey(privDataOrig); BTC_PUBKEY pubOrig = CryptoECDSA().ComputePublicKey(privOrig); cout << "Testing deterministic key generation" << endl; cout << " Verify again that pub/priv objects pair match : "; cout << (CryptoECDSA().CheckPubPrivKeyMatch(privOrig, pubOrig) ? 1 : 0) << endl; SecureBinaryData binPriv = CryptoECDSA().SerializePrivateKey(privOrig); SecureBinaryData binPub = CryptoECDSA().SerializePublicKey(pubOrig); cout << " Verify again that binary pub/priv pair match : "; cout << (CryptoECDSA().CheckPubPrivKeyMatch(binPriv, binPub) ? 1 : 0) << endl; cout << endl; SecureBinaryData chaincode = SecureBinaryData().GenerateRandom(32); cout << " Starting privKey:" << binPriv.toHexStr() << endl; cout << " Starting pubKey :" << binPub.toHexStr() << endl; cout << " Chaincode :" << chaincode.toHexStr() << endl; cout << endl; SecureBinaryData newBinPriv = CryptoECDSA().ComputeChainedPrivateKey(binPriv, chaincode); SecureBinaryData newBinPubA = CryptoECDSA().ComputePublicKey(newBinPriv); SecureBinaryData newBinPubB = CryptoECDSA().ComputeChainedPublicKey(binPub, chaincode); cout << " Verify new binary pub/priv pair match: "; cout << (CryptoECDSA().CheckPubPrivKeyMatch(newBinPriv, newBinPubA) ? 1 : 0) << endl; cout << " Verify new binary pub/priv pair match: "; cout << (CryptoECDSA().CheckPubPrivKeyMatch(newBinPriv, newBinPubB) ? 1 : 0) << endl; cout << " New privKey:" << newBinPriv.toHexStr() << endl; cout << " New pubKeyA:" << newBinPubA.getSliceCopy(0,30).toHexStr() << "..." << endl; cout << " New pubKeyB:" << newBinPubB.getSliceCopy(0,30).toHexStr() << "..." << endl; cout << endl; // Test arbitrary scalar/point operations BinaryData a = BinaryData::CreateFromHex("8c006ff0d2cfde86455086af5a25b88c2b81858aab67f6a3132c885a2cb9ec38"); BinaryData b = BinaryData::CreateFromHex("e700576fd46c7d72d7d22555eee3a14e2876c643cd70b1b0a77fbf46e62331ac"); BinaryData c = BinaryData::CreateFromHex("f700576fd46c7d72d7d22555eee3a14e2876c643cd70b1b0a77fbf46e62331ac"); BinaryData d = BinaryData::CreateFromHex("8130904787384d72d7d22555eee3a14e2876c643cd70b1b0a77fbf46e62331ac"); BinaryData e = CryptoECDSA().ECMultiplyScalars(a,b); BinaryData f = CryptoECDSA().ECMultiplyPoint(a, b, c); BinaryData g = CryptoECDSA().ECAddPoints(a, b, c, d); }
SecureBinaryData CryptoAES::Encrypt(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 encrData(data.getSize()); //cout << " StartPlain: " << data.toHexStr() << endl; //cout << " Key Data : " << key.toHexStr() << endl; // Caller can supply their own IV/entropy, or let it be generated here if(iv.getSize() == 0) iv = SecureBinaryData().GenerateRandom(BTC_AES::BLOCKSIZE); BTC_AES_MODE<BTC_AES>::Encryption aes_enc( (byte*)key.getPtr(), key.getSize(), (byte*)iv.getPtr()); aes_enc.ProcessData( (byte*)encrData.getPtr(), (byte*)data.getPtr(), data.getSize()); //cout << " IV Data : " << iv.toHexStr() << endl; //cout << " Ciphertext: " << encrData.toHexStr() << endl; return encrData; }