provider-corner / vigenere

A toy provider implementing an expanded vigenere cipher, to serve as a programming example
Other
12 stars 8 forks source link

I tried to write a rand provider using OpenSSL 3.1.4 but crashed. #10

Closed Syllinia closed 3 months ago

Syllinia commented 3 months ago

I wrote a rand provider and compiled it into rng.so using fips_rands in fipsprov.c as an example. Then in librnd.so I load and link shared library rng.so using dlopen() and dlsym(). In the binary test_get_rnd_bytes I link librnd.so and run some test cases.

code in librnd.so.

int rng_provider_load(void)
{
...
       handle = dlopen("/lib/rng.so", RTLD_LOCAL | RTLD_NOW);
        if (NULL == handle) {
                ERR_RND("failed to dlopen rng.so(%s)\n", dlerror());
                goto out;
        }   

        provider_init_fn = dlsym(handle, "OSSL_provider_init");
        if (NULL == provider_init_fn) {
                ERR_RND("Failed to load func(%s)\n", dlerror());
                goto out;
        }   

        if (OSSL_PROVIDER_add_builtin(NULL, "rng", provider_init_fn) == 0) {
                ERR_RND("Failed to add rng provider\n");
                goto out;
        }   

        rng_provider = OSSL_PROVIDER_load(NULL, "rng");
        if (NULL == rng_provider) {
                ERR_RND("Failed to load rng provider\n");
                goto out;
        }  
...
}

int get_random_bytes(u_char *buffer, int length)
{
...
        EVP_RAND *rand = NULL;
        EVP_RAND_CTX *ctx = NULL;

        rng_provider_load();

rand = EVP_RAND_fetch(NULL, "DRBG", "provider=rng,fips=no");
        if (!rand) {
                ERR_RND("failed to fetch EVP_RAND\n");
                goto end;
        }

        ctx = EVP_RAND_CTX_new(rand, NULL);
        if (!ctx) {
                ERR_RND("failed to create EVP_RAND_CTX from EVP_RAND. rand = %p\n", rand);
                goto end;
        }

        if (!EVP_RAND_generate(ctx, buffer, length, 0, 0, NULL, 0)) {
                ERR_RND("failed to generate rand bytes. length = %d\n", length);               
                goto end;
        }
...
}

The binary crashed. Any constructive suggestions are highly welcomed.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a85d40 in ?? ()
(gdb) bt
#0  0x00007ffff7a85d40 in ?? ()
#1  0x00007ffff7cb42a2 in algorithm_do_this (provider=0x415b40, cbdata=cbdata@entry=0x7fffffffe1b0) at crypto/core_algorithm.c:120
#2  0x00007ffff7cc5e7e in ossl_provider_doall_activated (ctx=<optimized out>, cb=cb@entry=0x7ffff7cb41c0 <algorithm_do_this>, 
    cbdata=cbdata@entry=0x7fffffffe1b0) at crypto/provider_core.c:1423
#3  0x00007ffff7cb4461 in ossl_algorithm_do_all (libctx=0x0, operation_id=<optimized out>, provider=<optimized out>, 
    pre=pre@entry=0x7ffff7cb4680 <ossl_method_construct_precondition>, 
    reserve_store=reserve_store@entry=0x7ffff7cb4500 <ossl_method_construct_reserve_store>, 
    fn=fn@entry=0x7ffff7cb4580 <ossl_method_construct_this>, unreserve_store=0x7ffff7cb4560 <ossl_method_construct_unreserve_store>, 
    post=0x7ffff7cb4600 <ossl_method_construct_postcondition>, data=0x7fffffffe240) at crypto/core_algorithm.c:162
#4  0x00007ffff7cb4794 in ossl_method_construct (libctx=<optimized out>, operation_id=operation_id@entry=5, 
    provider_rw=provider_rw@entry=0x7fffffffe2c0, force_store=force_store@entry=0, mcm=mcm@entry=0x7fffffffe2d0, 
    mcm_data=mcm_data@entry=0x7fffffffe310) at crypto/core_fetch.c:153
#5  0x00007ffff7c8bf76 in inner_evp_generic_fetch (free_method=0x7ffff7c909f0 <evp_rand_free>, 
    up_ref_method=0x7ffff7c90a60 <evp_rand_up_ref>, new_method=0x7ffff7c90a80 <evp_rand_from_algorithm>, 
    properties=0x7ffff7a8f057 "provider=rng,fips=no", name=0x7ffff7a8f052 "DRBG", operation_id=5, prov=<optimized out>, 
    methdata=0x7fffffffe310) at crypto/evp/evp_fetch.c:312
