Qt with OpenSSL AES 256 CBC Encryption
-
I found the reason for the error I was passing the encryption context instead of the decryption one now the only problem is that it encrypts a small part of the file and when it decrypts it doesn't decrypt it all right so I want to know what I am doing wrong for it to not encrypt all the file and for the decryption to go wrong .
Thanks in advance.
-
Now Igot it to encrypt all the file but the last problem remaining is that some part of the file after decryption gets messed up.
Can you please help me figure that out ?
The text on the left is the decrypted one, you can see the distortion in the file header.
Thanks. -
Hi, I see at least one problem: when you call AES,Encrypt() the returned ciphertext contains more or less random characters, so there's one chance in 256 that one unsigned char is '\0' (0x00 in hex). That means if you are using strlen(ciphertext) to determine how much to write to your file, there's a chance that strlen(ciphertext) will give you the wrong answer, i.e. returning a lower number. One way to get around this problem is to use the length of the ciphertext returned by the AES.Encrypt() call. it's the 3rd argument, instead of relying on that strlen() call.
-
Hi, I see at least one problem: when you call AES,Encrypt() the returned ciphertext contains more or less random characters, so there's one chance in 256 that one unsigned char is '\0' (0x00 in hex). That means if you are using strlen(ciphertext) to determine how much to write to your file, there's a chance that strlen(ciphertext) will give you the wrong answer, i.e. returning a lower number. One way to get around this problem is to use the length of the ciphertext returned by the AES.Encrypt() call. it's the 3rd argument, instead of relying on that strlen() call.
@hskoglund Thanks , yes i noticed that earlier and replaced all strlen() calls with a fixed length and that what fixed the crashes.Now the problem is the random bytes that appears inside the decrypted file and don't know what cause this can you please take a look and tell me what I am doing wrong ?
MainWindow:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QDebug> #include <QMessageBox> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->progressBar->setMinimum(0); ui->progressBar->setMaximum(100); ui->progressBar_2->setMinimum(0); ui->progressBar_2->setMaximum(100); connect(ui->lineEdit,SIGNAL(textEdited(QString)),this,SLOT(Enable_Encryption_Button(QString))); connect(&AES,SIGNAL(Encryption_Finished()),this,SLOT(Enable_Decryption_Button())); connect(&AES,SIGNAL(Encryption_Percentage_Changed(int)),this,SLOT(Change_Encryption_Precentage(int))); connect(&AES,SIGNAL(Decryption_Percentage_Changed(int)),this,SLOT(Change_Decryption_Precentage(int))); connect(&AES,SIGNAL(Encryption_Finished()),this,SLOT(Finish_Encryption())); connect(&AES,SIGNAL(Decryption_Finished()),this,SLOT(Finish_Decryption())); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_pushButton_clicked() { ui->progressBar->reset(); ui->progressBar_2->reset(); QString Extension; fileName = QFileDialog::getOpenFileName(this); if(!fileName.isEmpty()) { size = QFileInfo(fileName).size(); Extension = QFileInfo(fileName).suffix(); ui->label->setText(fileName); ui->label_2->setText(QString::number(size)+" Bytes"); ui->label_3->setText(Extension); ui->lineEdit->setEnabled(true); } } void MainWindow::on_pushButton_2_clicked() { unsigned char* plaintext = (unsigned char*)malloc(size); QString SavedfileName = QFileDialog::getSaveFileName(this, tr("Save Encryption file"), "", tr("All Files (*)")); if(!fileName.isEmpty()) { plaintext = AES.ReadFile(fileName); en = EVP_CIPHER_CTX_new(); de = EVP_CIPHER_CTX_new(); unsigned int salt[] = {12345, 54321}; unsigned char *key_data; int key_data_len; key_data = (unsigned char*)ui->lineEdit->text().constData(); key_data_len = strlen((const char*)key_data); /* Generate key and iv. init the cipher ctx object */ if (AES.Init(key_data, key_data_len, (unsigned char *)&salt, en, de)) { QMessageBox::warning(this, tr("Error while Initializing algorithm!"), tr("Couldn't initialize AES cipher!")); } int len; len = size+1; unsigned char *ciphertext =(unsigned char*)malloc(len+ AES_BLOCK_SIZE); ciphertext = AES.Encrypt(en, plaintext, &len); if (SavedfileName.isEmpty()){ QMessageBox::information(this, tr("Unable to open encryption file!"),tr("Please enter a name to the encryption file.")); return;} else { QFile file(SavedfileName); if (!file.open(QIODevice::WriteOnly)) { QMessageBox::information(this, tr("Unable to open file"), file.errorString()); return; } else{ AES.WriteFile(SavedfileName,ciphertext,size+1+ AES_BLOCK_SIZE); } } free(ciphertext); free(plaintext); EVP_CIPHER_CTX_free(en); } } void MainWindow::on_pushButton_3_clicked() { if(!ui->lineEdit->text().isEmpty()) { unsigned char* plaintext = (unsigned char*)malloc(size+AES_BLOCK_SIZE); QString fileName = QFileDialog::getOpenFileName(this, tr("Open encryption file"), "", tr("All Files (*)")); if(!fileName.isEmpty()) { QString SavedfileName = QFileDialog::getSaveFileName(this, tr("Save Decryption file"), "", tr("All Files (*)")); plaintext = AES.ReadFile(fileName); int len; len = size+AES_BLOCK_SIZE+1; unsigned char *ciphertext =(unsigned char*)malloc(len); ciphertext = AES.Decrypt(de, plaintext, &len); if (SavedfileName.isEmpty()){ QMessageBox::information(this, tr("Unable to open decryption file!"),tr("Please enter a name to the decryption file.")); return;} else { QFile file(SavedfileName); if (!file.open(QIODevice::WriteOnly)) { QMessageBox::information(this, tr("Unable to open file"), file.errorString()); return; } else{ AES.WriteFile(SavedfileName,ciphertext,size+AES_BLOCK_SIZE+1); } } free(ciphertext); free(plaintext); EVP_CIPHER_CTX_free(de); } } } void MainWindow::Change_Encryption_Precentage(int percentage) { ui->progressBar->setValue(percentage); } void MainWindow::Change_Decryption_Precentage(int percentage) { ui->progressBar_2->setValue(percentage); } void MainWindow::Finish_Encryption() { ui->progressBar->setValue(100); } void MainWindow::Finish_Decryption() { ui->progressBar_2->setValue(100); } void MainWindow::Enable_Encryption_Button(QString text) { if(!text.isEmpty()) ui->pushButton_2->setEnabled(true); else ui->pushButton_2->setDisabled(true); } void MainWindow::Enable_Decryption_Button() { if(!ui->lineEdit->text().isEmpty()) ui->pushButton_3->setEnabled(true); }
Encryption Class :
#include "encryption.h" #include <QDebug> #include <QFile> #include <QMessageBox> Encryption::Encryption(QWidget *parent) : QWidget(parent) { } int Encryption::Init(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx) { int i, nrounds = 5; unsigned char key[32], iv[32]; /* * Generate key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material. * nrounds is the number of times we hash the material. More rounds are more secure but * slower. */ i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv); if (i != 32) { QMessageBox::warning(this, tr("Error while generating the key"), tr("The key isn't 256 bit!")); return -1; } EVP_CIPHER_CTX_init(e_ctx); EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv); EVP_CIPHER_CTX_init(d_ctx); EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv); return 0; } unsigned char* Encryption:: Encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len) { // max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE bytes int c_len = *len + AES_BLOCK_SIZE, f_len = 0; unsigned char *ciphertext = (unsigned char*)malloc(c_len); // allows reusing of 'e' for multiple encryption cycles EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* update ciphertext, c_len is filled with the length of ciphertext generated, *len is the size of plaintext in bytes */ int encrypted_bytes = 0; for(int i=0;i<*len/AES_BLOCK_SIZE+1;++i) { EVP_EncryptUpdate(e, ciphertext+encrypted_bytes, &c_len, plaintext+encrypted_bytes, AES_BLOCK_SIZE); encrypted_bytes += AES_BLOCK_SIZE; float counter = i , size = *len/AES_BLOCK_SIZE+1; emit Encryption_Percentage_Changed(counter/size * 100); } // update ciphertext with the final remaining bytes EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len); *len = c_len + f_len; emit Encryption_Finished(); return ciphertext; } unsigned char* Encryption::Decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len) { // plaintext will always be equal to or lesser than length of ciphertext int p_len = *len, f_len = 0; unsigned char *plaintext = (unsigned char*)malloc(p_len); EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL); int decrypted_bytes = 0; for(int i=0;i<*len/AES_BLOCK_SIZE;++i){ EVP_DecryptUpdate(e, plaintext+decrypted_bytes, &p_len, ciphertext+decrypted_bytes, AES_BLOCK_SIZE); decrypted_bytes += AES_BLOCK_SIZE; float counter = i , size = *len/AES_BLOCK_SIZE; emit Decryption_Percentage_Changed(counter/size * 100); } EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len); *len = p_len + f_len; emit Decryption_Finished(); return plaintext; } unsigned char *Encryption::ReadFile(QString fileName) { QByteArray data; QFile file(fileName); if(!file.open(QFile::ReadOnly)) { QMessageBox::warning(this, tr("Error while Opening file to read!"), file.errorString()); return (unsigned char*) (data.constData()); } data = file.readAll(); file.close(); return (unsigned char*) (data.constData()); } void Encryption::WriteFile(QString fileName, unsigned char* data,qint64 size) { QFile file(fileName); if(!file.open(QFile::WriteOnly)) { QMessageBox::warning(this, tr("Error while Opening file to write!"), file.errorString()); return; } file.write((const char*)data,size); file.close(); }
Thanks in advance.
-
Hmm I see you are no longer using a single call to EVP_EncryptUpdate() and EVP_DecryptUpdate(), that means you have to very careful with the # of the returned encrypted unsigned chars, so this:
EVP_EncryptUpdate(e, ciphertext+encrypted_bytes, &c_len, plaintext+encrypted_bytes, AES_BLOCK_SIZE);
is probably wrong because you're using the variable encrypted_bytes to step through both the plaintext and the cipher unsigned chars. Problem is that the number of AES256 encrypted unsigned chars are usually more than the plaintext ones, so using encrypted_bytes will create a bug. The function EVP_EncryptUpdate() returns the number of encrypted unsigned chars in the 3rd argument (c_len), you could try using that for the stepping. -
@hskoglund Thanks for the help I tried the following with no success :
unsigned char* Encryption:: Encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len) { // max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE bytes int c_len = *len + AES_BLOCK_SIZE, f_len = 0; unsigned char *ciphertext = (unsigned char*)malloc(c_len); // allows reusing of 'e' for multiple encryption cycles EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* update ciphertext, c_len is filled with the length of ciphertext generated, *len is the size of plaintext in bytes */ int encrypted_bytes = 0; int Plain_Pos = 0; for(int i=0;i<*len/AES_BLOCK_SIZE+1;++i) { EVP_EncryptUpdate(e, ciphertext+encrypted_bytes, &c_len, plaintext+Plain_Pos, AES_BLOCK_SIZE); encrypted_bytes += c_len; Plain_Pos += AES_BLOCK_SIZE; float counter = i , size = *len/AES_BLOCK_SIZE+1; emit Encryption_Percentage_Changed(counter/size * 100); } // update ciphertext with the final remaining bytes EVP_EncryptFinal_ex(e, ciphertext+encrypted_bytes, &f_len); *len = encrypted_bytes + f_len; qDebug() << *len; emit Encryption_Finished(); return ciphertext; } unsigned char* Encryption::Decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len) { // plaintext will always be equal to or lesser than length of ciphertext int p_len = *len, f_len = 0; unsigned char *plaintext = (unsigned char*)malloc(p_len); EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL); int decrypted_bytes = 0; int Plain_Pos = 0; for(int i=0;i<*len/AES_BLOCK_SIZE;++i){ EVP_DecryptUpdate(e, plaintext+decrypted_bytes, &p_len, ciphertext+Plain_Pos, AES_BLOCK_SIZE); decrypted_bytes += p_len; Plain_Pos += AES_BLOCK_SIZE; float counter = i , size = *len/AES_BLOCK_SIZE; emit Decryption_Percentage_Changed(counter/size * 100); } EVP_DecryptFinal_ex(e, plaintext+decrypted_bytes, &f_len); *len = decrypted_bytes + f_len; qDebug() << *len; emit Decryption_Finished(); return plaintext; }
-
Hi, because you're doing multiple EVP_EncryptUpdate() and EVP_DecryptUpdate() calls, as I said, you have to very careful with the # of bytes.
encrypted_bytes looks to be counting up ok now, but what about the input (the plaintext)?If you look at the for loop in Encryption:: Encrypt:
... for(int i=0;i<*len/AES_BLOCK_SIZE+1;++i) ...
and then at the for loop in Encryption::Decrypt:
... for(int i=0;i<*len/AES_BLOCK_SIZE;++i){ ...
only one of them can be correct (either it's +1 or not). So Encryption::Encrypt will overshoot (encrypt unsigned chars beyond the plaintext) which means that the malloc() call
unsigned char *ciphertext = (unsigned char*)malloc(c_len);
will not allocate enough unsigned chars :-(One way around this problem is to pad the plaintext from the file with extra null bytes so that the length of the plaintext is always a multiple of AES_BLOCK_SIZE and change the first for loop to:
... for(int i=0;i<*len/AES_BLOCK_SIZE;++i) ...
-
@hskoglund I have a question :
Why does being ciphertext bigger than plaintext matter ?
aren't they different only because of the padding and when I am calling EVP_EncryptUpdate with some plaintext bytes they will result in a ciphertext bytes of the same size which are of AES_BLOCK_SIZE size? or am I wrong ? -
@hskoglund I think the problem is indeed in those numbers but I don't know how to tweak them because of the default padding maybe I can fix it by using manual padding like you suggested but I really don't wanna do that this task is for a company I interviewed for and they work in cyber security and I was given this task as a test so I think they would like me to know how to use the default padding.
-
@Mohammedbie said in Qt with OpenSSL AES 256 CBC Encryption:
EVP_EncryptUpdate
You should not use fixed size like you are doing. You should read the file you want to encrypt one block after the other. The block might be at most
AES_BLOCK_SIZE
but could be less because the last one will likely be smaller.