I use the following code to check a signature. For simplicity public key, message and signature are initialized in main.
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
int main(){
const unsigned char key[] = "-----BEGIN PUBLIC KEY-----\n"
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALulMfYFyX1kSm7oUxZyCWWhrBBcWrRA\n"
"V7LSz1PzMPxoxG3KS8H7PRKFkIk42yM8/vhobmcCmj7UM5572wWch50CAwEAAQ==\n"
"-----END PUBLIC KEY-----\n";
unsigned int key_len = sizeof(key);
const unsigned char data[] = {0x6d,0x65,0x73,0x65,0x0a};
unsigned int data_len = sizeof(data);
const unsigned char sig[] = {0xa9,0x29,0x81,0x07,0x8c,0xeb,0xf0,0x1b,0x2a,0x31,0xe5,0x60,0x94,0x8a,0x47,0x94,0x3a,0x8f,0x6b,
0x4e,0x85,0xb9,0xe7,0xe5,0x4a,0x6c,0x56,0x46,0xd1,0x80,0x15,0x57,0xce,0xcb,0x0a,0x3a,0x67,0x15,0xed,
0x68,0x03,0x58,0x99,0xa4,0x73,0x61,0xe3,0x30,0x85,0xff,0x89,0x7e,0x32,0xef,0x16,0xec,0x23,0x7f,0x14,
0xde,0xbf,0x53,0xe0,0x3a};
unsigned int sig_len = sizeof(sig);
EVP_PKEY* evp_pubkey = EVP_PKEY_new();
RSA* rsa_pubkey = NULL;
EVP_MD_CTX ctx;
BIO* bufio = BIO_new_mem_buf((void*)key, key_len);
if(bufio == NULL){
fprintf(stderr, "BIO not created.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 3;
}
evp_pubkey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL);
if(evp_pubkey == NULL){
fprintf(stderr, "evp_pubkey not created.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 4;
}
EVP_MD_CTX_init(&ctx);
if (!EVP_VerifyInit(&ctx, EVP_sha256())) {
fprintf(stderr, "EVP_SignInit: failed.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 5;
}
if (!EVP_VerifyUpdate(&ctx, data, data_len)) {
fprintf(stderr, "EVP_SignUpdate: failed.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 6;
}
if (!EVP_VerifyFinal(&ctx, sig, sig_len, evp_pubkey)) {
fprintf(stderr, "EVP_VerifyFinal: failed.\n");
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 7;
}
EVP_PKEY_free(evp_pubkey);
BIO_free(bufio);
return 0;
}
After compiling with gcc evp.c -lssl -lcrypto and running valgrind --tool=memcheck --leak-check=full ./a.out on it I get some memory leaks:
==7492== 56 bytes in 1 blocks are definitely lost in loss record 9 of 12
==7492== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7492== by 0x4E9AD77: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x4F5D459: EVP_PKEY_new (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x400C4C: main (in a.out)
==7492==
==7492== 120 bytes in 1 blocks are definitely lost in loss record 10 of 12
==7492== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7492== by 0x4E9AD77: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x4F55678: EVP_DigestInit_ex (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==7492== by 0x400D6A: main (in a.out)
Am I doing something wrong?
You are overwriting the pointer here
evp_pubkey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL);
so the reference to the pointer returned by EVP_PKEY_new() the first time is lost and you can't free it anymore.
What #iharob wrote in his answer and,
You used EVP_MD_CTX_init() but did not free the data it allocated using EVP_MD_CTX_cleanup(&ctx) or - depending on your openssl version - EVP_MD_CTX_free(&ctx).
And if you compile with debug info (using -g in gcc/clang) valgrind would show you the source code lines responsible for the leaks.
You could also use EVP_MD_CTX_create() instead of using a static EVP_MD_CTX.
Here is the updated code:
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
int main() {
const unsigned char key[] =
"-----BEGIN PUBLIC KEY-----\n"
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALulMfYFyX1kSm7oUxZyCWWhrBBcWrRA\n"
"V7LSz1PzMPxoxG3KS8H7PRKFkIk42yM8/vhobmcCmj7UM5572wWch50CAwEAAQ==\n"
"-----END PUBLIC KEY-----\n";
unsigned int key_len = sizeof(key) - 1;
const unsigned char data[] = { 0x6d,0x65,0x73,0x65,0x0a };
unsigned int data_len = sizeof(data);
const unsigned char sig[] = {
0xa9,0x29,0x81,0x07,0x8c,0xeb,0xf0,0x1b,0x2a,0x31,0xe5,0x60,
0x94,0x8a,0x47,0x94,0x3a,0x8f,0x6b,0x4e,0x85,0xb8,0xe7,0xe5,
0x4a,0x6c,0x56,0x46,0xd1,0x80,0x15,0x57,0xce,0xcb,0x0a,0x3a,
0x67,0x15,0xed,0x68,0x03,0x58,0x99,0xa4,0x73,0x61,0xe3,0x30,
0x85,0xff,0x89,0x7e,0x32,0xef,0x16,0xec,0x23,0x7f,0x14,0xde,
0xbf,0x53,0xe0,0x3a
};
unsigned int sig_len = sizeof(sig);
int ret = 0;
EVP_PKEY* evp_pubkey = NULL;
RSA* rsa_pubkey = NULL;
EVP_MD_CTX* evp_ctx;
BIO* bufio = NULL;
if (!(bufio = BIO_new_mem_buf((void*)key, key_len))) {
fprintf(stderr, "BIO not created.\n");
ret = 1;
goto out;
}
if (!(evp_pubkey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL))) {
fprintf(stderr, "evp_pubkey not created.\n");
ret = 2;
goto out;
}
if (!(evp_ctx = EVP_MD_CTX_create())) {
fprintf(stderr, "ctx not created.\n");
ret = 3;
goto out;
}
if (!EVP_VerifyInit(evp_ctx, EVP_sha256())) {
fprintf(stderr, "EVP_SignInit: failed.\n");
ret = 4;
goto out;
}
if (!EVP_VerifyUpdate(evp_ctx, data, data_len)) {
fprintf(stderr, "EVP_SignUpdate: failed.\n");
ret = 5;
goto out;
}
if (!EVP_VerifyFinal(evp_ctx, sig, sig_len, evp_pubkey)) {
fprintf(stderr, "EVP_VerifyFinal: failed.\n");
ret = 6;
goto out;
}
out:
if (evp_ctx)
EVP_MD_CTX_destroy(evp_ctx);
if (evp_pubkey)
EVP_PKEY_free(evp_pubkey);
if (bufio)
BIO_free(bufio);
return 0;
}
Related
I am making a small library to make getting data from web API's simple for the rest of my program.
The library request.c has 6 functions, 5 "public" and one "private".
WriteMemoryCallback (private, copied from libcurl docs)
request_init
request_set_headers
request_set_url
request_fetch
request_cleanup
I test all the "public" functions in this order using the unity testing framework. This causes a segfault on test_request_fetch.
But if I make a small program that calls the same functions in the same order, it works just fine.
So what is wrong with my test? I am new to using C. I tried using gdb and targeting valgrind, but even with those tools I am not sure how to debug this.
EDIT: In request.c file, request_set_headers function, commenting out the curl_easy_setopt and testing without headers set removes the SEGFAULT and 100% tests pass. I am not sure why this is.
Please let me know if I should add any info or clarify something.
EDIT: I have tried to make a minimum reproducible example per Andreas Wenzel's suggestion, but there are many files. I have made a git repo to make running the code easier: CLOSED
Project folder setup
requestlib/
src/
request.c
request.h
tests/
includes/ (unity v2.5.2 is here)
test_request.c
try/
simpletest.c
compile-simpletest.sh
ex_bin/
compile.sh
test.sh
CMakeLists.txt
request.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include "request.h"
MemoryStruct chunk = {
.memory = NULL,
.size = 0
};
CURL *curl_handle = NULL;
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
MemoryStruct *mem = (MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(!ptr) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
int request_fetch(MemoryStruct *putdatahere) {
if (!curl_handle) {
return 100;
}
int rc = 0;
putdatahere = &chunk;
if (chunk.memory != NULL) {
free(chunk.memory);
}
chunk.memory = malloc(1);
chunk.size = 0;
rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
rc += curl_easy_perform(curl_handle);
return rc;
}
int request_init(void) {
static int function_runs = 0;
if (function_runs != 0) {
return 100;
}
function_runs += 1;
int rc = 0;
rc += curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
rc += curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
// rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
rc += curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
return rc;
}
int request_set_headers(char **headers, int num) {
if (!curl_handle) {
return 100;
}
struct curl_slist *list = NULL;
int rc = 0;
char *s;
for (int a = 0; a < num; a = a + 1) {
s = headers[a];
// printf("header set: %s\n", s);
list = curl_slist_append(list, s);
}
/// REMOVING THIS LINE TAKES AWAY SEGFAULT ///
rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
curl_slist_free_all(list);
return rc;
}
int request_set_url(char *url) {
if (!curl_handle) {
return 100;
}
return curl_easy_setopt(curl_handle, CURLOPT_URL, url);
}
int request_cleanup(void) {
static int function_runs = 0;
if (function_runs != 0) {
return 100;
}
function_runs += 1;
if (chunk.memory != NULL) {
free(chunk.memory);
chunk.memory = NULL;
chunk.size = 0;
}
curl_easy_cleanup(curl_handle);
curl_handle = NULL;
curl_global_cleanup();
return 0;
}
request.h
#ifndef REQUEST_H
#define REQUEST_H
#include <stdlib.h>
#include <curl/curl.h>
typedef struct MemoryStruct {
char *memory;
size_t size;
} MemoryStruct;
extern MemoryStruct chunk;
extern CURL *curl_handle;
extern CURLcode res;
// static size_t
// WriteMemoryCallback(void *, size_t, size_t, void *);
int request_init(void);
int request_fetch(struct MemoryStruct *putdatahere);
int request_set_headers(char **, int);
int request_set_url(char *);
int request_cleanup(void);
#endif // REQUEST_H
test_request.c
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include "../tests/includes/ex_unity/src/unity.h"
#include "../src/request.h"
void setUp(void) {
// we don't need any setup
}
void tearDown(void) {
// don't need any teardown
}
void test_request_init(void) {
char *failmsg = "request_init SHOULD return 0 AND create a curl_handle that is NOT NULL, but it didn't.";
int cond;
int rc = request_init();
cond = (rc == 0 && curl_handle != NULL);
TEST_ASSERT_MESSAGE(cond, failmsg);
failmsg = "On second call, request_init SHOULD return 100, but it didn't.";
rc = request_init();
cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_set_headers(void) {
char *failmsg = "request_setopt SHOULD return 0, but did not.";
int cond;
char *headers[] = {"Content-Type: multipart/mixed"};
int rc = request_set_headers(headers, 1);
cond = (rc == 0);
TEST_ASSERT_MESSAGE(cond, failmsg);
// failmsg = "request_set_headers SHOULD return 100 when curl_handle is NULL, but did not.";
// CURL *tmpbak = curl_handle;
// curl_handle = NULL;
// rc = request_set_headers(headers, 1);
// curl_handle = tmpbak;
// cond = (rc == 100);
// TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_set_url(void) {
char *failmsg = "request_set_url SHOULD return 0, but did not.";
int cond;
char *url = "https://raw.githubusercontent.com/undoingtech/Sticky-Steps/main/README.md";
int rc = request_set_url(url);
cond = (rc == 0);
TEST_ASSERT_MESSAGE(cond, failmsg);
failmsg = "request_set_url SHOULD return 100 when curl_handle is NULL, but did not.";
CURL *tmpbak = curl_handle;
curl_handle = NULL;
rc = request_set_url(url);
curl_handle = tmpbak;
cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_fetch(void) {
char *failmsg = "request_fetch SHOULD return 100 when curl_handle is NULL, but did not.";
CURL *tmpbak = curl_handle;
curl_handle = NULL;
MemoryStruct *putdatahere = NULL;
int rc = request_fetch(putdatahere);
curl_handle = tmpbak;
int cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
/// THIS TEST CAUSES THE SEGFAULT ///
failmsg = "request_fetch SHOULD return 0 AND chunk size SHOULD be greater than 0, but did not.";
rc = request_fetch(putdatahere);
cond = (rc == 0);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_cleanup(void) {
char *failmsg = "request_cleanup SHOULD return 0 AND set curl_handle to NULL, but it didn't.";
int cond;
int rc = request_cleanup();
cond = (rc == 0 && curl_handle == NULL);
TEST_ASSERT_MESSAGE(cond, failmsg);
failmsg = "On second call, request_cleanup SHOULD return 100, but it didn't.";
rc = request_cleanup();
cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
// expect 2 "still reachable" leaks due to libcurl
// it could be libcurl just leaks
// or it could be I set it up wrong
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_request_init);
RUN_TEST(test_request_set_headers);
RUN_TEST(test_request_set_url);
RUN_TEST(test_request_fetch);
RUN_TEST(test_request_cleanup);
return UNITY_END();
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(requestlib)
add_library(request STATIC src/request.c)
target_link_libraries(request curl)
include(CTest)
find_program( MEMORYCHECK_COMMAND valgrind )
set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --dsymutil=yes" )
set( MEMORYCHECK_SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/valgrind_suppress.txt" )
add_executable(test_request tests/test_request.c)
add_library(unity STATIC tests/includes/ex_unity/src/unity.c)
target_include_directories(test_request PUBLIC "tests/includes/ex_unity/src")
target_link_libraries(test_request unity)
target_link_libraries(test_request curl)
target_link_libraries(test_request request)
add_test(
NAME test_request
COMMAND $<TARGET_FILE:test_request>
)
if ( CMAKE_COMPILER_IS_GNUCC )
target_compile_options(test_request PRIVATE -Wall -Wextra)
endif()
if ( MSVC )
target_compile_options(test_request PRIVATE /W4)
endif()
compile.sh
#!/bin/bash
# get script's directory
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
project_path="$(dirname "$SCRIPT_DIR")"
build_path="$project_path/build-linux"
mkdir -p $build_path
cmake -S $project_path -B $build_path
cmake --build $build_path --config "Debug"
test.sh
#!/bin/bash
# get script's directory
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
project_path="$(dirname "$SCRIPT_DIR")"
build_path="$project_path/build-linux"
mkdir -p $build_path
(cd $build_path; ctest -D ExperimentalMemCheck -C "Debug" -V)
Partial valgrind output when running with unity
1 ==7559== Invalid read of size 8
2 ==7559== at 0x48C21C8: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
3 ==7559== by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
4 ==7559== by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
5 ==7559== by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
6 ==7559== by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
7 ==7559== by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
8 ==7559== by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
9 ==7559== by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
10 ==7559== by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
11 ==7559== Address 0x55752c0 is 0 bytes inside an unallocated block of size 16 in arena "client"
12 ==7559==
13 ==7559== Invalid read of size 1
14 ==7559== at 0x48BD6E3: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
15 ==7559== by 0x48C21D5: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
16 ==7559== by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
17 ==7559== by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
18 ==7559== by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
19 ==7559== by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
20 ==7559== by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
21 ==7559== by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
22 ==7559== by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
23 ==7559== by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
24 ==7559== Address 0x0 is not stack'd, malloc'd or (recently) free'd
25 ==7559==
26 ==7559==
27 ==7559== Process terminating with default action of signal 11 (SIGSEGV)
28 ==7559== Access not within mapped region at address 0x0
29 ==7559== at 0x48BD6E3: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
30 ==7559== by 0x48C21D5: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
31 ==7559== by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
32 ==7559== by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
33 ==7559== by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
34 ==7559== by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
35 ==7559== by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
36 ==7559== by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
37 ==7559== by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
38 ==7559== by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
39 ==7559== If you believe this happened as a result of a stack
40 ==7559== overflow in your program's main thread (unlikely but
41 ==7559== possible), you can try to increase the size of the
42 ==7559== main thread stack using the --main-stacksize= flag.
43 ==7559== The main thread stack size used in this run was 8388608.
simpletest.c
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include "../src/request.h"
void step1(void) {
request_init();
}
void step2(void) {
char *headers[] = {"Content-Type: multipart/mixed"};
request_set_headers(headers, 1);
}
void step3(void) {
request_set_url("https://raw.githubusercontent.com/undoingtech/Sticky-Steps/main/README.md");
}
void step4(void) {
MemoryStruct *putdatahere = NULL;
request_fetch(putdatahere);
}
void step5(void) {
request_cleanup();
}
int main(void) {
step1();
step2();
step3();
step4();
step5();
return 0;
}
compile-simpletest.sh (run in try folder)
#!/bin/bash
gcc -Wall -Wextra -g -o simpletest simpletest.c ../src/request.c -lcurl
The culprit was in request.c in request_set_headers().
/// REMOVING THIS LINE TAKES AWAY SEGFAULT ///
rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
curl_slist_free_all(list); // <-- curl_easy_perform SHOULD BE BEFORE THIS LINE!!
I set the header with curl_easy_setopt and a curl_slist that held the header data.
Immediately free the data used to set the header with curl_slist_free_all.
Later in request_fetch() call curl_easy_perform.
The order according to this libcurl example is supposed to be:
Set header.
Call curl_easy_perform.
Free header data with curl_slist_free_all.
After a compilation with no warnings and errors for my file reorg.c, I am running the program using Valgrind and I am getting the following output. I am trying to understand why I am getting a segmentation fault but I can't really find something wrong with line 35:
Memcheck, a memory error detector
==29338== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29338== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==29338== Command: ./reorg /opt/lsde/dataset-sf100/
==29338==
==29338== Invalid read of size 2
==29338== at 0x109398: main (reorg.c:35)
==29338== Address 0x12cb4008 is not stack'd, malloc'd or (recently) free'd
==29338==
==29338==
==29338== Process terminating with default action of signal 11 (SIGSEGV)
==29338== Access not within mapped region at address 0x12CB4008
==29338== at 0x109398: main (reorg.c:35)
==29338== If you believe this happened as a result of a stack
==29338== overflow in your program's main thread (unlikely but
==29338== possible), you can try to increase the size of the
==29338== main thread stack using the --main-stacksize= flag.
==29338== The main thread stack size used in this run was 8388608.
==29338==
==29338== HEAP SUMMARY:
==29338== in use at exit: 15,280 bytes in 10 blocks
==29338== total heap usage: 10 allocs, 0 frees, 15,280 bytes allocated
==29338==
==29338== LEAK SUMMARY:
==29338== definitely lost: 5,120 bytes in 5 blocks
==29338== indirectly lost: 0 bytes in 0 blocks
==29338== possibly lost: 0 bytes in 0 blocks
==29338== still reachable: 10,160 bytes in 5 blocks
==29338== suppressed: 0 bytes in 0 blocks
==29338== Rerun with --leak-check=full to see details of leaked memory
==29338==
==29338== For lists of detected and suppressed errors, rerun with: -s
==29338== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
This is the code of the reorg.c program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "utils.h"
Person *person_map;
unsigned int *knows_map;
unsigned short *interest_map;
unsigned long person_num = 0;
int main(int argc, char *argv[]) {
unsigned long file_length;
interest_map = (unsigned short *) mmapr(makepath(argv[1], "interest", "bin"), &file_length);
printf("amin1");
knows_map = (unsigned int *) mmapr(makepath(argv[1], "knows", "bin"), &file_length);
printf("amin2");
person_map = (Person *) mmapr(makepath(argv[1], "person", "bin"), &file_length);
printf("amin3");
knows_map = (unsigned int *) mmapr(makepath(argv[1], "knows", "bin"), &file_length);
printf("knows");
person_map = (Person *) mmapr(makepath(argv[1], "person", "bin"), &file_length);
printf("person");
person_num = file_length/sizeof(person_map);
int counter=0;
FILE* fp_knows2 = fopen(makepath(argv[1], "knows2", "bin"), (char*) "w");
FILE* fp_person2 = fopen(makepath(argv[1], "person2", "bin"), (char*) "w");
long knows2_bytesize;
int *knows2_map;
int *person2_map;
long person2_bytesize;
for(long i=0; i<person_num; i++) {
for(long j = 0; j < person_map[i].knows_n; j++) {
int friend = knows_map[person_map[i].knows_first+j]; // person in my knows-list
if (person_map[friend].location == person_map[i].location) {
counter++;
fwrite(&friend, 1,sizeof(int), fp_knows2);
}
}
if(counter > 0){
fwrite(&person_map[i], 1, sizeof(int), fp_person2);
}
counter=0;
}
fclose(fp_knows2);
fclose(fp_person2);
person2_map = (int*) mmapr(makepath(argv[1], "person2","bin"), &person2_bytesize);
knows2_map = (int*) mmapr(makepath(argv[1], "knows2","bin"), &knows2_bytesize);
return 0;
}
And this is the code of the utils.h program that am including in:
#define REPORTING_N 1000000
#define LINEBUFLEN 1024
typedef unsigned long byteoffset;
typedef unsigned int entrycount;
typedef struct {
unsigned long person_id;
unsigned short birthday;
unsigned short location;
unsigned long knows_first;
unsigned short knows_n;
unsigned long interests_first;
unsigned short interest_n;
} Person;
void parse_csv(char* fname, void (*line_handler)(unsigned char nfields, char** fieldvals)) {
long nlines = 0;
FILE* stream = fopen(fname, "r");
if (stream == NULL) {
fprintf(stderr, "Can't read file at %s\n", fname);
exit(-1);
}
char line[LINEBUFLEN];
char* tokens[10];
unsigned int col, idx;
tokens[0] = line;
while (fgets(line, LINEBUFLEN, stream)) {
col = 0;
// parse the csv line into array of strings
for (idx=0; idx<LINEBUFLEN; idx++) {
if (line[idx] == '|' || line[idx] == '\n') {
line[idx] = '\0';
col++;
tokens[col] = &line[idx+1];
} // lookahead to find end of line
if (line[idx+1] == '\0') {
break;
}
}
(*line_handler)(col, tokens);
nlines++;
if (nlines % REPORTING_N == 0) {
printf("%s: read %lu lines\n", fname, nlines);
}
}
fclose(stream);
}
FILE* open_binout(char* filename) {
FILE* outfile;
outfile = fopen(filename, "wb");
if (outfile == NULL) {
fprintf(stderr, "Could not open %s for writing\n", filename);
exit(-1);
}
return outfile;
}
unsigned short birthday_to_short(char* date) {
unsigned short bdaysht;
char dmbuf[3];
dmbuf[2] = '\0';
dmbuf[0] = *(date + 5);
dmbuf[1] = *(date + 6);
bdaysht = atoi(dmbuf) * 100;
dmbuf[0] = *(date + 8);
dmbuf[1] = *(date + 9);
bdaysht += atoi(dmbuf);
return bdaysht;
}
void* mmapr(char* filename, byteoffset *filelen) {
int fd;
struct stat sbuf;
void *mapaddr;
if ((fd = open(filename, O_RDONLY)) == -1) {
fprintf(stderr, "failed to open %s\n", filename);
exit(1);
}
if (stat(filename, &sbuf) == -1) {
fprintf(stderr, "failed to stat %s\n", filename);
exit(1);
}
mapaddr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (mapaddr == MAP_FAILED) {
fprintf(stderr, "failed to mmap %s\n", filename);
exit(1);
}
*filelen = sbuf.st_size;
return mapaddr;
}
char* makepath(char* dir, const char* file, const char* ext) {
char* out = (char*) malloc(1024), *sep = (char*) "";
if (strlen(dir) && dir[strlen(dir)-1] != '/') sep = (char*) "/";
sprintf(out, "%s%s%s.%s", dir, sep, file, ext);
return out;
}
For industrial purposes, I want to decrypt an AES-encrypted message with an RSA-encrypted key in C. At first, I thought doing it step-by-step by first, using OpenSSL libcrypto library, by first RSA decoding the key then AES decoding the data.
I have found out that EVP tools were commonly seen as a better way to do this since it actually does what the low-levels functions do but correctly.
Here is what I see the flow of the program :
Initialize OpenSSL;
Read and store the RSA private key;
Initialize the decryption by specifying the decryption algorithm (AES) and the private key;
Update the decryption by giving the key, the data, the key and their length
Finally decrypt the data and return it.
I have been a lot confused by the fact that so far we do not intend to use any IV or ADD (although IV might come up later in the project). I have followed this guide it is not very clear and does not fit the way I use EVP.
So here is my actual code :
#include <openssl/evp.h>
#include <openssl/conf.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include "openssl\applink.c"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
const char PRIVATE_KEY_PATH[] = "C:/Users/Local_user/privateKey.pem";
EVP_PKEY* initializePrivateKey(void)
{
FILE* privateKeyfile;
if ((privateKeyfile = fopen(PRIVATE_KEY_PATH, "r")) == NULL) // Check PEM file opening
{
perror("Error while trying to access to private key.\n");
return NULL;
}
RSA *rsaPrivateKey = RSA_new();
EVP_PKEY *privateKey = EVP_PKEY_new();
if ((rsaPrivateKey = PEM_read_RSAPrivateKey(privateKeyfile, &rsaPrivateKey, NULL, NULL)) == NULL) // Check PEM file reading
{
fprintf(stderr, "Error loading RSA Private Key File.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
if (!EVP_PKEY_assign_RSA(privateKey, rsaPrivateKey))
{
fprintf(stderr, "Error when initializing EVP private key.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
return privateKey;
}
const uint8_t* decodeWrappingKey(uint8_t const* data, const size_t data_len, uint8_t const* wrappingKey, const size_t wrappingKey_len)
{
// Start Decryption
EVP_CIPHER_CTX *ctx;
if (!(ctx = EVP_CIPHER_CTX_new())) // Initialize context
{
fprintf(stderr, "Error when initializing context.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
EVP_PKEY *privateKey = initializePrivateKey();
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, privateKey, NULL)) // Initialize decryption
{
fprintf(stderr, "Error when initializing decryption.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
uint8_t* res;
if ((res = calloc(data_len, sizeof(uint8_t))) == NULL) // Check memory allocating
{
perror("Memory allocating error ");
return NULL;
}
puts("Initialization done. Decoding..\n");
size_t res_len = 0;
if (1 != EVP_DecryptUpdate(ctx, res, &res_len, data, data_len))
{
fprintf(stderr, "Error when preparing decryption.\n");
ERR_print_errors_fp(stderr);
}
if (1 != EVP_DecryptFinal_ex(ctx, res, &res_len))
{
fprintf(stderr, "Error when decrypting.\n");
ERR_print_errors_fp(stderr);
}
return res;
}
void hexToBytes(uint8_t *des, char const *source, const size_t size) {
for (int i = 0; i < size - 1; i += 2)
sscanf(source + i, "%02x", des + (i / 2));
}
int main(void) {
char const *strWrap = "5f82c48f85054ef6a3b2621819dd0e969030c79cc00deb89........";
char const *strData = "ca1518d44716e3a4588af741982f29ad0a3e7a8d67.....";
uint8_t *wrap = calloc(strlen(strWrap), sizeof(uint8_t));
hexToBytes(wrap, strWrap, strlen(strWrap)); // Converts string to raw data
uint8_t *data = calloc(strlen(strData), sizeof(uint8_t));
hexToBytes(data, strData, strlen(strData));
/* Load the human readable error strings for libcrypto */
ERR_load_crypto_strings();
/* Load all digest and cipher algorithms */
OpenSSL_add_all_algorithms();
/* Load config file, and other important initialisation */
OPENSSL_config(NULL);
const uint8_t *res = decodeWrappingKey(data, strlen(strData) / 2, wrap, strlen(strWrap) / 2);
if (res == NULL)
return 1;
return 0;
}
My output is the following one :
Initialization done. Decoding..
Error when preparing decryption.
Error when decrypting.
Obviously it fails when updating and finalising the decryption but I can't figure out why and the ERR_print_errors_fp(stderr); which had always worked for me so far seems to be mute.
Here is a complete working example of how you can encrypt a key using RSA, and encrypt a message using that key using AES, followed by the subsequent decryption of those things. It assumes AES-256-CBC is being used. If you want to use AES-256-GCM instead then you will need to make some changes to get and set the tag (ask me if you need some pointers on how to do this). It also assumes that the RSA encryption is done with PKCS#1 padding (which is all that the EVP_Seal* APIs support). If you need some other kind of padding then you will need to use a different method. Finally it assumes you are using OpenSSL 1.1.0. If you are using 1.0.2 then some changes will probably be necessary (at least you will need to explicitly init and de-init the library - that isn't required in 1.1.0).
The code reads the RSA private and public keys from files called privkey.pem and pubkey.pem which are in the current working directory. I generated these files like this:
openssl genrsa -out privkey.pem 2048
openssl rsa -in privkey.pem -pubout -out pubkey.pem
I've tested this on Linux only. The code is as follows:
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int envelope_seal(EVP_PKEY *pub_key, unsigned char *plaintext,
int plaintext_len, unsigned char **encrypted_key,
int *encrypted_key_len, unsigned char **iv,
int *iv_len, unsigned char **ciphertext,
int *ciphertext_len)
{
EVP_CIPHER_CTX *ctx;
int len, ret = 0;
const EVP_CIPHER *type = EVP_aes_256_cbc();
unsigned char *tmpiv = NULL, *tmpenc_key = NULL, *tmpctxt = NULL;
if((ctx = EVP_CIPHER_CTX_new()) == NULL)
return 0;
*iv_len = EVP_CIPHER_iv_length(type);
if ((tmpiv = malloc(*iv_len)) == NULL)
goto err;
if ((tmpenc_key = malloc(EVP_PKEY_size(pub_key))) == NULL)
goto err;
if ((tmpctxt = malloc(plaintext_len + EVP_CIPHER_block_size(type)))
== NULL)
goto err;
if(EVP_SealInit(ctx, type, &tmpenc_key, encrypted_key_len, tmpiv, &pub_key,
1) != 1)
goto err;
if(EVP_SealUpdate(ctx, tmpctxt, &len, plaintext, plaintext_len) != 1)
goto err;
*ciphertext_len = len;
if(EVP_SealFinal(ctx, tmpctxt + len, &len) != 1)
goto err;
*ciphertext_len += len;
*iv = tmpiv;
*encrypted_key = tmpenc_key;
*ciphertext = tmpctxt;
tmpiv = NULL;
tmpenc_key = NULL;
tmpctxt = NULL;
ret = 1;
err:
EVP_CIPHER_CTX_free(ctx);
free(tmpiv);
free(tmpenc_key);
free(tmpctxt);
return ret;
}
int envelope_open(EVP_PKEY *priv_key, unsigned char *ciphertext,
int ciphertext_len, unsigned char *encrypted_key,
int encrypted_key_len, unsigned char *iv,
unsigned char **plaintext, int *plaintext_len)
{
EVP_CIPHER_CTX *ctx;
int len, ret = 0;
unsigned char *tmpptxt = NULL;
if((ctx = EVP_CIPHER_CTX_new()) == NULL)
return 0;
if ((tmpptxt = malloc(ciphertext_len)) == NULL)
goto err;
if(EVP_OpenInit(ctx, EVP_aes_256_cbc(), encrypted_key, encrypted_key_len,
iv, priv_key) != 1)
return 0;
if(EVP_OpenUpdate(ctx, tmpptxt, &len, ciphertext, ciphertext_len) != 1)
return 0;
*plaintext_len = len;
if(EVP_OpenFinal(ctx, tmpptxt + len, &len) != 1)
return 0;
*plaintext_len += len;
*plaintext = tmpptxt;
tmpptxt = NULL;
ret = 1;
err:
EVP_CIPHER_CTX_free(ctx);
free(tmpptxt);
return ret;
}
int main(void)
{
EVP_PKEY *pubkey = NULL, *privkey = NULL;
FILE *pubkeyfile, *privkeyfile;
int ret = 1;
unsigned char *iv = NULL, *message = "Hello World!\n";
unsigned char *enc_key = NULL, *ciphertext = NULL, *plaintext = NULL;
int iv_len = 0, enc_key_len = 0, ciphertext_len = 0, plaintext_len = 0, i;
if ((pubkeyfile = fopen("pubkey.pem", "r")) == NULL) {
printf("Failed to open public key for reading\n");
goto err;
}
if ((pubkey = PEM_read_PUBKEY(pubkeyfile, &pubkey, NULL, NULL)) == NULL) {
fclose(pubkeyfile);
goto err;
}
fclose(pubkeyfile);
if ((privkeyfile = fopen("privkey.pem", "r")) == NULL) {
printf("Failed to open private key for reading\n");
goto err;
}
if ((privkey = PEM_read_PrivateKey(privkeyfile, &privkey, NULL, NULL))
== NULL) {
fclose(privkeyfile);
goto err;
}
fclose(privkeyfile);
if (!envelope_seal(pubkey, message, strlen(message), &enc_key, &enc_key_len,
&iv, &iv_len, &ciphertext, &ciphertext_len))
goto err;
printf("Ciphertext:\n");
for (i = 0; i < ciphertext_len; i++)
printf("%02x", ciphertext[i]);
printf("\n");
printf("Encrypted Key:\n");
for (i = 0; i < enc_key_len; i++)
printf("%02x", enc_key[i]);
printf("\n");
printf("IV:\n");
for (i = 0; i < iv_len; i++)
printf("%02x", iv[i]);
printf("\n");
if (!envelope_open(privkey, ciphertext, ciphertext_len, enc_key,
enc_key_len, iv, &plaintext, &plaintext_len))
goto err;
plaintext[plaintext_len] = '\0';
printf("Plaintext: %s\n", plaintext);
ret = 0;
err:
if (ret != 0) {
printf("Error\n");
ERR_print_errors_fp(stdout);
}
EVP_PKEY_free(pubkey);
EVP_PKEY_free(privkey);
free(iv);
free(enc_key);
free(ciphertext);
free(plaintext);
return ret;
}
Your key is encrypted with RSA, so you will decrypt it with RSA APIs like RSA_private_decrypt first, not with EVP* APIs.
Once you get key decrypted you need to use it with (EVP*) APIs to decrypt the data with AES.
I created a program to get all files in a directory, find the individual checksums and then find the total checksums using multithreading.
I am receiving a segmentation fault so I ran gdb and saw that the error is on line 60 where open() is. After researching the seg fault on SO, and on other forums, I attempted to implement a few different approaches such as changing open() to fopen() with a FILE *handle rather than an int. That change proved incorrect.
After hours of debugging and searching, I am clueless and would greatly appreciate any insight.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h> ///Compile with -pthread or -lpthread
#include <sys/stat.h>
#define BUFFER_SIZE (1<<16)
void cleanup();
void get_filenames();
void* get_checksum();
char **filenames;
int file_cnt;
DIR *dir;
//int handle;
FILE *handle;
unsigned int checksum;
unsigned char* ptr;
int length;
int count;
unsigned char* buffer;
int* sum;
unsigned int total = 0;
int main(int argc, char *argv[]){
int i;
pthread_t* file;
atexit(cleanup);
get_filenames();
printf("There are %d files:\n", file_cnt);
file = calloc(sizeof(pthread_t), file_cnt);
sum = calloc(sizeof(int), file_cnt);
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
}
for(i=0; i<file_cnt; i++){
total += sum[i];
}
printf("total is: %u\n", total);
return EXIT_SUCCESS;
}
void* get_checksum(void* a){
int b = *((int *)a);
//handle = open(filenames[b], O_RDONLY); //SEG FAULT HERE
handle = fopen(filenames[b], "r"); //SEG FAULT HERE
if( handle == NULL ){
printf( "Can't open file: %s\n", filenames[b]);
exit(1);
}
buffer = malloc(BUFFER_SIZE);
if( buffer == NULL ){
printf( "Can't get enough memory\n" );
exit(1);
}
checksum = 0;
do{
//length = read( handle, buffer, BUFFER_SIZE );
length = read( handle, buffer, (sizeof(char)));
if( length == -1 ){
printf( "Error reading file: %s\n", filenames[b]);
//return NULL;
exit(1);
}
ptr = buffer;
count = length;
while( count-- ){
checksum = checksum + (unsigned int)( *ptr++ );
sum[b] = checksum;
}
} while( length );
printf("Checksum= %d\nTimes at: %d\n", checksum, (int)clock());
}
void cleanup() {
if(filenames && file_cnt > 0) {
while(file_cnt-- > 0) {
if(filenames[file_cnt]) {
free(filenames[file_cnt]);
}
}
free(filenames);
}
if(dir) {
closedir(dir);
}
return;
}
void get_filenames() {
struct dirent *dir_entry;
if((dir = opendir(".")) == NULL) {
fprintf(stderr, "Couldn't open the directory entry for reading\n");
exit(1);
}
errno = 0;
file_cnt = 0;
while((dir_entry = readdir(dir)) != NULL) {
char **new_filenames = filenames;
static int realative_dirs = 0;
if(realative_dirs < 2 &&
(strcmp(".", dir_entry->d_name) == 0 || strcmp("..", dir_entry->d_name) == 0)
) {
realative_dirs++;
continue;
}
new_filenames = (char **)realloc(filenames, sizeof(char **) * (file_cnt + 1));
if(new_filenames == NULL) {
free(filenames[file_cnt]);
fprintf(stderr, "Could not allocate reference for filename[%d]\n", file_cnt);
exit(1);
}
filenames = new_filenames;
filenames[file_cnt] = (char *)calloc(strlen(dir_entry->d_name) + 1, sizeof(char));
if(filenames[file_cnt] == NULL) {
fprintf(stderr, "Could not allocate memory for filename[%d]'s string: \"%s\"\n",
file_cnt, dir_entry->d_name);
exit(1);
}
strcpy(filenames[file_cnt], dir_entry->d_name);
file_cnt++;
}
if(errno != 0) {
fprintf(stderr, "An error occured getting the filenam list\n");
exit(1);
}
return;
}
Below is the output and gdb debugging:
There are 24 files:
.windows
.xscreensaver
.alias
.cshrc
Segmentation fault
(gdb) run
Starting program: /home/nolooking/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
There are 24 files:
.windows
[New Thread 0x7ffff781e700 (LWP 15957)]
.xscreensaver
[New Thread 0x7ffff701d700 (LWP 15958)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff781e700 (LWP 15957)]
0x0000000000400d53 in get_checksum (a=0x60b610) at checksum.c:60
60 handle = open(filenames[b], O_RDONLY);
(gdb) backtrace
#0 0x0000000000400d53 in get_checksum (a=0x60b610) at checksum.c:60
#1 0x00007ffff7bc6374 in start_thread () from /lib64/libpthread.so.0
#2 0x00007ffff7907c3d in clone () from /lib64/libc.so.6
(gdb) quit
A debugging session is active.
UPDATE:
I took the advice of one user in the comments who suggested that I use:
handle=fopen((char*)a, "r");. I can successfully print out the file names when the if statement if(handle==NULL) is commented out. When I include that if statement I receive the following output:
There are 24 files:
.windows
.xscreensaver
.alias
.cshrc
Can't open file: p▒`
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/stat.h>
#define BUFFER_SIZE (1<<16)
void cleanup();
void get_filenames();
void* get_checksum();
char **filenames;
int file_cnt;
DIR *dir;
//int handle;
FILE *handle;
unsigned int checksum;
unsigned char* ptr;
int length;
int count;
unsigned char* buffer;
int* sum;
unsigned int total = 0;
int main(int argc, char *argv[]){
int i;
pthread_t* file;
atexit(cleanup);
get_filenames();
printf("There are %d files:\n", file_cnt);
file = calloc(sizeof(pthread_t), file_cnt);
sum = calloc(sizeof(int), file_cnt);
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
}
for(i=0; i<file_cnt; i++){
total += sum[i];
}
printf("total is: %u\n", total);
return EXIT_SUCCESS;
}
void* get_checksum(void* a){
int b = *((int *)a);
handle = fopen(((char*)a), "r");
if( handle == NULL ){
printf( "Can't open file: %s\n", ((char*)a));
exit(1);
}
buffer = malloc(BUFFER_SIZE);
if( buffer == NULL ){
printf( "Can't get enough memory\n" );
exit(1);
}
checksum = 0;
do{
length = read( handle, buffer, BUFFER_SIZE );
if( length == -1 ){
printf( "Error reading file: %s\n", ((char*)a));
//return NULL;
exit(1);
}
ptr = buffer;
count = length;
while( count-- ){
checksum = checksum + (unsigned int)( *ptr++ );
//sum[a] = checksum;
}
} while( length );
printf("Checksum= %d\nTimes at: %d\n", checksum, (int)clock());
}
void cleanup() {
if(filenames && file_cnt > 0) {
while(file_cnt-- > 0) {
if(filenames[file_cnt]) {
free(filenames[file_cnt]);
}
}
free(filenames);
}
if(dir) {
closedir(dir);
}
return;
}
void get_filenames() {
struct dirent *dir_entry;
if((dir = opendir(".")) == NULL) {
fprintf(stderr, "Couldn't open the directory entry for reading\n");
exit(1);
}
errno = 0;
file_cnt = 0;
while((dir_entry = readdir(dir)) != NULL) {
char **new_filenames = filenames;
static int realative_dirs = 0;
if(realative_dirs < 2 &&
(strcmp(".", dir_entry->d_name) == 0 || strcmp("..", dir_entry->d_name) == 0)
) {
realative_dirs++;
continue;
}
new_filenames = (char **)realloc(filenames, sizeof(char **) * (file_cnt + 1));
if(new_filenames == NULL) {
free(filenames[file_cnt]);
fprintf(stderr, "Could not allocate reference for filename[%d]\n", file_cnt);
exit(1);
}
filenames = new_filenames;
filenames[file_cnt] = (char *)calloc(strlen(dir_entry->d_name) + 1, sizeof(char));
if(filenames[file_cnt] == NULL) {
fprintf(stderr, "Could not allocate memory for filename[%d]'s string: \"%s\"\n",
file_cnt, dir_entry->d_name);
exit(1);
}
strcpy(filenames[file_cnt], dir_entry->d_name);
file_cnt++;
}
if(errno != 0) {
fprintf(stderr, "An error occured getting the filenam list\n");
exit(1);
}
return;
}
Why I am receiving that output once I uncomment the if statement?
Change this
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
to be
pthread_create(&(file[i]), NULL, get_checksum, (void*)i);
and this
int b = *((int *)a);
to be
int b = (int)a;
Also you cannot call read() on a FILE* as it is returned by fopen(). Use fread() instead.
Don't use &i. I'll explain in a bit. The argument you're passing to the thread is wrong a is not an integer. It's meant to be a pointer to a string...
Change the thread create to this...
pthread_create(&(file[i]), NULL, get_checksum, filenames[i]);
then print the string as follows...
void* get_checksum(void *a){
char *file_name = (char *)a;
printf("filename=%s\n", file_name);
You're passing the string as a pointer to the called function. In your code you're trying to use this as an index into the array.
If you want to pass the index as an integer beware... this won't work..
pthread_create(&(file[i]), NULL, get_checksum, &i);
This is multithreaded and the value pointed to by &i is changing as the loop runs. Pass the pointer to the string and do not under any circumstances change filenames as the threads run.
I think your problem is simply because you are passing &filenames[i] instead of simply &i.
Then in void* get_checksum(void* a) you are trying to use a char* as an int.
The code would be more like :
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&i);
}
and in void* get_checksum(void* a) :
int b = *((int *)a);
handle = fopen(filenames[b], "r");
if( handle == NULL ){
printf( "Can't open file: %s\n", filenames[b]);
exit(1);
}
I tried code(Using Linux Kernel Crypto APIs) for calculating HMAC-SHA512 on a file.
Surprisingly, HMAC calculated from the kernel code and from OpenSSL commands are different.
OpenSSL commands are:
openssl rand -base64 16
plakOhrXlfnBENPVBo91kg==
openssl dgst -sha512 -mac hmac -macopt key:plakOhrXlfnBENPVBo91kg== ../hello6.ko
HMAC-SHA512(../hello6.ko)= 9605f40851630f5b7a18fa30c7e5d6f1e77afb011d319efa515556d12ba6930f459825b3695a0d0d910a714724c0d99b36ccea5c878962b32a8de65dcbcc247d
HMAC-SHA512 from the kernel code is:
84306723b713379aa666fe9aa75af3192a9707d19136c25dd2286c5f6d86dfd8f76ceaa0ce147b53c0e9f7bfab485f38f5139c687c34c840db7f7fa9438bf8b0d8df8e770088ffffc8de8e770088ffffc3f027a0ffffffff672c00000088fffff0de8e770088ffff1900000000000080672c000000000000e8de8e770088ffff
How come these HMACs are different for the same content? Any pointers to get rid of this issue is very helpful.
Base64 encoded key is generated by below command and stored in a file:
openssl rand -base64 -out $Module_name.sha512key 16
HMAC is generated on a file and stored in a file.
openssl dgst -sha512 -mac hmac -macopt key:$HMAC_KEY $HMAC_PATH/$Module_name> temp.txt
Key and File content are read by below code:
static char hmac[HMAC_SHA512_SIZE];
const char *kofilename = "/home/sri/Documents/Hello/hello6.ko";
const char *hmackey_file = "/home/sri/Documents/Hello/hello6.ko.sha512key";
const char *hmac_file = "/home/sri/Documents/Hello/hello6.ko.sha512";
unsigned char *data = NULL;
int readkostatus;
int readkeystatus;
int readhmacstatus;
unsigned char *hmackey = NULL;
unsigned char *stored_hmac = NULL;
readkeystatus = read_file(hmackey_file, &hmackey);
if(readkeystatus < 0)
goto readkeyerr;
printk(KERN_INFO "HMAC Key is :%s", hmackey);
readkostatus = read_kofile(kofilename, &data);
if(readkostatus < 0)
goto readkoerr;
printk(KERN_INFO "File data size is :%ld", strlen(data));
printk(KERN_INFO "File data is :%s", data);
hmac_sha512(hmackey, data, hmac, sizeof(hmac));
printk(KERN_INFO "FINAL HMAC:%s", hmac);
readhmacstatus = read_file(hmac_file, &stored_hmac);
if(readhmacstatus < 0)
goto readhmacerr;
printk(KERN_INFO "Stored HMAC:%s", stored_hmac);
if(!memcmp(stored_hmac, hmac, HMAC_SHA512_SIZE))
printk(KERN_INFO "HMACs match");
else
printk(KERN_INFO "HMACs do not match");
vfree(stored_hmac);
vfree(data);
vfree(hmackey);
return 0;
readkeyerr:
{
printk(KERN_INFO "hmac key read error:%d", readkeystatus);
return readkeystatus;
}
readkoerr:
{
printk(KERN_INFO "ko read error:%d", readkostatus);
return readkostatus;
}
readhmacerr:
{
printk(KERN_INFO "hmac read error:%d", readhmacstatus);
return readhmacstatus;
}
Code for reading the files is given below:
int read_file(const char *filename, unsigned char **data)
{
struct file* filp = NULL;
long filesize;
int ret = 0;
mm_segment_t old_fs = get_fs();
set_fs(get_ds());
filp = filp_open(filename, O_RDONLY, 0);
if(IS_ERR(filp)) {
ret = PTR_ERR(filp);
printk(KERN_INFO "kofile is not opened");
}
else
{
loff_t offset;
offset = 0;
filesize = filp->f_dentry->d_inode->i_size;
if (filesize <= 0 || filesize > 131072)
{
goto fileoperror;
}
*data = (unsigned char *)vmalloc(filesize);
if (*data == NULL)
goto datanull;
ret = vfs_read(filp, *data, filesize, &offset);
if(ret != filesize)
goto read_error;
filp_close(filp, NULL);
}
set_fs(old_fs);
return ret;
fileoperror:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Invalid file operation '%s'\n", filename);
return (-EPERM);
}
datanull:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Data Buffer is not allocated");
return (-EFAULT);
}
read_error:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Failed to read '%s'.\n", filename);
return (-EFBIG);
}
}
int read_kofile(const char* filename, unsigned char **data)
{
return read_file(filename, data);
}
HMAC-SHA-512 is calculated on the file by calling Crypto APIs:
#define HMAC_SHA512_SIZE 64
struct hmac_sha512_result {
struct completion completion;
int err;
};
static void hmac_sha512_complete(struct crypto_async_request *req, int err) {
struct hmac_sha512_result *r=req->data;
if(err==-EINPROGRESS)
return;
r->err=err;
complete(&r->completion);
}
int hmac_sha512(const unsigned char *key, // key
const unsigned char *data_in, // data in
unsigned char *hash_out, size_t outlen) { // hash buffer and length
int rc=0;
struct crypto_ahash *tfm;
struct scatterlist sg;
struct ahash_request *req;
struct hmac_sha512_result tresult;
void *hash_buf;
size_t klen = strlen(key);
size_t dlen = strlen(data_in);
int len = HMAC_SHA512_SIZE;
char hash_tmp[HMAC_SHA512_SIZE];
char *hash_res = hash_tmp;
printk(KERN_INFO "hmac_sha512: HMAC key is %s ", key);
/* Set hash output to 0 initially */
memset(hash_out, 0, outlen);
init_completion(&tresult.completion);
tfm=crypto_alloc_ahash("hmac(sha512)",0,0);
if(IS_ERR(tfm)) {
printk(KERN_ERR "hmac_sha512: crypto_alloc_ahash failed.\n");
rc=PTR_ERR(tfm);
goto err_tfm;
}
if(!(req=ahash_request_alloc(tfm,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha512: failed to allocate request for hmac(sha512)\n");
rc=-ENOMEM;
goto err_req;
}
if(crypto_ahash_digestsize(tfm)>len) {
printk(KERN_ERR "hmac_sha512: tfm size > result buffer.\n");
rc=-EINVAL;
goto err_req;
}
ahash_request_set_callback(req,CRYPTO_TFM_REQ_MAY_BACKLOG,
hmac_sha512_complete,&tresult);
if(!(hash_buf=kzalloc(dlen,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha512: failed to kzalloc hash_buf");
rc=-ENOMEM;
goto err_hash_buf;
}
memcpy(hash_buf,data_in,dlen);
sg_init_one(&sg,hash_buf,dlen);
crypto_ahash_clear_flags(tfm,-0);
if((rc=crypto_ahash_setkey(tfm,key,klen))){
printk(KERN_ERR "hmac_sha512: crypto_ahash_setkey failed\n");
goto err_setkey;
}
ahash_request_set_crypt(req,&sg,hash_res,dlen);
rc=crypto_ahash_digest(req);
switch(rc) {
case 0:
while (len--) {
snprintf(hash_out, outlen, "%02x", (*hash_res++ & 0x0FF));
hash_out += 2;
}
break;
case -EINPROGRESS:
case -EBUSY:
rc=wait_for_completion_interruptible(&tresult.completion);
if(!rc && !(rc=tresult.err)) {
INIT_COMPLETION(tresult.completion);
break;
} else {
printk(KERN_ERR "hmac_sha512: wait_for_completion_interruptible failed\n");
goto out;
}
default:
goto out;
}
out:
err_setkey:
kfree(hash_buf);
err_hash_buf:
ahash_request_free(req);
err_req:
crypto_free_ahash(tfm);
err_tfm:
return rc;
}
Please let me know, if something is missing.
crypto_req_done defined in hash.h
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gfp.h>
#include <linux/msi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include <linux/ahci-remap.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <net/sock.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <linux/buffer_head.h>
#include <linux/libata.h>
#include <asm/uaccess.h>
#include <asm/traps.h>
#include <linux/cdev.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/hashtable.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/signal_types.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/crypto.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define TESTMGR_POISON_BYTE 0xfe
static int do_ahash_op(int (*op)(struct ahash_request *req), struct ahash_request *req, struct crypto_wait *wait)
{
int err;
err = op(req);
return crypto_wait_req(err, wait);
}
static int check_nonfinal_ahash_op(const char *op, int err){
if(err < 0){
pr_err("alg: ahash: %s() failed with err %d on test vector %s\n", op, err, get_error(err));
return 1;
}
return 0;
}
now hmac_sha256:
int hmac_sha256(unsigned char *key, size_t key_size, unsigned char *ikm, size_t ikm_len, unsigned char *okm, size_t okm_len){
int rc, key_is_null;
struct crypto_ahash *tfm;
struct ahash_request *req;
struct scatterlist pending_sgl;
DECLARE_CRYPTO_WAIT(wait);
if(key == NULL){
key = kzalloc(32, GFP_KERNEL);
key_size = 32;
key_is_null = 1;
}else{key_is_null = 0;}
tfm = crypto_alloc_ahash("hmac(sha256)", 0, 0);
if(IS_ERR(tfm)) {
rc=PTR_ERR(tfm);
printk(KERN_ERR "hmac_sha256: crypto_alloc_ahash failed(%s).\n", get_error(rc));
goto err_tfm;
}
if(!(req = ahash_request_alloc(tfm, GFP_KERNEL))) {
rc =- ENOMEM;
printk(KERN_ERR "hmac_sha256: failed to allocate request for hmac(sha512)(%s).\n", get_error(rc));
}
if(key_size){
if((rc = crypto_ahash_setkey(tfm, key, key_size))){
printk(KERN_ERR "hmac_sha256: crypto_ahash_setkey failed.(%s)\n", get_error(rc));
goto out;
}
}
sg_init_table(&pending_sgl, 1);
memset(req->__ctx, TESTMGR_POISON_BYTE, crypto_ahash_reqsize(tfm));
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);
ahash_request_set_crypt(req, NULL, okm, 0);
rc = do_ahash_op(crypto_ahash_init, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_init", rc);
if (rc)
goto out;
sg_set_buf(&pending_sgl, ikm, ikm_len);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);
ahash_request_set_crypt(req, &pending_sgl, okm, okm_len);
rc = do_ahash_op(crypto_ahash_update, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_update", rc);
if(rc)
goto out;
rc = do_ahash_op(crypto_ahash_final, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_final", rc);
if (rc)
goto out;
dump_memory(okm, "hmac_sha256", okm_len);
out:
ahash_request_free(req);
crypto_free_ahash(tfm);
err_tfm:
if(key_is_null)
kfree(key);
return rc;
}
Break A leg!
int hkdf_extract(unsigned char *salt, size_t salt_len, unsigned char *ikm, size_t ikm_len, unsigned char *okm, size_t okm_len){
return hmac_sha256(salt, salt_len, ikm, ikm_len, okm, okm_len);
}