Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 108 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,24 @@ zeroize = { version = "1", optional = true, default-features = false }
bincode = { version = "2", features = ["serde"] }
chacha20 = { version = "0.10.0-rc.3", default-features = false, features = ["rng"] }
criterion = { version = "0.7", features = ["html_reports"] }
gungraun = { version = "0.17.0"}
hex-literal = "1"
num-bigint = "0.4"
num-integer = "0.1"
num-modular = { version = "0.6", features = ["num-bigint", "num-integer", "num-traits"] }
proptest = "1.9"
rand_core = "0.10.0-rc-2"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1" }
walkdir = "2"


[features]
default = ["rand"]
alloc = ["serdect?/alloc"]

extra-sizes = []
gungraun = []
rand = ["rand_core"]
serde = ["dep:serdect"]

Expand Down Expand Up @@ -84,5 +90,11 @@ harness = false
name = "int"
harness = false

[[bench]]
name = "ct_gungraun"
path = "benches/gungraun/mod.rs"
harness = false
required-features = ["gungraun", "rand"]

[profile.dev]
opt-level = 2
69 changes: 69 additions & 0 deletions benches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Gungraun-Based Constant-Time Benchmarks

This directory contains the gungraun benchmarks and helper tooling used to
inspect the instruction-count behavior operations in
`crypto-bigint`.

## Running the benchmarks

The `ct_gungraun` bench is defined in `Cargo.toml` as a single bench
target which uses the gungraun harness in `benches/gungraun/mod.rs`:

```bash
cargo bench --bench ct_gungraun --features "gungraun rand"
```

To collect Callgrind summaries (used by the checker in CI), enable
`GUNGRAUN_SAVE_SUMMARY`:

```bash
GUNGRAUN_SAVE_SUMMARY=json \
cargo bench --bench ct_gungraun --features "gungraun rand"
```

This populates `target/gungraun/` with per-case `summary.json` files.

## Constant-time checker

There is an integration test `tests/check_ct_gungraun.rs` which parses
the gungraun `summary.json` files, extracts Callgrind’s `Ir` (instruction
count) metric, and enforces constant-time behavior:

- Groups whose module path contains `vartime` are **skipped**
(e.g. `uint_cmp_vartime`).
- All other groups are treated as constant-time and must have identical
`Ir` across all their benchmark cases.

Usage (after running the bench with summaries enabled):

```bash
cargo test --test check_ct_gungraun
```

If everything is as expected, the test passes and prints a summary like:

```text
Constant-time check passed: N const-time groups, M vartime groups skipped.
```

If any constant-time group has differing `Ir` values across cases, the
test prints a diagnostic and fails, which is intended to fail CI.

## Adding new benchmarks

For a new constant-time operation:

1. Create a new `ct_*` file (e.g. `ct_uint_foo.rs`) that:
- Uses `#[library_benchmark]` and `#[bench::...]` or
`#[benches::with_iter(...)]` to define cases.
- Keeps the function name and module path *without* `vartime` in the
name (so the checker will enforce constant-time behavior).
2. Re-export the function in `mod.rs` and add it to a
`library_benchmark_group!` that is included in the `main!` macro.
3. Re-run:
- `GUNGRAUN_SAVE_SUMMARY=json cargo bench --bench ct_gungraun --features "gungraun rand"`
- `cargo test --test check_ct_gungraun`

For a new **variable-time** operation (for comparison only), include
`vartime` in the module path or function name so that the checker skips
it automatically.***
24 changes: 24 additions & 0 deletions benches/gungraun/ct_uint_cmp_vartime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::hint::black_box;

use crypto_bigint::{BitOps, U1024};
use gungraun::library_benchmark;

use super::utils::random_uint;

fn high_bit_u1024() -> U1024 {
let mut x = U1024::ZERO;
x.set_bit_vartime(U1024::BITS - 1, true);
x
}

// Benchmark variable-time comparison for U1024 with different operand patterns.
#[library_benchmark]
#[bench::equal(U1024::ZERO, U1024::ZERO)]
#[bench::diff_low(U1024::ONE, U1024::ZERO)]
#[bench::diff_high(high_bit_u1024(), U1024::ZERO)]
#[bench::random_1(random_uint(1), random_uint(2))]
#[bench::random_2(random_uint(3), random_uint(4))]
#[bench::random_3(random_uint(5), random_uint(6))]
pub fn bench_u1024_cmp_vartime(a: U1024, b: U1024) -> core::cmp::Ordering {
black_box(a.cmp_vartime(&b))
}
18 changes: 18 additions & 0 deletions benches/gungraun/ct_uint_gcd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::hint::black_box;

use crypto_bigint::{Gcd, U1024};
use gungraun::library_benchmark;

use super::utils::random_uint;

// Constant-time GCD via the `Gcd` trait.
#[library_benchmark]
#[bench::zeros(U1024::ZERO, U1024::ZERO)]
#[bench::one_and_zero(U1024::ONE, U1024::ZERO)]
#[bench::max_and_one(U1024::MAX, U1024::ONE)]
#[bench::random_1(random_uint(1), random_uint(2))]
#[bench::random_2(random_uint(3), random_uint(4))]
#[bench::random_3(random_uint(5), random_uint(6))]
pub fn bench_u1024_gcd(a: U1024, b: U1024) -> U1024 {
black_box(a.gcd(black_box(&b)))
}
43 changes: 43 additions & 0 deletions benches/gungraun/ct_uint_modpow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::hint::black_box;

use crypto_bigint::{Odd, U1024, modular::MontyForm};
use gungraun::library_benchmark;

use super::utils::random_uint;

fn modpow_cases() -> Vec<(U1024, U1024, Odd<U1024>)> {
let values = [U1024::ZERO, U1024::ONE, U1024::MAX, random_uint(1)];

// Reuse the same value candidates as potential moduli, but keep only
// odd, non-zero ones.
let mut moduli = Vec::<Odd<U1024>>::new();
for &n in &values {
let candidate = Odd::new(n);
if bool::from(candidate.is_some()) {
moduli.push(candidate.unwrap());
}
}

let mut cases = Vec::new();
for &base in &values {
for &exp in &values {
for &m in &moduli {
cases.push((base, exp, m));
}
}
}

cases
}

// Benchmark modular exponentiation using Montgomery form (`MontyForm::pow`) for U1024.
#[library_benchmark]
#[benches::with_iter(iter = modpow_cases())]
pub fn bench_u1024_modpow((base, exponent, modulus): (U1024, U1024, Odd<U1024>)) -> U1024 {
let base = black_box(base);
let exponent = black_box(exponent);
let modulus = black_box(modulus);
let params = black_box(crypto_bigint::modular::MontyParams::new(modulus));
let base_monty = black_box(MontyForm::new(&base, params));
black_box(base_monty.pow(&exponent).retrieve())
}
Loading
Loading