-
Notifications
You must be signed in to change notification settings - Fork 707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bench: s2n_constant_time_equals #4717
Open
jmayclin
wants to merge
6
commits into
aws:main
Choose a base branch
from
jmayclin:constant-time-benching
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c5912d0
bench: s2n_constant_time_equals
jmayclin 68ed49a
Merge branch 'main' into constant-time-benching
jmayclin 8eb19a9
Update bindings/rust/bench/benches/constant_time_equals.rs
jmayclin 7208515
Update bindings/rust/bench/benches/constant_time_equals.rs
jmayclin e430d44
Update bindings/rust/bench/benches/constant_time_equals.rs
jmayclin c349ee0
address pr feedback
jmayclin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//! These benchmarks compare the performance s2n_constant_time_equals with memcmp. | ||
//! The benchmarks can be run with "cargo bench low-level-comparison" | ||
//! | ||
//! The results show that s2n_constant_time_equals is significantly slower than | ||
//! memcmp, but that for small amounts of data the comparisons remain very cheap, | ||
//! on the order of hundreds of nano-seconds. | ||
//! | ||
//! We also see that for pathological inputs, the overhead of the constant time | ||
//! equals grows more noticeable, e.g. ~30 μs. This pathological case is roughly | ||
//! modelled after what might be seen if a client totally fills an extension with | ||
//! data. For this reason we generally avoid using s2n_constant_time_equals to | ||
//! compare large amounts of data. | ||
|
||
use criterion::{criterion_group, criterion_main, Criterion}; | ||
use rand::RngCore; | ||
// we don't need any symbols from s2n-tls, but we do need to link against it to | ||
// make s2n_constant_time_equals available. | ||
use s2n_tls as _; | ||
use std::ffi::c_void; | ||
|
||
const SMALL_DATA_LENGTH: usize = u8::MAX as usize; | ||
const MAX_EXTENSION_SIZE: usize = u16::MAX as usize; | ||
|
||
// wrapper around the libc memcmp | ||
fn memcmp(a: &[u8], b: &[u8]) -> i32 { | ||
unsafe { | ||
libc::memcmp( | ||
a.as_ptr() as *const c_void, | ||
b.as_ptr() as *const c_void, | ||
a.len(), | ||
) | ||
} | ||
} | ||
|
||
extern "C" { | ||
// s2n_constant_time_equals is not exposed publicly, so we manually write the | ||
// bindings for it. | ||
// | ||
// bool s2n_constant_time_equals(const uint8_t *a, const uint8_t *b, const uint32_t len) | ||
fn s2n_constant_time_equals(a: *const u8, b: *const u8, len: u32) -> bool; | ||
} | ||
|
||
fn rust_s2n_constant_time_equals(a: &[u8], b: &[u8]) -> bool { | ||
unsafe { s2n_constant_time_equals(a.as_ptr(), b.as_ptr(), a.len() as u32) } | ||
} | ||
|
||
fn comparison(criterion: &mut Criterion) { | ||
let mut a = [0; SMALL_DATA_LENGTH]; | ||
let mut b = [0; SMALL_DATA_LENGTH]; | ||
let mut a_copy = [0; SMALL_DATA_LENGTH]; | ||
|
||
let mut rng = rand::thread_rng(); | ||
rng.fill_bytes(&mut a); | ||
rng.fill_bytes(&mut b); | ||
|
||
// use a separate copy to avoid the timing difference caused by loads | ||
a_copy.copy_from_slice(&a); | ||
|
||
// compare memcmp vs s2n_constant_time_equals, small data && data not equal | ||
let mut group = criterion.benchmark_group("low-level-comparison - small data, not equal"); | ||
group.bench_function("memcmp", |bencher| bencher.iter(|| memcmp(&a, &b))); | ||
|
||
group.bench_function("s2n_constant_time_equals", |bencher| { | ||
bencher.iter(|| rust_s2n_constant_time_equals(&a, &b)) | ||
}); | ||
group.finish(); | ||
|
||
// compare memcmp vs s2n_constant_time_equals, small data && data is equal | ||
let mut group = criterion.benchmark_group("low-level-comparison - small data, equal"); | ||
group.bench_function("memcmp", |bencher| bencher.iter(|| memcmp(&a, &a_copy))); | ||
group.bench_function("s2n_constant_time_equals_c", |bencher| { | ||
bencher.iter(|| rust_s2n_constant_time_equals(&a, &a_copy)) | ||
}); | ||
group.finish(); | ||
|
||
let mut pathological_data = [[0; SMALL_DATA_LENGTH]; MAX_EXTENSION_SIZE / SMALL_DATA_LENGTH]; | ||
for chunk in pathological_data.iter_mut() { | ||
rng.fill_bytes(chunk); | ||
} | ||
|
||
// compare memcmp vs s2n_constant_time_equals, lots of small data && data is not equal | ||
let mut group = criterion.benchmark_group("low-level-comparison - many small blobs, not equal"); | ||
group.bench_function("memcmp", |bencher| { | ||
bencher.iter(|| { | ||
for e in pathological_data.iter() { | ||
// use "black box" to prevent the compiler from optimizing out the | ||
// memcmp operation | ||
std::hint::black_box(memcmp(e, &a)); | ||
jmayclin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}) | ||
}); | ||
|
||
group.bench_function("s2n_constant_time_equals", |bencher| { | ||
bencher.iter(|| { | ||
for e in pathological_data.iter() { | ||
rust_s2n_constant_time_equals(e, &a); | ||
} | ||
}) | ||
}); | ||
group.finish(); | ||
} | ||
|
||
criterion_group!(benches, comparison); | ||
criterion_main!(benches); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are we trying to do with this PR? Like, it is relevant for #4709, but once that is merged we're not really going to have a use for this comparison. If we're actually trying to benchmark s2n_constant_time_equals(), I would expect to see a test that checks that the comparison of [1, 2, 3, 4, 5] to [1, 0, 0, 0, 0] takes exactly as much time as the comparison of [1, 2, 3, 4, 5] to [1, 2, 3, 4, 5]. But maybe we trust that that case is covered with cbmc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, I see this benchmark function as being strictly interested in the performance of
s2n_constant_time_equals
, and not at all concerned with the correctness of it. We formally assert its correctness with thectverif
proofs.