OpenSSL decrypted text length - c++

I am using this simple function for decrypting a AES Encrypted string
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
{
int p_len = *len, f_len = 0;
unsigned char *plaintext = (unsigned char*)malloc(p_len + 128);
memset(plaintext,0,p_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
*len = p_len + f_len;
return plaintext;
}
The problem is that len is returning a value that does not match the entire decoded string. What could be the problem ?

When you say "string", I assume you mean a zero-terminated textual string. The encryption process is dependent on a cipher block size, and oftentimes padding. What's actually being encoded and decoded is up to the application... it's all binary data to the cipher. If you're textual string is smaller than what's returned from the decrypt process, your application needs to determine the useful part. So for example if you KNOW your string inside the results is zero-terminated, you can get the length doing a simple strlen. That's risky of course if you can't guarantee the input... probably better off searching the results for a null up to the decoded length...

If you are using cipher in ECB, CBC or some other chaining modes, you must pad plain text to the length, which is multiple of cipher block length. You can see a PKCS#5 standard for example. High-level functions like in OpenSSL can perform padding transparently for programmer. So, encrypted text can be larger than plain text up to additional cipher block size.

Related

OpenSSL AES_CBC-256 decrypting without original text length in C++

I have this code that I found on SO and it works. My problem is that encryption and decryption are in the same file. Naturally, I want to separate them into two functions. The problem is the decoder needs the original input length. Isn't it a security vulnerability? How can I decrpyt without knowing the original length of the input?
/* computes the ciphertext from plaintext and key using AES256-CBC algorithm */
string cipher_AES(string key, string message)
{
size_t inputslength = message.length();
unsigned char aes_input[inputslength];
unsigned char aes_key[AES_KEYLENGTH];
memset(aes_input, 0, inputslength/8);
memset(aes_key, 0, AES_KEYLENGTH/8);
strcpy((char*) aes_input, message.c_str());
strcpy((char*) aes_key, key.c_str());
/* init vector */
unsigned char iv[AES_BLOCK_SIZE];
memset(iv, 0x00, AES_BLOCK_SIZE);
// buffers for encryption and decryption
const size_t encslength = ((inputslength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
unsigned char enc_out[encslength];
unsigned char dec_out[inputslength];
memset(enc_out, 0, sizeof(enc_out));
memset(dec_out, 0, sizeof(dec_out));
AES_KEY enc_key, dec_key;
AES_set_encrypt_key(aes_key, AES_KEYLENGTH, &enc_key);
AES_cbc_encrypt(aes_input, enc_out, inputslength, &enc_key, iv, AES_ENCRYPT);
AES_set_decrypt_key(aes_key, AES_KEYLENGTH, &dec_key);
AES_cbc_encrypt(enc_out, dec_out, encslength, &dec_key, iv, AES_DECRYPT);
stringstream ss;
for(int i = 0; i < encslength; i++)
{
ss << enc_out[i];
}
return ss.str(););
}
First of all, AES encryption takes place 1-to-1 in blocks of 128 bits, so you already know the message size with a 16-byte accuracy by just looking at the ciphertext.
Then, for the last block you just need to determine where the message ends. The standard solution for that is to use padding (e.g. PKCS#7). Or just store the message length at the beginning and encrypt it together with the message.
You can of course continue using OpenSSL AES API, and implement padding or some other mechanism yourself. But OpenSSL already has higher-level API (EVP), which does AES, CBC and PKCS padding automatically.
See EVP Symmetric Encryption and Decryption official OpenSSL wiki page for an example of using the EVP API.
Unrelated notes:
a fixed IV (especially zero IV) is insecure. Consider generating a random IV and storing it together with the ciphertext (e.g. using RAND_bytes).
check out also AES GCM mode for authenticated encryption (encryption + secure checksum), this way the encrypted message additionally becomes tamper-proof. See this example.

OpenSSL in C: after second decryption in application run, the first 16 bytes of result are garbage

I implemented simple file encryption/decryption with OpenSSL in C according to the instructions here. I do not need this to be truly secure (just want the files to not be readily readable on drive), the keys are hardcoded in the application and after reading the encrypted files from drive I decrypt them.
On first call, the decryptFileAsBytes function returns the correct decrypted file as byte vector. On the second call (within the same application run) the first 16 bytes of the result are garbage and the rest is correct. Does this have something to do with the size of the key (128 bits) I am using?
static bool decryptFileAsBytes(std::string filename, unsigned char *ckey, unsigned char *ivec, std::vector<unsigned char> &fileBytes)
{
std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
if (ifs.fail())
return false;
std::ifstream::pos_type pos = ifs.tellg();
fileBytes.resize(pos);
ifs.close();
FILE *ifp;
if (fopen_s(&ifp, filename.c_str(), "rb") != NULL)
return false;
int bytesRead;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char *writePtr = fileBytes.data();
/* data structure that contains the key itself */
AES_KEY key;
/* set the encryption key */
AES_set_encrypt_key(ckey, 128, &key);
/* set where on the 128 bit encrypted block to begin encryption*/
int num = 0;
while (1)
{
bytesRead = fread(indata, 1, AES_BLOCK_SIZE, ifp);
AES_cfb128_encrypt(indata, writePtr, bytesRead, &key, ivec, &num, AES_DECRYPT);
writePtr += bytesRead;
if (bytesRead < AES_BLOCK_SIZE)
break;
}
if (fclose(ifp) != NULL)
return false;
return true;
}
Alternatively to solving this, I welcome suggestions of a simple solution to the problem stated above ('encrypt' file on drive in a not bulletproof way so that it is not readily readable but the application can decrypt it).
The problem is likely that you're not retaining the original initialization vector for subsequent decryption operations.
As the AES encryption/decryption operations transpire, that memory is updated to continue with subsequent frames. If you instrument your code you'll see, with each encrypt/decrypt frame passing through the API, the ivec is changed.
If all you're doing this for is obfuscation (eg. you have a static key in your application) my suggestion is to do the following:
Don't pass the ivec into either the encryptor or decryptor.
Instead, generate a random ivec using RAND_bytes when encrypting. Store the ivec as the first block of data before continuing with the file content.
When decrypting, read the first block of data to prime your ivec.
Then, decrypt the remainder of the file as normal.
The benefits are:
Each encryption of a file will create a different byte representation, dependent on the initial random ivec. Eg. if you encrypt a file twice the resulting encrypted bytes will not be the same
You no longer have to use a static ivec from somewhere else in your code. The file contains it as the first block of data.
Just a suggestion. Unrelated, I prefer the EVP encryption interface, and suggest it worth a look.

How to get random salt from OpenSSL as std::string

I would like to generate a random string with OpenSSL and use this as a salt in a hashing function afterwards (will be Argon2). Currently I'm generating the random data this way:
if(length < CryptConfig::sMinSaltLen){
return 1;
}
if (!sInitialized){
RAND_poll();
sInitialized = true;
}
unsigned char * buf = new unsigned char[length];
if (!sInitialized || !RAND_bytes(buf, length)) {
return 1;
}
salt = std::string (reinterpret_cast<char*>(buf));
delete buf;
return 0;
But a std::cout of salt doesn't seem to be a proper string (contains control symbols and other stuff). This is most likely only my fault.
Am I using the wrong functions of OpenSSL to generate the random data?
Or is my conversion from buf to string faulty?
Random data is random data. That's what you're asking for and that's exactly what you are getting. Your salt variable is a proper string that happens to contain unprintable characters. If you wish to have printable characters, one way of achieving that is using base64 encoding, but that will blow up its length. Another option is to somehow discard non-printable characters, but I don't see any mechanism to force RAND_bytes to do this. I guess you could simply fetch random bytes in a loop until you get length printable characters.
If encoding base64 is acceptable for you, here is an example of how to use the OpenSSL base64 encoder, extracted from Joe Linoff's Cipher library:
string Cipher::encode_base64(uchar* ciphertext,
uint ciphertext_len) const
{
DBG_FCT("encode_base64");
BIO* b64 = BIO_new(BIO_f_base64());
BIO* bm = BIO_new(BIO_s_mem());
b64 = BIO_push(b64,bm);
if (BIO_write(b64,ciphertext,ciphertext_len)<2) {
throw runtime_error("BIO_write() failed");
}
if (BIO_flush(b64)<1) {
throw runtime_error("BIO_flush() failed");
}
BUF_MEM *bptr=0;
BIO_get_mem_ptr(b64,&bptr);
uint len=bptr->length;
char* mimetext = new char[len+1];
memcpy(mimetext, bptr->data, bptr->length-1);
mimetext[bptr->length-1]=0;
BIO_free_all(b64);
string ret = mimetext;
delete [] mimetext;
return ret;
}
To this code, I suggest adding BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL), because otherwise you'll get a new line character inserted after every 64 characters. See OpenSSL's -A switch for details.

What's the need of NULL terminator at end of decrypted string?

I am working on a google chrome NaCl extension that involves encryption and decryption of data using openssl library functions. The encryption works perfectly and the decryption also works fine as of now but for that I had to put in a kind of hack but I am not sure if that's the correct way to handle it.
else if(action == "decryption")
{
pp::Var content = dict_message.Get("content");
//aes decryption starts here
pp::VarArrayBuffer buffer(content);
const char *password = "password";
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
int cipherlen = buffer.ByteLength();
int len = cipherlen + EVP_MAX_BLOCK_LENGTH;
unsigned char *plaintext = (unsigned char*)malloc(len*sizeof(unsigned char));
unsigned char* ciphertext = static_cast<unsigned char*>(buffer.Map());
aes_init(password, (int)strlen(password), key, iv);
len = decrypt(ciphertext, cipherlen, key, iv, plaintext);
buffer.Unmap();
plaintext[len]='\0'; //fix a bug of overshooting plaintext sent
//aes decryption ends here
pp::VarDictionary reply;
reply.Set("action","decryption");
reply.Set("plaintext", (char*)plaintext);
reply.Set("fileSize", len);
PostMessage(reply);
free(plaintext);
}
Now this code decrypts the data and sends back to the javascript on extension page. Notice the line plaintext[len]='\0';, if I dont put it then sometimes I get a garbage after the correctly decrypted text in plaintext and that reflects as a null in my javascript. So is the correct way to handle the bug ?
As others have mentioned, you're using a C-string, which must be NULL-terminated. When you call pp::VarDictionary::Set, the parameters are pp::Vars, and you're taking advantage of an implicit conversion constructor from C-string to pp::Var.
If instead you make plaintext a std::string or use pp::VarArrayBuffer, this won't be necessary. PostMessage() is optimized to deal with large VarArrayBuffers, so you should probably prefer that anyway. So I'd suggest replacing this line:
unsigned char *plaintext = (unsigned char*)malloc(len*sizeof(unsigned char));
with something like:
pp::VarArrayBuffer plaintext(len);
...and change your decrypt line to something like:
len = decrypt(ciphertext, cipherlen, key, iv,\b
static_cast<unsigned char*>(plaintext.Map()));
Note, this will change the type JavaScript receives from a string to an ArrayBuffer. If you want it to remain a string, you can use a std::string instead:
std::string plaintext(len, '\0');
and access the string's buffer using operator[]. So the call to decrypt looks like this (I'm assuming len is >0):
len = decrypt(ciphertext, cipherlen, key, iv, &plaintext[0]);
The '\0' is the terminator for all strings in C. Since your output is in a string, if the '\0' character is missing, your program won't know where the string ends, and so may move on to areas beyond the string when using it, which corresponds to the garbage value near the end.
Whenever strings are declared, '\0' is put at the end. However, you are first allocating the memory for the decrypted text, and then writing into it. In this case you have to take care to at the string terminating character at the end.
The reason for the existence of the string terminating character is that strings are stored in the form of a character array in C, and the size of the array is not known by just the pointer to the beginning of the string.
If the \0 is not there at the end of a c-string, the code will not know when the string ends and will walk off into unknown parts of memory. The null terminator lets the program know "this is the end of my string". Without this, you are inviting undefined behavior into your program.

Decrypt AES string

I am using the openssl library in order to decrypt some raw strings that come from a device.
The encryption that the device is using is AES - 128 bit.
Here is my code :
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
{
int p_len = *len, f_len = 0;
unsigned char *plaintext = new unsigned char [p_len + 128];
memset(plaintext,0,p_len + 128);
syslog(LOG_NOTICE,"P_LEN BEFORE: %d",p_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
syslog(LOG_NOTICE,"P_LEN : %d",p_len);
syslog(LOG_NOTICE,"F_LEN : %d",f_len);
*len = p_len + f_len;
syslog(LOG_NOTICE,"MARIMEA ESTE %d",*len);
return plaintext;
}
My questions are :
Is the encrypted string length equal to the decrypted string length? (in AES 128 bit)
If f_len represents the decrypted amount of bytes (correct me if I am wrong) then why is it smaller than the actual data decrypted?
Thanks
AES-128 is a block cipher. Block size is 128 bits (16 bytes). So length of ciphertext is always a multiple of 16 bytes. So the ciphertext can be bigger than plaintext.
EDIT:
Answers:
No, encrypted data can be bigger or equal length.
Wrong, f_len does represents the decrypted amount of bytes. (p_len + f_len) does it.