Requirements
I'm a developer but with near-zero experience with C until a week ago. For a project, I need to encrypt/decrypt source-code/text files and need to use C for that.
What I did till now?
I'm using Kokke/tony-AES-c library for it and implementing it with pkcs7 padding as explained in this Gist.
In my code (given below), I have a function my_encrypt, which is encrypting contents and returning uint8_t array (which contains hex). I need to save it and read it later from a file for decryption. I know how to save a string into a file but how can I save uint8_t in a file.
For this, I need to convert uint8_t to string but after trying multiple solution, I am unable to do it.
Question
How can I convert uint8_t array (which contains hex) to string in C.
My code
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <stdlib.h>
#define CBC 1
#define DECRYPT 0;
#define ENCRYPT 1;
#include "aes.h"
#include "pkcs7_padding.h"
const uint8_t * my_encrypt(char *report) {
//Initialization Vector
uint8_t iv[] = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };
uint8_t i;
// char* report = "my super secret thing that needs to remain that way!";
char* key = "thisIstheKey";
int dlen = strlen(report);
int klen = strlen(key);
printf("THE PLAIN TEXT STRING = ");
for (i=0; i<dlen;i++){
printf("%c", report[i]);
}
printf("\n");
//Proper Length of report
int dlenu = dlen;
if (dlen % 16) {
dlenu += 16 - (dlen % 16);
printf("The original length of the STRING = %d and the length of the padded STRING = %d\n", dlen, dlenu);
}
//Proper length of key
int klenu = klen;
if (klen % 16) {
klenu += 16 - (klen % 16);
printf("The original length of the KEY = %d and the length of the padded KEY = %d\n", klen, klenu);
}
// Make the uint8_t arrays
uint8_t hexarray[dlenu];
uint8_t kexarray[klenu];
// Initialize them with zeros
memset( hexarray, 0, dlenu );
memset( kexarray, 0, klenu );
// Fill the uint8_t arrays
for (int i=0;i<dlen;i++) {
hexarray[i] = (uint8_t)report[i];
}
for (int i=0;i<klen;i++) {
kexarray[i] = (uint8_t)key[i];
}
int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );
printf("The padded STRING in hex is = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
printf("The padded key in hex is = ");
for (i=0; i<klenu;i++){
printf("%02x",kexarray[i]);
}
printf("\n");
// In case you want to check if the padding is valid
int valid = pkcs7_padding_valid( hexarray, dlen, sizeof(hexarray), 16 );
int valid2 = pkcs7_padding_valid( kexarray, klen, sizeof(kexarray), 16 );
printf("Is the pkcs7 padding valid report = %d | key = %d\n", valid, valid2);
//start the encryption
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, kexarray, iv);
// encrypt
AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);
printf("the encrypted STRING = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
return hexarray;
}
int main(int argc, char *argv[]) {
// File pointer
FILE *input_file;
int operation;
char * input_file_buffer = 0;
long input_file_length;
// ////////////////////////////////////////////////////////
// Read command line arguments
// ////////////////////////////////////////////////////////
// Make sure proper command params are supplied
if (argc != 3) {
printf("Usage: %s encrypt/decrypt filename\n", argv[0]);
return -1;
}
// Define operation type
if (strcmp(argv[1], "encrypt") == 0) {
operation = ENCRYPT;
} else if (strcmp(argv[1], "decrypt") == 0) {
operation = DECRYPT;
}
// ////////////////////////////////////////////////////////
// Open File contents
// ////////////////////////////////////////////////////////
// Open input_file to encrypt/decrypt.
input_file = fopen(argv[2], "rb");
if (!input_file) {
fprintf(stderr, "ERROR: '%s' file fopen error: %s\n", argv[2], strerror(errno));
return errno;
}
// Read contents of file in buffer
fseek(input_file, 0, SEEK_END);
input_file_length = ftell (input_file);
fseek(input_file, 0, SEEK_SET);
input_file_buffer = malloc (input_file_length);
if (input_file_buffer)
{
fread(input_file_buffer, 1, input_file_length, input_file);
}
// Close input_file
fclose(input_file);
// We have contents of file in input_file_buffer, let's print them.
printf("File contents:\n-------\n%s\n", input_file_buffer);
// Let's encrypt input_file_buffer
uint8_t result = 0;
result = my_encrypt(input_file_buffer);
// Convery result to string.
printf("main: Encrypted File contents:\n-------\n%d\n", result);
return 0;
}
Related
In reference to my previous question I just have to change most part of the code and use function call. Now the file can read and print as expected, but I noticed that after when it goes to the expected fileReader and prints the file, it automatically exited by calling choiceReader(case: else ) function which is not the expected function to call. My expectation is that algorithm() will be the next call. Please what am I missing here again?
The new code is:
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int success = 0;
char counter[1000];//used to monitor the read file length
int c;
size_t cipher_len; // size_t is sizeof(type)
unsigned char* ciphertext;
unsigned char plainpassword[] = "00000";
unsigned char* password = &plainpassword[0];
unsigned char salt[8];
unsigned char key[16];
unsigned char iv[16];
FILE *fp;
FILE *fptr;
char* plaintext = "To be or not to be is the question.";
char dict[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int decryptedtext_len, ciphertext_len, dict_len;
char* ciphertext_base64;
unsigned char *plaintexts;
int len;
int plaintext_len;
void handleOpenSSLErrors(void)
{
ERR_print_errors_fp(stderr);
abort();
}
unsigned char* decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ){
EVP_CIPHER_CTX *ctx;
unsigned char* plaintext = malloc(ciphertext_len);
bzero(plaintext,ciphertext_len);
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleOpenSSLErrors();
/* Initialise the decryption operation. IMPORTANT - ensure you use a key
* and IV size appropriate for your cipher
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits */
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
handleOpenSSLErrors();
EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleOpenSSLErrors();
plaintext_len = len;
/* Finalise the decryption. Further plaintext bytes may be written at
* this stage.
*/
// return 1 if decryption successful, otherwise 0
if(1 == EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
success = 1;
plaintext_len += len;
/* Add the null terminator */
plaintext[plaintext_len] = 0;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
//delete [] plaintext;
return plaintext;
}
size_t calcDecodeLength(char* b64input) {
size_t len = strlen(b64input), padding = 0;
if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
padding = 2;
else if (b64input[len-1] == '=') //last char is =
padding = 1;
return (len*3)/4 - padding;
}
void Base64Decode( char* b64message, unsigned char** buffer, size_t* length) {
BIO *bio, *b64; // A BIO is an I/O strean abstraction
int decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char*)malloc(decodeLen + 1);
(*buffer)[decodeLen] = '\0';
bio = BIO_new_mem_buf(b64message, -1);
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
//BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
*length = BIO_read(bio, *buffer, strlen(b64message));
BIO_free_all(bio);
}
void initAES(const unsigned char *pass, unsigned char* salt, unsigned char* key, unsigned char* iv )
{
//initialisatio of key and iv with 0
bzero(key,sizeof(key));
bzero(iv,sizeof(iv));
EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha1(), salt, pass, strlen(pass), 1, key, iv);
}
int checkPlaintext(char* plaintext, char* result){
int length = 10; // we just check the first then characters
return strncmp(plaintext, result, length);
}
//this tries to crack the password.
int algorithm(){
time_t begin = time(NULL);
for(int i=0; i<dict_len; i++)
for(int j=0; j<dict_len; j++)
for(int k=0; k<dict_len; k++)
for(int l=0; l<dict_len; l++)
for(int m=0; m<dict_len; m++){
*password = dict[i];
*(password+1) = dict[j];
*(password+2) = dict[k];
*(password+3) = dict[l];
*(password+4) = dict[m];
initAES(password, salt, key, iv);
unsigned char* result = decrypt(ciphertext, cipher_len, key, iv);
if (success == 1){
if(checkPlaintext(plaintext, result)==0){
printf("Password is %s\n", password);
time_t end = time(NULL);
printf("Time elpased is %ld seconds", (end - begin));
return 0;
}
}
free(result);
}
}
// this given cipher text
int firstReader(){
if ((fp = fopen("givenfile.txt", "r")) == NULL) {
printf("Error! File cannot be opened.");
exit(1);
}
fscanf(fp, "%[^\n]", counter);
printf("Cipher text is:\n%s", counter);
printf("\nCracking cipher text one in progress ....\n");
algorithm();//calls the algorithm function
}
int secondReader(){
if ((fptr = fopen("cipher1.txt", "r")) == NULL) {
printf("Error! File cannot be opened.");
// Program exits if the file pointer returns NULL.
exit(1);
}
fscanf(fptr, "%[^\n]", counter);
printf("Cipher text is:\n%s", counter);
printf("\nCracking cipher text tow in progress ....\n");
algorithm(); // calls the algorithm function
}
// reads user's choice and performs the next operation accordingly
void choiceReader(){
printf("User's application guide.\nIst cipher decryption = 1\nSecond cipher decryption = 2\nQuit = Any other input\n");
scanf("%d", &c);
if(c==1){
firstReader();
}
if(c==2){
secondReader();
}
else{
printf("Application Exited ...");
exit(1);
}
}
int main (void)
{
//the core function that controls the user's action.
choiceReader();
dict_len = strlen(dict);
if (c==1){
ciphertext_base64 = (char*) fp;
}
if (c==2){
ciphertext_base64 = (char*) fptr;
}
printf(ciphertext_base64);
ERR_load_crypto_strings();
Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);
// retrive the slater from ciphertext (binary)
if (strncmp((const char*)ciphertext,"Salted__",8) == 0) { // find the keyword "Salted__"
memcpy(salt,&ciphertext[8],8);
ciphertext += 16;
cipher_len -= 16;
}
// Clean up
fclose(fptr);
EVP_cleanup();
ERR_free_strings();
return 0;
}
The output is screenshot below:
You wrote:
if(c==1){
firstReader();
}
if(c==2){
secondReader();
}
else{
printf("Application Exited ...");
exit(1);
}
which means:
If c is 1, call firstReader.
If c is 2, call secondReader, otherwise exit.
If c is 1, then it calls firstReader, and then exits because c is not 2. The computer is not smart enough to figure out what you actually meant. You probably meant to write else if(c==2) which links the two ifs together so that the final else only runs if neither one is true.
Everything is perfectly working as expected now, the sequential error was simply due to the wrong logical call to the ciphertext_base64
Thanks everyone for your time and guide.
I wrote this code which reads a file binary and determines its filetype (for a few test fileendings).It works fine for PDF, MP3 but not for jpg.
What is the problem? For jpg the line printf("%s[%d]: %x\n", "Buffer", j, buffer[j]); shows me multiple bytes (i.e ffffff instead of just one byte)
#include <stdio.h>
const int header[6][8] = {
{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A},
{0xFF,0xD8,0x00,0x00,0x00,0x00,0x00,0x00},
{0xFF,0xFB,0x00,0x00,0x00,0x00,0x00,0x00},
{0x49,0x44,0x33,0x00,0x00,0x00,0x00,0x00},
{0x25,0x50,0x44,0x46,0x2D,0x00,0x00,0x00},
{0x42,0x4C,0x45,0x4E,0x44,0x45,0x52,0x00}
};
const char* filetype[6] = {"PNG","JPG","MP3","MP3v2","PDF","Blender"};
int main()
{
FILE *fd;
char buffer[8];
if ((fd = fopen("C:\\Users\\***\\Desktop\\Unnamed.jpg", "rb")) == NULL) {
return -1;
}
//fread(buffer, sizeof(char), 8, fd);
fread(buffer, sizeof(buffer), 1, fd);
for (int i = 0; i < 6; i++) {
for(int j = 0; j < 8; j++){
printf("%s[%d][%d]: %x\n","Header",i,j,header[i][j]);
printf("%s[%d]: %x\n", "Buffer", j, buffer[j]);
if (header[i][j] == 0x00) {
printf("%s: %s","Found file type",filetype[i]);
return 1;
}
if (header[i][j] != buffer[j]) {
break;
}
}
}
printf("%s", "Couldn't determine filetype - Not in library");
return 0;
}
You forgot to make your buffer unsigned char, so you're getting sign extension on the values.
Clearly the table of built-in magic signatures should be const unsigned char, too, and the comparison should be made with a single memcmp() call.
Use unsigned char for buffer (you can also for header) to avoid problem if your char are signed and give a negative value (you compare with int like 0x89 where the bit 7 is set)
You also have a problem for PNG because the values for PNG are :
{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A},
there are not ended by 0 like in all the other cases, this is needed because your algorithm need to find 0 to indicate you found :
if (header[i][j] == 0x00) {
printf("%s: %s","Found file type",filetype[i]);
return 1;
}
Just add a column to also have 0 also for PNG.
Finally :
const unsigned char header[6][9] = {
{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A, 0x00},
{0xFF,0xD8,0x00,0x00,0x00,0x00,0x00,0x00, 0x00},
{0xFF,0xFB,0x00,0x00,0x00,0x00,0x00,0x00, 0x00},
{0x49,0x44,0x33,0x00,0x00,0x00,0x00,0x00, 0x00},
{0x25,0x50,0x44,0x46,0x2D,0x00,0x00,0x00, 0x00},
{0x42,0x4C,0x45,0x4E,0x44,0x45,0x52,0x00, 0x00}
};
const char* filetype[6] = {"PNG","JPG","MP3","MP3v2","PDF","Blender"};
int main()
{
FILE *fd;
unsigned char buffer[sizeof(header[0])];
if ((fd = fopen("C:\\Users\\***\\Desktop\\Unnamed.jpg", "rb")) == NULL) {
return -1;
}
fread(buffer, sizeof(buffer), 1, fd);
for (int i = 0; i < ; i++) {
for(int j = 0; j < sizeof(header[0]); j++){
printf("%s[%d][%d]: %x\n","Header",i,j,header[i][j]);
printf("%s[%d]: %x\n", "Buffer", j, buffer[j]);
if (header[i][j] == 0x00) {
printf("%s: %s","Found file type",filetype[i]);
return 1;
}
if (header[i][j] != buffer[j]) {
break;
}
}
}
printf("%s", "Couldn't determine filetype - Not in library");
return 0;
}
I am a beginner with the libcrypto library. I am trying to give an encrypted string to a function, decrypt it and return it decrypted. The string is encoded with my public key which which is 4096-bit sized.
char* decodeStr(const char* str, const size_t sizeStr)
{
puts("Starting");
FILE* file = fopen(PRIVATE_KEY_PATH, "r");
if (file == NULL)
{
perror("Error while trying to access to Presto's private key.\n");
return NULL;
}
RSA *privateKey = RSA_new();
privateKey = PEM_read_RSAPrivateKey(file, &privateKey, NULL, NULL);
if (privateKey == NULL)
{
fprintf(stderr, "Error loading RSA private key.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
char* res = malloc(sizeStr);
if (res == NULL)
{
perror("Memory allocating error ");
return NULL;
}
const int sizeDecoded = RSA_private_decrypt(sizeStr, str, res, privateKey, RSA_PKCS1_PADDING);
if (sizeDecoded == -1)
{
fprintf(stderr, "Error while decoding RSA-encoded wrapping key.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
if ((res = realloc(res, (size_t)sizeDecoded)) == NULL)
{
perror("Memory allocating error ");
return NULL;
}
return res;
}
The following code outputs :
Starting
Error while decoding RSA-encoded wrapping key.
6928:error;04069506C:lib<4>:func<101>:reason<108>:.\crypto\rsa\rsa_eay.c:518:
Since the error is unknown and I haven't been able to find any information about it on the net, and moreover I am a beginner with libcrypto, does strneed to be in a certain format ?
Obivously it is this which breaks the program but I can't be sure of it, neither do I know how to fix this.
const int sizeDecoded = RSA_private_decrypt(sizeStr, str, res, privateKey, RSA_PKCS1_PADDING);
EDIT : I have been working with a client which provides me those encoded dat for me to decrypt them. I don't know how they are processed exactly. Unfortunately, the encoded strings are even more sensitive than the private key itself, so I can't share it. It looks like 0c79cc00deb89a614db6ebe42be748219089fb5356 but with 1024 characters.
The problem
Your encoded strings
0c79cc00deb89a614db6ebe42be748219089fb5356 but with thousands of characters.
look like hexadecimal representation of the encoded message. I think you really need to convert the message to a raw data, converting hex to bytes.
Your strings are composed by 1024 (hex) chars => 512 bytes (you need two hex digits to represent one byte) => 4096 bits, which is equal to the key length.
Try this simple function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
void hex_2_bytes(uint8_t *des, char *source, size_t size);
int main()
{
char *str = "0c79cc00deb89a614db6ebe42be748219089fb5356";
uint8_t *dest = (uint8_t *) malloc(sizeof(uint8_t) * strlen(str) / 2);
hex_2_bytes(dest, str, strlen(str));
for(int i = 0; i < strlen(str) / 2; i++) {
printf(" %02X ", dest[i]);
}
return 0;
}
void hex_2_bytes(uint8_t *des, char *source, size_t size) {
for(int i = 0; i < size - 1; i+=2) {
sscanf(source+i, "%02x", des + (i/2));
}
}
What is raw data? Your encrypted message is a string, which is an array of characters. I see only digits (from 0 to 9) and the letters from a to f, which make me guess that your original message (which is, originally a binary string, raw data) has been represented as a very long number using hexadecimal digits. You need to convert it back.
If you have a message composed by n bytes, stored as a binary string (raw data), you will have an hexadecimal representation, stored as a text, which is 2*n bytes long.
Example:
uint8_t raw_data_message[] = {0x3c, 0x2f}; // 2 bytes
char hex_representation_as_string_message[] = {'3', 'c', '2', 'f'}; // 4 bytes
Your messages a like hex_representation_as_string_message, but you need them as raw_data_message.
The solution
Here's a fully working example:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#define PRIVATE_KEY_PATH ".ssh/id_rsa"
/*
* Generation of the encrypted message:
* echo "Hello, this is a super secret message" | openssl rsautl -encrypt -pubin -inkey ~/.ssh/id_rsa.pub.pem >message.encrypted
*
* Resulting encrypted message in hex representation:
* 917ab2ebd663bba1dcd0f22aef98b93b039f001e19c997f015d90eaaf35731eb1895c13dfb08250aa28a9dac4f3e1b5fefc53926d3f6422f8055124bb15c24e2b73645dc61f29486deaf278705987738e916a6288531aa923dff15b667dabf4465506e1ee68e6f27a06e3fb4f5040a7775ce69ba10ec337f5bc5ef45969a8fe7c672c9b51243296677385f1b613f4f3edceef620f6ab5dcadec5034c330331bf8e8c3b42554f01c6cf1c0dbc58f23c8f0068e750fc4bb97636b2b3455e7f3932ab9559ff4de5bfc6769bbafefec722441458066ab4a6fdfe99c78bfdd5c1851d411a451925c5ad7ecb0c93618304ae6bc5402193f58af6e6a65208075be35a00
* (converted with: hexdump -ve '1/1 "%.2x"' message.encrypted)
*/
void hex_2_bytes (uint8_t *des, char *source, size_t size);
int decode (uint8_t *dest, const uint8_t *src, const size_t size);
int main (int argc, char **argv) {
// Reading the encrypted message in hex representation
FILE *file = fopen(argv[1], "r");
printf("%s\n", argv[1]);
if (file == NULL) {
perror("Error while trying to access to the encrypted message"
".\n");
return -1;
}
fseek(file, 0, SEEK_END);
long fsize = ftell(file);
fseek(file, 0, SEEK_SET);
char *hex_repr = malloc(fsize);
fread(hex_repr, fsize, 1, file);
fclose(file);
printf("Hexadecimal representation of the encrypted message\n");
for (int i = 0; i < fsize; ++i) {
printf("%c", hex_repr[i]);
}
printf("\nSize: %d\n", fsize);
// Converting to raw data
size_t raw_data_size = fsize / 2;
uint8_t *raw_data = (uint8_t *) malloc(
raw_data_size * sizeof(uint8_t));
hex_2_bytes(raw_data, hex_repr, (size_t) fsize);
printf("Raw encrypted message\n");
for (int i = 0; i < raw_data_size; ++i) {
printf("%02X", raw_data[i]);
}
printf("\nSize: %d\n", raw_data_size);
// Decryption
char *res = malloc(raw_data_size * sizeof(char));
if (res == NULL) {
perror("Memory allocating error ");
return -1;
}
int msg_size = decode(res, raw_data, raw_data_size);
printf("Decrypted message:\n");
for (int j = 0; j < msg_size; ++j) {
printf("%c", res[j]);
}
printf("\nSize: %d\n", msg_size);
return 0;
}
void hex_2_bytes (uint8_t *des, char *source, size_t size) {
for (int i = 0; i < size - 1; i += 2) {
sscanf(source + i, "%02x", des + (i / 2));
}
}
int decode (uint8_t *res, const uint8_t *src, const size_t size) {
puts("Starting");
FILE *file = fopen(PRIVATE_KEY_PATH, "r");
if (file == NULL) {
perror("Error while trying to access to Presto's private key.\n");
return -1;
}
RSA *privateKey = RSA_new();
privateKey = PEM_read_RSAPrivateKey(file, &privateKey, NULL, NULL);
if (privateKey == NULL) {
fprintf(stderr, "Error loading RSA private key.\n");
ERR_print_errors_fp(stderr);
return -1;
}
const int sizeDecoded = RSA_private_decrypt(size, src, res,
privateKey,
RSA_PKCS1_PADDING);
if (sizeDecoded == -1) {
fprintf(stderr,
"Error while decoding RSA-encoded wrapping key.\n");
ERR_print_errors_fp(stderr);
return -1;
}
return sizeDecoded;
}
And the results (as a proof of work):
Note that I'm using 2048 bit rsa keys.
I solved part of the issue. I added :
/* Load the human readable error strings for libcrypto */
ERR_load_crypto_strings();
At the beginning of the function. The output turned into :
Starting
Error while decoding RSA-encoded wrapping key.
1124:errir:0406506C:rsa routines: RSA_EAY_PRIVATE_DECRYPT:data greater than mod len:.\crypto\rsa\rsa_eay.c:518:
Which I guess means that the length of the key is too small for the provided data. Still left to know if the data is wrong, the key is wrong and how to fix it.
Also, to avoid making such mistakes later, I added :
const size_t sizeRSA = RSA_size(prestoPrivateKey);
if (sizeRSA < sizeWrappingKey)
{
fprintf(stderr, "Size of key : %d\n Size of data : %d\n The size of the key should higher than data length.\n", sizeRSA, sizeWrappingKey);
return NULL;
}
I'm trying to encrypt and decrypt keystore using AES_cbc_encrypt function from openssl library.
I have a problem with memory allocation, it will be nice if someone can give me some advices how to do this in proper way.
For now I can't figure out how to do this so I'm getting Segmentation fault while AES_cbc_encrypt is called.
Another question is how to allocate memory for char tables in C (clean way). I want to do this dynamically. Places in code for this question are marked with QUESTION string.
unsigned char *input;
input = malloc(sizeof(unsigned char)*length);
unsigned char input_question[length];
Output:
[enc_dec_keystore] length: 82
[enc_dec_keystore] QUESTION #1: why sizeof(input) != sizeof(input_question)
[enc_dec_keystore] input size: 8
[enc_dec_keystore] input_question size: 82
Why sizeof(input) is different. Because I'm getting sizeof pointer in this way? I expected to get 82 too because sizeof(unsigned char) should be 1, multiplied by 82 should be 82, right?
Here is a code from my program:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#define DEBUG 1
#define KEYLEN 128
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");
}
void PBKDF2_HMAC_SHA_512(const char *pass, const unsigned char *salt, int32_t iterations, uint32_t outputBytes, unsigned char *result) {
if (DEBUG) {
puts("[PBKDF2_HMAC_SHA_512] in");
}
unsigned char digest[outputBytes];
PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, strlen(salt), iterations, EVP_sha512(), outputBytes, digest);
for (int i = 0; i < sizeof(digest); i++)
{
result[i] = digest[i];
};
if (DEBUG) {
puts("[PBKDF2_HMAC_SHA_512] out");
}
}
int deriver_key(const char *pass, const char *salt, unsigned char *key) {
if (DEBUG) {
puts("[deriver_key] in");
}
/* allocate 16 bytes of memory for key (128 bits) */
key = (unsigned char *) malloc(sizeof(unsigned char)*16);
/* let's make 10 iteration for now */
PBKDF2_HMAC_SHA_512(pass, salt, 10, KEYLEN/8, key);
if (DEBUG) {
printf("[deriver_key] key(string): %s\n", key);
printf("[deriver_key] key(bytes): ");
for(int i=0; i<KEYLEN/8; i++) {
printf("%02x", key[i]);
}
printf("\n");
puts("[deriver_key] out");
}
return 0;
}
int enc_dec_keystore(char *keystore, char *key) {
/* length of keystore */
int length;
/* if (length % AES_BLOCK_SIZE) !=0 -> fill to 16 bytes block */
int pad_length;
int pad_counter = 0;
FILE *fp;
fp = fopen(keystore, "rb");
fseek(fp, 0L, SEEK_END);
length = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (DEBUG) {
printf("[enc_dec_keystore] keystore length: %i\n", length);
}
/* check if input fills blocks correctly */
pad_length = length;
while(pad_length % AES_BLOCK_SIZE !=0) {
pad_length++;
pad_counter++;
}
if (DEBUG) {
printf("[enc_dec_keystore] pad_length: %i\n", pad_length);
}
/* IV - fill with 0 for now */
unsigned char iv[AES_BLOCK_SIZE];
memset(iv, 0x00, AES_BLOCK_SIZE);
unsigned char *input;
input = malloc(sizeof(unsigned char)*length);
unsigned char input_question[length];
if (DEBUG) {
printf("\n[enc_dec_keystore] QUESTION #1: why sizeof(input) != sizeof(input_question)\n");
printf("[enc_dec_keystore] input size: %lu\n", sizeof(input));
printf("[enc_dec_keystore] input_question size: %lu\n\n", sizeof(input_question));
}
/* read data from file */
int ret = 0;
ret = fread(input, 1, length, fp);
if (ret == 0) {
puts("[enc_dec_keystore] file doesn't exist");
}
/* close the file after read */
fclose(fp);
if (DEBUG) {
printf("[enc_dec_keystore] sizeof input: %lu, input length: %lu\n", sizeof(input), strlen(input));
printf("[enc_dec_keystore] input data: \n%s\n", input);
}
/* allocate memory for aes's output */
unsigned char *enc_out;
enc_out = malloc(sizeof(unsigned char)*pad_length);
unsigned char enc_out_question[pad_length];
/* padding with 0 */
memset(enc_out, 0, sizeof(enc_out));
if (DEBUG) {
printf("\n[enc_dec_keystore] QUESTION #2: (again) why sizeof(enc_out) != sizeof(enc_out_question)\n");
printf("[enc_dec_keystore] enc_out size: %lu\n", sizeof(enc_out));
printf("[enc_dec_keystore] enc_out_question size: %lu\n\n", sizeof(enc_out_question));
}
/* AES-128 bit CBC Encryption */
/* set up a key */
/*
unsigned char key_again[KEYLEN/8];
for (int i=0; i<KEYLEN/8; i++) {
key_again[i] = key[i];
}
*/
AES_KEY enc_key;
AES_set_encrypt_key(key, 128, &enc_key);
/* encrypt input data to enc_out */
AES_cbc_encrypt(input, enc_out, sizeof(input), &enc_key, iv, AES_ENCRYPT);
if (DEBUG) {
printf("[enc_dec_keystore] encryption - aes output(string): %s\n", enc_out);
printf("[enc_dec_keystore] encryption - aes output(bytes): ");
for(int i=0; i<pad_length; i++) {
printf("%02x", enc_out[i]);
}
printf("\n");
}
/* AES-128 bit CBC Decryption */
/*
AES_KEY dec_key;
AES_set_encrypt_key(key, 128, &enc_key);
memset(iv, 0x00, AES_BLOCK_SIZE);
unsigned char *enc_out;
enc_out = malloc(sizeof(unsigned char)*pad_length);
AES_cbc_encrypt(enc_out, dec_out, sizeof(enc_out), &enc_key, iv, AES_ENCRYPT);
if (DEBUG) {
printf("[enc_dec_keystore] decryption - aes output(string): %s\n", enc_out);
printf("[enc_dec_keystore] decryption - aes output(bytes): ");
for(int i=0; i<pad_length; i++) {
printf("%02x", enc_out[i]);
}
printf("\n");
puts("[enc_dec_keystore] out");
}
*/
return 0;
}
int main(int argc, char **argv) {
char *user = "user";
char *key_id = "key1";
const char *pass = "temppass";
const char *salt = "tempsalt";
char *keystore = "keystore";
/* Deriver key from password and salt */
unsigned char *key;
if (deriver_key(pass, salt, key) != 0) {
puts("[main] error with key derivery");
}
/* Encrypt and decrypt keystore (for tests) */
if (enc_dec_keystore(keystore, key) != 0) {
puts("[main] error with encrypting/decrypting keystore");
}
return 0;
}
Keystore file contains:
keyid1:01020304050607080900010203040506:
keyid2:06050403020100090807060504030201:
Why sizeof(input) is not 82, because sizeof returns only the size of pointer variable, it does not return the size of dynamically allocated data.
PBKDF2_HMAC_SHA_512 returns a binary key, which may not be delimited by '\0', this could lead to segfault when you print the key
I am writing a wrapper for Karl Malbrain's AES implementation to work with input files bigger then 16 bytes.
Therefore, I wrote a functions aes_encrypt_block which would split the input buffer (message) into blocks of 16 bytes (chunk / chunk_cipher), calls the encryption/decryption functions and put the encrypted/decrypted 16 bytes back into the result buffer.
This works. However, I need to pad the message before encrypting it. So, the last 16 bytes I encrypt (outside the for-loop) are the padding bytes. When copying these bytes to the output buffer (cipher) I get a segfault. I can't really see what is going wrong.
Do you see the mistake?
Regards
#include "aes.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#define DEBUG 1
void aes_encrypt_block(uint8_t **message, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes);
void aes_decrypt_block(uint8_t **msg_decrypted, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes);
unsigned long readFile(char *fileName, uint8_t **buffer);
//unsigned long readFile1(char *fileName, uint8_t *buffer);
uint8_t secret[16] = {0x44, 0x43, 0x45, 0x33, 0x44, 0x03, 0x34, 0x44, 0x43, 0x45, 0x33, 0x44, 0x03, 0x34, 0x03, 0x34};
uint8_t *chunk = NULL;
uint8_t *chunk_cipher = NULL;
uint8_t expanded[176] = {0x00};
uint8_t *buffer = NULL;
uint8_t *cipher = NULL;
uint8_t *msg_decrypted = NULL;
uint8_t mode = -1;
int main(int argc, char *argv[]) {
uint32_t i = 0;
uint8_t blocks = -1, pad_bytes = -1;
unsigned long fileLen;
char* ch = NULL;
if(argc<3) {
printf("Wrong arguments supplied.\n\n%s {0/1} OUT\n\n\t0 - encrypt\n\t1 - decrypt\n\tOUT - file to read from / to write to.\n\n", argv[0]);
return 0;
} else {
mode = atoi(argv[1]);
if(mode<0 || mode>1) {
printf("Wrong arguments supplied.\n\n%s {0/1} OUT\n\n\t0 - encrypt\n\t1 - decrypt\n\tOUT - file to read from / to write to.\n\n", argv[0]);
return 0;
}
}
puts("");
// Read file
fileLen = readFile(argv[2], &buffer);
// Setting up parameters and memory
if(fileLen%16!=0) {
printf("- ");
blocks = fileLen/16+1;
pad_bytes = (blocks*16)-fileLen;
} else {
printf("+ ");
blocks = fileLen/16+1;
pad_bytes = 16;
}
cipher = malloc((blocks*16)*sizeof(int));
if(cipher==NULL) printf("malloc() error!\n");
aes_expand_key(secret, expanded);
if(DEBUG) printf("size: %u, size/16: %d, blocks: %d, padding: %d\n\n", fileLen, fileLen/16, blocks, pad_bytes);
if(!mode) { // We will encrypt
aes_encrypt_block(&buffer, &cipher, blocks, pad_bytes);
free(buffer);
FILE *file_enc;
file_enc = fopen("bla.enc", "wb");
fileLen = fwrite(&cipher, sizeof(uint8_t), 32, file_enc);
printf("\nWrote %ld bytes to %s\n", fileLen, argv[2]);
fclose(file_enc);
} else { // We will decrypt
msg_decrypted = malloc((16*blocks)*sizeof(uint8_t));
// Decrypting blocks
aes_decrypt_block(msg_decrypted, cipher, blocks, pad_bytes);
puts("\nDecrypted message:");
printf("%s\n", msg_decrypted);
puts("");
free(msg_decrypted);
}
return 0;
}
unsigned long readFile(char *fileName, uint8_t **buffer) {
unsigned long fileLen = 0;
uint8_t i;
char* ch = NULL;
FILE *file;
file = fopen (fileName, "rb"); /* open the file for reading */
if(file==NULL) {
perror(fileName);
return 0;
}
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);
*buffer=malloc(fileLen+1);
if (!buffer) {
fprintf(stderr, "Memory error!");
fclose(file);
return;
}
fread(*buffer, 1, fileLen, file);
printf( "Source message in hex(%s, %ld bytes):\n", fileName, fileLen );
for (ch = *buffer ; ch < *buffer + fileLen; ++ch) {
printf( "%02X", *ch );
}
puts("\nASCII:\n---------");
for (ch = *buffer ; ch < *buffer + fileLen; ++ch) {
printf( "%c", *ch );
}
puts("");
fclose(file);
return fileLen;
}
void aes_encrypt_block(uint8_t **message, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes) {
uint8_t i;
chunk = malloc(16*sizeof(uint8_t));
if(chunk==NULL) printf("malloc() error!\n");
chunk_cipher = malloc(16*sizeof(uint8_t));
if(chunk_cipher == NULL) printf("malloc() error!\n");
for(i=0; i<(blocks-1); i++) {
memcpy(chunk, message[i*16], 16*sizeof(uint8_t));
aes_encrypt(chunk, expanded, chunk_cipher);
memcpy(cipher[i*16], chunk_cipher, 16*sizeof(uint8_t));
}
// Padding
memcpy(chunk, message[(blocks-1)*16], (16-pad_bytes)*sizeof(uint8_t));
uint8_t j;
for(j=0; j<=pad_bytes; j++) {
chunk[15-j] = pad_bytes;
}
aes_encrypt(chunk, expanded, chunk_cipher);
memcpy(cipher[i*16], chunk, 16*sizeof(uint8_t));
}
EDIT:
Valgrind output (line 149 in test.c corresponds to: memcpy(cipher[i*16], chunk, 16*sizeof(uint8_t));)
Valgrind outputs:
InvalidWrite: Invalid write of size 4
Call stack:
/usr/lib/valgrind/vgpreload_memcheck-x86-linux.so 0x402E08A: memcpy
/home/dev/aes/test.c|149|0x804A604: aes_encrypt_block
/home/dev/aes/test.c|59|0x8049AFE: main
Address 0x0 is not stack'd, malloc'd or (recently) free'd
Valgrind found 1 errors!
If pad_bytes == 16 then this loop:
for(j=0; j<=pad_bytes; j++) {
chunk[15-j] = pad_bytes;
}
will write into an invalid location before the start of chunk, possibly corrupting your heap.
It should probably be:
for(j=0; j<pad_bytes; j++) {
chunk[15-j] = pad_bytes;
}
Note also that you also appear to have a number of memory leaks where memory allocated via malloc is not beeing freed, e.g. chunk and chunk_cipher in aes_encrypt_block().
I'd say this line
memcpy(cipher[i*16], chunk_cipher, 16*sizeof(uint8_t));
should be
memcpy((*cipher) + i*16, chunk_cipher, 16*sizeof(uint8_t));
(for the 2nd second call of memcpy() apply same to 1st argument)
Or even better change the signature of aes_encrypt_block() from
void aes_encrypt_block(uint8_t **message, uint8_t **cipher, uint8_t blocks, uint8_t pad_bytes)
to
void aes_encrypt_block(uint8_t **message, uint8_t (*cipher)[16], uint8_t blocks, uint8_t pad_bytes)
and do
memcpy(cipher + i, chunk_cipher, sizeof(*cipher));
(for the 2nd second call of memcpy() apply same to 1st argument)