I read in this question about Importing a public key from native components.
I tried doing the same for private key based on the BLOB documentation, but I'm getting a NTE_BAD_DATA error.
Is my idea feasible? If so, Can you help?
My Try:
void old_RSA_decrypt(PBYTE blob, DWORD blobSize)
{
HCRYPTPROV hCryptProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwDecryptedLen = 0;
DWORD length;
std::ifstream f;
f.open("c:\\Programming\\encrypted.txt", std::ios::binary);
if (!f.is_open())
{
std::cout << "Error on open file: " << GetLastError() << std::endl;
return;
}
f.seekg(0, f.end);
length = f.tellg();
f.seekg(0, f.beg);
char * buffer = new char[length];
f.read(buffer, length);
if (!f)
std::cout << "error: only " << f.gcount() << " could be read" << std::endl;
f.close();
PBYTE bBuffer = (PBYTE)buffer;
//now to get the decryption thing going
if (!CryptAcquireContext(
&hCryptProv,
NULL,
MS_STRONG_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
std::cout << "Error on CryptAcquireContext " << GetLastError() << std::endl;
return;
}
if (!CryptImportKey(
hCryptProv,
blob,
blobSize,
NULL,
0,
&hKey))
{
std::cout << "Error on CryptImportKey " << GetLastError() << std::endl;
return;
}
if (!CryptDecrypt(hKey, NULL, TRUE, 0, NULL, &dwDecryptedLen))
{
std::cout << "Error on CryptDecrypt (First Pass) " << GetLastError() << std::endl;
return;
}
PBYTE decBuffer = new BYTE[dwDecryptedLen];
for(int i = 0; i < length ; ++i)
decBuffer[i] = bBuffer[i];
if (!CryptDecrypt(hKey, NULL, TRUE, 0, decBuffer, &length))
{
std::cout << "Error on CryptDecrypt (Second Pass) " << GetLastError() << std::endl;
return;
}
std::cout << "Yurika2!" << std::endl;
std::ofstream of;
of.open("c:\\Programming\\decrypted.txt", std::ios::binary);
if (!of.is_open())
{
std::cout << "Error on open write file: " << GetLastError() << std::endl;
return;
}
string sDecMsg = string(reinterpret_cast<char*>(decBuffer), length);
of << sDecMsg << std::endl;
of.close();
cleanup:
delete[] buffer;
delete[] decBuffer;
}
void do_decrypt()
{
string modStr = "yVUndgQFuB5Z5FgC0/WgWCg6Y8VuB582avGjQDdeoJDa1+RBKCyXo700sAMSGjM/bVakOlFqvCsVFNBysx1CH731CDb2DR1a0bsmYmDQ9d0ZHX+AOohVDIx9mc7bkDQZoEFpe9NqFsu95Y9yktpl1JKPmKyLOFgufGJYYvQyoOM=";
string expStr = "AQAB";
string PStr = "/JydNn89lSWjgWOG1XRJm1qTWDekzzoLfTQU+GK+h8DGQ6gkUbgqGosLGo+eAxbO/ETZV3ibbBuIdvL4UxC5Qw==";
string QStr = "zAh23Gc8Oqz/Uh2wh+yt8DqUesVLwMn2koc9CbyF9/Z5Qe8OIR4yygJtuYruRC1x/KYj85l6DGzstUZOtYmv4Q==";
string DPStr ="+1INj1SUPjjOLUKJuQAS4z7/7PqfO5RyLcSNQHltOb5vAozcZXkmWnYPPAO6nzQoBg+xdDcH2kyiPkWJDYtL5Q==";
string DQStr = "cbYh8HJEufrijTRox0hcJG+xgr7kmjy1BDMFDKEaFPkz2VBPEpwO+FDkMC1C35JoXcOGc+RMhhJK1jip8zkaYQ==";
string InverseQStr = "3PAXzlAXgvLVrbOEygjA2zhJEYALBEi6VTKqfDKlnv8/D9QUkC39bEDIRLG0wMFFxN8NlLx5zTiiVswxnMy8Mw==";
string DStr = "KKBSyKkyID+bowyxcWUAuJlRgv19YPNbL0RYTWZ+5UalqmfoT/uDk+pjndrYxcmulFkl5ZC1SYgmBl+zrXoLc/Ei86BtNiuwfcqHlUDp0fdP+fyYN45wh/251HQ3UM1zBpMP8XeYB6zjpCU/s3/wCBE6WpJWN9fKcG0W5PLq8eE=";
//FROM STRINGS TO BYTE VECTORS!
vector<BYTE> modBinMSB = base64_decode(modStr);
vector<BYTE> expBinMSB = base64_decode(expStr);
vector<BYTE> PBinMSB = base64_decode(PStr);
vector<BYTE> QBinMSB = base64_decode(QStr);
vector<BYTE> DPBinMSB = base64_decode(DPStr);
vector<BYTE> DQBinMSB = base64_decode(DQStr);
vector<BYTE> InverseQBinMSB = base64_decode(InverseQStr);
vector<BYTE> DBinMSB = base64_decode(DStr);
//TURN MSB TO LSB
DWORD offset = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY); // to keep track of things
const DWORD modulusLengthInBytes = 128;
DWORD keyBlobLength = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + (modulusLengthInBytes * 4) + (modulusLengthInBytes / 2);
BYTE* keyBlob = (PBYTE)malloc(keyBlobLength);
BLOBHEADER* blobheader = (BLOBHEADER*)keyBlob;
blobheader->bType = PRIVATEKEYBLOB;
blobheader->bVersion = CUR_BLOB_VERSION;
blobheader->reserved = 0;
blobheader->aiKeyAlg = CALG_RSA_KEYX;
RSAPUBKEY* rsapubkey = (RSAPUBKEY*)(keyBlob + sizeof(BLOBHEADER));
rsapubkey->magic = 0x31415352;
rsapubkey->bitlen = modulusLengthInBytes * 8 *4 + modulusLengthInBytes*4;
rsapubkey->pubexp = MSBByteVectorToDword(expBinMSB);
BYTE* modulus = keyBlob + offset;
copyReversed(modBinMSB, modulus);
offset += modulusLengthInBytes;
BYTE* prime1 = keyBlob + offset ;
copyReversed(PBinMSB, prime1);
offset += modulusLengthInBytes / 2;
BYTE* prime2 = keyBlob + offset;
copyReversed(QBinMSB, prime2);
offset += (modulusLengthInBytes / 2);
BYTE* exponent1 = keyBlob + offset;
copyReversed(DPBinMSB, exponent1);
offset += (modulusLengthInBytes / 2);
BYTE* exponent2 = keyBlob + offset;
copyReversed(DQBinMSB, exponent2);
offset += (modulusLengthInBytes / 2);
BYTE* coefficient = keyBlob + offset;
copyReversed(InverseQBinMSB, coefficient);
offset += modulusLengthInBytes / 2;
BYTE* privateExponent = keyBlob + offset;
copyReversed(DBinMSB, privateExponent);
old_RSA_decrypt(keyBlob, keyBlobLength);
}
It's certainly possible to do so. You mentioned both Windows crypto stacks, and there are some differences:
Encoding:
CAPI: all of the variable-length fields are little-endian.
CNG: all of the variable-length fields are big-endian.
Rigidity:
CAPI: the modulus and D must have the same length. Also, P, Q, DP, DQ, InverseQ all have the same length (which must be half (round up) the length of the modulus).
CNG: private keys ask only for n, e, p, and q... and you specify the length of each field separately.
I see one obvious error in your code:
rsapubkey->bitlen = modulusLengthInBytes * 8 *4 + modulusLengthInBytes*4;
Per the documentation:
bitlen
Number of bits in the modulus. In practice, this must always be a multiple of eight.
So just
rsapubkey->bitlen = modulusLengthInBytes * 8;
Your setting of the dwMagic value seems to be incorrect, too.
rsapubkey->magic = 0x31415352;
0x31415352 is RSA_PUB_MAGIC, so you are calling yourself a public key. You want RSA_PRIV_MAGIC (and to use the constant).
rsapubkey->magic = RSA_PRIV_MAGIC;
Compare to http://source.dot.net/#System.Security.Cryptography.Csp/System/Security/Cryptography/CapiHelper.Shared.cs,b7bc764e6deb34f5, which is a working blob writer in C#.
Related
This is a repost, as my last one was too much, so I'm going to make it simple.
On the second call to BCryptDecrypt(), it fails returning 0xc000000d. I am aware the code is bad and has many points of failure, but in my test none of those points failed.
I compared this to the C# version of the code that works, debuggers for both show they have all the same values for buffers / returned values just fails at the second call of BCryptDecrypt().
My goal is to decrypt an Encrypted / Ciphered Text Block using a Key from that Block. Uses AES-GCM I want to use ONLY Bcrypt. It is possible as I have a C# version, I don't want to import extra libs, like OpenSSL.
If you want to try, here is the Base64 Encoded Key, you need to decode from base64, skip 5 bytes for "DPAPI", then call to CryptUnprotectData().
The value to be Decrypted / Decoded is (Base64 + AES-GCM):
RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAADeBU2aOO6lQ73DYvrL8hhAAAAAAAIAAAAAABBmAAAAAQAAIAAAALXsSijRcSAV3S8PSvKZXUddV2eE+nv5xtn8JaePfpjrAAAAAA6AAAAAAgAAIAAAAJ96r2xQdEkkXuEaGEcVG24QJSKXG9s14w/yS8gdO/CxMAAAABs64+DTxkrQnkH1e3d0w/tOPIRrB1OBPrw4uxX4Q0AfYv6pyMZKXchhn1qol8bMvUAAAADisCS/m6UBBzMxVLBgpCvQsGIa6hYT/J8NGZuFOqydlstlxLBL2K8tCX550IoHtwuEA8EGJTxMsW6wh/0H3VRn
(this is my password/key for my CTF / Project, I allow it to be decrypted / decoded)
Code to decrypt:
std::vector<uint8_t> AesGcm::DecryptWithKey(
const std::vector<uint8_t>& ciphered_data,
const std::vector<uint8_t>& key)
{
//Create the IV
std::vector<uint8_t> iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // IV 12 bytes
std::copy(ciphered_data.begin() + 3, ciphered_data.begin() + 15, iv.begin());
//Create Buffer witth Ciphered Data only
std::vector<uint8_t> buffer(ciphered_data.size() - 15);
std::copy(ciphered_data.begin() + 15, ciphered_data.end(), buffer.begin());
//Create TAG
std::vector<uint8_t> tag(16);
std::vector<uint8_t> data(buffer.size() - tag.size());
//Last 16 bytes for tag
std::copy(buffer.end() - 16, buffer.end(), tag.begin());
//encrypted password
std::copy(buffer.begin(), buffer.end() - tag.size(), data.begin());
std::vector<uint8_t> aad(0);//Just an Empty AAD param
return Decrypt(key, iv, aad, data, tag);
}
std::vector<uint8_t> AesGcm::Decrypt(
const std::vector<uint8_t>& key,
std::vector<uint8_t>& iv,
std::vector<uint8_t>& aad,
std::vector<uint8_t>& cipher_text,
std::vector<uint8_t>& authTag)
{
std::vector<uint8_t> p_text(0);
BCRYPT_ALG_HANDLE hAlg;
if (!OpenAlgorithmProvider(hAlg, MS_PRIMITIVE_PROVIDER, BCRYPT_CHAIN_MODE_GCM))
return p_text;
auto key_sz_bts = AesGcm::GetProperty(hAlg, BCRYPT_OBJECT_LENGTH);
if (key_sz_bts.empty() || key_sz_bts.size() != 4) {
std::cout << "Inavlid PropertySize::" << key_sz_bts.size() << std::endl;
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0x0);
return p_text;
}
auto blob_vec = AesGcm::CreateBlob(key);
auto key_sz = *reinterpret_cast<int*>(key_sz_bts.data());
if (blob_vec.empty()) {
std::cout << "Invalid Blob Size::" << blob_vec.size() << std::endl;
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0x0);
return p_text;
}
BCRYPT_KEY_HANDLE hBck;
std::vector<uint8_t> kdata_buffer(key_sz);
std::vector<uint8_t> mac(authTag.size());
DWORD dwStatus =
BCryptImportKey(hAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hBck, kdata_buffer.data(), kdata_buffer.size(), blob_vec.data(), blob_vec.size(), 0x0);
if (FAILED(dwStatus)) {
std::cout << "BCryptImportKey(FAILED)::[0x" << std::hex << dwStatus << "]" << std::endl;
if (hBck) BCryptDestroyKey(hBck);
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0x0);
return p_text;
}
//Create the AUTH Struct
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
authInfo.dwInfoVersion = 0x00000001;
authInfo.pbNonce = iv.data();
authInfo.cbNonce = iv.size();
authInfo.pbAuthData = aad.data(); //aad.data();
authInfo.cbAuthData = aad.size();//aad.size();
authInfo.pbTag = authTag.data();
authInfo.cbTag = authTag.size();
authInfo.pbMacContext = mac.data();
authInfo.cbMacContext = mac.size();//authTag.size();
authInfo.cbAAD = 0;
authInfo.cbData = 0;
//authInfo.cbSize = sizeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO);
authInfo.cbSize = sizeof(authInfo);
auto authtg_tg_bts = AesGcm::GetProperty(hAlg, BCRYPT_AUTH_TAG_LENGTH);
//Get Auth Tag Size
auto atag_size = *reinterpret_cast<int*>(&authtg_tg_bts[4]);
std::vector<uint8_t> iv_data(atag_size);
DWORD ptext_size = 0;
//Decrypt Data now
dwStatus = BCryptDecrypt(
hBck, cipher_text.data(), cipher_text.size(), &authInfo, iv_data.data(), iv_data.size(), nullptr, 0, &ptext_size, 0x0);
if (FAILED(dwStatus) || ptext_size <= 0) {
std::cout << "BCryptDecrypt2(FAILED)::[0x" << std::hex << dwStatus << "]::SIZE::" << ptext_size << std::endl;
goto end;
}
p_text.resize(ptext_size);
//Fails at this Second Call
dwStatus = BCryptDecrypt(
hBck,
cipher_text.data(),
cipher_text.size(),
&authInfo,
iv_data.data(),
iv_data.size(), p_text.data(), p_text.size(), &ptext_size, 0x0);
if (FAILED(dwStatus)) {
std::cout << "BCryptDecrypt2(FAILED)::[0x" << std::hex << dwStatus << "]" << std::endl;
goto end;
}
end:
if (hBck) BCryptDestroyKey(hBck);
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0x0);
return p_text;
}
"Unprotect" decoded Base64 Key:
//encryptedData is the Decoded Base64 Data skipped 5 Bytes
DATA_BLOB DataIn;
DataIn.cbData = encryptedData.size();
DataIn.pbData = (BYTE*)encryptedData.data();//const_cast<uint8_t*>(encryptedData.data());
auto num = 1u;
if (dwFlags == 1) {
num |= 4u;
}
DATA_BLOB DataOut;
if (!CryptUnprotectData(&DataIn, NULL, NULL, NULL, NULL, num, &DataOut))
throw std::exception("CryptUnprotectData failed.");
std::vector<uint8_t> result(DataOut.pbData, DataOut.pbData + DataOut.cbData);
LocalFree(DataOut.pbData);
Rest of the code (incase I am doing something wrong somewhere else)
std::vector<uint8_t> AesGcm::CreateBlob(const std::vector<uint8_t>& key) {
//Create KeyBlob
std::vector<uint8_t> key_bloc_vec(4 * 3 + key.size());
//Write The MAGIC
DWORD kbm = BCRYPT_KEY_DATA_BLOB_MAGIC;
//uint8_t* bytes = reinterpret_cast<uint8_t*>(&kbm);
std::memcpy(key_bloc_vec.data(), reinterpret_cast<uint8_t*>(&kbm), sizeof(int));
//Write 0x1
DWORD rnd = 0x1;
//uint8_t* bytes = reinterpret_cast<uint8_t*>(&rnd);
std::memcpy(key_bloc_vec.data() + 4, reinterpret_cast<uint8_t*>(&rnd), sizeof(int));
//Write Size
DWORD ksz = key.size();
std::memcpy(key_bloc_vec.data() + 8, reinterpret_cast<uint8_t*>(&ksz), sizeof(int));
//Write KEY
std::memcpy(key_bloc_vec.data() + 12, key.data(), key.size());
return key_bloc_vec;
}
bool AesGcm::OpenAlgorithmProvider(BCRYPT_ALG_HANDLE& hBC, const TCHAR* provider, const TCHAR* chaining_mode) {
DWORD dwStatus = BCryptOpenAlgorithmProvider(&hBC, BCRYPT_AES_ALGORITHM, provider, 0x0);
if (FAILED(dwStatus)) {
std::cout << "BCryptOpenAlgorithmProvider(FAILED)::[0x" << std::hex << dwStatus << "]" << std::endl;
return false;
}
//std::wstring cc_mode(BCRYPT_CHAIN_MODE_GCM);
//std::vector<uint8_t> bmode_bys(cc_mode.size() * sizeof(wchar_t) + 2);
//std::memcpy(bmode_bys.data(), cc_mode.data(), bmode_bys.size());
//dwStatus = BCryptSetProperty(hBC, BCRYPT_CHAINING_MODE, bmode_bys.data(), bmode_bys.size() - 2, 0x0);
//if (FAILED(dwStatus)) {
// std::cout << "BCryptSetProperty(FAILED)::[0x" << std::hex << dwStatus << "]" << std::endl;
// return false;
//}
dwStatus = BCryptSetProperty(hBC, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0x0);
if (FAILED(dwStatus)) {
std::cout << "BCryptSetProperty(FAILED)::[0x" << std::hex << dwStatus << "]" << std::endl;
return false;
}
return true;
}
std::vector<uint8_t> AesGcm::GetProperty(BCRYPT_ALG_HANDLE& hCH, const TCHAR* prop_name) {
DWORD sz_1 = 0;//BCRYPT_AUTH_TAG_LENGTH / BCRYPT_OBJECT_LENGTH
DWORD dwStatus = BCryptGetProperty(hCH, prop_name, NULL, 0, &sz_1, 0x0);
if (FAILED(dwStatus) || sz_1 <= 0)
throw std::exception("BCryptGetProperty failed");
std::vector<uint8_t> prop_bys(sz_1);
dwStatus = BCryptGetProperty(hCH, prop_name, (PBYTE)prop_bys.data(), prop_bys.size(), &sz_1, 0x0);
if (FAILED(dwStatus))
throw std::exception("BCryptGetProperty failed2");
return prop_bys;
}
Changing from std::vector to HeapAlloc
Changing around the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO a bit
Changing the params for BCryptSetProperty
Using well written version in C++ from GitHub Pages
Trying 4+ fifferent ways of the code, including using Chat Bot GPT
So I will leave this up for the future of people, I started to look into padding given this post BCryptEncrypt returns STATUS_INVALID_PARAMETER on AES-GCM
I created a vector with X amount of Bytes , Copied the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO ovver using memcpy based off the static X size
I see no padding least that will cut off / malformed the data, but I do see a value that keeps changing each debug at the end of the struct that seems to keep changing each run. I cast it back over to the trust seeing if its malformed or anything it seems fine. I do notice a field in the debugger though that I did not touch called "dwFlags" , it has a random value it sets to each run.
Set:
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.dwFlags = 0;
Problem Fixed :D
It was also mentioned in comments about "dwFlags" but I am assuming he meant the function signature not the structure if not then I assumed wrong. Either way problem fixed !
My problem is that, I can't retrieve a Registry value correctly in a hex format, converting it to a string so it appears this way in my ListBox (image 1), I know how to do it correctly in C#, but I'm new to C++.
String ks;
DWORD reg2()
{
char value[255];
DWORD BufferSize = BUFFER;
RegGetValue(HKEY_CURRENT_USER, namebuf, "415846243", RRF_RT_ANY, NULL, (PVOID)&value, &BufferSize);
std::wstringstream box_messages;
box_messages << value;
ks = box_messages.str().c_str();
}
void __fastcall TAAP_Test::Button1Click(TObject *Sender)
{
ListBox1->Items->Add(ks);
}
This image shows the result on my program:
This image shows what registry value I'm trying to retrieve:
The Registry value you are reading is in a raw binary format (REG_BINARY). What you see in the Registry Editor is not the raw data itself, but a human-readable hex representation of the data. RegGetValueA() will not return that hex representation to you, it will return the raw binary data instead.
You are reading the raw data fine (minus the lack of error checking), but you are trying to write it as-is to your std::wstringstream, which is why you get the weird result you see in your ListBox. You need to instead loop through the individual bytes of the data, encoding each byte to a hex representation that is then put in to your std::wstringstream, eg:
#include <iostream>
#include <iomanip>
void reg2()
{
char value[255];
DWORD BufferSize = sizeof(value);
if (RegGetValueA(HKEY_CURRENT_USER, namebuf, "415846243", RRF_RT_ANY, NULL, value, &BufferSize) == ERROR_SUCCESS)
{
std::wstringstream box_messages;
for(DWORD i = 0; i < BufferSize; ++i) {
box_messages << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(value[i]) << L" ";
}
ks = box_messages.str().c_str();
}
else
ks = _D("error");
}
Alternatively, the RTL has an IntToHex() function available:
#include <System.SysUtils.hpp>
void reg2()
{
char value[255];
DWORD BufferSize = sizeof(value);
if (RegGetValueA(HKEY_CURRENT_USER, namebuf, "415846243", RRF_RT_ANY, NULL, value, &BufferSize) == ERROR_SUCCESS)
{
std::wstringstream box_messages;
for(DWORD i = 0; i < BufferSize; ++i) {
box_messages << IntToHex(static_cast<int>(value[i]), 2).c_str() << _D(" ");
}
ks = box_messages.str().c_str();
/* or simpler:
ks = _D("");
for(DWORD i = 0; i < BufferSize; ++i) {
ks += (IntToHex(static_cast<int>(value[i]), 2) + _D(" "));
}
*/
}
else
ks = _D("error");
}
Alternatively, UnicodeString has a cat_sprintf() method available:
void reg2()
{
char value[255];
DWORD BufferSize = sizeof(value);
if (RegGetValueA(HKEY_CURRENT_USER, namebuf, "415846243", RRF_RT_ANY, NULL, value, &BufferSize) == ERROR_SUCCESS)
{
ks = _D("");
for(DWORD i = 0; i < BufferSize; ++i) {
ks.cat_sprintf(_D("%02X "), static_cast<int>(value[i]));
}
}
else
ks = _D("error");
}
I'm writing a small console program to test out DPAPI based on the constraints I have to work with for a project (all wstring, need to output encrypted data in base64) and ran into an issue where if I call LocalFree on the pbData of the CryptProtectData output blob, the decrypt would fail.
My Encrypt() and Decrypt() helpers:
std::wstring Encrypt(std::wstring input)
{
char *inputBuf = new char[input.size() + 1];
size_t temp = 0;
wcstombs_s(&temp, inputBuf, input.size() + 1, input.c_str(), input.size());
CRYPT_INTEGER_BLOB inputBlob;
inputBlob.cbData = strlen(inputBuf) + 1;
inputBlob.pbData = (BYTE*)inputBuf;
CRYPT_INTEGER_BLOB outputBlob;
CryptProtectData(&inputBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &outputBlob);
BYTE *outputBlobPtr = (BYTE *)(&outputBlob);
DWORD encodedLen = 0;
CryptBinaryToStringW(outputBlobPtr, sizeof(CRYPT_INTEGER_BLOB), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &encodedLen);
wchar_t *outputBuf = new wchar_t[encodedLen];
CryptBinaryToStringW(outputBlobPtr, sizeof(CRYPT_INTEGER_BLOB), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, outputBuf, &encodedLen);
std::wstring output = std::wstring(outputBuf, encodedLen);
LocalFree(outputBlob.pbData); // <-- This is the offending line
delete[] inputBuf;
delete[] outputBuf;
return output;
}
std::wstring Decrypt(std::wstring input)
{
wchar_t *inputBuf = new wchar_t[input.size() + 1];
wcscpy_s(inputBuf, input.size() + 1, input.c_str());
DWORD decodedLen = 0;
CryptStringToBinaryW(inputBuf, wcslen(inputBuf), CRYPT_STRING_BASE64, NULL, &decodedLen, NULL, NULL);
BYTE *encryptedBlobPtr = new BYTE[decodedLen];
if (CryptStringToBinaryW(inputBuf, wcslen(inputBuf), CRYPT_STRING_BASE64, encryptedBlobPtr, &decodedLen, NULL, NULL) == 0)
{
std::cout << "String to blob conversion error: " << GetLastError() << std::endl;
return std::wstring();
}
CRYPT_INTEGER_BLOB encryptedBlob = *((CRYPT_INTEGER_BLOB *)encryptedBlobPtr);
CRYPT_INTEGER_BLOB decryptedBlob;
if (CryptUnprotectData(&encryptedBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &decryptedBlob) == 0)
{
std::cout << "Decryption error: " << GetLastError() << std::endl;
return std::wstring();
}
wchar_t *outputBuf = new wchar_t[decryptedBlob.cbData + 1];
size_t temp = 0;
mbstowcs_s(&temp, outputBuf, decryptedBlob.cbData + 1, (char *)decryptedBlob.pbData, decryptedBlob.cbData);
std::wstring output = std::wstring(outputBuf, decryptedBlob.cbData + 1);
LocalFree(decryptedBlob.pbData);
delete[] inputBuf;
delete[] encryptedBlobPtr;
delete[] outputBuf;
return output;
}
If the line is commented out, then calling Encrypt(L"Some string"), storing the result in a wstring, and then calling Decrypt(resultOfEncrypt) would get me "Some string" back. If the line is there there, then the program fails at CryptUnprotectData() with GetLastError() == 13 (invalid data).
Am I misunderstanding how DPAPI or LocalFree() works? Why must the outputBlob be in memory if I'm already preserving it by converting it to a string?
I am trying to add a public key into diffie Hellman implementation of Bcrypt.
example suggested to create and export a public key. In my case I already got the public key from the client. I am trying to use that public key to generate shared session key. I get STATUS_INVALID_PARAMETER when i try to use BCryptImportKeyPair.
I created PBCRYPT_DH_KEY_BLOB and added the values manually.
Is there any other way to do this? an example of importing public key into BCrypt from string would be appreciated.
Thanks in Advance. Please check the code below.
KeyLength = 1024;//bits
const int sz = 1024;
char * shared_public_key = "48DE15D8E46B857B387E315D518B7D9EDDA1FCA6661CFC9C066B3A352E8644A30BFBB7F84C93818F67B7037235D11A5B0F31E15BCB344C2A7C13E339ED98939CF3F092E64C0DEA28A150404432E3B7077DE3E4D40E421EA88FFAF4D7AD53851912389674B24C80E5FD05D1C60344535159E7A4CAF9F9DCAF712C2A41EF524632";
char * prime = "CBBD1F895B751A803674B4CF6178DAFF87E3AADD017B96CA0D536215091AC55C0D777ADB6206581E7681C5059BEFF7990E4B3DD074266B608800CF7110BE99B861D189A82A26D569CAA2F314E8E79838AEE8DA96380BDFA55B34CA43866B24C0A822947E669C9AA037A8FA765F637663AB4103A9251C70000A689796CE42A2A3";
BYTE OakleyGroup1P[sz /8];
int fld_sz = sz / 8;
string s_prime(prime, fld_sz*2);
transform(s_prime.begin(), s_prime.end(), s_prime.begin(), ::tolower);
string res = "";
for (int i = 0; i < s_prime.size(); i += 2) {
res += s_prime.substr(i, 2);
res += " ";
}
std::istringstream hex_chars_stream(res);
unsigned int c;
int i = 0;
while (hex_chars_stream >> std::hex >> c)
{
OakleyGroup1P[i++] = c;
}
cout << "size of OakleyGroup1P :" << i << endl;
/*for (unsigned char x : OakleyGroup1P) {
cout << ((x >> 4) & 0x0F) << " " << (x & 0x0F) << endl;
}*/
PBYTE PubBlobA2 = new BYTE[sz/8];
string s_shared(shared_public_key, fld_sz * 2);
transform(s_shared.begin(), s_shared.end(), s_shared.begin(), ::tolower);
string temp = "";
for (int i = 0; i < s_shared.size(); i += 2) {
temp += s_shared.substr(i, 2);
temp += " ";
}
std::istringstream hex_chars_stream2(temp);
i = 0;
while (hex_chars_stream2 >> std::hex >> c)
{
PubBlobA2[i++] = c;
}
cout << "size of PubBlobA2 :" << i << endl;
for (int j = 0; j < i; j++) {
cout << ((*(PubBlobA2 + j) >> 4) & 0x0F) << " " << (*(PubBlobA2 + j) & 0x0F) << endl;
}
//
// Construct the DH parameter blob. this is the only supported
// method for DH in CNG.
//
// Calculate size of param blob and allocate memory
DhParamBlobLength = sizeof(BCRYPT_DH_PARAMETER_HEADER) +
sizeof(OakleyGroup1G) +
sizeof(OakleyGroup1P);
DhParamBlob = (PBYTE)HeapAlloc (
GetProcessHeap (),
0,
DhParamBlobLength);
if( NULL == DhParamBlob )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
DhParamHdrPointer = (BCRYPT_DH_PARAMETER_HEADER *)DhParamBlob;
//
// Set header properties on param blob
//
DhParamHdrPointer->cbLength = DhParamBlobLength;
DhParamHdrPointer->cbKeyLength = KeyLength/8;//bytes
DhParamHdrPointer->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
//
// Set prime
//
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER),
OakleyGroup1P,
sizeof(OakleyGroup1P));
//
// Set generator
//
memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER) + sizeof(OakleyGroup1P),
OakleyGroup1G,
sizeof(OakleyGroup1G));
//
// Open alg provider handle
//
Status = BCryptOpenAlgorithmProvider(
&ExchAlgHandleB,
BCRYPT_DH_ALGORITHM,
NULL,
0);
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// B generates a private key
//
Status = BCryptGenerateKeyPair(
ExchAlgHandleB, // Algorithm handle
&PrivKeyHandleB, // Key handle - will be created
KeyLength, // Length of the key - in bits
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
Status = BCryptSetProperty(
PrivKeyHandleB,
BCRYPT_DH_PARAMETERS,
DhParamBlob,
DhParamBlobLength,
0);
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
Status = BCryptFinalizeKeyPair(
PrivKeyHandleB, // Key handle
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// B exports DH public key
//
Status = BCryptExportKey(
PrivKeyHandleB, // Handle of the key to export
NULL, // Handle of the key used to wrap the exported key
BCRYPT_DH_PUBLIC_BLOB, // Blob type (null terminated unicode string)
NULL, // Buffer that recieves the key blob
0, // Buffer length (in bytes)
&PubBlobLengthB, // Number of bytes copied to the buffer
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
PubBlobB = (PBYTE)HeapAlloc (
GetProcessHeap (),
0,
PubBlobLengthB);
if( NULL == PubBlobB )
{
Status = STATUS_NO_MEMORY;
ReportError(Status);
goto cleanup;
}
Status = BCryptExportKey(
PrivKeyHandleB, // Handle of the key to export
NULL, // Handle of the key used to wrap the exported key
BCRYPT_DH_PUBLIC_BLOB, // Blob type (null terminated unicode string)
PubBlobB, // Buffer that recieves the key blob
PubBlobLengthB, // Buffer length (in bytes)
&PubBlobLengthB, // Number of bytes copied to the buffer
0); // Flags
if( !NT_SUCCESS(Status) )
{
ReportError(Status);
goto cleanup;
}
//
// Build KDF parameter list
//
//specify hash algorithm, SHA1 if null
//specify secret to append
BufferArray[0].BufferType = KDF_TLS_PRF_SEED;
BufferArray[0].cbBuffer = sizeof(rgbrgbTlsSeed);
BufferArray[0].pvBuffer = (PVOID)rgbrgbTlsSeed;
//specify secret to prepend
BufferArray[1].BufferType = KDF_TLS_PRF_LABEL;
BufferArray[1].cbBuffer = (DWORD)((wcslen(Label) + 1) * sizeof(WCHAR));
BufferArray[1].pvBuffer = (PVOID)Label;
ParameterList.cBuffers = 2;
ParameterList.pBuffers = BufferArray;
ParameterList.ulVersion = BCRYPTBUFFER_VERSION;
//
// B imports A's public key
//
// dh public key blob structure
PBCRYPT_DH_KEY_BLOB p_dh_pub_key_blob = (PBCRYPT_DH_KEY_BLOB)HeapAlloc(
GetProcessHeap(),
0,
sizeof(BCRYPT_DH_KEY_BLOB));
p_dh_pub_key_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC;
p_dh_pub_key_blob->cbKey = sz/8;
DWORD df_pub_key_data_length = sizeof(BCRYPT_DH_KEY_BLOB) + sz / 8;
PBYTE p_df_pub_key_data = (PBYTE)HeapAlloc(
GetProcessHeap(),
0,
df_pub_key_data_length);
memcpy(p_df_pub_key_data,
p_dh_pub_key_blob,
sizeof(BCRYPT_DH_KEY_BLOB));
memcpy(p_df_pub_key_data + sizeof(BCRYPT_DH_KEY_BLOB),
PubBlobA2,
sz/8);
Status = BCryptImportKeyPair(
ExchAlgHandleB, // Alg handle
NULL, // Parameter not used
BCRYPT_DH_PUBLIC_BLOB, // Blob type (Null terminated unicode string)
&PubKeyHandleB, // Key handle that will be recieved
p_df_pub_key_data, // Buffer than points to the key blob
df_pub_key_data_length, // Buffer length in bytes
0);
I am trying to compress and decompress raw PCM (16-Bit) audio, using OPUS.
Here below is my code for opus_encoder.c. If I remove my decoder.c, the buffer works just fine as in the microphone is able to take in raw PCM data. However, once I have implemented my decoder class, it gave me a lot of errors such as memory allocation, heap corruption and so on. Here are some of my errors:
std::bad_alloc at memory location 0x0031D4BC
Stack overflow (parameters: 0x00000000, 0x05122000)
Access violation reading location 0x04A40000.
Based on my understanding, I think my decoder size cannot allocate the memory properly. Can you take a look at my codes and see what went wrong?
Opus_encoder.c
#include "opusencoder.h"
#include <QtConcurrent/QtConcurrent>
opusencoder::opusencoder(){
}
opusencoder::~opusencoder(){
}
OpusEncoder *enc;
int error;
unsigned char *compressedbuffer;
opus_uint32 enc_final_range;
short pcm = 0;
unsigned char *opusencoder::encodedata(const char *audiodata, const unsigned int& size) {
if (size == 0)
return false;
enc = (OpusEncoder *)malloc(opus_encoder_get_size(1));
enc = opus_encoder_create(8000, 1, OPUS_APPLICATION_VOIP, &error);
if (enc == NULL)
{
exit;
}
opus_int32 rate;
opus_encoder_ctl(enc, OPUS_GET_BANDWIDTH(&rate));
this->encoded_data_size = rate;
int len;
for (int i = 0; i < size / 2; i++)
{
//combine pairs of bytes in the original data into two-byte number
//convert const char to short
pcm= audiodata[2 * i] << 8 | audiodata[(2 * i) + 1];
}
qDebug() << "audiodata: " << pcm << endl;
compressedbuffer = new (unsigned char[this->encoded_data_size]);
len = opus_encode(enc, &pcm, 320, compressedbuffer, this->encoded_data_size);
len = opus_packet_unpad(compressedbuffer, len);
len++;
if (len < 0)
{
qDebug() << "Failure to compress";
return NULL;
}
qDebug() << "COmpressed buffer:" << compressedbuffer << endl;
qDebug() << "opus_encode() ................................ OK.\n" << endl;
}
Opus_decoder.c
##include "opusdecoder.h"
#include <QtConcurrent/QtConcurrent>
#define OPUS_CLEAR(dst, n) (memset((dst), 0, (n)*sizeof(*(dst))))
int num_channels = 1;
opusdecoder::opusdecoder(){
}
opusdecoder::~opusdecoder(){
}
opus_int16* opusdecoder::decodedata(int frame_size, const unsigned char *data)
{
dec = opus_decoder_create(8000, 1, &err);
if (dec == NULL)
{
exit;
}
opus_int32 rate;
opus_decoder_ctl(dec, OPUS_GET_BANDWIDTH(&rate));
rate = decoded_data_size;
this->num_channels = num_channels;
int decodedatanotwo;
opus_int16 *decompress = new (opus_int16[frame_size * this->num_channels]);
opus_packet_get_nb_channels(data);
decodedatanotwo= opus_decode(dec, data, this->decoded_data_size, decompress, 320, 0);
if (decodedatanotwo < 0)
{
qDebug() << "Failure to decompress";
return NULL;
}
qDebug() << "opus_decode() ................................ OK.\n" << decodedatanotwo << endl;
if (decodedatanotwo != frame_size)
{
exit;
}
}