AES/CFB8 with OpenSSL - c++

I recently started a small side-project to recreate the Minecraft Server in C++ but I stumbled on a problem.
I need to use the AES/CFB8 Cipher according to this link with continuous updating (not finished and restarted every packet).
I surfed trough the internet to try to find a proper solution but could not figure out one that was actually working or it was using the command line interface.
Any help is welcomed !
Cipher.h
class Cipher {
public:
enum CipherType {
ENCRYPT,
DECRYPT
};
private:
EVP_CIPHER_CTX* ctx;
CipherType type;
bool hasFinished;
public:
Cipher(const char* key, const char* iv, CipherType type);
~Cipher();
int update(char* in, int inl, char* out);
int final(char* out);
int getMinimumBufLength(int size);
};
Cipher.cpp
Cipher::Cipher(const char* key, const char* iv, CipherType type) : type(type), hasFinished(false)
{
ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(ctx);
if(type == CipherType::ENCRYPT)
EVP_EncryptInit_ex(ctx, EVP_aes_256_cfb8(), nullptr, (unsigned char*)key, (unsigned char*)iv);
else
EVP_DecryptInit_ex(ctx, EVP_aes_256_cfb8(), nullptr, (unsigned char*)key, (unsigned char*)iv);
}
Cipher::~Cipher()
{
EVP_CIPHER_CTX_free(ctx);
}
int Cipher::getMinimumBufLength(int size)
{
return type == CipherType::DECRYPT ? size : size + AES_BLOCK_SIZE * 2;
}
int Cipher::update(char* in, int inl, char* out)
{
int len;
EVP_CipherUpdate(ctx, (unsigned char*)out, &len, (unsigned char*)in, inl);
return len;
}
int Cipher::final(char* out)
{
int len;
EVP_EncryptFinal_ex(ctx, (unsigned char*)out, &len);
return len;
}
Cipher-test.cpp
TEST_CASE("Cipher AES/CFB8")
{
char* key = "AAAAAAAAAZEFRGUINJFKDFIVJ";
char* iv = "AUJVFEUB";
Cipher encryptionCipher(key, iv, Cipher::CipherType::ENCRYPT);
Cipher decryptionCipher(key, iv, Cipher::CipherType::DECRYPT);
std::string data = "Hello World ! This is some soon to be encrypted string !";
char encData[encryptionCipher.getMinimumBufLength(data.size() + 1)];
int outLen = encryptionCipher.update(data.data(), data.size() + 1, encData);
int fLen = encryptionCipher.final(encData + outLen);
CHECK(data != std::string(encData));
char decData[decryptionCipher.getMinimumBufLength(outLen + fLen)];
outLen = decryptionCipher.update(encData, outLen + fLen, decData);
decryptionCipher.final(decData + outLen);
CHECK(data == std::string(decData));
}
Thanks in advance
EDIT: I am just wondering if there was a need for supplementary code because of the "continuous" aspect of the Cipher.

Related

Decrypting a file with AES_256_CBC return "bad decrypt" error

