I'm trying to implement AES using CBC for my application. However the data I'm getting out is different every time I run it. I have been trying to identify the problems using a debugger, but I don't really understand what exactly I'm looking for.
My implementation looks like;
#define AES_KEYLENGTH 256
void NetworkHandler::SendEncryptedPacket(std::vector<std::uint8_t>& data)
{
std::string keyString = "AC3CF1B84D7C946640447DE9670E18BE8A45F49A286FC4D8404DD729491064E4";
std::string ivString = "5D4FB5A040DE76B316794BAC89FC3A48";
printf("noncrypted data: %s\n", data.data());
size_t inputLen = data.size();
size_t encLen = ((inputLen + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
std::vector<std::uint8_t> encOut(encLen);
// Zero out memory for iv & key
std::uint8_t iv[AES_BLOCK_SIZE];
std::uint8_t key[AES_KEYLENGTH];
memset(iv, 0x00, AES_BLOCK_SIZE);
memset(key, 0x00, AES_KEYLENGTH);
std::copy(keyString.begin(), keyString.end(), key);
std::copy(ivString.begin(), ivString.end(), iv);
AES_KEY encKey;
AES_set_encrypt_key(key, AES_KEYLENGTH, &encKey);
AES_cbc_encrypt(data.data(), encOut.data(), encLen, &encKey, iv, AES_ENCRYPT);
printf("encrypted data: %s\n", encOut.data());
SendPacket(encOut);
}
I initially tried to follow the an implemented example found here; AES 256-cbc encryption C++ using OpenSSL.
But looks like I came short.
I came up with this solution.
I'm not sure if it's 100% correct, but I'm getting the same ciphertext each time.
std::vector<std::uint8_t> HexToBytes(const std::string& hexString) {
std::vector<std::uint8_t> byteArray;
for (std::size_t i = 0; i < hexString.size(); i += 2) {
std::string hexByte = hexString.substr(i, 2);
std::uint8_t byte = std::stoi(hexByte, nullptr, 16);
byteArray.push_back(byte);
}
return byteArray;
}
#define AES_KEYLENGTH 256
void NetworkHandler::SendEncryptedPacket(std::vector<std::uint8_t>& data)
{
std::string keyString = "FE7F64F9B5592EDFC84CA5B07DE0901F0671EDB6105FDD5D7C5006C2C10F4ADB";
std::string ivString = "95E060482AD77FB9714DF74150753A37";
printf("noncrypted data: %s\n", data.data());
std::vector<std::uint8_t> encOut(data.size());
auto keyBytes = HexToBytes(keyString);
auto ivBytes = HexToBytes(ivString);
// Zero out memory for iv & key
std::uint8_t iv[AES_BLOCK_SIZE];
std::uint8_t key[AES_KEYLENGTH];
memset(iv, 0x00, AES_BLOCK_SIZE);
memset(key, 0x00, AES_KEYLENGTH);
std::copy(keyBytes.begin(), keyBytes.end(), key);
std::copy(ivBytes.begin(), ivBytes.end(), iv);
AES_KEY encKey;
AES_set_encrypt_key(key, AES_KEYLENGTH, &encKey);
AES_cbc_encrypt(data.data(), encOut.data(), data.size(), &encKey, iv, AES_ENCRYPT);
printf("encrypted data: %s\n", encOut.data());
SendPacket(encOut);
}
Related
I've been working on a project recently that should connect to a server with the help of a protocol. So far so good, but when I combed to decrypt the packages, I quickly noticed that something is not working properly.
The first 16 bytes of all packets are decrypted incorrectly. I have tried it with different libraries but that does not work either. I work in the C++ language and have so far used Crypto++ and OpenSSL for decryption, without success.
Under this Link you can find the protocol, here the decryption protocol Link and here is my corresponding code:
OpenSSL:
void init() {
unsigned char* sharedSecret = new unsigned char[AES_BLOCK_SIZE];
std::generate(sharedSecret,
sharedSecret + AES_BLOCK_SIZE,
std::bind(&RandomGenerator::GetInt, &m_RNG, 0, 255));
for (int i = 0; i < 16; i++) {
sharedSecretKey += sharedSecret[i];
}
// Initialize AES encryption and decryption
if (!(m_EncryptCTX = EVP_CIPHER_CTX_new()))
std::cout << "123" << std::endl;
if (!(EVP_EncryptInit_ex(m_EncryptCTX, EVP_aes_128_cfb8(), nullptr, (unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str())))
std::cout << "123" << std::endl;
if (!(m_DecryptCTX = EVP_CIPHER_CTX_new()))
std::cout << "123" << std::endl;
if (!(EVP_DecryptInit_ex(m_DecryptCTX, EVP_aes_128_cfb8(), nullptr, (unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str())))
std::cout << "123" << std::endl;
m_BlockSize = EVP_CIPHER_block_size(EVP_aes_128_cfb8());
}
std::string result;
int size = 0;
result.resize(1000);
EVP_DecryptUpdate(m_DecryptCTX, &((unsigned char*)result.c_str())[0], &size, &sendString[0], data.size());
Crypto++:
CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption AESDecryptor((byte*)sharedSecret.c_str(), (unsigned int)16, sharedSecret.c_str(), 1);
std::string sTarget("");
CryptoPP::StringSource ss(data, true, new CryptoPP::StreamTransformationFilter(AESDecryptor, new CryptoPP::StringSink(sTarget)));
I think important to mention is that I use one and the same shared secret for the key and the iv (initialization vector). In other posts, this was often labeled as a problem. I do not know how to fix it in this case because the protocol want it.
I would be looking forward to a constructive feedback.
EVP_EncryptInit_ex(m_EncryptCTX, EVP_aes_128_cfb8(), nullptr,
(unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str()))
And:
CFB_Mode<AES>::Decryption AESDecryptor((byte*)sharedSecret.c_str(),
(unsigned int)16, sharedSecret.c_str(), 1);
std::string sTarget("");
StringSource ss(data, true, new StreamTransformationFilter(AESDecryptor, new StringSink(sTarget)));
It is not readily apparent, but you need to set feedback size for the mode of operation of the block cipher in Crypto++. The Crypto++ feedback size is 128 by default.
The code to set the feedback size of CFB mode can be found at CFB Mode on the Crypto++ wiki. You want the 3rd or 4th example down the page.
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(iv));
That is kind of an awkward way to pass parameters. It is documented in the sources files and on the wiki at NameValuePairs. It allows you to pass arbitrary parameters through consistent interfaces. It is powerful once you acquire a taste for it.
And then use params to key the encryptor and decryptor:
CFB_Mode< AES >::Encryption enc;
enc.SetKey( key, key.size(), params );
// CFB mode must not use padding. Specifying
// a scheme will result in an exception
StringSource ss1( plain, true,
new StreamTransformationFilter( enc,
new StringSink( cipher )
) // StreamTransformationFilter
); // StringSource
I believe your calls would look something like this (if I am parsing the OpenSSL correctly):
const byte* ptr = reinterpret_cast<const byte*>(sharedSecret.c_str());
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(ptr, 16));
CFB_Mode< AES >::Encryption enc;
enc.SetKey( ptr, 16, params );
In your production code you should use unique key and iv. So do something like this using HKDF:
std::string seed(AES_BLOCK_SIZE, '0');
std::generate(seed, seed + AES_BLOCK_SIZE,
std::bind(&RandomGenerator::GetInt, &m_RNG, 0, 255));
SecByteBlock sharedSecret(32);
const byte usage[] = "Key and IV v1";
HKDF<SHA256> hkdf;
hkdf.DeriveKey(sharedSecret, 32, &seed[0], 16, usage, COUNTOF(usage), nullptr, 0);
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(sharedSecret+16, 16));
CFB_Mode< AES >::Encryption enc;
enc.SetKey(sharedSecret+0, 0, params);
In the code above, sharedSecret is twice as large as it needs to be. You derive the key and iv from the seed using HDKF. sharedSecret+0 is the 16-byte key, and sharedSecret+16 is the 16-byte iv.
I want to use the openssl library in C++ to decrypt data.
I have the key and IV and the encoded base64 string.
I couldn't make it through the documentation, all decryption methods in the header file (openssl/des.h) take 3 keys.
I've managed to achieve the result with the following python code.
from pyDes import *
import base64
key = base64.b64decode("****")
iv = base64.b64decode("***")
enc = base64.b64decode("******")
encryptor = triple_des(key, CBC, iv)
plain = encryptor.decrypt(enc)
print(plain.decode("utf-8"))
I want to get the same result using C++ code and OpenSSL library.
3DES uses three keys. The python function you are using probably derives three keys from the key argument you pass, probably splitting it in three parts.
To use the OpenSSL function, you have to generate 3 keys with 8 bytes each (or a 24 bytes key split in 3).
I adapted the code I found here to use ECB instead of CBC. But, for security reasons, you should consider using CBC, or even AES encryption instead of 3DES.
The example only shows how to use the DES_ecb3_encrypt function with hard coded keys. In a final solution, you have to generate your own keys, using a good RNG.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>
/* Triple DES key for Encryption and Decryption */
DES_cblock Key1 = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 };
DES_cblock Key2 = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 };
DES_cblock Key3 = { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33 };
DES_key_schedule SchKey1,SchKey2,SchKey3;
/* Print Encrypted and Decrypted data packets */
void print_data(const char *tittle, const void* data, int len);
int main()
{
/* Input data to encrypt */
DES_cblock input_data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8};
/* Check for Weak key generation */
if ( -2 == (DES_set_key_checked(&Key1, &SchKey1) || DES_set_key_checked(&Key2, &SchKey2) || DES_set_key_checked(&Key3, &SchKey3)))
{
printf(" Weak key ....\n");
return 1;
}
/* Buffers for Encryption and Decryption */
DES_cblock cipher;
DES_cblock text;
/* Triple-DES ECB Encryption */
DES_ecb3_encrypt(&input_data, &cipher, &SchKey1, &SchKey2, &SchKey3, DES_ENCRYPT);
/* Triple-DES ECB Decryption */
DES_ecb3_encrypt(&cipher, &text, &SchKey1, &SchKey2, &SchKey3, DES_DECRYPT);
/* Printing and Verifying */
print_data("\n Original ", (const void*) input_data, sizeof(input_data));
print_data("\n Encrypted", (const void*) cipher, sizeof(input_data));
print_data("\n Decrypted", (const void*) text, sizeof(input_data));
return 0;
}
void print_data(const char *tittle, const void* data, int len)
{
printf("%s : ",tittle);
const unsigned char * p = (const unsigned char*)data;
int i = 0;
for (; i<len;++i)
printf("%02X ", *p++);
printf("\n");
}
OpenSSL reference:
void DES_ecb3_encrypt(const_DES_cblock *input, DES_cblock *output,
DES_key_schedule *ks1, DES_key_schedule *ks2,
DES_key_schedule *ks3, int enc);
DES_ecb3_encrypt() encrypts/decrypts the input block by using three-key Triple-DES encryption in ECB mode. This involves encrypting the input with ks1, decrypting with the key schedule ks2, and then encrypting with ks3. This routine greatly reduces the chances of brute force breaking of DES and has the advantage of if ks1, ks2 and ks3 are the same, it is equivalent to just encryption using ECB mode and ks1 as the key.
I'm trying to encrypt in DES a text with dynamic length with a 16 bytes key, but there is a problem with the block size of the key and text, i'm using openssl library for DES encryption. How can I use keys with 16 bytes of length.
Here my example:
char * Encrypt( char Key, char *Msg, int size) {
static char* Res;
DES_cblock Key2;
DES_key_schedule schedule;
Res = ( char * ) malloc( size );
memcpy(Key2, Key, 8);
DES_set_odd_parity( &Key2 );
DES_set_key_checked( &Key2, &schedule );
unsigned char buf[9];
buf[8] = 0;
DES_ecb_encrypt(( DES_cblock ) &Msg, ( DES_cblock ) &buf, &schedule, DES_ENCRYPT );
memcpy(Res, buf, sizeof(buf));
return (Res);
}
int main(int argc, char const *argv[]) {
char key[] = "password";
char clear[] = "This is a secret message";
char *encrypted;
encrypted = (char *) malloc(sizeof(clear));
printf("Clear text\t : %s \n",clear);
memcpy(encrypted, Encrypt(key, clear, sizeof(clear)), sizeof(clear));
printf("Encrypted text\t : %s \n",encrypted);
return 0;
}
DES has a 8-byte 56-bit key (the LSB is not used as part of the key, it is for parity) so you can't use a 16-byte key (parity is generally ignored).
Don't use DES, it is not secure and has been replaced with AES.
Don't use ECB mode, it is insecure, see ECB mode, scroll down to the Penguin.
AES allows 128, 192 and 256 bit keys.
How can I encrypt a byte array with Crypto++'s RSA implementation? I already found an example for strings. But I can't find a good example how to do the same for a byte array.
This is my first attempt:
//dataSize: Size of data that is going to be send
//dataToSend Bytes to send to the user
//seedPool is an AutoSeededRandomPool
CryptoPP::RSAES_OAEP_SHA_Encryptor encryptor(publicKey);
int size = 64000;
byte * cipher = new byte(size);
CryptoPP::ArraySink* test = new CryptoPP::ArraySink(cipher, size);
CryptoPP::ArraySource as((byte*)dataToSend, dataSize, true, new CryptoPP::PK_EncryptorFilter(seedPool, encryptor, test));
int newDataSize = test->TotalPutLength();
unsigned int bytesSend = ::send(socketLink, (char *)(cipher), (int)newDataSize, 0);
delete[] cipher;
This doesn't work. TotalPutLength will always return 0 but there is data put in cipher.
What is a safe way to implement this? I don't want to be vulnerable for buffer overflows or any other attack.
byte * cipher = new byte(size);
I believe this should be:
byte * cipher = new byte[size];
Otherwise, I think you get one byte initialized to 6400 (which is truncated to 0x00).
CryptoPP::ArraySink * test = new CryptoPP::ArraySink(cipher, size);
This is kind of different. You can stay out of the memory manager if you'd like:
CryptoPP::ArraySink test(cipher, size);
int newDataSize = test->TotalPutLength();
I've never used TotalPutLength, and I did not see it documented on a BufferedTransformation or Sink. So I don't really have any advice on what its returning.
TotalPutLength is OK to use. An ArraySink could return the wrong value if the sink was full. It would happen if the array was fixed and too small for all the data. We cleared that issue at Crypto++ 5.6.3 or 5.6.4.
If you want to count the number of bytes processed (even if the sink cannot store they bytes), then you can also use a MeterFilter:
byte data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
string encoded;
MeterFilter meter( new StringSink( encoded ) );
ArraySource( data, sizeof( data ), true,
new HexEncoder(
new Redirector( meter ),
true /*UCase*/, 2 /*Group*/,
" " /*Separator*/
)
);
cout << "processed " << meter.GetTotalBytes() << " bytes" << endl;
cout << encoded << endl;
Output:
Processed 23 bytes
00 01 02 03 04 05 06 07
How can you encrypt a byte array with Cryptopp RSA implementation
Now we're talking ;) Try this from the Crypto++ wiki on RSA Encryption.
////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize( rng, 1536 );
RSA::PrivateKey privateKey( params );
RSA::PublicKey publicKey( params );
string plain="RSA Encryption", cipher, recovered;
////////////////////////////////////////////////
// Encryption
RSAES_OAEP_SHA_Encryptor e( publicKey );
StringSource ss1( plain, true,
new PK_EncryptorFilter( rng, e,
new StringSink( cipher )
) // PK_EncryptorFilter
); // StringSource
////////////////////////////////////////////////
// Decryption
RSAES_OAEP_SHA_Decryptor d( privateKey );
StringSource ss2( cipher, true,
new PK_DecryptorFilter( rng, d,
new StringSink( recovered )
) // PK_DecryptorFilter
); // StringSource
assert( plain == recovered );
I'm trying to send some data to a device using serial comunication:
void VcpBridge::write_speed(char address, int spd) {
uint8_t speed = (uint8_t)(127);
ROS_ERROR("VCP BRIDGE: Sending %u to %u", speed, address);
char msg[8];
char command = 0x55, size = 0x02, csum;
csum = speed + 0x64 + address;
sprintf(msg, "%c%c%c%c%c%c", command, address, speed, size, 0x64, csum);
ROS_ERROR(msg);
write(fd_, msg, 6);
}
ROS_ERROR here does the same as printf.
Everything works fine except when the value of speed is over 127. Then it always prints a ? in it's position and the device doesn't recive the right info. Do you know any way to cast it correctly? I've tried %u but then the program crashes.
There is no good reason to use sprintf in your example. Try this:
void VcpBridge::write_speed(char address, int spd) {
uint8_t speed = (uint8_t)(127);
ROS_ERROR("VCP BRIDGE: Sending %u to %u", speed, address);
char command = 0x55, size = 0x02, csum;
csum = speed + 0x64 + address;
ROS_ERROR(msg);
char msg[] = { command, address, speed, size, 0x64, csum};
write(fd_, msg, sizeof msg);
}
Thanks to your answer I could figure out hot to fix the problem. Not using sprintf and using unsigned int was the kay.There's the final code:
void VcpBridge::write_speed(char address,int spd){
uint8_t speed = (uint8_t)(200);
unsigned char command = 0x55, size=0x02, csum;
csum=speed+0x64+address;
unsigned char msg[8]= { command, address, speed, size, 0x64, csum };
write( fd_, msg, 6);
}