wolfcrypt-ring-testing 0.2.1

Tests and benchmarks for wolfcrypt-ring-compat
use criterion::{criterion_group, criterion_main, Criterion};

#[derive(Debug)]
pub enum HMACAlgorithm {
    SHA1,
    SHA256,
    SHA384,
    SHA512,
}

pub struct HMACConfig {
    algorithm: HMACAlgorithm,
}

impl HMACConfig {
    #[must_use]
    pub fn new(algorithm: HMACAlgorithm) -> HMACConfig {
        HMACConfig { algorithm }
    }
}

macro_rules! benchmark_hmac {
    ( $pkg:ident ) => {
        paste::item! {
        mod [<$pkg _benchmarks>]  {

            use $pkg::{hmac, digest};

            use std::hint::black_box;
            use crate::HMACConfig;


            pub fn algorithm(config: &crate::HMACConfig) ->  hmac::Algorithm {
                black_box(match &config.algorithm {
                    crate::HMACAlgorithm::SHA1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
                    crate::HMACAlgorithm::SHA256 => hmac::HMAC_SHA256,
                    crate::HMACAlgorithm::SHA384 => hmac::HMAC_SHA384,
                    crate::HMACAlgorithm::SHA512 => hmac::HMAC_SHA512,
                })
            }

            pub fn create_hmac_key(config: &HMACConfig) -> hmac::Key {
                let key_val = vec![1u8; get_digest_length(&config)];
                hmac::Key::new(algorithm(&config), &key_val)
            }

            // A HMAC key longer than the corresponding digest length will be hashed once
            // before being processed.
            pub fn create_longer_hmac_key(config: &HMACConfig) -> hmac::Key {
                let key_val = vec![1u8; get_digest_length(&config) + 1];
                hmac::Key::new(algorithm(&config), &key_val)
            }

            #[expect(unused_must_use)]
            pub fn run_hmac_incremental(key: &hmac::Key, chunk: &[u8]) {
                let mut s_ctx = hmac::Context::with_key(&key);
                s_ctx.update(chunk);
                s_ctx.sign();
            }

            #[expect(unused_must_use)]
            pub fn run_hmac_one_shot(key: &hmac::Key, chunk: &[u8]) {
                hmac::sign(&key, chunk);
            }

            fn get_digest_length(config: &HMACConfig) -> usize {
                match &config.algorithm {
                    crate::HMACAlgorithm::SHA1 => digest::SHA1_OUTPUT_LEN,
                    crate::HMACAlgorithm::SHA256 => digest::SHA256_OUTPUT_LEN,
                    crate::HMACAlgorithm::SHA384 => digest::SHA384_OUTPUT_LEN,
                    crate::HMACAlgorithm::SHA512 => digest::SHA512_OUTPUT_LEN,
                }
            }
        }
        }
    };
}

benchmark_hmac!(wolfcrypt_ring_compat);
#[cfg(feature = "ring-benchmarks")]
benchmark_hmac!(ring);

fn bench_hmac_sha1(c: &mut Criterion) {
    let config = HMACConfig::new(HMACAlgorithm::SHA1);
    bench_hmac_one_shot(c, &config);
    bench_hmac_incremental(c, &config);
    bench_hmac_longer_key(c, &config);
}

fn bench_hmac_sha256(c: &mut Criterion) {
    let config = HMACConfig::new(HMACAlgorithm::SHA256);
    bench_hmac_one_shot(c, &config);
    bench_hmac_incremental(c, &config);
    bench_hmac_longer_key(c, &config);
}

fn bench_hmac_sha384(c: &mut Criterion) {
    let config = HMACConfig::new(HMACAlgorithm::SHA384);
    bench_hmac_one_shot(c, &config);
    bench_hmac_incremental(c, &config);
    bench_hmac_longer_key(c, &config);
}

fn bench_hmac_sha512(c: &mut Criterion) {
    let config = HMACConfig::new(HMACAlgorithm::SHA512);
    bench_hmac_one_shot(c, &config);
    bench_hmac_incremental(c, &config);
    bench_hmac_longer_key(c, &config);
}

const G_CHUNK_LENGTHS: [usize; 5] = [16, 256, 1350, 8192, 16384];

// Benchmark hmac::sign one-shot API.
fn bench_hmac_one_shot(c: &mut Criterion, config: &HMACConfig) {
    for &chunk_len in &G_CHUNK_LENGTHS {
        let chunk = vec![1u8; chunk_len];

        let bench_group_name = format!("HMAC-{:?}-one-shot-{}-bytes", config.algorithm, chunk_len);
        let mut group = c.benchmark_group(bench_group_name);

        group.bench_function("wolfcrypt-ring-compat", |b| {
            b.iter(|| {
                let wc_key = wolfcrypt_ring_compat_benchmarks::create_hmac_key(config);
                wolfcrypt_ring_compat_benchmarks::run_hmac_one_shot(&wc_key, &chunk);
            });
        });

        #[cfg(feature = "ring-benchmarks")]
        {
            group.bench_function("Ring", |b| {
                b.iter(|| {
                    let ring_key = ring_benchmarks::create_hmac_key(config);
                    ring_benchmarks::run_hmac_one_shot(&ring_key, &chunk);
                });
            });
        }
    }
}

// Benchmark hmac::sign with keys longer than the block size. If a key is longer than the block
// length then it will be compressed using the digest algorithm.
fn bench_hmac_longer_key(c: &mut Criterion, config: &HMACConfig) {
    for &chunk_len in &G_CHUNK_LENGTHS {
        let chunk = vec![1u8; chunk_len];

        let bench_group_name = format!(
            "HMAC-{:?}-one-shot-long-key-{}-bytes",
            config.algorithm, chunk_len
        );
        let mut group = c.benchmark_group(bench_group_name);

        group.bench_function("wolfcrypt-ring-compat", |b| {
            b.iter(|| {
                let wc_key = wolfcrypt_ring_compat_benchmarks::create_longer_hmac_key(config);
                wolfcrypt_ring_compat_benchmarks::run_hmac_one_shot(&wc_key, &chunk);
            });
        });

        #[cfg(feature = "ring-benchmarks")]
        {
            group.bench_function("Ring", |b| {
                b.iter(|| {
                    let ring_key = ring_benchmarks::create_longer_hmac_key(config);
                    ring_benchmarks::run_hmac_one_shot(&ring_key, &chunk);
                });
            });
        }
    }
}

// Benchmark incremental hmac update/sign.
fn bench_hmac_incremental(c: &mut Criterion, config: &HMACConfig) {
    for &chunk_len in &G_CHUNK_LENGTHS {
        let chunk = vec![123u8; chunk_len];

        let bench_group_name = format!(
            "HMAC-{:?}-incremental-{}-bytes",
            config.algorithm, chunk_len
        );
        let mut group = c.benchmark_group(bench_group_name);

        group.bench_function("wolfcrypt-ring-compat", |b| {
            b.iter(|| {
                let wc_key = wolfcrypt_ring_compat_benchmarks::create_hmac_key(config);
                wolfcrypt_ring_compat_benchmarks::run_hmac_incremental(&wc_key, &chunk);
            });
        });
        #[cfg(feature = "ring-benchmarks")]
        {
            group.bench_function("Ring", |b| {
                b.iter(|| {
                    let ring_key = ring_benchmarks::create_hmac_key(config);
                    ring_benchmarks::run_hmac_incremental(&ring_key, &chunk);
                });
            });
        }
    }
}

criterion_group!(
    benches,
    bench_hmac_sha1,
    bench_hmac_sha256,
    bench_hmac_sha384,
    bench_hmac_sha512,
);

criterion_main!(benches);