NTE_BAD_SIGNATURE while verifying rsa using Win32 API - c++

I tried everything to make it working, so i decided to do full example. I hope someone would help me.
Message to sign: Hello World
Signing example:
https://gchq.github.io/CyberChef/#recipe=RSA_Sign('-----BEGIN%20RSA%20PRIVATE%20KEY-----%5CnMIICWwIBAAKBgQDCLDny1MwbGVFrjtNXH%2BHFxZ/4C5Gz0q2b5exrJLHums5YDSFl%5Cn6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi%2B53nP7zJsxmLnpN5J8QEQJgTlkw%2Bk%5CnIa8%2Bc8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQpGuPpUTCtyfRmBNuGbQIDAQAB%5CnAoGABH78qoRF45y%2B92Qbvak105wDW7181rKapYD56/MyEYnRHFXVf6QdzU3zyTSr%5Cn4rMPov6ygDtRNadCK2DiPqDsvOiQU%2By7ZSt%2Bt0hdUv8nkQOzR167dM8dLAoVMVtm%5CnKgUYO1MUnERvh%2BpmPKOmYozBAZbJJshOLbmqfGoJH1wUzCECQQDkC1SDsmLbT9uA%5Cn8MR6u4sn%2Bkghli5oxP3dnj/1WRAATt3SrQNGUyJsLCCDc70EOPwx/nIY4aDog/3K%5Cnixut8zVdAkEA2fnp0rCOXXikNV69z5Y2lj4OYpLtJiaJQK6NOYF997zso2oln/xX%5Cnddxle0%2Ba5KufdkP4DGP0QSnZKA%2B6qIf0UQJAf9hOQCrQuwzRDT9tlzTu9bGdoJ62%5CnU%2BwkOotOZfjRPKr6NvLhxBo1URmH/Mn07JoZ4Nk6E/LiJ5hfvp4wHVwczQJAIDxR%5CnVBNAOpqIzkvAjl6MnBN5VSKdZ7LzQVmPER4RXv3VkSU1gz9yP7/kUiQnqAGph3ft%5CnywdNLAXgU4hf9mSEwQJAbLV0c7GWXPCSk3k0gwOZCjHKaIxDIBjZudv5cvO9sPQx%5CnsqE17eyl5%2BufQq1xOQZL4HL%2BnUjlOcgT/pIdI9430A%3D%3D%5Cn-----END%20RSA%20PRIVATE%20KEY-----','','MD5')To_Hex('0x%20with%20comma',0)&input=SGVsbG8gV29ybGQ
Verification example: https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')RSA_Verify('-----BEGIN%20PUBLIC%20KEY-----%5CnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCLDny1MwbGVFrjtNXH%2BHFxZ/4%5CnC5Gz0q2b5exrJLHums5YDSFl6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi%2B53nP%5Cn7zJsxmLnpN5J8QEQJgTlkw%2BkIa8%2Bc8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQp%5CnGuPpUTCtyfRmBNuGbQIDAQAB%5Cn-----END%20PUBLIC%20KEY-----','Hello%20World','MD5')&input=YjU5ODE1MzYwNjUyZWU3MWE1OTlhNTljZDQzM2Q4MmMyMzFhY2UzNzdjZWRkZDc3YzBkNTBhMmJkYWZhNDJmMjM0OTVkODVhNDg4Y2RiZmUwNDczNjNkOTI3NGE0MWM0MzgwZTgyNzJjMTEzNTExNDZkYmM4NDFhNTNkMTVkZGE0NTE2MGEyMzM4YzQxMzcwZDg2NzFhMTZmYTlkZTExNzQzZjY1N2UzZjMzYzA5MTBmMzYwMWY0MzQzNTZlNzllMWQ3NTEzNzRmOGVkZmZkZmY3MWMxMWZjOWUzNDI0ZmJlNmY0OWI2ZWI3Yzc4Zjc2MDRlZWZhYTU5ZTBhNmNhOA
Public and private key are generated only for that example purpose
C++ code:
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include "windows.h"
#include "wincrypt.h"
using namespace std;
std::string GetLastErrorAsString() {
//Get the error message ID, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0) {
return std::string(); //No error message has been recorded
}
LPSTR messageBuffer = nullptr;
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, NULL);
//Copy the error message into a std::string.
std::string message(messageBuffer, size);
//Free the Win32's string's buffer.
LocalFree(messageBuffer);
return message;
}
int test4(const vector<unsigned char> &signature, const vector<unsigned char> &data_to_verify) {
string rsaKey = "-----BEGIN PUBLIC KEY-----\n"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCLDny1MwbGVFrjtNXH+HFxZ/4\n"
"C5Gz0q2b5exrJLHums5YDSFl6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi+53nP\n"
"7zJsxmLnpN5J8QEQJgTlkw+kIa8+c8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQp\n"
"GuPpUTCtyfRmBNuGbQIDAQAB\n"
"-----END PUBLIC KEY-----";
char derPubKey[2048];
size_t derPubKeyLen = 2048;
CERT_PUBLIC_KEY_INFO *publicKeyInfo;
int publicKeyInfoLen;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
/*
* Convert from PEM format to DER format - removes header and footer and decodes from base64
*/
if (!CryptStringToBinaryA(rsaKey.data(), 0, CRYPT_STRING_BASE64HEADER, reinterpret_cast<BYTE *>(derPubKey),
reinterpret_cast<DWORD *>(&derPubKeyLen), NULL, NULL)) {
fprintf(stderr, "CryptStringToBinary failed. Err: %d\n", GetLastError());
}
/*
* Decode from DER format to CERT_PUBLIC_KEY_INFO
*/
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, reinterpret_cast<const BYTE *>(derPubKey),
derPubKeyLen,
CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo,
reinterpret_cast<DWORD *>(&publicKeyInfoLen))) {
fprintf(stderr, "CryptDecodeObjectEx 1 failed. Err: %p\n", GetLastError());
return -1;
}
/*
* Acquire context
*/
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
{
printf("CryptAcquireContext failed - err=0x%x.\n", GetLastError());
return -1;
}
}
/*
* Import the public key using the context
*/
if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey)) {
fprintf(stderr, "CryptImportPublicKeyInfo failed. error: %d\n", GetLastError());
return -1;
}
LocalFree(publicKeyInfo);
/*
* Now use hKey to encrypt whatever you need.
*/
// Hash the data_to_verify
HCRYPTHASH hHash = 0;
if (!CryptCreateHash(hProv, CALG_MD5, NULL, 0, &hHash)) {
printf("CryptCreateHash failed with error 0x%.8lX\n", GetLastError());
return -1;
}
if (!CryptHashData(hHash, data_to_verify.data(), data_to_verify.size(), 0)) {
printf("CryptHashData failed with error 0x%.8lX\n", GetLastError());
return -1;
}
// Sign the hash using our imported key
if (!CryptVerifySignature(hHash, signature.data(), signature.size(), hKey, nullptr, 0)) {
printf("Signature verification failed with error");
cout << GetLastErrorAsString() << endl;
return false;
} else {
printf("Signature verification succeeded.\n");
return true;
}
return 0;
}
//vector<unsigned char> to hex
std::string to_hex(const std::vector<unsigned char> &v) {
std::string result;
char hex[] = "0123456789abcdef";
for (unsigned char c : v) {
result += hex[c >> 4];
result += hex[c & 15];
}
return result;
}
int main() {
vector<unsigned char> signature = {0xb5, 0x98, 0x15, 0x36, 0x06, 0x52, 0xee, 0x71, 0xa5, 0x99, 0xa5, 0x9c, 0xd4,
0x33, 0xd8, 0x2c, 0x23, 0x1a, 0xce, 0x37, 0x7c, 0xed, 0xdd, 0x77, 0xc0, 0xd5,
0x0a, 0x2b, 0xda, 0xfa, 0x42, 0xf2, 0x34, 0x95, 0xd8, 0x5a, 0x48, 0x8c, 0xdb,
0xfe, 0x04, 0x73, 0x63, 0xd9, 0x27, 0x4a, 0x41, 0xc4, 0x38, 0x0e, 0x82, 0x72,
0xc1, 0x13, 0x51, 0x14, 0x6d, 0xbc, 0x84, 0x1a, 0x53, 0xd1, 0x5d, 0xda, 0x45,
0x16, 0x0a, 0x23, 0x38, 0xc4, 0x13, 0x70, 0xd8, 0x67, 0x1a, 0x16, 0xfa, 0x9d,
0xe1, 0x17, 0x43, 0xf6, 0x57, 0xe3, 0xf3, 0x3c, 0x09, 0x10, 0xf3, 0x60, 0x1f,
0x43, 0x43, 0x56, 0xe7, 0x9e, 0x1d, 0x75, 0x13, 0x74, 0xf8, 0xed, 0xff, 0xdf,
0xf7, 0x1c, 0x11, 0xfc, 0x9e, 0x34, 0x24, 0xfb, 0xe6, 0xf4, 0x9b, 0x6e, 0xb7,
0xc7, 0x8f, 0x76, 0x04, 0xee, 0xfa, 0xa5, 0x9e, 0x0a, 0x6c, 0xa8};
string message = "Hello World";
vector<unsigned char> data = vector<unsigned char>(message.begin(), message.end());
cout << "Signature: " << to_hex(signature) << endl;
cout << "Message: " << message << endl;
test4(signature, data);
}
After executing that code, i'm getting NTE_BAD_SIGNATURE error
I used PEM import from Load an PEM encoded X.509 certificate into Windows CryptoAPI
I really hope someone would help me resolving that issue