This is a follow up question from this one:
OpenSSL EVP_DecryptFinal_ex returns "wrong final block length" error when decrypting a file
I am trying to decrypt a file. At first I was reading it as ASCII file instead of binary. Having this fixed (I hope) and reading it as a binary I always get the an "bad decrypt" error:
15208:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:crypto\evp\evp_enc.c:570:
Here is a sample for how I am encrypting and decrypting:
Encryption:
Cipher cipher;
ifstream f("d:/test.YML");
ofstream out("d:/temp.YML");
byte key[KEY_SIZE] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2};
byte iv[BLOCK_SIZE] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};
secure_string line;
secure_string temp;
while (getline(f, line)) {
cipher.Encrypt(key, iv, line, temp);
out << temp << endl;
}
Decryption:
Cipher cipher;
ifstream f("d:/temp.YML", ifstream::binary);
ofstream out("d:/tempDecrypt.YML");
byte key[KEY_SIZE] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2};
byte iv[BLOCK_SIZE] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};
secure_string temp;
vector<char> buffer(1024, 0);
while (!f.eof()) {
f.read(buffer.data(), buffer.size());
streamsize dataSize = f.gcount();
secure_string chunk = { buffer.begin(), buffer.begin() + dataSize };
cipher.Decrypt(key, iv, chunk, temp);
}
Now I am not sure where start with this investigation:
Is there an encryption problem ? The encrypted file is generated, I am not seeing anything wrong with it.
Is there a problem with how I am reading chunks of file and decrypt them ? Again I don't see the issue here.(the error is on EVP_DecryptFinal_ex)
I also heard there could be a problem with padding. I am not doing anything related to padding, so I am not sure if this is an issue.
I am using same version of OpenSsl, on Windows, I have 2 Visual Studio projects, so there shouldn,t be an issue with imcompatible OpenSsl libraries.
If someone has any pointers pleaase let me know. I never worked with encryption before so some things are hard to understand.
PS: I haven't included the Encrypt and Decrypt methods, they are the same as on the Openssl Wiki website, let me know if I should.
There are a number of issues in your code... to name a few:
ofstream out("d:/temp.YML"); should be opened in binary mode.
out << temp << endl; will damage binary (encrypted) data by adding newlines unnecessarily.
Output buffer should contain enough space to fit (input buffer + block_size).
Encryption/decryption in chunks must follow the update/final pattern. You can't encrypt/decrypt chunks independently.
The IV should be random and should be stored with the ciphertext.
Have a look at the following sample application, which works:
#include <cstdint>
#include <fstream>
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <string.h>
#include <stdio.h>
static const size_t KEY_SIZE = 256 / 8, BLOCK_SIZE = 128 / 8;
class AESBase {
protected:
const uint8_t *key, *iv;
EVP_CIPHER_CTX *ctx;
AESBase(const uint8_t *key, const uint8_t *iv) : key(key), iv(iv) {
if (!(ctx = EVP_CIPHER_CTX_new()))
handleErrors();
}
~AESBase() {
EVP_CIPHER_CTX_free(ctx);
}
static void handleErrors(void) {
ERR_print_errors_fp(stderr);
abort();
}
};
class Encrypt : AESBase {
public:
Encrypt(const uint8_t *key, const uint8_t *iv) : AESBase(key, iv) {
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleErrors();
}
int update(const char *plaintext, int plaintext_len, char *ciphertext) {
int len;
if (1 != EVP_EncryptUpdate(ctx, (uint8_t*)ciphertext, &len, (const uint8_t*)plaintext, plaintext_len))
handleErrors();
return len;
}
int final(char *ciphertext) {
int len;
if (1 != EVP_EncryptFinal_ex(ctx, (uint8_t*)ciphertext, &len))
handleErrors();
return len;
}
};
class Decrypt : AESBase {
public:
Decrypt(const uint8_t *key, const uint8_t *iv) : AESBase(key, iv) {
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleErrors();
}
int update(const char *ciphertext, int ciphertext_len, char *plaintext) {
int len;
if (1 != EVP_DecryptUpdate(ctx, (uint8_t*)plaintext, &len, (const uint8_t*)ciphertext, ciphertext_len))
handleErrors();
return len;
}
int final(char *plaintext) {
int len;
if (1 != EVP_DecryptFinal_ex(ctx, (uint8_t*)plaintext, &len))
handleErrors();
return len;
}
};
void test_encrypt(const uint8_t *key, const char* in, const char* out) {
std::ifstream fin(in, std::ios_base::binary);
std::ofstream fout(out, std::ios_base::binary);
uint8_t iv[BLOCK_SIZE];
RAND_bytes(iv, sizeof(iv));
char buf[1024], temp[sizeof(buf) + BLOCK_SIZE];
Encrypt aes(key, iv);
fout.write((char*)iv, sizeof(iv));
while (fin) {
fin.read(buf, sizeof(buf));
int len = (int)fin.gcount();
if (len <= 0)
break;
len = aes.update(buf, len, temp);
fout.write(temp, len);
}
int len = aes.final(temp);
fout.write(temp, len);
}
void test_decrypt(const uint8_t *key, const char* in, const char* out) {
std::ifstream fin(in, std::ios_base::binary);
std::ofstream fout(out, std::ios_base::binary);
uint8_t iv[BLOCK_SIZE];
fin.read((char*)iv, sizeof(iv));
char buf[1024], temp[sizeof(buf) + BLOCK_SIZE];
Decrypt aes(key, iv);
while (fin) {
fin.read(buf, sizeof(buf));
int len = (int)fin.gcount();
if (len <= 0)
break;
len = aes.update(buf, len, temp);
fout.write(temp, len);
}
int len = aes.final(temp);
fout.write(temp, len);
}
int main()
{
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
uint8_t key[KEY_SIZE] = { 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2 };
test_encrypt(key, "main.cpp", "main.cpp.enc");
test_decrypt(key, "main.cpp.enc", "main.cpp.txt");
}

Decrypt AES in C++ Example

