Skip to content

Commit

Permalink
fix OpenSSL's deterministic RNG (tlspuffin#309)
Browse files Browse the repository at this point in the history
* fix(openssl): correctly reseed OpenSSL's deterministic RNG
* refactor(openssl): don't rely on RAND_seed to reset deterministic RNG
* docs(openssl): remove outdated comments
* feat(openssl): only reseed deterministic RNG when requested
* fix(openssl): avoid race conditions when reseeding RNG
* build: fix just fmt-clang recipe on macos
  • Loading branch information
michaelmera authored Apr 5, 2024
1 parent c3a6d8a commit d0ab7ab
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 46 deletions.
44 changes: 33 additions & 11 deletions crates/openssl-src-111/src/deterministic_rand.c
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
// based on https://stackoverflow.com/a/7510354
#include <openssl/rand.h>
#include <stdint.h>
#include <stdlib.h>

static uint64_t seed = 42;
#ifndef thread_local
// since C11 the standard include _Thread_local
#if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
#define thread_local _Thread_local

// note that __GNUC__ covers clang and ICC
#elif defined __GNUC__ || defined __SUNPRO_C || defined __xlC__
#define thread_local __thread

#else
#error "no support for thread-local declarations"
#endif
#endif

#define DEFAULT_RNG_SEED 42

static thread_local uint64_t seed = DEFAULT_RNG_SEED;

#define UNUSED(x) (void)(x)

// Seed the RNG. srand() takes an unsigned int, so we just use the first
// sizeof(uint64_t) bytes in the buffer to seed the RNG.
void deterministic_rng_set();
void deterministic_rng_reseed(const uint8_t *buffer, size_t length);

static int stdlib_rand_seed(const void *buf, int num)
{
if (num < sizeof(uint64_t))
{
return 0;
}
seed = *((uint64_t *)buf);
deterministic_rng_reseed(buf, num);
return 1;
}