better use CNG api (if not need support xp) and always need free allocated memory and other resources, what you not do. this code work ok
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
HRESULT StringToBin(_Out_ PDATA_BLOB pdb, _In_ ULONG dwFlags, _In_ PCSTR pszString, _In_ ULONG cchString = 0)
{
PUCHAR pb = 0;
ULONG cb = 0;
while (CryptStringToBinaryA(pszString, cchString, dwFlags, pb, &cb, 0, 0))
{
if (pb)
{
pdb->pbData = pb, pdb->cbData = cb;
return S_OK;
}
if (!(pb = (PUCHAR)LocalAlloc(LMEM_FIXED, cb)))
{
break;
}
}
return HRESULT_FROM_WIN32(GetLastError());
}
HRESULT VerifyTest(_In_ PCWSTR pszAlgId,
_In_ PCSTR szKey,
_In_ PCSTR szSig,
_In_ const UCHAR *pbData,
_In_ ULONG cbData)
{
DATA_BLOB db, db2;
HRESULT hr;
if (NOERROR == (hr = StringToBin(&db, CRYPT_STRING_BASE64HEADER, szKey)))
{
ULONG cb;
CERT_PUBLIC_KEY_INFO *publicKeyInfo;
hr = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
db.pbData, db.cbData, CRYPT_DECODE_ALLOC_FLAG, 0, &publicKeyInfo, &cb));
LocalFree(db.pbData);
if (NOERROR == hr)
{
BCRYPT_KEY_HANDLE hKey;
hr = BOOL_TO_ERROR(CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, publicKeyInfo, 0, 0, &hKey));
LocalFree(publicKeyInfo);
if (NOERROR == hr)
{
UCHAR hash[32];
if (NOERROR == (hr = BOOL_TO_ERROR(CryptHashCertificate2(pszAlgId, 0, 0, pbData, cbData, hash, &(cb = sizeof(hash))))))
{
if (0 <= (hr = StringToBin(&db, CRYPT_STRING_BASE64, szSig)))
{
hr = StringToBin(&db2, CRYPT_STRING_HEXRAW, (PCSTR)db.pbData, db.cbData);
LocalFree(db.pbData);
if (0 <= hr)
{
BCRYPT_PKCS1_PADDING_INFO pi = { pszAlgId };
if (0 > (hr = BCryptVerifySignature(hKey, &pi, hash, cb, db2.pbData, db2.cbData, BCRYPT_PAD_PKCS1)))
{
hr |= FACILITY_NT_BIT;
}
LocalFree(db2.pbData);
}
}
}
BCryptDestroyKey(hKey);
}
}
}
return HRESULT_FROM_WIN32(hr);
}
void VerifyTest()
{
static const CHAR szKey[] =
"-----BEGIN PUBLIC KEY-----\n"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCLDny1MwbGVFrjtNXH+HFxZ/4\n"
"C5Gz0q2b5exrJLHums5YDSFl6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi+53nP\n"
"7zJsxmLnpN5J8QEQJgTlkw+kIa8+c8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQp\n"
"GuPpUTCtyfRmBNuGbQIDAQAB\n"
"-----END PUBLIC KEY-----";
static const CHAR szSig[] =
"YjU5ODE1MzYwNjUyZWU3MWE1OTlhNTljZDQzM2Q4MmMyMzFhY2UzNzdjZWRkZDc3"
"YzBkNTBhMmJkYWZhNDJmMjM0OTVkODVhNDg4Y2RiZmUwNDczNjNkOTI3NGE0MWM0"
"MzgwZTgyNzJjMTEzNTExNDZkYmM4NDFhNTNkMTVkZGE0NTE2MGEyMzM4YzQxMzcw"
"ZDg2NzFhMTZmYTlkZTExNzQzZjY1N2UzZjMzYzA5MTBmMzYwMWY0MzQzNTZlNzll"
"MWQ3NTEzNzRmOGVkZmZkZmY3MWMxMWZjOWUzNDI0ZmJlNmY0OWI2ZWI3Yzc4Zjc2"
"MDRlZWZhYTU5ZTBhNmNhOA";
static const UCHAR data[] = "Hello World";
VerifyTest(BCRYPT_MD5_ALGORITHM, szKey, szSig, data, sizeof(data) - 1);
}
for sign can be used next code:
void PrintStr(PSTR psz, ULONG cch)
{
ULONG len;
do
{
DbgPrint("%.*s", len = min(0x100, cch), psz);
} while (psz += len, cch -= len);
}
NTSTATUS ImportRsaKey(_Out_ BCRYPT_KEY_HANDLE* phKey, _In_ PBYTE pbKey, _In_ ULONG cbKey)
{
if (CryptDecodeObjectEx(X509_ASN_ENCODING, CNG_RSA_PRIVATE_KEY_BLOB,
pbKey, cbKey, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, &pbKey, &cbKey))
{
BCRYPT_ALG_HANDLE hAlgorithm;
NTSTATUS status;
if (0 <= (status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0)))
{
status = BCryptImportKeyPair (hAlgorithm, 0, BCRYPT_PRIVATE_KEY_BLOB, phKey, pbKey, cbKey, 0);
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
LocalFree(pbKey);
return 0 > status ? HRESULT_FROM_NT(status) : S_OK;
}
return HRESULT_FROM_WIN32(GetLastError());
}
HRESULT BinToString(_Out_ PSTR *ppszString, _In_ ULONG dwFlags, _In_ const UCHAR* pb, _In_ ULONG cb)
{
PSTR pszString = 0;
ULONG cch = 0;
while (CryptBinaryToStringA(pb, cb, dwFlags, pszString, &cch))
{
if (pszString)
{
*ppszString = pszString;
return S_OK;
}
if (!(pszString = (PSTR)LocalAlloc(LMEM_FIXED, cch)))
{
break;
}
}
return HRESULT_FROM_WIN32(GetLastError());
}
HRESULT SignTest(_In_ PCWSTR pszAlgId,
_In_ PCSTR szKey,
_In_ const UCHAR *pbData,
_In_ ULONG cbData)
{
DATA_BLOB db;
HRESULT hr;
if (NOERROR == (hr = StringToBin(&db, CRYPT_STRING_BASE64HEADER, szKey)))
{
BCRYPT_KEY_HANDLE hKey=0;
hr = ImportRsaKey(&hKey, db.pbData, db.cbData);
LocalFree(db.pbData);
if (0 <= hr)
{
UCHAR hash[32];
ULONG cb = sizeof(hash);
if (NOERROR == (hr = BOOL_TO_ERROR(CryptHashCertificate2(pszAlgId, 0, 0, pbData, cbData, hash, &cb))))
{
BCRYPT_PKCS1_PADDING_INFO pi = { pszAlgId };
PUCHAR pbSig = 0;
ULONG cbSig = 0;
while (0 <= (hr = BCryptSignHash(hKey, &pi, hash, cb, pbSig, cbSig, &cbSig, BCRYPT_PAD_PKCS1)))
{
if (pbSig)
{
PSTR psz, psz2;
if (NOERROR == BinToString(&psz, CRYPT_STRING_HEXRAW|CRYPT_STRING_NOCRLF, pbSig, cbSig))
{
if (NOERROR == BinToString(&psz2, CRYPT_STRING_BASE64, (PUCHAR)psz, (ULONG)strlen(psz)))
{
PrintStr(psz2, (ULONG)strlen(psz2));
LocalFree(psz2);
}
LocalFree(psz);
}
break;
}
pbSig = (PUCHAR)alloca(cbSig);
}
}
BCryptDestroyKey(hKey);
}
}
return HRESULT_FROM_WIN32(hr);
}
void sign()
{
static const UCHAR data[] = "Hello World";
static const CHAR szPrivKey[] =
"-----BEGIN RSA PRIVATE KEY-----"
"MIICWwIBAAKBgQDCLDny1MwbGVFrjtNXH+HFxZ/4C5Gz0q2b5exrJLHums5YDSFl"
"6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi+53nP7zJsxmLnpN5J8QEQJgTlkw+k"
"Ia8+c8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQpGuPpUTCtyfRmBNuGbQIDAQAB"
"AoGABH78qoRF45y+92Qbvak105wDW7181rKapYD56/MyEYnRHFXVf6QdzU3zyTSr"
"4rMPov6ygDtRNadCK2DiPqDsvOiQU+y7ZSt+t0hdUv8nkQOzR167dM8dLAoVMVtm"
"KgUYO1MUnERvh+pmPKOmYozBAZbJJshOLbmqfGoJH1wUzCECQQDkC1SDsmLbT9uA"
"8MR6u4sn+kghli5oxP3dnj/1WRAATt3SrQNGUyJsLCCDc70EOPwx/nIY4aDog/3K"
"ixut8zVdAkEA2fnp0rCOXXikNV69z5Y2lj4OYpLtJiaJQK6NOYF997zso2oln/xX"
"ddxle0+a5KufdkP4DGP0QSnZKA+6qIf0UQJAf9hOQCrQuwzRDT9tlzTu9bGdoJ62"
"U+wkOotOZfjRPKr6NvLhxBo1URmH/Mn07JoZ4Nk6E/LiJ5hfvp4wHVwczQJAIDxR"
"VBNAOpqIzkvAjl6MnBN5VSKdZ7LzQVmPER4RXv3VkSU1gz9yP7/kUiQnqAGph3ft"
"ywdNLAXgU4hf9mSEwQJAbLV0c7GWXPCSk3k0gwOZCjHKaIxDIBjZudv5cvO9sPQx"
"sqE17eyl5+ufQq1xOQZL4HL+nUjlOcgT/pIdI9430A=="
"-----END RSA PRIVATE KEY-----";
SignTest(BCRYPT_MD5_ALGORITHM, szPrivKey, data, sizeof(data) - 1);
}