#6  evp_generic_fetch (libctx=<optimized out>, operation_id=operation_id@entry=5, name=0x7ffff7a8f052 "DRBG", 
    properties=0x7ffff7a8f057 "provider=rng,fips=no", new_method=new_method@entry=0x7ffff7c90a80 <evp_rand_from_algorithm>, 
    up_ref_method=up_ref_method@entry=0x7ffff7c90a60 <evp_rand_up_ref>, free_method=0x7ffff7c909f0 <evp_rand_free>)
    at crypto/evp/evp_fetch.c:364
#7  0x00007ffff7c9118e in EVP_RAND_fetch (libctx=<optimized out>, algorithm=<optimized out>, properties=<optimized out>)
    at crypto/evp/evp_rand.c:288
#8  0x00007ffff7a8e44a in get_rnd_bytes (buffer=0x7fffffffe410 "", length=4) at rnd.c:120
#9  0x0000000000401199 in test (i=1, length=4) at test_get_rnd_bytes.c:10
#10 0x000000000040124e in main () at test_get_rnd_bytes.c:24

(gdb) f 1
#1  0x00007ffff7cb42a2 in algorithm_do_this (provider=0x415b40, cbdata=cbdata@entry=0x7fffffffe1b0) at crypto/core_algorithm.c:120
120         map = ossl_provider_query_operation(provider, cur_operation,
(gdb) p *provider
$1 = {flag_initialized = 1, flag_activated = 1, flag_lock = 0x41a3b0, refcnt = 3, refcnt_lock = 0x0, activatecnt = 2, 
  name = 0x4194c0 "rng", path = 0x0, module = 0x0, init_function = 0x7ffff7a858c0, parameters = 0x41be30, libctx = 0x0, 
  store = 0x4062b0, error_lib = 128, error_strings = 0x415c30, teardown = 0x7ffff7a85d20, gettable_params = 0x0, get_params = 0x0, 
  get_capabilities = 0x0, self_test = 0x0, query_operation = 0x7ffff7a85d40, unquery_operation = 0x0, operation_bits = 0x0, 
  operation_bits_sz = 0, opbits_lock = 0x41ace0, handle = 0x0, ischild = 0, provctx = 0x41bcf0, dispatch = 0x7ffff7a87d20}
Syllinia commented 3 months ago
$ /inst/bin/openssl rand -provider-path . -provider rng -propquery "provider=rng,fips=no" 4
80927D044D7F0000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto/evp/evp_fetch.c:341:Global default library context, Algorithm (CTR-DRBG : 117), Properties (<null>)
80927D044D7F0000:error:12000090:random number generator:rand_new_drbg:unable to fetch drbg:crypto/rand/rand_lib.c:647:
mattcaswell commented 3 months ago
    provider_init_fn = dlsym(handle, "OSSL_provider_init");
    if (NULL == provider_init_fn) {
            ERR_RND("Failed to load func(%s)\n", dlerror());
            goto out;
    }   

    if (OSSL_PROVIDER_add_builtin(NULL, "rng", provider_init_fn) == 0) {
            ERR_RND("Failed to add rng provider\n");
            goto out;
    } 

This is a little odd. Your rng.so seems to have an exported OSSL_provider_init function - but you are adding it as a "built-in". Why not just load it as a regular provider?

The binary crashed. Any constructive suggestions are highly welcomed.

We really need to see the code from your provider. Can you share that?

Syllinia commented 3 months ago

@mattcaswell Thanks Matt. I use dlopen/dlsym/OSSL_PROVIDER_add_builtin for legacy.so which is NOT in the default location indicated by "openssl version -d". So I thought the same method may work for rng.so.

Code from rng provider is as follows.

static const char RNG_FIPS_PROPERTIES[] = "provider=rng,fips=yes";
static const char RNG_NON_FIPS_PROPERTIES[] = "provider=rng,fips=no";

const OSSL_DISPATCH rng_fips_drbg_ctr_functions[] = {
    { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))rng_newctx },
    { OSSL_FUNC_RAND_FREECTX, (void(*)(void))rng_freectx },
    { OSSL_FUNC_RAND_INSTANTIATE, (void(*)(void))rng_drbg_ctr_instantiate },
    { OSSL_FUNC_RAND_UNINSTANTIATE, (void(*)(void))rng_drbg_ctr_uninstantiate },
    { OSSL_FUNC_RAND_GENERATE, (void(*)(void))rng_fips_drbg_ctr_generate },
    //{ OSSL_FUNC_RAND_RESEED, (void(*)(void))rng_drbg_ctr_reseed },
    /*{ OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking },
    { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock },
    { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock },
    { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS, (void(*)(void))drbg_ctr_settable_ctx_params },
    { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_ctr_set_ctx_params },
    { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, (void(*)(void))drbg_ctr_gettable_ctx_params },
    { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_ctr_get_ctx_params },*/
    { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, (void(*)(void))rng_drbg_ctr_verify_zeroization },
    /*{ OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed },
    { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))rng_drbg_clear_seed },*/
    { 0, NULL }
};

