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
7 changes: 7 additions & 0 deletions alpha_0.1.3_release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@
## Major features

## Minor features / bug fixes

SHA-3 documentation:

* Crate docs gained "Memory Usage" and "Security Considerations" sections.
* New `mem_usage_benches/bench_sha3_mem_usage.rs` reports `size_of` for the SHA-3 / SHAKE objects (440 bytes) and the
suspended state (415 bytes) -- the figures in the crate's Memory Usage table -- and provides valgrind massif entry
points for the hash, XOF and suspend/resume paths.
25 changes: 25 additions & 0 deletions crypto/sha3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,31 @@
//! [`KeyType::CryptographicRandom`] since the input [`KeyMaterial`] is 16 bytes but [`SHA3_256`] needs at least 32 bytes of
//! full-entropy input key material in order to be able to produce full entropy output key material.
//!
//! # Memory Usage
//!
//! All SHA3 and SHAKE variants share the same Keccak-f\[1600\] sponge and so have identical memory
//! footprints. No heap memory is used by the algorithms themselves; the `Vec<u8>`-returning
//! convenience methods allocate only the output buffer, and the `*_out` variants allocate nothing.
//!
//! | Object | Size (bytes) |
//! |-----------------------------------------|--------------|
//! | `SHA3_224` .. `SHA3_512`, `SHAKE128/256` | 440 |
//! | Suspended state ([`Suspendable`]) | 415 |
//!
//! Sizes are `core::mem::size_of` values reported by `mem_usage_benches/bench_sha3_mem_usage.rs`
//! (`cargo run --release -p mem_usage_benches --bin bench_sha3_mem_usage`), which also has valgrind
//! massif entry points for measuring peak stack usage of the hash, XOF and suspend/resume paths.
//!
//! # Security Considerations
//!
//! * SHA3-224/256/384/512 offer 112/128/192/256 bits of collision resistance respectively; SHAKE128
//! and SHAKE256 offer 128 and 256 bits of security for output lengths at least twice that size
//! (FIPS 202 Appendix A.1).
//! * SHAKE is an XOF, not a hash: `SHAKE128(m, 32)` is a prefix of `SHAKE128(m, 64)`. If the output
//! length must be bound to the digest, include it in the message (FIPS 202 Appendix A.2).
//! * The sponge state and queue are held in [`bouncycastle_utils::secret::Secret`] and zeroized on
//! drop.
//!
//! # Suspending and resuming execution
//!
//! When hashing a large message, it can be advantageous to be able to suspend the operation
Expand Down
4 changes: 4 additions & 0 deletions mem_usage_benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ path = "bench_mldsa_mem_usage.rs"
[[bin]]
name = "bench_mlkem_mem_usage"
path = "bench_mlkem_mem_usage.rs"

[[bin]]
name = "bench_sha3_mem_usage"
path = "bench_sha3_mem_usage.rs"
122 changes: 122 additions & 0 deletions mem_usage_benches/bench_sha3_mem_usage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! The purpose of this binary is to perform a single run of the primitive under test so that
//! its peak memory usage can be measured with:
//!
//! valgrind --tool=massif --heap=no --stacks=yes -- target/release/bench_sha3_mem_usage > /dev/null
//!
//! ms_print massif.out.835000
//!
//! or, shoved all into one line:
//!
//! clear; clear; valgrind --tool=massif --heap=no --stacks=yes -- target/release/bench_sha3_mem_usage > /dev/null; ms_print massif.out.*; rm massif.out.*
//!
//! Make sure you build in release mode!
//!
//! The code is using print!() to force the compiler not to optimize away the actual code.
//! It is printing important outputs for benchmarking to stderr so that the rest can be mapped to /dev/null
//! (this is because /usr/bin/time prints useful outputs to stderr as well)
//!
//! Main is at the bottom, controls which this was actually run. `print_struct_sizes()` is the source of
//! the numbers in the "Memory Usage" table in the bouncycastle-sha3 crate docs.

#![allow(dead_code)]
#![allow(unused_imports)]

use bouncycastle::core::traits::{Hash, Suspendable, XOF};
use bouncycastle::sha3::{
SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256, SUSPENDED_SHA3_STATE_LEN,
};

/// A 1 KiB message so that the sponge is permuted several times.
const MSG: [u8; 1024] = [0xA5; 1024];

/// This prints the in-memory size of all the hash / XOF objects and the suspended state.
fn print_struct_sizes() {
use core::mem::size_of;

println!("\nSHA3 / SHAKE");
println!("size_of<SHA3_224>: {}", size_of::<SHA3_224>());
println!("size_of<SHA3_256>: {}", size_of::<SHA3_256>());
println!("size_of<SHA3_384>: {}", size_of::<SHA3_384>());
println!("size_of<SHA3_512>: {}", size_of::<SHA3_512>());
println!("size_of<SHAKE128>: {}", size_of::<SHAKE128>());
println!("size_of<SHAKE256>: {}", size_of::<SHAKE256>());
println!("SUSPENDED_SHA3_STATE_LEN: {}", SUSPENDED_SHA3_STATE_LEN);
}

fn bench_do_nothing() {
eprintln!("DoNothing");

print!("{}", 1 + 1);
}

fn bench_sha3_256_hash() {
eprintln!("SHA3-256/hash");

let mut out = [0u8; 32];
SHA3_256::new().hash_out(&MSG, &mut out);
println!("{:x?}", out);
}

fn bench_sha3_512_hash() {
eprintln!("SHA3-512/hash");

let mut out = [0u8; 64];
SHA3_512::new().hash_out(&MSG, &mut out);
println!("{:x?}", out);
}

fn bench_sha3_256_streaming() {
eprintln!("SHA3-256/do_update+do_final_out");

let mut h = SHA3_256::new();
for chunk in MSG.chunks(100) {
h.do_update(chunk);
}
let mut out = [0u8; 32];
h.do_final_out(&mut out);
println!("{:x?}", out);
}

fn bench_shake128_xof() {
eprintln!("SHAKE128/absorb+squeeze_out");

let mut x = SHAKE128::new();
x.absorb(&MSG).expect("absorb before squeeze is infallible");
let mut out = [0u8; 512];
x.squeeze_out(&mut out);
println!("{:x?}", out);
}

fn bench_shake256_xof() {
eprintln!("SHAKE256/absorb+squeeze_out");

let mut x = SHAKE256::new();
x.absorb(&MSG).expect("absorb before squeeze is infallible");
let mut out = [0u8; 512];
x.squeeze_out(&mut out);
println!("{:x?}", out);
}

fn bench_sha3_256_suspend_resume() {
eprintln!("SHA3-256/suspend+from_suspended");

let mut h = SHA3_256::new();
h.do_update(&MSG[..500]);
let state = h.suspend();
let mut h = SHA3_256::from_suspended(state).expect("round-trip of a freshly suspended state");
h.do_update(&MSG[500..]);
let mut out = [0u8; 32];
h.do_final_out(&mut out);
println!("{:x?}", out);
}

fn main() {
print_struct_sizes()
// bench_do_nothing()
// bench_sha3_256_hash()
// bench_sha3_512_hash()
// bench_sha3_256_streaming()
// bench_shake128_xof()
// bench_shake256_xof()
// bench_sha3_256_suspend_resume()
}
3 changes: 2 additions & 1 deletion mem_usage_benches/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod bench_mldsa_mem_usage;
mod bench_mlkem_mem_usage;
mod bench_mlkem_mem_usage;
mod bench_sha3_mem_usage;
Loading