Working example after some edit:
#include <iostream>
#include <cstdio>
#include "windows.h"
#include "wincrypt.h"
#include "comdef.h"
using namespace std;
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
HRESULT StringToBin(_Out_ PDATA_BLOB pdb, _In_ ULONG dwFlags, _In_ PCSTR pszString, _In_ ULONG cchString = 0)
{
PUCHAR pb = 0;
ULONG cb = 0;
while (CryptStringToBinaryA(pszString, cchString, dwFlags, pb, &cb, 0, 0))
{
if (pb)
{
pdb->pbData = pb, pdb->cbData = cb;
return S_OK;
}
if (!(pb = (PUCHAR)LocalAlloc(LMEM_FIXED, cb)))
{
break;
}
}
return HRESULT_FROM_WIN32(GetLastError());
}
#define SIGNATURE_SIZE 128
HRESULT VerifyTest(_In_ PCWSTR algorithm,
_In_ PCSTR keyAsPem,
_In_ BYTE *signatureBase64,
_In_ const UCHAR *dataToCheck,
_In_ ULONG dataToCheckSize)
{
DATA_BLOB db, db2;
HRESULT hr;
if (NOERROR == (hr = StringToBin(&db, CRYPT_STRING_BASE64HEADER, keyAsPem)))
{
ULONG cb;
CERT_PUBLIC_KEY_INFO *publicKeyInfo;
hr = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
db.pbData, db.cbData, CRYPT_DECODE_ALLOC_FLAG, 0, &publicKeyInfo, &cb));
LocalFree(db.pbData);
if (NOERROR == hr)
{
BCRYPT_KEY_HANDLE hKey;
hr = BOOL_TO_ERROR(CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, publicKeyInfo, 0, 0, &hKey));
LocalFree(publicKeyInfo);
if (NOERROR == hr)
{
UCHAR hash[32];
if (NOERROR == (hr = BOOL_TO_ERROR(CryptHashCertificate2(algorithm, 0, 0, dataToCheck, dataToCheckSize, hash, &(cb = sizeof(hash))))))
{
BCRYPT_PKCS1_PADDING_INFO pi = {algorithm };
if (0 > (hr = BCryptVerifySignature(hKey, &pi, hash, cb, signatureBase64, SIGNATURE_SIZE, BCRYPT_PAD_PKCS1)))
{
hr |= FACILITY_NT_BIT;
}
}
BCryptDestroyKey(hKey);
}
}
}
return HRESULT_FROM_WIN32(hr);
}
void VerifyTest()
{
static const CHAR szKey[] =
"-----BEGIN PUBLIC KEY-----\n"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCLDny1MwbGVFrjtNXH+HFxZ/4\n"
"C5Gz0q2b5exrJLHums5YDSFl6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi+53nP\n"
"7zJsxmLnpN5J8QEQJgTlkw+kIa8+c8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQp\n"
"GuPpUTCtyfRmBNuGbQIDAQAB\n"
"-----END PUBLIC KEY-----";
static BYTE dataToVerify[] = {0xb5, 0x98, 0x15, 0x36, 0x06, 0x52, 0xee, 0x71, 0xa5, 0x99, 0xa5, 0x9c, 0xd4,
0x33, 0xd8, 0x2c, 0x23, 0x1a, 0xce, 0x37, 0x7c, 0xed, 0xdd, 0x77, 0xc0, 0xd5,
0x0a, 0x2b, 0xda, 0xfa, 0x42, 0xf2, 0x34, 0x95, 0xd8, 0x5a, 0x48, 0x8c, 0xdb,
0xfe, 0x04, 0x73, 0x63, 0xd9, 0x27, 0x4a, 0x41, 0xc4, 0x38, 0x0e, 0x82, 0x72,
0xc1, 0x13, 0x51, 0x14, 0x6d, 0xbc, 0x84, 0x1a, 0x53, 0xd1, 0x5d, 0xda, 0x45,
0x16, 0x0a, 0x23, 0x38, 0xc4, 0x13, 0x70, 0xd8, 0x67, 0x1a, 0x16, 0xfa, 0x9d,
0xe1, 0x17, 0x43, 0xf6, 0x57, 0xe3, 0xf3, 0x3c, 0x09, 0x10, 0xf3, 0x60, 0x1f,
0x43, 0x43, 0x56, 0xe7, 0x9e, 0x1d, 0x75, 0x13, 0x74, 0xf8, 0xed, 0xff, 0xdf,
0xf7, 0x1c, 0x11, 0xfc, 0x9e, 0x34, 0x24, 0xfb, 0xe6, 0xf4, 0x9b, 0x6e, 0xb7,
0xc7, 0x8f, 0x76, 0x04, 0xee, 0xfa, 0xa5, 0x9e, 0x0a, 0x6c, 0xa8};
static const UCHAR data[] = "Hello World";
HRESULT i = VerifyTest(BCRYPT_MD5_ALGORITHM, szKey, dataToVerify, data, sizeof(data) - 1);
_com_error err(i);
LPCTSTR errMsg = err.ErrorMessage();
cout << "Result: " << errMsg << endl;
}
int main() {
VerifyTest();
}
I used code in answer on the top

