///////////////////////////////////////////////////////////////////////////// // 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; }
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()); }
///////////////////////////////////////////////////////////////////////////// // 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; }
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 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 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 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; }
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 & 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::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; }
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; }
///////////////////////////////////////////////////////////////////////////// // 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); }
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); }
///////////////////////////////////////////////////////////////////////////// // 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; }
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_); }