I need some help with decrypt a char array in C++ using AES decrypt with Open SSL library. I already done encryption mode and works fine, but decryption is not working.
This is the Encrypt Function:
string Encrypt(char *Key, char *Msg, int size)
{
static char* Res;
static const char* const lut = "0123456789ABCDEF";
string output;
AES_KEY enc_key;
Res = (char *)malloc(size);
AES_set_encrypt_key((unsigned char *)Key, 128, &enc_key);
for(int vuelta = 0; vuelta <= size; vuelta += 16)
{
AES_ecb_encrypt((unsigned char *)Msg + vuelta, (unsigned char *)Res + vuelta, &enc_key, AES_ENCRYPT);
}
output.reserve(2 * size);
for (size_t i = 0; i < size; ++i)
{
const unsigned char c = Res[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
}
free(Res);
return output;
}
This is the Decrypt Function (not working):
char * Decrypt( char *Key, char *Msg, int size)
{
static char* Res;
AES_KEY dec_key;
Res = ( char * ) malloc( size );
AES_set_decrypt_key(( unsigned char * ) Key, 128, &dec_key);
for(int vuelta= 0; vuelta<=size; vuelta+=16)
{
AES_ecb_encrypt(( unsigned char * ) Msg+vuelta, ( unsigned char * ) Res+vuelta, &dec_key, AES_DECRYPT);
}
return (Res);
}
This is an Example of the Main function that call the methods, the problem is thar no mather how i print the "Res" variable in the Decrypt function, it always show random ASCII values, and i like to show the result in a string like the Encrypt function:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "openSSL/aes.h"
using namespace std;
int main(int argc, char const *argv[])
{
char key[16];
char message[128];
char enc_message[128];
string s_key = "THIS_IS_THE_KEY_";
string s_message = "Hello World !!!";
memset(key, 0, sizeof(key));
strcpy(key, s_key.c_str());
memset(message, 0, sizeof(message));
strcpy(message, s_message.c_str());
string response = Encrypt(key, message, sizeof(message));
cout<<"This is the Encrypted Message: "<<response<<endl;
memset(enc_message, 0, sizeof(enc_message));
strcpy(enc_message, response.c_str());
Decrypt(key, enc_message, sizeof(enc_message));
return 0;
}
Any improve in this methods?
I wanted to put the answer to how I solved it: The problem with my example was that I was trying to use the decrypt function with a HEXADECIMAL STRING and it should be done with an ASCII STRING with the values ​​as delivered by the encryption function.
That is, instead of trying to decrypt a string like this: 461D019896EFA3
It must be decrypted with a string like this: #(%_!#$
After that, the decryption will be delivered in ASCII values. They must be passed to Hexadecimal and finally to a String.
Here is the example that worked for me:
string Decrypt_string(char *Key, string HEX_Message, int size)
{
static const char* const lut = "0123456789ABCDEF";
int i = 0;
char* Res;
AES_KEY dec_key;
string auxString, output, newString;
for(i = 0; i < size; i += 2)
{
string byte = HEX_Message.substr(i, 2);
char chr = (char) (int)strtol(byte.c_str(), NULL, 16);
auxString.push_back(chr);
}
const char *Msg = auxString.c_str();
Res = (char *)malloc(size);
AES_set_decrypt_key((unsigned char *)Key, 128, &dec_key);
for(i = 0; i <= size; i += 16)
{
AES_ecb_encrypt((unsigned char *)Msg + i, (unsigned char *)Res + i, &dec_key, AES_DECRYPT);
}
output.reserve(2 * size);
for (size_t i = 0; i < size; ++i)
{
const unsigned char c = Res[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
}
int len = output.length();
for(int i = 0; i < len; i += 2)
{
string byte = output.substr(i, 2);
char chr = (char) (int)strtol(byte.c_str(), NULL, 16);
newString.push_back(chr);
}
free(Res);
return newString;
}

Is it possibile to find out the length of original data from a AES-CBC cphertext?

I'm using VC++/OPENSSL and AES-256-CBC, and I noticed that since AES is automatic padding by OPENSSL and when I try to decryption , the data I got is with the padding bytes. not just original data.
is there anyway to get length of the original data at decryption so I can cut the padding bytes?
here is my code
void Cryptology::OpenSSLAESDecodeByMapleArray(MapleByteArray& source, MapleByteArray& ba,MapleByteArray &res,bool nosalt)
{
EVP_CIPHER_CTX de;
unsigned int salt[] = { 56756, 352466 };
int i, nrounds = 7;
unsigned char key[32] = { 0 }, iv[32] = { 0 };
if (nosalt)
{
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL, ba.data_ptr(), ba.GetLength(), nrounds, key, iv);
}
else
{
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), (unsigned char *)salt, ba.data_ptr(), ba.GetLength(), nrounds, key, iv);
}
if (i != 32) {
exit(0);
}
EVP_CIPHER_CTX_init(&de);
EVP_DecryptInit_ex(&de, EVP_aes_256_cbc(), NULL, key, iv);
int p_len = source.GetLength(), f_len = 0;
unsigned char *plaintext = (unsigned char *)malloc(p_len + AES_BLOCK_SIZE);
ZeroMemory(plaintext, p_len + AES_BLOCK_SIZE);
EVP_DecryptInit_ex(&de, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(&de, plaintext, &p_len, (unsigned char *)source.data_ptr(), source.GetLength());
EVP_DecryptFinal_ex(&de, plaintext + p_len, &f_len);
int len = p_len + f_len;
//qDebug() << QString::number(len);
EVP_CIPHER_CTX_cleanup(&de);
res.fromByte((BYTE*)plaintext, p_len + AES_BLOCK_SIZE);
ZeroMemory(plaintext, p_len + AES_BLOCK_SIZE);
free(plaintext);
ZeroMemory(key, 32);
ZeroMemory(iv, 32);
return;
}
If padding is enabled - then EVP_DecryptFinal(..) will verify the padding but not return it in the result. Those the decrypted data would be slightly shorter than the encrypted data.
The actual length of decrypted data is returned in the outl variable with each call to EVP_CipherUpdate(..) and EVP_CipherFinal_ex(..)
int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl, unsigned char *in, int inl);
int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm,
int *outl);
In your code, the value of len is properbly the length of your decrypted data.

