diff --git a/.github/workflows/run-checker-daily.yml b/.github/workflows/run-checker-daily.yml index abd663c03e..af9bd2ad99 100644 --- a/.github/workflows/run-checker-daily.yml +++ b/.github/workflows/run-checker-daily.yml @@ -191,6 +191,34 @@ jobs: if: steps.sctp_auth.outcome == 'success' && steps.sctp_auth.conclusion == 'success' run: make test HARNESS_JOBS=${HARNESS_JOBS:-4} + jitter_provider: + runs-on: ubuntu-latest + steps: + - name: checkout openssl + uses: actions/checkout@v4 + - name: checkout jitter + uses: actions/checkout@v4 + with: + repository: smuellerDD/jitterentropy-library + ref: v3.5.0 + path: jitter + - name: build jitter + run: make -C jitter/ + - name: checkout fuzz/corpora submodule + run: git submodule update --init --depth 1 fuzz/corpora + - name: config + run: ./config enable-jitter --with-jitter-include=jitter/ --with-jitter-lib=jitter/ && perl configdata.pm --dump + - name: make + run: make -s -j4 + - name: get cpu info + run: | + cat /proc/cpuinfo + ./util/opensslwrap.sh version -c + - name: make test + run: make test HARNESS_JOBS=${HARNESS_JOBS:-4} + - name: test jitter entropy + run: ./util/wrap.pl -jitter ./apps/openssl rand -hex 8 + enable_brotli_dynamic: runs-on: ubuntu-latest steps: diff --git a/CHANGES.md b/CHANGES.md index 594efc44de..13c28816ac 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -167,6 +167,11 @@ OpenSSL 3.3 ### Changes between 3.2 and 3.3.0 [9 Apr 2024] + * Add a new random seed source RNG `JITTER` using a statically linked + jitterentropy library. + + *Dimitri John Ledkov* + * The `-verify` option to the `openssl crl` and `openssl req` will make the program exit with 1 on failure. diff --git a/Configurations/00-base-templates.conf b/Configurations/00-base-templates.conf index a9ccb0ced8..d55ecea6dc 100644 --- a/Configurations/00-base-templates.conf +++ b/Configurations/00-base-templates.conf @@ -59,6 +59,8 @@ my %targets=( includes => sub { my @incs = (); + push @incs, $withargs{jitter_include} + if !$disabled{jitter} && $withargs{jitter_include}; push @incs, $withargs{brotli_include} if !$disabled{brotli} && $withargs{brotli_include}; push @incs, $withargs{zlib_include} @@ -95,6 +97,7 @@ my %targets=( lflags => sub { my @libs = (); + push(@libs, "-L".$withargs{jitter_lib}) if $withargs{jitter_lib}; push(@libs, "-L".$withargs{zlib_lib}) if $withargs{zlib_lib}; push(@libs, "-L".$withargs{brotli_lib}) if $withargs{brotli_lib}; push(@libs, "-L".$withargs{zstd_lib}) if $withargs{zstd_lib}; @@ -103,6 +106,7 @@ my %targets=( ex_libs => sub { my @libs = (); + push(@libs, "-l:libjitterentropy.a") if !defined($disabled{jitter}); push(@libs, "-lz") if !defined($disabled{zlib}) && defined($disabled{"zlib-dynamic"}); if (!defined($disabled{brotli}) && defined($disabled{"brotli-dynamic"})) { push(@libs, "-lbrotlienc"); diff --git a/Configure b/Configure index 37e0b51c4f..a7ba06f4a1 100755 --- a/Configure +++ b/Configure @@ -476,6 +476,7 @@ my @disablables = ( "gost", "http", "idea", + "jitter", "ktls", "legacy", "loadereng", @@ -586,6 +587,7 @@ our %disabled = ( # "what" => "comment" "fuzz-afl" => "default", "fuzz-libfuzzer" => "default", "pie" => "default", + "jitter" => "default", "ktls" => "default", "md2" => "default", "msan" => "default", @@ -1019,6 +1021,14 @@ while (@argvcopy) { $config{openssldir}=$1; } + elsif (/^--with-jitter-include=(.*)$/) + { + $withargs{jitter_include}=$1; + } + elsif (/^--with-jitter-lib=(.*)$/) + { + $withargs{jitter_lib}=$1; + } elsif (/^--with-zlib-lib=(.*)$/) { $withargs{zlib_lib}=$1; diff --git a/INSTALL.md b/INSTALL.md index 2060973ec2..0d87a3bd48 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -521,6 +521,46 @@ at the end of this document. [rng]: #notes-on-random-number-generation +# jitter + +When configured with `enable-jitter`, a "JITTER" RNG is compiled that +can provided alternative software seed source. It can be configured by +loadin setting `seed` setting in `openssl.cnf`. An example +`openssl.cnf` is shown below: + + openssl_conf = openssl_init + + # Comment out the next line to ignore configuration errors + config_diagnostics = 1 + + [openssl_init] + providers = provider_sect + random = random + + [provider_sect] + default = default_sect + + [default_sect] + activate = 1 + + [random] + seed=JITTER + +It uses statically linked [jitterentropy-library](https://github.com/smuellerDD/jitterentropy-library) as the seed source. + +Additional configuration flags available: + + --with-jitter-include=DIR + +The directory for the location of the jitterentropy.h include file, if +it is outside the system include path. + + --with-jitter-lib=DIR + +This is the directory containing the static libjitterentropy.a +library, if it is outside the system library path. + + Setting the FIPS HMAC key ------------------------- diff --git a/crypto/info.c b/crypto/info.c index 0dc26bd6f5..f1fec3882d 100644 --- a/crypto/info.c +++ b/crypto/info.c @@ -15,6 +15,11 @@ #include "internal/e_os.h" #include "buildinf.h" +#ifndef OPENSSL_NO_JITTER +# include +# include +#endif + #if defined(__arm__) || defined(__arm) || defined(__aarch64__) # include "arm_arch.h" # define CPU_INFO_STR_LEN 128 @@ -182,6 +187,11 @@ DEFINE_RUN_ONCE_STATIC(init_info_strings) #endif #ifdef OPENSSL_RAND_SEED_OS add_seeds_string("os-specific"); +#endif +#ifndef OPENSSL_NO_JITTER + char jent_version_string[32]; + sprintf(jent_version_string, "JITTER (%d)", jent_version()); + add_seeds_string(jent_version_string); #endif seed_sources = seeds; } diff --git a/providers/baseprov.c b/providers/baseprov.c index 6b8de7cb36..d27fd28e2c 100644 --- a/providers/baseprov.c +++ b/providers/baseprov.c @@ -93,6 +93,9 @@ static const OSSL_ALGORITHM base_store[] = { static const OSSL_ALGORITHM base_rands[] = { { PROV_NAMES_SEED_SRC, "provider=base", ossl_seed_src_functions }, +#ifndef OPENSSL_NO_JITTER + { PROV_NAMES_JITTER, "provider=base", ossl_jitter_functions }, +#endif { NULL, NULL, NULL } }; diff --git a/providers/defltprov.c b/providers/defltprov.c index f02e04835d..004aea6b2f 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -387,6 +387,9 @@ static const OSSL_ALGORITHM deflt_rands[] = { { PROV_NAMES_HASH_DRBG, "provider=default", ossl_drbg_hash_functions }, { PROV_NAMES_HMAC_DRBG, "provider=default", ossl_drbg_ossl_hmac_functions }, { PROV_NAMES_SEED_SRC, "provider=default", ossl_seed_src_functions }, +#ifndef OPENSSL_NO_JITTER + { PROV_NAMES_JITTER, "provider=default", ossl_jitter_functions }, +#endif { PROV_NAMES_TEST_RAND, "provider=default", ossl_test_rng_functions }, { NULL, NULL, NULL } }; diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 80b544c429..d845b67d88 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -290,6 +290,7 @@ extern const OSSL_DISPATCH ossl_kdf_argon2id_functions[]; /* RNGs */ extern const OSSL_DISPATCH ossl_test_rng_functions[]; extern const OSSL_DISPATCH ossl_seed_src_functions[]; +extern const OSSL_DISPATCH ossl_jitter_functions[]; extern const OSSL_DISPATCH ossl_drbg_hash_functions[]; extern const OSSL_DISPATCH ossl_drbg_ossl_hmac_functions[]; extern const OSSL_DISPATCH ossl_drbg_ctr_functions[]; diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h index 800ff5c414..67af02f755 100644 --- a/providers/implementations/include/prov/names.h +++ b/providers/implementations/include/prov/names.h @@ -311,6 +311,7 @@ #define PROV_NAMES_HMAC_DRBG "HMAC-DRBG" #define PROV_NAMES_TEST_RAND "TEST-RAND" #define PROV_NAMES_SEED_SRC "SEED-SRC" +#define PROV_NAMES_JITTER "JITTER" /*- * Asymmetric algos diff --git a/providers/implementations/include/prov/seeding.h b/providers/implementations/include/prov/seeding.h index af6cb79fb2..5067aa3026 100644 --- a/providers/implementations/include/prov/seeding.h +++ b/providers/implementations/include/prov/seeding.h @@ -13,6 +13,7 @@ /* Hardware-based seeding functions. */ size_t ossl_prov_acquire_entropy_from_tsc(RAND_POOL *pool); size_t ossl_prov_acquire_entropy_from_cpu(RAND_POOL *pool); +size_t ossl_prov_acquire_entropy_from_jitter(RAND_POOL *pool); /* * External seeding functions from the core dispatch table. diff --git a/providers/implementations/rands/build.info b/providers/implementations/rands/build.info index 8bcac43be7..70f8454193 100644 --- a/providers/implementations/rands/build.info +++ b/providers/implementations/rands/build.info @@ -3,4 +3,4 @@ SUBDIRS=seeding $RANDS_GOAL=../../libdefault.a ../../libfips.a SOURCE[$RANDS_GOAL]=drbg.c test_rng.c drbg_ctr.c drbg_hash.c drbg_hmac.c crngt.c -SOURCE[../../libdefault.a]=seed_src.c +SOURCE[../../libdefault.a]=seed_src.c seed_src_jitter.c diff --git a/providers/implementations/rands/seed_src_jitter.c b/providers/implementations/rands/seed_src_jitter.c new file mode 100644 index 0000000000..6aadeed4fa --- /dev/null +++ b/providers/implementations/rands/seed_src_jitter.c @@ -0,0 +1,322 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "crypto/rand.h" +#include "crypto/rand_pool.h" + +#ifndef OPENSSL_NO_JITTER +# include + +# define JITTER_MAX_NUM_TRIES 3 + +static OSSL_FUNC_rand_newctx_fn jitter_new; +static OSSL_FUNC_rand_freectx_fn jitter_free; +static OSSL_FUNC_rand_instantiate_fn jitter_instantiate; +static OSSL_FUNC_rand_uninstantiate_fn jitter_uninstantiate; +static OSSL_FUNC_rand_generate_fn jitter_generate; +static OSSL_FUNC_rand_reseed_fn jitter_reseed; +static OSSL_FUNC_rand_gettable_ctx_params_fn jitter_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn jitter_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn jitter_verify_zeroization; +static OSSL_FUNC_rand_enable_locking_fn jitter_enable_locking; +static OSSL_FUNC_rand_lock_fn jitter_lock; +static OSSL_FUNC_rand_unlock_fn jitter_unlock; +static OSSL_FUNC_rand_get_seed_fn jitter_get_seed; +static OSSL_FUNC_rand_clear_seed_fn jitter_clear_seed; +static size_t get_jitter_random_value(unsigned char *buf, size_t len); + +typedef struct { + void *provctx; + int state; +} PROV_JITTER; + +/* + * Acquire entropy from jitterentropy library + * + * Returns the total entropy count, if it exceeds the requested + * entropy count. Otherwise, returns an entropy count of 0. + */ +size_t ossl_prov_acquire_entropy_from_jitter(RAND_POOL *pool) +{ + size_t bytes_needed; + unsigned char *buffer; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /* entropy_factor */); + if (bytes_needed > 0) { + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + + if (buffer != NULL) { + if (get_jitter_random_value(buffer, bytes_needed) == bytes_needed) { + ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed); + } else { + ossl_rand_pool_add_end(pool, 0, 0); + } + } + } + + return ossl_rand_pool_entropy_available(pool); +} + +/* Obtain random bytes from the jitter library */ +static size_t get_jitter_random_value(unsigned char *buf, size_t len) +{ + struct rand_data *jitter_ec = NULL; + ssize_t result = 0; + size_t num_tries; + + jitter_ec = jent_entropy_collector_alloc(0, JENT_FORCE_FIPS); + if (jitter_ec == NULL) + return 0; + + for (num_tries = 0; num_tries < JITTER_MAX_NUM_TRIES; num_tries++) { + /* + * Do not use _safe API variant with built-in retries, until + * failure because it reseeds the entropy source which is not + * certifyable + */ + result = jent_read_entropy(jitter_ec, (char *) buf, len); + + /* Success */ + if (result == len) { + jent_entropy_collector_free(jitter_ec); + return len; + } + } + + jent_entropy_collector_free(jitter_ec); + + /* Catastrophic failure, maybe should abort here */ + return 0; +} + +static void *jitter_new(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + PROV_JITTER *s; + + if (parent != NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT); + return NULL; + } + + s = OPENSSL_zalloc(sizeof(*s)); + if (s == NULL) + return NULL; + + s->provctx = provctx; + s->state = EVP_RAND_STATE_UNINITIALISED; + return s; +} + +static void jitter_free(void *vseed) +{ + OPENSSL_free(vseed); +} + +static int jitter_instantiate(void *vseed, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, + size_t pstr_len, + ossl_unused const OSSL_PARAM params[]) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + + if (jent_entropy_init_ex(0, JENT_FORCE_FIPS)) + return 0; + + s->state = EVP_RAND_STATE_READY; + return 1; +} + +static int jitter_uninstantiate(void *vseed) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + + s->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static int jitter_generate(void *vseed, unsigned char *out, size_t outlen, + unsigned int strength, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + size_t entropy_available; + RAND_POOL *pool; + + if (s->state != EVP_RAND_STATE_READY) { + ERR_raise(ERR_LIB_PROV, + s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE + : PROV_R_NOT_INSTANTIATED); + return 0; + } + + pool = ossl_rand_pool_new(strength, 1, outlen, outlen); + if (pool == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB); + return 0; + } + + /* Get entropy from jitter entropy library. */ + entropy_available = ossl_prov_acquire_entropy_from_jitter(pool); + + if (entropy_available > 0) + memcpy(out, ossl_rand_pool_buffer(pool), ossl_rand_pool_length(pool)); + + ossl_rand_pool_free(pool); + return entropy_available > 0; +} + +static int jitter_reseed(void *vseed, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *ent, + ossl_unused size_t ent_len, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + + if (s->state != EVP_RAND_STATE_READY) { + ERR_raise(ERR_LIB_PROV, + s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE + : PROV_R_NOT_INSTANTIATED); + return 0; + } + return 1; +} + +static int jitter_get_ctx_params(void *vseed, OSSL_PARAM params[]) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, s->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, 1024)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 128)) + return 0; + return 1; +} + +static const OSSL_PARAM *jitter_gettable_ctx_params(ossl_unused void *vseed, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int jitter_verify_zeroization(ossl_unused void *vseed) +{ + return 1; +} + +static size_t jitter_get_seed(void *vseed, unsigned char **pout, + int entropy, size_t min_len, + size_t max_len, + int prediction_resistance, + const unsigned char *adin, + size_t adin_len) +{ + size_t ret = 0; + size_t entropy_available = 0; + size_t i; + RAND_POOL *pool; + + pool = ossl_rand_pool_new(entropy, 1, min_len, max_len); + if (pool == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB); + return 0; + } + + /* Get entropy from jitter entropy library. */ + entropy_available = ossl_prov_acquire_entropy_from_jitter(pool); + + if (entropy_available > 0) { + ret = ossl_rand_pool_length(pool); + *pout = ossl_rand_pool_detach(pool); + + /* xor the additional data into the output */ + for (i = 0; i < adin_len; ++i) + (*pout)[i % ret] ^= adin[i]; + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK); + } + ossl_rand_pool_free(pool); + return ret; +} + +static void jitter_clear_seed(ossl_unused void *vdrbg, + unsigned char *out, size_t outlen) +{ + OPENSSL_secure_clear_free(out, outlen); +} + +static int jitter_enable_locking(ossl_unused void *vseed) +{ + return 1; +} + +int jitter_lock(ossl_unused void *vctx) +{ + return 1; +} + +void jitter_unlock(ossl_unused void *vctx) +{ +} + +const OSSL_DISPATCH ossl_jitter_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))jitter_new }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))jitter_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))jitter_instantiate }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))jitter_uninstantiate }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))jitter_generate }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))jitter_reseed }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))jitter_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))jitter_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))jitter_unlock }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))jitter_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))jitter_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))jitter_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))jitter_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))jitter_clear_seed }, + OSSL_DISPATCH_END +}; +#else +NON_EMPTY_TRANSLATION_UNIT +#endif diff --git a/test/default-and-jitter.cnf b/test/default-and-jitter.cnf new file mode 100644 index 0000000000..aec9b85445 --- /dev/null +++ b/test/default-and-jitter.cnf @@ -0,0 +1,17 @@ +openssl_conf = openssl_init + +# Comment out the next line to ignore configuration errors +config_diagnostics = 1 + +[openssl_init] +providers = provider_sect +random = random + +[provider_sect] +default = default_sect + +[default_sect] +activate = 1 + +[random] +seed=JITTER diff --git a/util/wrap.pl.in b/util/wrap.pl.in index 7d11cb7c27..9b2b684c57 100644 --- a/util/wrap.pl.in +++ b/util/wrap.pl.in @@ -36,6 +36,18 @@ if ($ARGV[0] eq '-fips') { $std_openssl_conf_include = catdir($there, 'providers'); } +if ($ARGV[0] eq '-jitter') { + $std_openssl_conf = {- + use Cwd qw(abs_path); + + "'" . abs_path(catfile($config{sourcedir}, 'test/default-and-jitter.cnf')) . "'"; + -}; + shift; + + $std_openssl_conf_include = catdir($there, 'providers'); +} + + local $ENV{OPENSSL_CONF_INCLUDE} = $std_openssl_conf_include if defined $std_openssl_conf_include &&($ENV{OPENSSL_CONF_INCLUDE} // '') eq ''