const OSSL_DISPATCH rng_non_fips_drbg_ctr_functions[] = {
    { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))rng_newctx },
    { OSSL_FUNC_RAND_FREECTX, (void(*)(void))rng_freectx },
    { OSSL_FUNC_RAND_INSTANTIATE, (void(*)(void))rng_drbg_ctr_instantiate },
    { OSSL_FUNC_RAND_UNINSTANTIATE, (void(*)(void))rng_drbg_ctr_uninstantiate },
    { OSSL_FUNC_RAND_GENERATE, (void(*)(void))rng_non_fips_drbg_ctr_generate },
    //{ OSSL_FUNC_RAND_RESEED, (void(*)(void))rng_drbg_ctr_reseed },
    /*{ OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking },
    { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock },
    { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock },
    { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS, (void(*)(void))drbg_ctr_settable_ctx_params },
    { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_ctr_set_ctx_params },
    { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, (void(*)(void))drbg_ctr_gettable_ctx_params },
    { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_ctr_get_ctx_params },*/
    { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, (void(*)(void))rng_drbg_ctr_verify_zeroization },
    /*{ OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed },
    { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))rng_drbg_clear_seed },*/
    { 0, NULL }
};

/* The table of rands this provider offers */
static const OSSL_ALGORITHM rng_rands[] = {
    { "CTR-DRBG", RNG_FIPS_PROPERTIES, rng_fips_drbg_ctr_functions },
    { "CTR-DRBG", RNG_NON_FIPS_PROPERTIES, rng_non_fips_drbg_ctr_functions },
    /*{ PROV_NAMES_HASH_DRBG, RNG_DEFAULT_PROPERTIES, ossl_drbg_hash_functions },
    { PROV_NAMES_HMAC_DRBG, RNG_DEFAULT_PROPERTIES, ossl_drbg_ossl_hmac_functions },
    { PROV_NAMES_TEST_RAND, RNG_UNAPPROVED_PROPERTIES, ossl_test_rng_functions },*/
    { NULL, NULL, NULL }
};

/* The function that returns the appropriate algorithm table per operation */
static const OSSL_ALGORITHM *rng_prov_operation(void *vprovctx,
                                                     int operation_id,
                                                     int *no_cache)
{
    *no_cache = 0;
    switch (operation_id) {
    case OSSL_OP_RAND:
        return rng_rands;
    }
    return NULL;
}

typedef void (*funcptr_t)(void);

/* The base dispatch table */
static const OSSL_DISPATCH provider_functions[] = { 
    { OSSL_FUNC_PROVIDER_TEARDOWN, (funcptr_t)rng_prov_teardown },
    { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (funcptr_t)rng_prov_operation },
    { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, (funcptr_t)rng_prov_get_reason_strings },
    //{ OSSL_FUNC_PROVIDER_GET_PARAMS, (funcptr_t)vigenere_prov_get_params },
    { 0, NULL }
};

int OSSL_provider_init(const OSSL_CORE_HANDLE *core,
                       const OSSL_DISPATCH *in,
                       const OSSL_DISPATCH **out,
                       void **vprovctx)
{
    if ((*vprovctx = provider_ctx_new(core, in)) == NULL)
        return 0;
    *out = provider_functions;
    return 1;
}
Syllinia commented 3 months ago

I rewrite rng_provider_load() and don't use dlopen/dlsym.

int rng_provider_load(void)
{
...
       rng_provider = OSSL_PROVIDER_load(NULL, "/lib/librng.so");
        if (NULL == rng_provider) {
                ERR_RND("Failed to load rng provider\n");
                goto out;
        }
...
}

Then I run the binary test_get_rnd_bytes and there's no SEGV caused core any more. But I got error "failed to fetch EVP_RAND" in get_rnd_bytes().