OpenSSL error "assertion failed"

I have a problem, when i use http://slproweb.com/products/Win32OpenSSL.html , i try to Init Encryption, but always catch error like
"OpenSSL assertion failed, evp_enc.c(282)"
Can somebody help me with this matter?
My code:
bool do_encrypt(const char *in, unsigned char *out, int *outlen, unsigned char *key, unsigned char *iv)
{
int buflen, tmplen;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_rc4(), NULL, key, iv);
if(!EVP_EncryptUpdate(&ctx, out, &buflen, (unsigned char*)in, strlen(in))) // error here
{
return false;
}
if(!EVP_EncryptFinal_ex(&ctx, out + buflen, &tmplen))
{
return false;
}
buflen += tmplen;
*outlen = buflen;
EVP_CIPHER_CTX_cleanup(&ctx);
return true;
}
I use key for test {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, iv is NULL.
Code that uses this function above:
int WINAPI OwnSend(SOCKET s, const char FAR *buff, int len, int flags )
{
if(s == ServerSocket)
{
if(len > 0)
{
int outlen;
unsigned char EncryptBuffer[5500];
do_encrypt(buff, EncryptBuffer, &outlen, KeyTest, NULL);
buff = (const char *) EncryptBuffer;
return pTrampolineSend(s, buff, outlen, flags);
}
}
return pTrampolineSend(s, buff, len, flags);
}
Thanks!
The only assertion in EVP_EncryptUpdate is one that asserts that the buffer length is less than or equal to the block size of the encryption algorithm.
Instead of calling strlen(in) as the size of your input, try looping EVP_EncryptUpdate and each time you go through, make sure to limit the input size with:
int in_size_limit = EVP_CIPHER_CTX_block_size(&ctx);
While looping, be sure to increment the offset of the 2nd and 4th args by the number of bytes you've already encrypted:
if ( EVP_EncryptUpdate(&ctx,
out+encrypted_bytes,
&bytes_encrypted_this_call,
in+encrypted_bytes,
in_size_limit) != 1)
{
/* error */
}
else
{
encrypted_bytes += bytes_encrypted_this_call;
}

HMAC_Init crashes while calculating HMAC-SHA1

Here is my code:
int function(const char * buffer,size_t len,unsigned char * value)
{
char* user = "username";
char*password = "password";
size_t text_len = strlen(user) + strlen(password) + 2;
unsigned char* key = (unsigned char*)calloc(1,16);
unsigned char* text= (unsigned char *)calloc(1,text_len);
snprintf((char*)text, text_len, "%s:%s",user,password );
MD5(text, text_len-1, key)
HMAC_CTX *ctx = NULL;
unsigned int md_len = 20;
ctx = (HMAC_CTX*) calloc(1,sizeof(HMAC_CTX));
if(ctx == NULL){return -1;}
HMAC_CTX_init(ctx);
`HMAC_Init(ctx, key, 16, EVP_sha1());` //crashing everytime, saying heap corruption
HMAC_Update(ctx, buffer, len);
HMAC_Final(ctx, value, &md_len);
HMAC_CTX_cleanup(ctx);
return 0;
}
I am using openssl 0.9.8.c. If anyone faced this problem please let me know.
According to the man page HMAC_Init is deprecated. Might be worth trying HMAC_Init_ex.