mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 22:57:59 -05:00
chore(csprng): add dieharder test suite
This commit is contained in:
74
.github/workflows/csprng_randomness_testing.yml
vendored
Normal file
74
.github/workflows/csprng_randomness_testing.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
name: CSPRNG randomness testing Workflow
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
RUSTFLAGS: "-C target-cpu=native"
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Allows you to run this workflow manually from the Actions tab as an alternative.
|
||||||
|
workflow_dispatch:
|
||||||
|
# All the inputs are provided by Slab
|
||||||
|
inputs:
|
||||||
|
instance_id:
|
||||||
|
description: "AWS instance ID"
|
||||||
|
type: string
|
||||||
|
instance_image_id:
|
||||||
|
description: "AWS instance AMI ID"
|
||||||
|
type: string
|
||||||
|
instance_type:
|
||||||
|
description: "AWS instance product type"
|
||||||
|
type: string
|
||||||
|
runner_name:
|
||||||
|
description: "Action runner name"
|
||||||
|
type: string
|
||||||
|
request_id:
|
||||||
|
description: 'Slab request ID'
|
||||||
|
type: string
|
||||||
|
fork_repo:
|
||||||
|
description: 'Name of forked repo as user/repo'
|
||||||
|
type: string
|
||||||
|
fork_git_sha:
|
||||||
|
description: 'Git SHA to checkout from fork'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
csprng-randomness-teting:
|
||||||
|
name: CSPRNG randomness testing
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}_${{ github.ref }}_${{ inputs.instance_image_id }}_${{ inputs.instance_type }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
runs-on: ${{ inputs.runner_name }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout tfhe-rs
|
||||||
|
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
|
||||||
|
with:
|
||||||
|
repository: ${{ inputs.fork_repo }}
|
||||||
|
ref: ${{ inputs.fork_git_sha }}
|
||||||
|
|
||||||
|
- name: Set up home
|
||||||
|
run: |
|
||||||
|
echo "HOME=/home/ubuntu" >> "${GITHUB_ENV}"
|
||||||
|
|
||||||
|
- name: Install latest stable
|
||||||
|
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
default: true
|
||||||
|
|
||||||
|
- name: Dieharder randomness test suite
|
||||||
|
run: |
|
||||||
|
make dieharder_csprng
|
||||||
|
|
||||||
|
- name: Slack Notification
|
||||||
|
if: ${{ failure() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: rtCamp/action-slack-notify@b24d75fe0e728a4bf9fc42ee217caa686d141ee8
|
||||||
|
env:
|
||||||
|
SLACK_COLOR: ${{ job.status }}
|
||||||
|
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
|
||||||
|
SLACK_ICON: https://pbs.twimg.com/profile_images/1274014582265298945/OjBKP9kn_400x400.png
|
||||||
|
SLACK_MESSAGE: "concrete-csprng randomness check finished with status: ${{ job.status }}. (${{ env.ACTION_RUN_URL }})"
|
||||||
|
SLACK_USERNAME: ${{ secrets.BOT_USERNAME }}
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
@@ -32,3 +32,4 @@ jobs:
|
|||||||
@slab-ci cpu_integer_test
|
@slab-ci cpu_integer_test
|
||||||
@slab-ci cpu_multi_bit_test
|
@slab-ci cpu_multi_bit_test
|
||||||
@slab-ci cpu_wasm_test
|
@slab-ci cpu_wasm_test
|
||||||
|
@slab-ci csprng_randomness_testing
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,3 +13,6 @@ target/
|
|||||||
# Some of our bench outputs
|
# Some of our bench outputs
|
||||||
/tfhe/benchmarks_parameters
|
/tfhe/benchmarks_parameters
|
||||||
**/*.csv
|
**/*.csv
|
||||||
|
|
||||||
|
# dieharder run log
|
||||||
|
dieharder_run.log
|
||||||
|
|||||||
13
Makefile
13
Makefile
@@ -79,6 +79,15 @@ install_node:
|
|||||||
$(SHELL) -i -c 'nvm install node' || \
|
$(SHELL) -i -c 'nvm install node' || \
|
||||||
( echo "Unable to install node, unknown error." && exit 1 )
|
( echo "Unable to install node, unknown error." && exit 1 )
|
||||||
|
|
||||||
|
.PHONY: install_dieharder # Install dieharder for apt distributions or macOS
|
||||||
|
install_dieharder:
|
||||||
|
@dieharder -h > /dev/null 2>&1 || \
|
||||||
|
if [[ "$(OS)" == "Linux" ]]; then \
|
||||||
|
sudo apt update && sudo apt install -y dieharder; \
|
||||||
|
elif [[ "$(OS)" == "Darwin" ]]; then\
|
||||||
|
brew install dieharder; \
|
||||||
|
fi || ( echo "Unable to install dieharder, unknown error." && exit 1 )
|
||||||
|
|
||||||
.PHONY: fmt # Format rust code
|
.PHONY: fmt # Format rust code
|
||||||
fmt: install_rs_check_toolchain
|
fmt: install_rs_check_toolchain
|
||||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt
|
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt
|
||||||
@@ -434,6 +443,10 @@ no_tfhe_typo:
|
|||||||
no_dbg_log:
|
no_dbg_log:
|
||||||
@./scripts/no_dbg_calls.sh
|
@./scripts/no_dbg_calls.sh
|
||||||
|
|
||||||
|
.PHONY: dieharder_csprng # Run the dieharder test suite on our CSPRNG implementation
|
||||||
|
dieharder_csprng: install_dieharder build_concrete_csprng
|
||||||
|
./scripts/dieharder_test.sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Benchmarks
|
# Benchmarks
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -77,3 +77,8 @@ check_run_name = "PBS CPU AWS Benchmarks"
|
|||||||
workflow = "wasm_client_benchmark.yml"
|
workflow = "wasm_client_benchmark.yml"
|
||||||
profile = "cpu-small"
|
profile = "cpu-small"
|
||||||
check_run_name = "WASM Client AWS Benchmarks"
|
check_run_name = "WASM Client AWS Benchmarks"
|
||||||
|
|
||||||
|
[command.csprng_randomness_testing]
|
||||||
|
workflow = "csprng_randomness_testing.yml"
|
||||||
|
profile = "cpu-small"
|
||||||
|
check_run_name = "CSPRNG randomness testing"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ libc = "0.2.133"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
clap = "=4.2.7"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
parallel = ["rayon"]
|
parallel = ["rayon"]
|
||||||
@@ -45,7 +46,7 @@ path = "benches/benchmark.rs"
|
|||||||
harness = false
|
harness = false
|
||||||
required-features = ["seeder_x86_64_rdseed", "generator_x86_64_aesni"]
|
required-features = ["seeder_x86_64_rdseed", "generator_x86_64_aesni"]
|
||||||
|
|
||||||
[[bin]]
|
[[example]]
|
||||||
name = "generate"
|
name = "generate"
|
||||||
path = "src/main.rs"
|
path = "examples/generate.rs"
|
||||||
required-features = ["seeder_unix", "generator_fallback"]
|
required-features = ["seeder_unix", "generator_fallback"]
|
||||||
|
|||||||
113
concrete-csprng/examples/generate.rs
Normal file
113
concrete-csprng/examples/generate.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
//! This program uses the concrete csprng to generate an infinite stream of random bytes on
|
||||||
|
//! the program stdout. It can also generate a fixed number of bytes by passing a value along the
|
||||||
|
//! optional argument `--bytes_total`. For testing purpose.
|
||||||
|
use clap::{value_parser, Arg, Command};
|
||||||
|
#[cfg(feature = "generator_x86_64_aesni")]
|
||||||
|
use concrete_csprng::generators::AesniRandomGenerator as ActivatedRandomGenerator;
|
||||||
|
#[cfg(feature = "generator_aarch64_aes")]
|
||||||
|
use concrete_csprng::generators::NeonAesRandomGenerator as ActivatedRandomGenerator;
|
||||||
|
#[cfg(all(
|
||||||
|
not(feature = "generator_x86_64_aesni"),
|
||||||
|
not(feature = "generator_aarch64_aes"),
|
||||||
|
feature = "generator_fallback"
|
||||||
|
))]
|
||||||
|
use concrete_csprng::generators::SoftwareRandomGenerator as ActivatedRandomGenerator;
|
||||||
|
|
||||||
|
use concrete_csprng::generators::RandomGenerator;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use concrete_csprng::seeders::AppleSecureEnclaveSeeder as ActivatedSeeder;
|
||||||
|
#[cfg(all(not(target_os = "macos"), feature = "seeder_x86_64_rdseed"))]
|
||||||
|
use concrete_csprng::seeders::RdseedSeeder as ActivatedSeeder;
|
||||||
|
#[cfg(all(
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(feature = "seeder_x86_64_rdseed"),
|
||||||
|
feature = "seeder_unix"
|
||||||
|
))]
|
||||||
|
use concrete_csprng::seeders::UnixSeeder as ActivatedSeeder;
|
||||||
|
|
||||||
|
use concrete_csprng::seeders::Seeder;
|
||||||
|
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::io::{stdout, StdoutLock};
|
||||||
|
|
||||||
|
fn write_bytes(
|
||||||
|
buffer: &mut [u8],
|
||||||
|
generator: &mut ActivatedRandomGenerator,
|
||||||
|
stdout: &mut StdoutLock<'_>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
buffer.iter_mut().zip(generator).for_each(|(b, g)| *b = g);
|
||||||
|
stdout.write_all(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infinite_bytes_generation(
|
||||||
|
buffer: &mut [u8],
|
||||||
|
generator: &mut ActivatedRandomGenerator,
|
||||||
|
stdout: &mut StdoutLock<'_>,
|
||||||
|
) {
|
||||||
|
while write_bytes(buffer, generator, stdout).is_ok() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytes_generation(
|
||||||
|
bytes_total: usize,
|
||||||
|
buffer: &mut [u8],
|
||||||
|
generator: &mut ActivatedRandomGenerator,
|
||||||
|
stdout: &mut StdoutLock<'_>,
|
||||||
|
) {
|
||||||
|
let quotient = bytes_total / buffer.len();
|
||||||
|
let remaining = bytes_total % buffer.len();
|
||||||
|
|
||||||
|
for _ in 0..quotient {
|
||||||
|
write_bytes(buffer, generator, stdout).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
write_bytes(&mut buffer[0..remaining], generator, stdout).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let matches = Command::new(
|
||||||
|
"Generate a stream of random numbers, specify no flags for infinite generation",
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("bytes_total")
|
||||||
|
.short('b')
|
||||||
|
.long("bytes_total")
|
||||||
|
.value_parser(value_parser!(usize))
|
||||||
|
.help("Total number of bytes that has to be generated"),
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
// Ugly hack to be able to use UnixSeeder
|
||||||
|
#[cfg(all(
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(feature = "seeder_x86_64_rdseed"),
|
||||||
|
feature = "seeder_unix"
|
||||||
|
))]
|
||||||
|
let new_seeder = || ActivatedSeeder::new(0);
|
||||||
|
#[cfg(not(all(
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(feature = "seeder_x86_64_rdseed"),
|
||||||
|
feature = "seeder_unix"
|
||||||
|
)))]
|
||||||
|
let new_seeder = || ActivatedSeeder;
|
||||||
|
|
||||||
|
let mut seeder = new_seeder();
|
||||||
|
let seed = seeder.seed();
|
||||||
|
// Don't print on std out
|
||||||
|
eprintln!("seed={seed:?}");
|
||||||
|
let mut generator = ActivatedRandomGenerator::new(seed);
|
||||||
|
let stdout = stdout();
|
||||||
|
let mut buffer = [0u8; 16];
|
||||||
|
|
||||||
|
// lock stdout as there is a single thread running
|
||||||
|
let mut stdout = stdout.lock();
|
||||||
|
|
||||||
|
match matches.get_one::<usize>("bytes_total") {
|
||||||
|
Some(&total) => {
|
||||||
|
bytes_generation(total, &mut buffer, &mut generator, &mut stdout);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
infinite_bytes_generation(&mut buffer, &mut generator, &mut stdout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
//! This program uses the concrete csprng to generate an infinite stream of random bytes on
|
|
||||||
//! the program stdout. For testing purpose.
|
|
||||||
#[cfg(feature = "generator_x86_64_aesni")]
|
|
||||||
use concrete_csprng::generators::AesniRandomGenerator as ActivatedRandomGenerator;
|
|
||||||
#[cfg(feature = "generator_aarch64_aes")]
|
|
||||||
use concrete_csprng::generators::NeonAesRandomGenerator as ActivatedRandomGenerator;
|
|
||||||
#[cfg(all(
|
|
||||||
not(feature = "generator_x86_64_aesni"),
|
|
||||||
not(feature = "generator_aarch64_aes"),
|
|
||||||
feature = "generator_fallback"
|
|
||||||
))]
|
|
||||||
use concrete_csprng::generators::SoftwareRandomGenerator as ActivatedRandomGenerator;
|
|
||||||
|
|
||||||
use concrete_csprng::generators::RandomGenerator;
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
use concrete_csprng::seeders::AppleSecureEnclaveSeeder as ActivatedSeeder;
|
|
||||||
#[cfg(all(not(target_os = "macos"), feature = "seeder_x86_64_rdseed"))]
|
|
||||||
use concrete_csprng::seeders::RdseedSeeder as ActivatedSeeder;
|
|
||||||
#[cfg(all(
|
|
||||||
not(target_os = "macos"),
|
|
||||||
not(feature = "seeder_x86_64_rdseed"),
|
|
||||||
feature = "seeder_unix"
|
|
||||||
))]
|
|
||||||
use concrete_csprng::seeders::UnixSeeder as ActivatedSeeder;
|
|
||||||
|
|
||||||
use concrete_csprng::seeders::Seeder;
|
|
||||||
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::io::stdout;
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
// Ugly hack to be able to use UnixSeeder
|
|
||||||
#[cfg(all(
|
|
||||||
not(target_os = "macos"),
|
|
||||||
not(feature = "seeder_x86_64_rdseed"),
|
|
||||||
feature = "seeder_unix"
|
|
||||||
))]
|
|
||||||
let new_seeder = || ActivatedSeeder::new(0);
|
|
||||||
#[cfg(not(all(
|
|
||||||
not(target_os = "macos"),
|
|
||||||
not(feature = "seeder_x86_64_rdseed"),
|
|
||||||
feature = "seeder_unix"
|
|
||||||
)))]
|
|
||||||
let new_seeder = || ActivatedSeeder;
|
|
||||||
|
|
||||||
let mut seeder = new_seeder();
|
|
||||||
let mut generator = ActivatedRandomGenerator::new(seeder.seed());
|
|
||||||
let mut stdout = stdout();
|
|
||||||
let mut buffer = [0u8; 16];
|
|
||||||
loop {
|
|
||||||
buffer
|
|
||||||
.iter_mut()
|
|
||||||
.zip(&mut generator)
|
|
||||||
.for_each(|(b, g)| *b = g);
|
|
||||||
stdout.write_all(&buffer).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
77
scripts/dieharder_test.sh
Executable file
77
scripts/dieharder_test.sh
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# dieharder does not support running a subset of its tests, so we'll check which ones are not good
|
||||||
|
# and ignore the output from those tests in the final log
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DIEHARDER_RUN_LOG_FILE="dieharder_run.log"
|
||||||
|
|
||||||
|
bad_tests="$(dieharder -l | \
|
||||||
|
# select lines with the -d
|
||||||
|
grep -w '\-d' | \
|
||||||
|
# forget about the good tests
|
||||||
|
grep -v -i 'good' | \
|
||||||
|
# get the test id
|
||||||
|
cut -d ' ' -f 4 | \
|
||||||
|
# nice formatting
|
||||||
|
xargs)"
|
||||||
|
|
||||||
|
|
||||||
|
bad_test_filter=""
|
||||||
|
for bad_test in ${bad_tests}; do
|
||||||
|
bad_test_filter="${bad_test_filter:+${bad_test_filter}|}$(dieharder -d "${bad_test}" -t 1 -p 1 -D test_name | xargs)"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "The following tests will be ignored as they are marked as either 'suspect' or 'do not use': "
|
||||||
|
echo ""
|
||||||
|
echo "${bad_test_filter}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# by default we may have no pv just forward the input
|
||||||
|
pv="cat"
|
||||||
|
if which pv > /dev/null; then
|
||||||
|
pv="pv -t -a -b"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "${DIEHARDER_RUN_LOG_FILE}"
|
||||||
|
|
||||||
|
# ignore potential errors and parse the log afterwards
|
||||||
|
set +e
|
||||||
|
|
||||||
|
# We are writing in both cases
|
||||||
|
# shellcheck disable=SC2094
|
||||||
|
./target/release/examples/generate 2>"${DIEHARDER_RUN_LOG_FILE}" | \
|
||||||
|
$pv | \
|
||||||
|
# -a: all tests
|
||||||
|
# -g 200: get random bytes from input
|
||||||
|
# -Y 1: disambiguate results, i.e. if a weak result appear check if it's a random failure/weakness
|
||||||
|
# -k 2: better maths formulas to determine some test statistics
|
||||||
|
dieharder -a -g 200 -Y 1 -k 2 | \
|
||||||
|
tee -a "${DIEHARDER_RUN_LOG_FILE}"
|
||||||
|
set -e
|
||||||
|
|
||||||
|
printf "\n\n"
|
||||||
|
|
||||||
|
cat "${DIEHARDER_RUN_LOG_FILE}"
|
||||||
|
|
||||||
|
if ! grep -q -i "failed" < "${DIEHARDER_RUN_LOG_FILE}"; then
|
||||||
|
echo "All tests passed!"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n\n"
|
||||||
|
|
||||||
|
failed_tests="$(grep -i "failed" < "${DIEHARDER_RUN_LOG_FILE}")"
|
||||||
|
true_failed_test="$(grep -i "failed" < "${DIEHARDER_RUN_LOG_FILE}" | { grep -v -E "${bad_test_filter}" || true; } | sed -z '$ s/\n$//')"
|
||||||
|
|
||||||
|
if [[ "${true_failed_test}" == "" ]]; then
|
||||||
|
echo "There were test failures, but the tests were either marked as 'suspect' or 'do not use'"
|
||||||
|
echo "${failed_tests}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "The following tests failed:"
|
||||||
|
echo "${true_failed_test}"
|
||||||
|
|
||||||
|
exit 1
|
||||||
Reference in New Issue
Block a user