// Fill the buffer with random bytes. For each byte in the buffer, we generate
// a random number and clamp it to the range of a byte, 0-255.
static int stdlib_rand_bytes(unsigned char *buf, int num)
{
for (int index = 0; index < num; ++index)
Expand Down Expand Up @@ -56,7 +68,17 @@ RAND_METHOD stdlib_rand_meth = {
stdlib_rand_status,
};

void make_openssl_deterministic()
void deterministic_rng_set()
{
RAND_set_rand_method(&stdlib_rand_meth);
}

void deterministic_rng_reseed(const uint8_t *buffer, size_t length)
{
if (buffer == NULL || length < sizeof(uint64_t))
{
seed = DEFAULT_RNG_SEED;
}

seed = *((uint64_t *)buffer);
}
1 change: 0 additions & 1 deletion crates/openssl-src-111/src/openssl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
extern crate cc;

use std::io::ErrorKind;
use std::{
env,
fs::canonicalize,
Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fmt-clang:
find {{ justfile_directory() }} -type f \
| grep -v "^{{ justfile_directory() / "vendor" }}" \
| grep -v "^{{ justfile_directory() / "target" }}" \
| grep -E ".*\.(c|h|C|H|cpp|hpp|cc|hh|c++|h++|cxx|hxx)$"
| grep -E ".*\.(c|h|C|H|cpp|hpp|cc|hh|c\+\+|h\+\+|cxx|hxx)$"
)
printf '%s\n' "${FILES}" | xargs -L1 clang-format --verbose -style=file -i
Expand All @@ -75,7 +75,7 @@ fmt-clang-check:
find {{ justfile_directory() }} -type f \
| grep -v "^{{ justfile_directory() / "vendor" }}" \
| grep -v "^{{ justfile_directory() / "target" }}" \
| grep -E ".*\.(c|h|C|H|cpp|hpp|cc|hh|c++|h++|cxx|hxx)$"
| grep -E ".*\.(c|h|C|H|cpp|hpp|cc|hh|c\+\+|h\+\+|cxx|hxx)$"
)
check() {
Expand Down
79 changes: 59 additions & 20 deletions tlspuffin/src/openssl/deterministic.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
use std::os::raw::c_int;
use log::debug;

use log::warn;

#[cfg(feature = "deterministic")]
extern "C" {
fn make_openssl_deterministic();
fn RAND_seed(buf: *mut u8, num: c_int);
fn deterministic_rng_set();
fn deterministic_rng_reseed(buffer: *const u8, length: libc::size_t);
}

#[cfg(feature = "deterministic")]
pub fn set_openssl_deterministic() {
warn!("OpenSSL is no longer random!");
pub fn rng_set() {
debug!("setting OpenSSL in deterministic mode");
unsafe {
make_openssl_deterministic();
let mut seed: [u8; 4] = 42u32.to_le().to_ne_bytes();
let buf = seed.as_mut_ptr();
RAND_seed(buf, 4);
deterministic_rng_set();
}
}

pub fn rng_reseed() {
const DEFAULT_SEED: [u8; 8] = 42u64.to_le().to_ne_bytes();
rng_reseed_with(&DEFAULT_SEED);
}

pub fn rng_reseed_with(buffer: &[u8]) {
unsafe {
deterministic_rng_reseed(buffer.as_ptr(), buffer.len());
}
}

Expand All @@ -24,12 +28,47 @@ mod tests {
use openssl::rand::rand_bytes;

#[test]
#[cfg(feature = "openssl111-binding")]
fn test_openssl_no_randomness() {
use crate::openssl::deterministic::set_openssl_deterministic;
set_openssl_deterministic();
let mut buf1 = [0; 2];
rand_bytes(&mut buf1).unwrap();
assert_eq!(buf1, [183, 96]);
fn test_openssl_rng_reseed_with_default_seed_has_not_changed() {
crate::openssl::deterministic::rng_set();
crate::openssl::deterministic::rng_reseed();

let mut bytes = [0; 2];
rand_bytes(&mut bytes).unwrap();
assert_eq!(bytes, [183, 96]);
}

#[test]
fn test_openssl_rng_reseed_with_same_seed_are_identical() {
const SEED: [u8; 8] = 789u64.to_le().to_ne_bytes();

crate::openssl::deterministic::rng_set();

let mut reseed1 = [0; 2];
crate::openssl::deterministic::rng_reseed_with(&SEED);
rand_bytes(&mut reseed1).unwrap();

let mut reseed2 = [0; 2];
crate::openssl::deterministic::rng_reseed_with(&SEED);
rand_bytes(&mut reseed2).unwrap();

assert_eq!(reseed1, reseed2);
}

#[test]
fn test_openssl_rng_reseed_with_different_seeds_are_different() {
const SEED1: [u8; 8] = 123u64.to_le().to_ne_bytes();
const SEED2: [u8; 8] = 321u64.to_le().to_ne_bytes();

crate::openssl::deterministic::rng_set();

crate::openssl::deterministic::rng_reseed_with(&SEED1);
let mut bytes_seed1 = [0; 2];
rand_bytes(&mut bytes_seed1).unwrap();

crate::openssl::deterministic::rng_reseed_with(&SEED2);
let mut bytes_seed2 = [0; 2];
rand_bytes(&mut bytes_seed2).unwrap();

assert_ne!(bytes_seed1, bytes_seed2);
}
}
20 changes: 8 additions & 12 deletions tlspuffin/src/openssl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,6 @@ mod bindings;
mod deterministic;
mod util;

/*
Change openssl version:
cargo clean -p openssl-src
cd openssl-src/openssl
git checkout OpenSSL_1_1_1j
*/

pub fn new_openssl_factory() -> Box<dyn Factory<TLSProtocolBehavior>> {
struct OpenSSLFactory;
impl Factory<TLSProtocolBehavior> for OpenSSLFactory {
Expand Down Expand Up @@ -85,16 +78,19 @@ pub fn new_openssl_factory() -> Box<dyn Factory<TLSProtocolBehavior>> {
OpenSSL::version()
}

fn determinism_set_reseed(&self) -> () {
fn determinism_set_reseed(&self) {
debug!("[Determinism] set and reseed");
#[cfg(feature = "deterministic")]
deterministic::set_openssl_deterministic();
{
deterministic::rng_set();
deterministic::rng_reseed();
}
}

fn determinism_reseed(&self) -> () {
fn determinism_reseed(&self) {
debug!("[Determinism] reseed");
#[cfg(feature = "deterministic")]
deterministic::set_openssl_deterministic();
deterministic::rng_reseed();
}
}

Expand Down Expand Up @@ -235,7 +231,7 @@ impl Put<TLSProtocolBehavior> for OpenSSL {
fn determinism_reseed(&mut self) -> Result<(), Error> {
#[cfg(feature = "deterministic")]
{
deterministic::set_openssl_deterministic();
deterministic::rng_reseed();
Ok(())
}
#[cfg(not(feature = "deterministic"))]
Expand Down

0 comments on commit d0ab7ab

Please sign in to comment.