MikelAlejoBR MikelAlejoBR - 3 years ago 230
C Question

Assertion —to a function pointer— failed

I am playing with Signal's libsignal library, trying to guess how to compile and run a little toy program. However, I am stuck at the very beginning. I understand I have to populate a variable with pointers to functions that will be used later in the library, and even though I am trying to replicate what the library does in its tests, I don't see where the difference is between the tests and my code, and why my program fails in runtime. The code I am using is the following one:

#include <stdlib>
#include <signal/signal_protocol.h>
#include <signal/key_helper.h>
#include <openssl/rand.h>

int random(uint8_t *data, size_t len, void *user_data)
{
if(RAND_bytes(data, len)) {
return 0;
}
else {
return SG_ERR_UNKNOWN;
}
}

int main(int argc, char **argv) {
signal_crypto_provider provider = {
.random_func = random
/*.hmac_sha256_init_func = HMAC_CTX_new,
.hmac_sha256_update_func = HMAC_Update,
.hmac_sha256_final_func = HMAC_Final,
.hmac_sha256_cleanup_func = HMAC_CTX_free,
.sha512_digest_init_func = SHA512_Init,
.sha512_digest_update_func = SHA512_Update,
.sha512_digest_final_func = SHA512_Final,
.sha512_digest_cleanup_func = EVP_MD_CTX_free,
.encrypt_func = EVP_aes_256_cbc,
.decrypt_func = EVP_aes_256_cbc,
.user_data = 0*/
};

signal_context *global_context;
signal_context_create(&global_context, 0);
signal_context_set_crypto_provider(global_context, &provider);
//signal_context_set_locking_functions(global_context, lock_function,
//unlock_function);


ratchet_identity_key_pair *identity_key_pair;
uint32_t registration_id;
signal_protocol_key_helper_pre_key_list_node *pre_keys_head;
session_signed_pre_key *signed_pre_key;


signal_protocol_key_helper_generate_identity_key_pair(
&identity_key_pair,
global_context
);

exit(EXIT_SUCCESS);

}


The problem arises when the program reaches
signal_protocol_key_helper_generate_identity_key_pair
. Going through the library and following the calls it makes, I ended up in the following function:

int signal_crypto_random(signal_context *context, uint8_t *data, size_t len)
{
assert(context);
assert(context->crypto_provider.random_func);
return context->crypto_provider.random_func(data, len, context->crypto_provider.user_data);
}


The assertion that fails is the second one, giving me the following error:

signal_crypto_random: Assertion `context->crypto_provider.random_func' failed.


The only explanation that I can think of —I am kind of new with C— is that somehow the pointer does not point to the function I previously specified. If this guess is correct, why does this happen?

Checking their tests code, and comparing it with my code, I don't see what makes the crucial difference that makes my program fail. When debugging, the variable seems to have the right content.

Variable contents

Thank you.




test-common.c

void setup_test_crypto_provider(signal_context *context)
{
signal_crypto_provider provider = {
.random_func = test_random_generator,
.hmac_sha256_init_func = test_hmac_sha256_init,
.hmac_sha256_update_func = test_hmac_sha256_update,
.hmac_sha256_final_func = test_hmac_sha256_final,
.hmac_sha256_cleanup_func = test_hmac_sha256_cleanup,
.sha512_digest_init_func = test_sha512_digest_init,
.sha512_digest_update_func = test_sha512_digest_update,
.sha512_digest_final_func = test_sha512_digest_final,
.sha512_digest_cleanup_func = test_sha512_digest_cleanup,
.encrypt_func = test_encrypt,
.decrypt_func = test_decrypt,
.user_data = 0
};

signal_context_set_crypto_provider(context, &provider);
}


test-common-openssl.c

int test_random_generator(uint8_t *data, size_t len, void *user_data)
{
if(RAND_bytes(data, len)) {
return 0;
}
else {
return SG_ERR_UNKNOWN;
}
}

Answer Source

In signal_context_set_crypto_provider() there is a check:

if(!crypto_provider
        || !crypto_provider->hmac_sha256_init_func
        || !crypto_provider->hmac_sha256_update_func
        || !crypto_provider->hmac_sha256_final_func
        || !crypto_provider->hmac_sha256_cleanup_func) {
    return SG_ERR_INVAL;
}

So, the answer is that you can't leave those callbacks unset, and that's why nothing is copied to the context and the assert() fires eventually.

I understand why the test code doesn't check the return code, but in production code you need to check return values to keep an eye on errors - it's simply considered to be a good practice.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download