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); } if (nDerivationMethod == 1) { // Passphrase conversion uint256 scryptHash = scrypt_salted_multiround_hash((const void*)strKeyData.c_str(), strKeyData.size(), &chSalt[0], 8, nRounds); i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0], (unsigned char *)&scryptHash, sizeof scryptHash, nRounds, chKey, chIV); OPENSSL_cleanse(&scryptHash, sizeof scryptHash); } if (i != (int)WALLET_CRYPTO_KEY_SIZE) { OPENSSL_cleanse(chKey, sizeof(chKey)); OPENSSL_cleanse(chIV, sizeof(chIV)); return false; } fKeySet = true; return true; }
// 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; EVP_CIPHER_CTX_init(&ctx); 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_cleanup(&ctx); if (!fOk) return false; sCiphertext.resize(nCLen + nFLen); return true; }
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; // Try to keep the keydata out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap) // Note that this does nothing about suspend-to-disk (which will put all our key data on disk) // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process. // mlock(&chKey[0], sizeof chKey); // mlock(&chIV[0], sizeof chIV); 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); if (i != (int)WALLET_CRYPTO_KEY_SIZE) { // memset(&chKey, 0, sizeof chKey); // memset(&chIV, 0, sizeof chIV); OPENSSL_cleanse(chKey, sizeof(chKey)); OPENSSL_cleanse(chIV, sizeof(chIV)); return false; } fKeySet = true; return true; }
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()); di.Write(chSalt.data(), 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 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; EVP_CIPHER_CTX_init(&ctx); 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_cleanup(&ctx); if (!fOk) return false; sPlaintext.resize(nPLen + nFLen); return true; }
void permutation(int k, SecureString &s) { for(unsigned int j = 1; j < s.size(); ++j) { swap(s, k % (j + 1), j); k = k / (j + 1); } }
void CKeePassIntegrator::CKeePassRequest::init() { SecureString sIVSecure = generateRandomKey(KEEPASS_CRYPTO_BLOCK_SIZE); sIV = std::string(&sIVSecure[0], sIVSecure.size()); // Generate Nonce, Verifier and RequestType SecureString sNonceBase64Secure = EncodeBase64Secure(sIVSecure); addStrParameter("Nonce", std::string(&sNonceBase64Secure[0], sNonceBase64Secure.size())); // Plain addStrParameter("Verifier", sNonceBase64Secure); // Encoded addStrParameter("RequestType", sType); }
// passphrase must be at most 256 characters or code may crash void CMnemonic::ToSeed(SecureString mnemonic, SecureString passphrase, SecureVector& seedRet) { SecureString ssSalt = SecureString("mnemonic") + passphrase; SecureVector vchSalt(ssSalt.begin(), ssSalt.end()); seedRet.resize(64); // int PKCS5_PBKDF2_HMAC(const char *pass, int passlen, // const unsigned char *salt, int saltlen, int iter, // const EVP_MD *digest, // int keylen, unsigned char *out); PKCS5_PBKDF2_HMAC(mnemonic.c_str(), mnemonic.size(), &vchSalt[0], vchSalt.size(), 2048, EVP_sha512(), 64, &seedRet[0]); }
int __pass_cb(char *buf, int size, int rwflag, void *u) { Key::PassphraseFunctor* pf = (Key::PassphraseFunctor*)u; bool verify = (rwflag == 1); SecureString ss = (*pf)(verify); int len; len = ss.size(); if (len <= 0) return 0; // if too long, truncate if (len > size) len = size; memcpy(buf, ss.c_str(), len); return len; }
void CKeePassIntegrator::rpcSetLogin(const SecureString& strWalletPass, const SecureString& sEntryId) { // Convert key format SecureString sKey = DecodeBase64Secure(sKeyBase64); CKeePassRequest request(sKey, "set-login"); request.addStrParameter("Id", sKeePassId); request.addStrParameter("Url", sUrl); if(fDebug) LogPrintf("CKeePassIntegrator::rpcSetLogin - send Url: %s\n", sUrl.c_str()); //request.addStrParameter("SubmitUrl", sSubmitUrl); // Is used to construct the entry title request.addStrParameter("Login", SecureString("Nodes")); request.addStrParameter("Password", strWalletPass); if(sEntryId.size() != 0) { request.addStrParameter("Uuid", sEntryId); // Update existing } int nStatus; std::string sResponse; doHTTPPost(request.getJson(), nStatus, sResponse); if(fDebug) LogPrintf("CKeePassIntegrator::rpcSetLogin - send result: status: %d response: %s\n", nStatus, sResponse.c_str()); if(nStatus != 200) { std::string sErrorMessage = "Error returned: HTTP code "; sErrorMessage += boost::lexical_cast<std::string>(nStatus); sErrorMessage += " - Response: "; sErrorMessage += " response: ["; sErrorMessage += sResponse; sErrorMessage += "]"; throw std::runtime_error(sErrorMessage); } // Parse the response CKeePassResponse response(sKey, sResponse); if(!response.getSuccess()) { throw std::runtime_error("KeePassHttp returned failure status"); } }
bool OldSetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod, unsigned char* chKey, unsigned char* chIV) { 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); if (i != (int)WALLET_CRYPTO_KEY_SIZE) { memory_cleanse(chKey, WALLET_CRYPTO_KEY_SIZE); memory_cleanse(chIV, WALLET_CRYPTO_IV_SIZE); return false; } return true; }
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); if (i != (int)WALLET_CRYPTO_KEY_SIZE) { OPENSSL_cleanse(chKey, sizeof(chKey)); OPENSSL_cleanse(chIV, sizeof(chIV)); return false; } fKeySet = true; return true; }
void CKeePassIntegrator::rpcAssociate(std::string& sId, SecureString& sKeyBase64) { sKey = generateRandomKey(KEEPASS_CRYPTO_KEY_SIZE); CKeePassRequest request(sKey, "associate"); sKeyBase64 = EncodeBase64Secure(sKey); request.addStrParameter("Key", std::string(&sKeyBase64[0], sKeyBase64.size())); int nStatus; std::string sResponse; doHTTPPost(request.getJson(), nStatus, sResponse); if(fDebug) LogPrintf("CKeePassIntegrator::rpcAssociate - send result: status: %d response: %s\n", nStatus, sResponse.c_str()); if(nStatus != 200) { std::string sErrorMessage = "Error returned: HTTP code "; sErrorMessage += boost::lexical_cast<std::string>(nStatus); sErrorMessage += " - Response: "; sErrorMessage += " response: ["; sErrorMessage += sResponse; sErrorMessage += "]"; throw std::runtime_error(sErrorMessage); } // Parse the response CKeePassResponse response(sKey, sResponse); if(!response.getSuccess()) { throw std::runtime_error("KeePassHttp returned failure status"); } // If we got here, we were successful. Return the information sId = response.getStr("Id"); }
bool CMnemonic::Check(SecureString mnemonic) { if (mnemonic.empty()) { return false; } uint32_t nWordCount{}; for (size_t i = 0; i < mnemonic.size(); ++i) { if (mnemonic[i] == ' ') { nWordCount++; } } nWordCount++; // check number of words if (nWordCount != 12 && nWordCount != 18 && nWordCount != 24) { return false; } SecureString ssCurrentWord; SecureVector bits(32 + 1); uint32_t nWordIndex, ki, nBitsCount{}; for (size_t i = 0; i < mnemonic.size(); ++i) { ssCurrentWord = ""; while (i + ssCurrentWord.size() < mnemonic.size() && mnemonic[i + ssCurrentWord.size()] != ' ') { if (ssCurrentWord.size() >= 9) { return false; } ssCurrentWord += mnemonic[i + ssCurrentWord.size()]; } i += ssCurrentWord.size(); nWordIndex = 0; for (;;) { if (!wordlist[nWordIndex]) { // word not found return false; } if (ssCurrentWord == wordlist[nWordIndex]) { // word found on index nWordIndex for (ki = 0; ki < 11; ki++) { if (nWordIndex & (1 << (10 - ki))) { bits[nBitsCount / 8] |= 1 << (7 - (nBitsCount % 8)); } nBitsCount++; } break; } nWordIndex++; } } if (nBitsCount != nWordCount * 11) { return false; } bits[32] = bits[nWordCount * 4 / 3]; CSHA256().Write(&bits[0], nWordCount * 4 / 3).Finalize(&bits[0]); bool fResult = 0; if (nWordCount == 12) { fResult = (bits[0] & 0xF0) == (bits[32] & 0xF0); // compare first 4 bits } else if (nWordCount == 18) { fResult = (bits[0] & 0xFC) == (bits[32] & 0xFC); // compare first 6 bits } else if (nWordCount == 24) { fResult = bits[0] == bits[32]; // compare 8 bits } return fResult; }
int main(int argc, char* argv[]) { if (argc != 7){ printf("Hashchecker needs to know the cryptographic details of your wallet!\nUsage: hashchecker pw_to_permute iterations salt crypted_key public_key crypted_secret\n"); return 1; } CCrypter crypter; CKeyingMaterial vMasterKey; // Try any password as input SecureString attempt = argv[1]; const unsigned int nDeriveIterations = atoi(argv[2]);//29731; const vector<unsigned char> chSalt = Convert(argv[3]);//"b29a2e128e8e0a2f");//argv[1]; const vector<unsigned char> vchCryptedKey = Convert(argv[4]);//"982a07407ccb8d70514e7b7ccae4b53d68318ec41fd2bf99bf9dbcafd2f150a92c6eb8f9ea743b782fc5b85403421c1d");//argv[2]; const vector<unsigned char> vchPubKey = Convert(argv[5]);//"03fefd771544971f3ab95b041bbce02cc799a335d0d12c3bcd46c7c61a4e3ba897"); const vector<unsigned char> vchCryptedSecret = Convert(argv[6]);//"17169083a74b07ff3497027af7423b9aec1593c90f15a57f52c368593947c85e37b03430840ad48ef409e97ba5a4cdeb"); double count = Factorial(attempt.size()); bool found = false; for (int i = 0; i <= count; i++) { if (i > 0) {//test the word as typed in on first iteration permutation(i-1, attempt); } const SecureString strWalletPassphrase = attempt; cout << i << "-" << strWalletPassphrase <<"\n"; if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, chSalt, nDeriveIterations, 0)) { cout << i << " " << strWalletPassphrase <<"\n"; continue; } if (!crypter.Decrypt(vchCryptedKey, vMasterKey)) { cout << i << " " << strWalletPassphrase <<"\n"; continue; } CSecret vchSecret; if(!DecryptSecret(vMasterKey, vchCryptedSecret, Hash(vchPubKey.begin(), vchPubKey.end()), vchSecret)) { cout << "** didn't decrypt **" <<"\n"; continue; } if (vchSecret.size() != 32) { cout << "** wrong size secret **" <<"\n"; continue; } CKey key; key.SetPubKey(vchPubKey); key.SetSecret(vchSecret); if (key.GetPubKey() == vchPubKey) { cout<<"Found one: "<<strWalletPassphrase<<"\n"; found = true; break; } // else // cout << "** didn't get the pubkey back **\n"; } if (found) cout << "Found it! Congratulations\n"; return 0; }