Related

Conditionally initialize an array

I want an array to have two different values, based on a condition. I can initialize the array inside the condition with the values i want.
if (myCondition == 0)
{
byte my_message[8] = {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
}
else if (myCondition == 1)
{
byte my_message[8] = {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
}
The problem with the previous approach is that the array has local scope, and it cannot be "seen" by the code beneath.
If I try to declare the array outside the condition with:
byte my_message[8];
then, inside the condition, I cannot use the previous way of initializing the whole array all at once.
There is no pattern in the data so I can use a for loop- inside the condition- in order to give value to each element of the array easily.
Is there a way of giving values to the array, besides the cumbersome:
if (myCondition == 0)
{
my_message[0] = {0x00};
my_message[1] = {0xAB};
my_message[2] = {0xEE};
....
}
In C you can use standard function memcpy and compound literals the following way
byte my_message[8];
if (myCondition == 0)
{
memcpy( my_message, ( byte [] ){0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B}, 8 * sizeof( byte ));
}
else if (myCondition == 1)
{
memcpy( my_message, ( byte[] ){0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10}, 8 * sizeof( byte ) );
}
In C++ it is better to use std::array instead of a raw array. For example
std::array<byte, 8> my_message;
if (myCondition == 0)
{
my_message = {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
}
else if (myCondition == 1)
{
my_message = {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
}
Or if to use a raw array then if the compiler supports C++ 20 then you can use the range based for loop the following way
byte my_message[8];
if (myCondition == 0)
{
for ( auto *p = my_message; auto item : {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B} )
{
*p++ = item;
}
}
else if (myCondition == 1)
{
for ( auto *p = my_message; auto item : {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10} )
{
*p++ = item;
}
}
Otherwise if the compiler does not support C++ 20 then this declaration
auto *p = my_message;
must be moved outside the range based for loop.
If my_message isn't changed, you could use a pointer instead of an array.
const byte my_messages[2][8] = {
{ 0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B },
{ 0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10 },
};
const byte *my_message = my_messages[ myCondition ];
If you need to be able to change my_array, I'd use the following:
const byte my_messages[2][8] = {
{ 0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B },
{ 0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10 },
};
byte my_message[8];
memcpy( my_message, my_messages[ myCondition ], sizeof( my_message ) );
You could memcpy from an anonymous array, but it's overly complicated and involves code repetition:
byte my_message[8];
if ( myCondition ) {
memcpy( my_message, ( byte[] ){ 0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10 }, sizeof( my_message) );
} else {
memcpy( my_message, ( byte[] ){ 0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B }, sizeof( my_message) );
}
You can wrap the message into the struct and use assign operator with compound literal.
You can use compound literal + memcpy
typedef unsigned char byte;
typedef struct
{
byte my_message[8];
}message_t;
int foo(int myCondition)
{
message_t my_message;
if (myCondition == 0)
{
my_message = (message_t){0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
}
else if (myCondition == 1)
{
my_message = (message_t){0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
}
return 0;
}
int bar(int myCondition)
{
byte my_message[8];
if (myCondition == 0)
{
memcpy(my_message, (byte[]){0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B}, sizeof(my_message));
}
else if (myCondition == 1)
{
memcpy(my_message, (byte[]){0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10}, sizeof(my_message));
}
return 0;
}
or you can myCondition as index:
int zoo(int myCondition)
{
byte my_message[8];
memcpy(my_message,
(byte[][8]){{0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B},
{0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10}}[myCondition],
sizeof(my_message));
return 0;
}
memcpy way looks the most efficient:
https://godbolt.org/z/d6bMfejz5
Variadic template variant:
#include <array>
#include <cstdint>
#include <cstdlib>
#include <iostream>
using byte = unsigned char;
template <typename T, typename... Ts>
std::array<T, sizeof...(Ts)> make_array(Ts... ts) {
return std::array<T, sizeof...(Ts)> { (T)ts... };
}
int main()
{
bool myCondition = false;
std::array<byte, 8> ary = myCondition
? make_array<byte>(7,6,5,4,3,2,1,0)
: make_array<byte>(0,1,2,3,4,5,6,7);
for(auto it=ary.begin(); it != ary.end(); ++it)
{
std::cout << (int)*it << std::endl;
}
return 0;
}
Why "design-in" a boolean limitation of only two conditions?
The following allows for what could be easily be less code in main(), and up to four conditions... (I've altered the values of the 4x8 bytes to bury an Easter Egg for those who are curious. Happy hunting...)
#include <stdio.h>
void show( byte *m ) { printf( "%.8s\n", m ); }
int main() {
const byte my_messages[][8] = {
{ 0x41, 0x4C, 0x4c, 0x20, 0x47, 0x4F, 0x4F, 0x44 },
{ 0x48, 0x41, 0x54, 0x45, 0x4D, 0x41, 0x52, 0x53 },
{ 0x53, 0x45, 0x4E, 0x44, 0x48, 0x45, 0x4C, 0x50 },
{ 0x54, 0x4F, 0x4F, 0x20, 0x4C, 0x41, 0x54, 0x45 },
};
byte msg[8];
# define FETCH( m, cond ) do { memcpy( m, my_messages[cond], sizeof m ); } while(0)
FETCH( msg, 0 ); show( msg ); // prove
FETCH( msg, 1 ); show( msg ); // prove
FETCH( msg, 2 ); show( msg ); // prove
FETCH( msg, 3 ); show( msg ); // prove
return 0;
}
Surprisingly, this effect is achievable using a reference to an array in C++. I'm not sure how compliant this solution is but it compiles in pedantic mode for CLANG, GCC, ICC and MSVC.
int foo(bool cond) {
byte arr0[8] = {0x00, 0xAB, 0xEE, 0xFF, 0x00, 0x01, 0x0A, 0x0B};
byte arr1[8] = {0x11, 0xA1, 0xBC, 0x71, 0x00, 0x02, 0x94, 0x10};
byte (&tab)[8] = cond ? arr0 : arr1;
return tab[0];
}
See https://godbolt.org/z/P6Th7bfcn

How can I create a bitmap from bytes array of png image in C++

Tried using the below code but it does not seem to work. The value of bm is null
BYTE imageData[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x5d, 0x52, 0x1c, 0x00, 0x00, 0x00, 0x0f, 0x50,
0x4c, 0x54, 0x45, 0x7a, 0xdf, 0xfd, 0xfd, 0xff, 0xfc, 0x39, 0x4d, 0x52, 0x19, 0x16, 0x15, 0xc3, 0x8d, 0x76, 0xc7,
0x36, 0x2c, 0xf5, 0x00, 0x00, 0x00, 0x40, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x95, 0xc9, 0xd1, 0x0d, 0xc0, 0x20,
0x0c, 0x03, 0xd1, 0x23, 0x5d, 0xa0, 0x49, 0x17, 0x20, 0x4c, 0xc0, 0x10, 0xec, 0x3f, 0x53, 0x8d, 0xc2, 0x02, 0x9c,
0xfc, 0xf1, 0x24, 0xe3, 0x31, 0x54, 0x3a, 0xd1, 0x51, 0x96, 0x74, 0x1c, 0xcd, 0x18, 0xed, 0x9b, 0x9a, 0x11, 0x85,
0x24, 0xea, 0xda, 0xe0, 0x99, 0x14, 0xd6, 0x3a, 0x68, 0x6f, 0x41, 0xdd, 0xe2, 0x07, 0xdb, 0xb5, 0x05, 0xca, 0xdb,
0xb2, 0x9a, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(imageData));
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, imageData, sizeof(imageData));
IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);
Gdiplus::Bitmap* bm = Gdiplus::Bitmap::FromStream(pStream, true);
I can suggest 2 ways to convert PNG to BMP.
You can use third-party libraries. In case of opencv it will be something like that:
Mat image = imread("image.png", -1);
Mat image_bmp;
image.convertTo(image_bmp, CV_8UC3);
imwrite("image.bmp", image_bmp);
In VS you can use GDI+. The following code is runnable in my VS2019.
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;
INT GetEncoderClsid(const WCHAR* format, CLSID* pClsid); // helper function
INT main()
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CLSID encoderClsid;
Status stat;
Image* image = new Image(L"image.png");
// Get the CLSID of the encoder.
GetEncoderClsid(L"image/bmp", &encoderClsid);
stat = image->Save(L"image.bmp", &encoderClsid, NULL);
if (stat == Ok)
printf("Image was saved successfully\n");
else
printf("Failure: stat = %d\n", stat);
delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}

How to properly use the aes cbc api from mbedtls

My current approach doesn't even produce the correct encrypted data as checked against golang's aes implementation. It also doesn't decrypt back to the original plaintext, but that's expected if the encryption step isn't working correctly. My best guess is that I'm misusing the api somehow. This is a self-contained example and can run as is.
#include <mbedtls/aes.h>
#include <vector>
void LogVec(const std::vector<uint8_t>& bin)
{
printf("(size: %i) ", bin.size());
printf("{");
for (auto& b : bin)
{
printf("%#02x, ", b);
}
printf("}\n");
}
mbedtls_aes_context AesContext;
std::vector<uint8_t> EncryptAes(std::vector<uint8_t>& iv, std::vector<uint8_t>& data)
{
std::vector<uint8_t> ivCpy(iv);
uint8_t padByte = 16 - (data.size() % 16);
for (int i = 0; i < padByte; i++)
data.push_back(padByte);
std::vector<uint8_t> ret(data.size());
mbedtls_aes_crypt_cbc(&AesContext, MBEDTLS_AES_ENCRYPT, data.size(), ivCpy.data(), data.data(), ret.data());
return ret;
}
std::vector<uint8_t> DecryptAes(const std::vector<uint8_t>& iv, std::vector<uint8_t>& data)
{
std::vector<uint8_t> ivCpy(iv);
std::vector<uint8_t> ret(data.size());
mbedtls_aes_crypt_cbc(&AesContext, MBEDTLS_AES_DECRYPT, data.size(), ivCpy.data(), data.data(), ret.data());
ret.resize(ret.size() - ret[ret.size() - 1]);
return ret;
}
int main()
{
mbedtls_aes_init(&AesContext);
std::vector<uint8_t> data = { 0x3b, 0xb1, 0x99, 0x3, 0x67, 0xf3, 0x2e, 0x1f, 0x00, 0x67, 0x38, 0xc9, 0x53, 0x92, 0xa4 };
std::vector<uint8_t> key = { 0x15, 0x1, 0xc0, 0xd0, 0xe4, 0xfd, 0xdf, 0xd7, 0x7a, 0x65, 0xf1, 0x2f, 0x45, 0x61, 0xb,
0x59, 0xd9, 0xa, 0x9c, 0x61, 0xc, 0x4, 0x76, 0xdb, 0xb, 0xbe, 0x9e, 0xe4, 0x7f, 0x8d, 0xe1, 0x46 };
std::vector<uint8_t> iv = { 0xa2, 0x78, 0xc9, 0xa4, 0xd8, 0x34, 0x88, 0x9b, 0x28, 0xdc, 0xb9, 0xe2, 0xc0, 0x58, 0x8c, 0xbc };
mbedtls_aes_setkey_enc(&AesContext, key.data(), 256);
mbedtls_aes_setkey_dec(&AesContext, key.data(), 256);
std::vector<uint8_t> dataEnc = EncryptAes(iv, data);
printf("Encrypted data: ");
LogVec(dataEnc);
//std::vector<uint8_t> dataDec = DecryptAes(iv, dataEnc);
//printf("Decrypted data: ");
//LogVec(dataDec);
getchar();
return 1;
}
Output:
Encrypted data: (size: 16) {0x5d, 0x1c, 0x9, 0x2e, 0x92, 0x8e, 0x24, 0x43, 0xfa, 0xaf, 0xb3, 0xf5, 0x37, 0x8, 0x99, 0x93, }
Expected output from using the same key, iv, data in golang:
Encrypted: 34730cba3543e5facf4b94ba9dc8a275
Before calling mbedtls_aes_crypt_cbc to encrypt you should call mbedtls_aes_setkey_enc and before calling mbedtls_aes_crypt_cbc to decrypt you should call mbedtls_aes_setkey_dec. When both are called at initialization like in your code, the latter call to setkey_dec will overwrite important data in the context structure set by setkey_enc required for encryption.

DNS query with winsock fails

I understand winsock has gethostbyname for this. But I need to debug for embedded application and first I try the code on Windows. Here is a my code:
Hide Expand Copy Code
sockaddr_in dns_addr;
int dns_size = sizeof(dns_addr);
ZeroMemory(&dns_addr, dns_size);
dns_addr.sin_family = AF_INET;
dns_addr.sin_port = htons(53);
dns_addr.sin_addr.s_addr = inet_addr("8.8.8.8");
SOCKET out = socket(AF_INET, SOCK_DGRAM, 0);
char msg[] = {
0x12, 0x34, // ID
0x01, 0x00, // QR ... RCODE
0x00, 0x01, // QDCOUNT
0x00, 0x00, // ANCOUNT
0x00, 0x00, // NSCOUNT
0x00, 0x00, // ARCOUNT
0x03,
0x77, 0x77, 0x77, // www
0x03,
0x74, 0x75, 0x74, // tut
0x02,
0x62, 0x79, // by
0x00,
0x00, 0x01, // TYPE_A
0x00, 0x01 // CLASS_IN
};
int msg_size = sizeof(msg);
int iSendOk = sendto(out, msg, msg_size, 0, (sockaddr*)&dns_addr, dns_size);
if (iSendOk != SOCKET_ERROR)
{
char bufstr[100];
sockaddr_in dest_addr;
int dest_addr_size = sizeof(dest_addr);
int iReceived = recvfrom(out, bufstr, 100, 0,(sockaddr*)&dest_addr, &dest_addr_size);
}
closesocket(out);
And this code freezes when recvfrom function is execution. The function is executed with an infinite loop and never returns. What do I do wrong?

C++ convert UTF8 char array to CP1250

I have a problem converting UTF8 encoded char array to CP1250 encoded char array.
I tried making a static iconv lib, but when compiling my UTF8->CP1250 code it always fail with
1>foo.obj : error LNK2001: unresolved external symbol libiconv_close
1>foo.obj : error LNK2001: unresolved external symbol libiconv_open
1>foo.obj : error LNK2001: unresolved external symbol libiconv
I also tried a lot of compiled static libraries, but always the same error.
Doesn't matter if /MD or /MT is set.
I do not insist on using iconv. Is there any other simple solution for this problem? Thanks
Since you're coding in Windows, why not use the Windows API. Use MultiByteToWideChar to convert losslessly up to UTF-16. Then use WideCharToMultiByte to convert down to e.g. CP 1250.
Addendum: the code below seems to work OK.
#include <assert.h> // assert
#include <iostream> // std::wcout, std::wcerr
#include <iterator> // std::begin, std::end
#include <string> // std::string, std::wstring
#include <stddef.h> // ptrdiff_t
#include <stdexcept> // std::system_error
#include <stdlib.h> // EXIT_SUCCESS
#include <string.h> // memcmp
#include <system_error> // std::system_error etc.
// Example of how to include Microsoft's <windows.h>.
// More support stuff is generally needed for more serious code.
#undef UNICODE
#define UNICODE
#undef NOMINMAX
#define NOMINMAX
#undef STRICT
#define STRICT
#include <windows.h> // E_FAIL, etc.
namespace cppx {
using std::begin;
using std::end;
using std::error_code;
using std::string;
using std::system_error;
using std::system_category;
typedef unsigned char Byte;
typedef ptrdiff_t Size;
template< class Type >
auto size( Type const& c )
-> Size
{ return end( c ) - begin( c ); }
auto hopefully( bool const condition ) -> bool { return condition; }
auto fail(
string const& message,
int const code = 0
)
-> bool
{
throw system_error(
error_code( code, system_category() ), message
);
}
} // namespace cppx
namespace data {
using cppx::Byte;
using cppx::hopefully;
using cppx::fail;
using std::string;
using std::wstring;
char const utf8_bom[] = "\xEF\xBB\xBF";
template< class Type, int n >
auto dummy()
-> Type&
{ static Type the_dummy; return the_dummy; }
auto utf16_from_utf8( char const* bytes, int length )
-> wstring
{
if( length >= 3 && ::memcmp( bytes, utf8_bom, 3 ) == 0 )
{
bytes += 3; length -= 3;
}
assert( length >= 0 );
if( length == 0 ) { return L""; }
int const buffer_size = ::MultiByteToWideChar(
CP_UTF8,
0, // flags, must be 0 for UTF-8
bytes,
length,
nullptr, // output buffer
0 // buffer size
);
hopefully( buffer_size > 0 )
|| fail( "MultiByteToWideChar (1st call)", ::GetLastError() );
wstring result( buffer_size, L'#' );
int const n_encoding_values = ::MultiByteToWideChar(
CP_UTF8,
0, // flags, must be 0 for UTF-8
bytes,
length,
&result[0], // output buffer
buffer_size
);
hopefully( n_encoding_values > 0 )
|| fail( "MultiByteToWideChar (2nd call)", ::GetLastError() );
assert( n_encoding_values <= buffer_size );
result.resize( n_encoding_values ); // Possible down-sizing.
return result;
}
auto utf16_from_utf8( Byte const* const bytes, int const length )
-> wstring
{
return utf16_from_utf8( reinterpret_cast<char const*>( bytes ), length );
}
auto sbcs_from(
wchar_t const* const s,
int const length,
unsigned const codepage = ::GetACP(),
bool& used_default_char = dummy<bool, 1>()
)
-> string
{
assert( codepage != CP_UTF8 );
if( length == 0 ) { return ""; }
DWORD const flags = WC_NO_BEST_FIT_CHARS; // Must be 0 for UTF-8.
char const* const default_char = "\x7F"; // ASCII DEL
int const buffer_size = WideCharToMultiByte(
codepage,
flags,
s,
length,
nullptr, // buffer
0, // buffer size
default_char,
nullptr
);
hopefully( buffer_size > 0 )
|| fail( "WideCharToMultiByte (1st call)", ::GetLastError() );
string result = string( buffer_size, '#' );
BOOL defaulted = false;
int const n_bytes = WideCharToMultiByte(
codepage,
flags,
s,
length,
&result[0], // buffer
buffer_size,
default_char,
&defaulted
);
hopefully( n_bytes > 0 )
|| fail( "WideCharToMultiByte (2nd call)", ::GetLastError() );
assert( n_bytes <= buffer_size );
result.resize( n_bytes );
used_default_char = !!defaulted;
return result;
}
auto sbcs_from(
wstring const& s,
unsigned const codepage = ::GetACP(),
bool& used_default_char = dummy<bool, 1>()
)
-> string
{
if( s.length() == 0 ) { return ""; }
return sbcs_from( &s[0], s.length(), codepage, used_default_char );
}
} // namespace data
void cpp_main()
{
using cppx::Byte;
using cppx::fail;
using cppx::size;
using std::string;
using std::wstring;
auto const infobox = MB_ICONINFORMATION | MB_SETFOREGROUND;
Byte const utf8_bytes[] = // UTF-8 with BOM, as if from a file.
{
0xEF, 0xBB, 0xBF, 0x42, 0x6C, 0xC3, 0xA5, 0x62, 0xC3, 0xA6,
0x72, 0x73, 0x79, 0x6C, 0x74, 0x65, 0x74, 0xC3, 0xB8, 0x79,
0x21, 0x20, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE5, 0x9B,
0xBD, 0x20, 0xD0, 0xBA, 0xD0, 0xBE, 0xD1, 0x88, 0xD0, 0xBA,
0xD0, 0xB0, 0x21, 0x0D, 0x0A, 0x0D, 0x0A, 0x48, 0x75, 0x6E,
0x67, 0x61, 0x72, 0x69, 0x61, 0x6E, 0x20, 0x61, 0x6C, 0x70,
0x68, 0x61, 0x62, 0x65, 0x74, 0x3A, 0x0D, 0x0A, 0x41, 0x20,
0xC3, 0x81, 0x20, 0x42, 0x20, 0x43, 0x20, 0x43, 0x73, 0x20,
0x44, 0x20, 0x44, 0x7A, 0x20, 0x44, 0x7A, 0x73, 0x20, 0x45,
0x20, 0xC3, 0x89, 0x20, 0x46, 0x20, 0x47, 0x20, 0x47, 0x79,
0x20, 0x48, 0x20, 0x49, 0x20, 0xC3, 0x8D, 0x20, 0x4A, 0x20,
0x4B, 0x20, 0x4C, 0x20, 0x4C, 0x79, 0x20, 0x4D, 0x20, 0x4E,
0x0D, 0x0A, 0x4E, 0x79, 0x20, 0x4F, 0x20, 0xC3, 0x93, 0x20,
0xC3, 0x96, 0x20, 0xC5, 0x90, 0x20, 0x50, 0x20, 0x28, 0x51,
0x29, 0x20, 0x52, 0x20, 0x53, 0x20, 0x53, 0x7A, 0x20, 0x54,
0x20, 0x54, 0x79, 0x20, 0x55, 0x20, 0xC3, 0x9A, 0x20, 0xC3,
0x9C, 0x20, 0xC5, 0xB0, 0x20, 0x56, 0x20, 0x28, 0x57, 0x29,
0x20, 0x28, 0x58, 0x29, 0x20, 0x28, 0x59, 0x29, 0x20, 0x5A,
0x20, 0x5A, 0x73, 0x0D, 0x0A
};
wstring const text = data::utf16_from_utf8( utf8_bytes, size( utf8_bytes ) );
::MessageBox( 0, text.c_str(), L"Original text:", infobox );
string const sbcs_text = data::sbcs_from( text, 1250 );
WORD const hungarian = MAKELANGID(
LANG_HUNGARIAN, SUBLANG_HUNGARIAN_HUNGARY
);
DWORD const hungarian_locale_id = MAKELCID( hungarian, SORT_DEFAULT );
SetThreadLocale( hungarian_locale_id )
|| fail( "SetThreadLocale", ::GetLastError() );
DWORD thread_cp = 0;
::GetLocaleInfo(
::GetThreadLocale(), // Not LOCALE_USER_DEFAULT,
LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
reinterpret_cast<wchar_t*>( &thread_cp ),
sizeof( thread_cp )/sizeof( wchar_t )
)
|| fail( "GetLocaleInfo", ::GetLastError() );
//::MessageBox( 0, std::to_wstring( thread_cp ).c_str(), L"Codepage:", MB_SETFOREGROUND );
assert( thread_cp == 1250 );
::MessageBoxA( 0, sbcs_text.c_str(), "SBCS codepage 1250 text:", infobox );
}
auto main()
-> int
{
using namespace std;
try
{
cpp_main();
return EXIT_SUCCESS;
}
catch( system_error const& x )
{
auto const code = x.code().value();
cerr << "!" << x.what() << " (code: " << code << ")" << endl;
return code;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
return E_FAIL;
}
}
Example output (Windows messageboxes):
And yes, the dummy function is an abomination, unsound and ungood, but hey. :)