I'm trying to use Ncrypt.lib to encrypt plain text with AES and then decrypt it.
I use Ncrypt.lib because I want to use a persistent symetric key.
My problem is that the decryption works partially. Indeed, I don't have my first 16 bytes decrypted correctly.
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <ncrypt.h>
#include <bcrypt.h>
void PrintBytes(
IN BYTE *pbPrintData,
IN DWORD cbDataLen) {
DWORD dwCount = 0;
for (dwCount = 0; dwCount < cbDataLen; dwCount++) {
printf("0x%02x, ", pbPrintData[dwCount]);
if (0 == (dwCount + 1) % 10) putchar('\n');
}
}
int main() {
BYTE plaintext[] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
static const int plainTextLen = 48;
printf("PlainText:\n");
PrintBytes(plaintext, plainTextLen);
printf("\n");
LPCWSTR keyName = L"NCryptTest";
SECURITY_STATUS status;
NCRYPT_PROV_HANDLE hProvider;
NCRYPT_KEY_HANDLE hKey;
// Open storage provider
status = NCryptOpenStorageProvider(&hProvider, NULL, 0);
// Get stored key
status = NCryptOpenKey(hProvider, &hKey, keyName, 0, 0);
if (status == NTE_BAD_KEYSET) {
// Create key if it doesn't exist
status = NCryptCreatePersistedKey(hProvider, &hKey, BCRYPT_AES_ALGORITHM, keyName, 0, 0);
status = NCryptFinalizeKey(hKey, 0);
}
// Set the chaining mode to cipher feedback
LPCWSTR chainMode = BCRYPT_CHAIN_MODE_CFB;
status = NCryptSetProperty(hKey, NCRYPT_CHAINING_MODE_PROPERTY,
(PBYTE)chainMode, wcslen(chainMode) * 2 + 2, 0);
// Random iv but here, it's fixed
//char* iv = "0123456789abcdef";
//status = NCryptSetProperty(hKey, BCRYPT_INITIALIZATION_VECTOR,
//(PBYTE)iv, 16, 0);
// Get size of the cipher text
DWORD cbCipherText = 0;
status = NCryptEncrypt(hKey, plaintext, plainTextLen, NULL, NULL, 0,
&cbCipherText, 0);
PBYTE pbCipherText = NULL;
pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText);
if (pbCipherText == NULL) {
printf("Error! memory allocation failed\n");
}
// Encrypt
DWORD outlen = -1;
status = NCryptEncrypt(hKey, plaintext, plainTextLen, NULL, pbCipherText,
cbCipherText, &outlen, 0);
printf("CipherText:\n");
PrintBytes(pbCipherText, cbCipherText);
printf("\n");
// Get size of the plain text
DWORD cbPlainText = 0;
status = NCryptDecrypt(hKey, pbCipherText, cbCipherText, NULL, NULL, 0,
&cbPlainText, 0);
PBYTE pbPlainText = NULL;
pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbPlainText);
if (pbPlainText == NULL) {
printf("Error! memory allocation failed\n");
}
// Decrypt
outlen = -1;
status = NCryptDecrypt(hKey, pbCipherText, cbCipherText, NULL,
pbPlainText, cbPlainText, &outlen, 0);
printf("PlainText:\n");
PrintBytes(pbPlainText, cbPlainText);
printf("\n");
// Cleanup
NCryptFreeObject(hKey);
NCryptFreeObject(hProvider);
getchar();
return 0;
}
And the result is :
PlainText:
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
CipherText:
0xc5, 0xdc, 0x7e, 0xde, 0x83, 0x35, 0xbc, 0x34, 0x27, 0x4b,
0xf9, 0xde, 0x40, 0x36, 0xeb, 0x6d, 0xaf, 0x51, 0x8c, 0x48,
0x69, 0xa0, 0x16, 0xfb, 0x6d, 0x80, 0x44, 0xea, 0x5c, 0x74,
0x27, 0x38, 0xf1, 0x20, 0xa3, 0x87, 0x65, 0xc3, 0xcf, 0x62,
0x94, 0x84, 0xc9, 0xcd, 0x55, 0x4c, 0x7b, 0x48,
PlainText:
0x1d, 0x52, 0x88, 0x1b, 0x0c, 0x01, 0x13, 0xed, 0xe0, 0x39,
0x1e, 0x96, 0x67, 0x39, 0x72, 0x38, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
I suspect a initialisation vector problem but I don't know how to use it, simply with BCRYPT_INITIALIZATION_VECTOR ? or I must place the random iv in front of the plain text ?
Thanks for your help.
Answer in comment:
this is because every success call NCryptEncrypt or NCryptDecrypt change state of the hKey. so you can not use the same key. after you encrypt - you need again obtaining key for decrypt – RbMm
Thank you #RbMm !
Currently I'm working on Modbus protocol, which requires a CRC checksum. I implemented three functions, generating different results:
CRC16_table: implemented based on modbus manual above page 114, this's a look-up-table way, but I didn't verify the table provided in the manual.
CRC16_modbus: implemented based on modbus manual page 112, this's a naive way
CRC16_origin: implemented based on this article and have the same result with this calculator at least.
My code is as follow:
#include <iostream>
#include <iomanip>
using namespace std;
static unsigned char auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
};
static char auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};
unsigned short CRC16_table(char* message, int len)
{
unsigned char uchCRCHi = 0xFF;
unsigned char uchCRCLo = 0xFF;
unsigned uIndex;
while(len--)
{
uIndex = uchCRCHi ^ *message++;
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
uchCRCLo = auchCRCLo[uIndex];
}
return (((unsigned short)uchCRCHi << 8) | (unsigned short)uchCRCLo);
}
unsigned short CRC16_origin(char* message, int len)
{
const unsigned short generator = 0xA001;
unsigned short crc = 0;
for(int i = 0; i < len; ++i)
{
crc ^= (unsigned short)(message[i] << 8);
for(int b = 0; b < 8; ++b)
{
if((crc & 0x8000) != 0)
{
crc <<= 1;
crc ^= generator;
}
else
crc <<= 1;
}
}
return crc;
}
unsigned short CRC16_modbus(char* message, int len)
{
const unsigned short generator = 0xA001;
unsigned short crc = 0xFFFF;
for(int i = 0; i < len; ++i)
{
crc ^= (unsigned short)message[i];
for(int b = 0; b < 8; ++b)
{
if((crc & 1) != 0)
{
crc >>= 1;
crc ^= generator;
}
else
crc >>= 1;
}
}
return crc;
}
int main()
{
char message[] = {0x01, 0x06,0x00, 0x63, 0x04, 0x00};
int len = 6;
unsigned short temp1 = CRC16_table(message, len);
unsigned short temp2 = CRC16_origin(message, len);
unsigned short temp3 = CRC16_modbus(message, len);
cout<<hex<<temp1<<endl;
cout<<hex<<temp2<<endl;
cout<<hex<<temp3<<endl;
return 0;
}
Could anyone help me point out which one is correct? Based on what I read, I believe CRC16_origin is most likely right, but I'm still not sure because the other two are from an official manual; I'm stuck on this for almost a week, let me know if you have any advice, thanks!
Modbus uses an initial value of 0xffff.
Your third example uses an initial value of zero, poorly enforced in their illiterate example.
They aren't going to deliver the same results. Stick with the Modbus code.
Of the three approaches:
CRC16_table is correct, but only on a big endian CPU. In the old days a table-based approach would be the fastest approach, but it does not port between processors well.
CRC16_origin uses the wrong seed value. It computes a CRC for something, but not modbus.
CRC16_modbus is correct and portable.
With a modern processor the performance difference between the CRC16_table and CRC16_modbus approaches may not be worth the portability issues. Profile it and find out. On my PC they run neck-and-neck, which say go with the simplest and most portable. On an ARM or MSP430, who can say?
Experimental hack code:
#include <stdint.h>
#include <iostream>
#include <chrono>
uint16_t sizeeffect; // hack global to force execution of loops
static unsigned char auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
};
static unsigned char auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};
unsigned short CRC16_table(char* message, int len)
{
unsigned char uchCRCHi = 0xFF;
unsigned char uchCRCLo = 0xFF;
unsigned uIndex;
while(len--)
{
uIndex = uchCRCHi ^ *message++;
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
uchCRCLo = auchCRCLo[uIndex];
}
return (((unsigned short)uchCRCHi << 8) | (unsigned short)uchCRCLo);
}
// CRC table I'm using for Wintell and PC linux
static const uint16_t lCRCTable[] =
{
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
// CRC table I'm using for Wintell and PC linux
uint16_t CRC16 (const uint8_t *datap, size_t len)
{
uint8_t loc;
uint16_t crc = 0xFFFF;
while (len--)
{
loc = *datap++ ^ crc;
crc >>= 8;
crc ^= lCRCTable[loc];
}
return crc;
}
unsigned short CRC16_modbus(const char* message, int len)
{
const unsigned short generator = 0xA001;
unsigned short crc = 0xFFFF;
for(int i = 0; i < len; ++i)
{
crc ^= (unsigned short)message[i];
for(int b = 0; b < 8; ++b)
{
if((crc & 1) != 0)
{
crc >>= 1;
crc ^= generator;
}
else
crc >>= 1;
}
}
return crc;
}
int main()
{
char message[] = "I am the very model of a modern major general.";
uint16_t a = CRC16((uint8_t *)message, sizeof(message));
uint16_t b = CRC16_modbus(message, sizeof(message));
uint16_t c = CRC16_table(message, sizeof(message));
if (a == b)
{
std::cout << "A-B Match"<< std::endl;
}
else
{
std::cout << a << std::endl;
std::cout << b << std::endl;
}
if (a == c)
{
std::cout << "A-C Match"<< std::endl;
}
else
{
std::cout << a << std::endl;
std::cout << c << std::endl;
}
int count = 10000000;
std::chrono::time_point<std::chrono::high_resolution_clock> start, end;
start = std::chrono::high_resolution_clock::now();
while (count--)
{
sizeeffect = CRC16((uint8_t *)message, sizeof(message));
}
end = std::chrono::high_resolution_clock::now();
std::cout << "Table took " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count() << "\n";
count = 10000000;
start = std::chrono::high_resolution_clock::now();
while (count--)
{
sizeeffect = CRC16((uint8_t *)message, sizeof(message));
}
end = std::chrono::high_resolution_clock::now();
std::cout << "Calc took " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count() << "\n";
}
Typical output:
A-B Match
5874
61974
Table took 900167
Calc took 892935
Besides other answer, I should use unsigned char* as the first parameter instead of char* in my functions because when I convert it to unsigned short, if it's char * and MSB is 1, it will add ff for the high byte, which is obviously wrong!
Thanks everyone here! Solved!
#pragma once
#include <string>
#include <vector>
#include <stdint.h>
class PasswordCrypt
{
public:
PasswordCrypt(std::vector<uint8_t> buffer);
~PasswordCrypt(void);
void PassCrypto(std::vector<uint8_t> buffer);
const std::vector<uint8_t>& Encrypt(const std::vector<uint8_t>& buffer);
const std::vector<uint8_t>& Decrypt(std::vector<uint8_t>& buffer);
private:
uint8_t* key;
};
--------------------------------------------------------------------------------------
#include "PasswordCrypt.h"
PasswordCrypt::PasswordCrypt(std::vector<uint8_t> buffer)
{
this->key = new uint8_t[200];
int sum = 0;
for (int i = 0 ; i< buffer.size() ;i++)
sum += buffer[i];
srand(sum);
uint8_t hash[0x10];
for (int i = 0; i < 0x10; i++)
hash[i] =(uint8_t)rand();
for (int i = 1; i < 0x100; i++)
{
key[i * 2] = (uint8_t)i;
key[(i * 2) + 1] = (uint8_t)(i ^ hash[i & 0x0F]);
}
for (int i = 1; i < 0x100; i++)
for (int j = 1 + i; j < 0x100; j++)
if (key[(i * 2) + 1] < key[(j * 2) + 1])
{
key[i * 2] ^= key[j * 2];
key[j * 2] ^= key[i * 2];
key[i * 2] ^= key[j * 2];
key[(i * 2) + 1] ^= key[(j * 2) + 1];
key[(j * 2) + 1] ^= key[(i * 2) + 1];
key[(i * 2) + 1] ^= key[(j * 2) + 1];
}
}
PasswordCrypt::~PasswordCrypt(void)
{
delete[] this->key;
}
const uint8_t scanCodeToVirtualKeyMap[] =
{
0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0xBD, 0xBB, 0x08, 0x09,
0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0xDB, 0xDD, 0x0D, 0x11, 0x41, 0x53,
0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0xBA, 0xC0, 0xDF, 0x10, 0xDE, 0x5A, 0x58, 0x43, 0x56,
0x42, 0x4E, 0x4D, 0xBC, 0xBE, 0xBF, 0x10, 0x6A, 0x12, 0x20, 0x14, 0x70, 0x71, 0x72, 0x73, 0x74,
0x75, 0x76, 0x77, 0x78, 0x79, 0x90, 0x91, 0x24, 0x26, 0x21, 0x6D, 0x25, 0x0C, 0x27, 0x6B, 0x23,
0x28, 0x22, 0x2D, 0x2E, 0x2C, 0x00, 0xDC, 0x7A, 0x7B, 0x0C, 0xEE, 0xF1, 0xEA, 0xF9, 0xF5, 0xF3,
0x00, 0x00, 0xFB, 0x2F, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0xED,
0x00, 0xE9, 0x00, 0xC1, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x09, 0x00, 0xC2, 0x00,
};
const uint8_t virtualKeyToScanCodeMap[] =
{
0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x0F, 0x00, 0x00, 0x4C, 0x1C, 0x00, 0x00,
0x2A, 0x1D, 0x38, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x39, 0x49, 0x51, 0x4F, 0x47, 0x4B, 0x48, 0x4D, 0x50, 0x00, 0x00, 0x00, 0x54, 0x52, 0x53, 0x63,
0x0B, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18,
0x19, 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x5B, 0x5C, 0x5D, 0x00, 0x5F,
0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, 0x48, 0x49, 0x37, 0x4E, 0x00, 0x4A, 0x53, 0x35,
0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x45, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2A, 0x36, 0x1D, 0x1D, 0x38, 0x38, 0x6A, 0x69, 0x67, 0x68, 0x65, 0x66, 0x32, 0x20, 0x2E, 0x30,
0x19, 0x10, 0x24, 0x22, 0x6C, 0x6D, 0x6B, 0x21, 0x00, 0x00, 0x27, 0x0D, 0x33, 0x0C, 0x34, 0x35,
0x28, 0x73, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x56, 0x1B, 0x2B, 0x29,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x5C, 0x7B, 0x00, 0x6F, 0x5A, 0x00,
0x00, 0x5B, 0x00, 0x5F, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00
};
const std::vector<uint8_t>& PasswordCrypt::Encrypt(const std::vector<uint8_t>& buffer)
{
std::vector<uint8_t> result(buffer.size());
for (int i = 0; i < buffer.size(); i++)
{
bool upper = false;
if (buffer[i] == 0) break;
else
{
uint8_t b =this->key[buffer[i] * 2];
if (b > 0x80)
{
b = (uint8_t)(this->key[buffer[i] * 2] - 0x80);
upper = true;
}
result[i] += scanCodeToVirtualKeyMap[b];
}
if (!upper && result[i] >= 'A' && result[i] <= 'Z') result[i] += 'a' - 'A';
}
return result;
}
const std::vector<uint8_t>& PasswordCrypt::Decrypt(std::vector<uint8_t>& buffer)
{
std::vector<uint8_t> result(buffer.size(), 0);
for (int j = 0; j < buffer.size(); j++)
{
uint8_t c = buffer[j];
if (buffer[j] >= 'a' && buffer[j] <= 'z')
buffer[j] -= 'a' - 'A';
uint8_t d = virtualKeyToScanCodeMap[buffer[j]];
if (c >= 'A' && c <= 'Z')
d += 0x80;
for (uint8_t i = 0; i <= 255; i++)
{
uint8_t b = (uint8_t)this->key[i * 2];
if (b == d)
{
result[j] = i;
break;
}
}
}
return result;
}
---------------------------------------------------------------------------------------
#include <iostream>
#include <Windows.h>
#include <string>
#include <stdint.h>
#include "PasswordCrypt.h"
#include <iomanip>
using namespace std;
void output_hex(std::ostream& out, std::vector<uint8_t>& data) {
for (std::vector<uint8_t>::iterator i=data.begin();i<data.end();i++)
out << std::hex
<< std::setw(2)
<< std::setfill('0')
<< static_cast<int>(data[*i])
<< " ";
out << endl;
}
int main()
{
std::string test="Hellow World!";
std::vector<uint8_t> buf(test.begin(), test.end());
PasswordCrypt dec(buf);
//output_hex(cout, buf);
std::vector<uint8_t> enced = dec.Encrypt(buf);
//output_hex(cout, enced);
std::vector<uint8_t> deced = dec.Decrypt(enced);
//output_hex(cout, deced);
system("pause");
return 0;
}
Error:
Windows has triggered a breakpoint in
testcrypto.exe.
This may be due to a corruption of the
heap, which indicates a bug in
testcrypto.exe or any of the DLLs it
has loaded.
This may also be due to the user
pressing F12 while testcrypto.exe has
focus.
You have a classic buffer overrun. Here you allocate 200 bytes for key:
this->key = new uint8_t[200];
Here, however:
for (int i = 1; i < 0x100; i++)
{
key[i * 2] = (uint8_t)i;
You (attempt to) write to k[2] through key[2 * 0x100]. 2 * 0x100 is 0x200, which is 512 in decimal. It would appear that where you allocate the buffer, you should really allocate 0x200 elements.
Some of the other code looks like it tries to access key[0x200] -- to make that work, you'd want/need to allocate 0x201 elements (0x200 elements will run from key[0] through key[0x1ff] inclusive).
Edit: doing a bit more looking, it gets even worse. Perhaps putting these three lines next to each other will make the next problem more obvious:
const std::vector<uint8_t>& PasswordCrypt::Encrypt(const std::vector<uint8_t>& buffer)
{
std::vector<uint8_t> result(buffer.size());
[ ...]
return result;
You're returning a reference to a local variable, so the caller receives a dangling reference.
j < 0x100;
0x100 is not the same as 100. It's 256. And your key array is only 200 elements long.
You then go off and try to access key[j * 2], which is way off the end of the array, trampling all over random bits of memory. That's your problem.
Your other problem is using xor-swapping instead of something more readable - while you might think it's a "neat trick", it really isn't. It makes things much less legible and doesn't provide any meaningful performance benefit.