int get_rnd_bytes(u_char *buffer, int length)
{
        EVP_RAND *rand = NULL;
        EVP_RAND_CTX *ctx = NULL;
        int ret = 0;

        if (openssl_init_rng()) {
                ERR_RND("openssl_init_rng failed\n");
                goto end;
        }

                rand = EVP_RAND_fetch(NULL, "CTR-DRBG", "provider=rng,fips=no");

        if (!rand) {
                ERR_RND("failed to fetch EVP_RAND\n");
                goto end;
        }
...

Then I made the following change.

int rng_provider_load(void)
{
...
       rng_provider = OSSL_PROVIDER_load(NULL, "/lib/rng.so");

Move rng.so to OpenSSL 3.1.4 installation directory /openssl-3.1.4/inst/lib64/ossl-modules but I still get the error "failed to fetch EVP_RAND".

levitte commented 3 months ago

You can use the environment variables OPENSSL_MODULES to tell the OpenSSL library an alternative place to look for providers.

levitte commented 3 months ago
static const OSSL_ALGORITHM rng_rands[] = {
    { "CTR-DRBG", RNG_FIPS_PROPERTIES, rng_fips_drbg_ctr_functions },
    { "CTR-DRBG", RNG_NON_FIPS_PROPERTIES, rng_non_fips_drbg_ctr_functions },
...

Oh, two implementations with the same name in the same array? ........ Does OpenSSL support that? What happens if you try with the property query string "provider=rng,fips=yes"?

levitte commented 3 months ago

If it turns out that it works with "provider=rng,fips=yes", it's time to file an issue with OpenSSL

mattcaswell commented 3 months ago

Calling ERR_print_errors_fp(stderr) or similar after the failed fetch call might give some additional hints as to what the problem is.

Syllinia commented 3 months ago
OSSL_ALGORITHM 

/*

Both algorithm_names and property_definition have comment "key". So I think their combination is the key.

Syllinia commented 3 months ago

You can use the environment variables OPENSSL_MODULES to tell the OpenSSL library an alternative place to look for providers.

In my scenario it's not feasible to use environment variables.

Syllinia commented 3 months ago

ERR_print_errors_fp

./test_get_rnd_bytes Case 1: length = 4

: failed to fetch EVP_RAND 80B23E572D7F0000:error:030000C1:digital envelope routines:evp_rand_from_algorithm:invalid provider functions:crypto/evp/evp_rand.c:271: 80B23E572D7F0000:error:030000C1:digital envelope routines:evp_rand_from_algorithm:invalid provider functions:crypto/evp/evp_rand.c:271: 80B23E572D7F0000:error:0308010D:digital envelope routines:inner_evp_generic_fetch:fetch failed:crypto/evp/evp_fetch.c:341:Global default library context, Algorithm (CTR-DRBG : 0), Properties (provider=rng,fips=no) test failed. ret = 0 Case 2: length = 16 : failed to fetch EVP_RAND 80B23E572D7F0000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto/evp/evp_fetch.c:341:Global default library context, Algorithm (CTR-DRBG : 117), Properties (provider=rng,fips=no) test failed. ret = 0 Case 3: length = 32 : failed to fetch EVP_RAND 80B23E572D7F0000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto/evp/evp_fetch.c:341:Global default library context, Algorithm (CTR-DRBG : 117), Properties (provider=rng,fips=no) test failed. ret = 0
levitte commented 3 months ago

80B23E572D7F0000:error:030000C1:digital envelope routines:evp_rand_from_algorithm:invalid provider functions:crypto/evp/evp_rand.c:271:

This happens because something is missing in your OSSL_DISPATCH tables. I had a look at the code, and from what I can see, it requires OSSL_FUNC_RAND_GET_CTX_PARAMS to be present. Perhaps others too, i haven't checked very deeply.

Syllinia commented 3 months ago

80B23E572D7F0000:error:030000C1:digital envelope routines:evp_rand_from_algorithm:invalid provider functions:crypto/evp/evp_rand.c:271:

This happens because something is missing in your OSSL_DISPATCH tables. I had a look at the code, and from what I can see, it requires OSSL_FUNC_RAND_GET_CTX_PARAMS to be present. Perhaps others too, i haven't checked very deeply.

Thanks so much. Let me add more functions including OSSL_FUNC_RAND_GET_CTX_PARAMS.

Syllinia commented 3 months ago

After adding 3 new functions for OSSL_FUNC_RAND_ENABLE_LOCKING, OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS and OSSL_FUNC_RAND_GET_CTX_PARAMS. Now the binary works. Thanks so much for your help.

$ ./test_get_rnd_bytes Case 1: length = 4 test succeeded. random bytes are as follows: 15 b2 38 b4 Case 2: length = 16 test succeeded. random bytes are as follows: 92 d0 46 e2 78 ea 38 87 eb 48 b9 a2 68 67 61 90 Case 3: length = 32 test succeeded. random bytes are as follows: 46 4a 77 5f f3 da 25 24 95 c6 7b 81 66 f9 4a ec 1e 71 e5 f5 a c9 b8 5b 14 c0 dd a8 f0 b e9 34

Syllinia commented 3 months ago

$ openssl-3.1.4-quic1/inst/bin/openssl rand -provider-path . -provider rng -propquery "provider=rng,fips=no" 4|od -tx1 0000000 9e 3b 47 19 0000004 $ openssl-3.1.4-quic1/inst/bin/openssl rand -provider-path . -provider rng -propquery "provider=rng,fips=yes" 4|od -tx1 0000000 43 bc 52 57 0000004 $ openssl-3.1.4-quic1/inst/bin/openssl rand -provider-path . -provider rng 4|od -tx1 0000000 3c 61 43 65 0000004

levitte commented 3 months ago

Congrats!