use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use dcrypt_algorithms::ec::p521::{self, FieldElement, Point, Scalar};
use dcrypt_internal::random::ChaCha20Rng;
use dcrypt_params::traditional::ecdsa::NIST_P521;
fn bench_rng() -> ChaCha20Rng {
ChaCha20Rng::from_seed([0x75; 32])
}
fn bench_field_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p521/field");
let x = FieldElement::from_bytes(&NIST_P521.g_x).unwrap();
let y = FieldElement::from_bytes(&NIST_P521.g_y).unwrap();
group.bench_function("add", |b| b.iter(|| black_box(x.add(&y))));
group.bench_function("sub", |b| b.iter(|| black_box(x.sub(&y))));
group.bench_function("mul", |b| b.iter(|| black_box(x.mul(&y))));
group.bench_function("square", |b| b.iter(|| black_box(x.square())));
group.bench_function("invert", |b| b.iter(|| black_box(x.invert().unwrap())));
group.bench_function("sqrt", |b| {
let square = x.square(); b.iter(|| black_box(square.sqrt().unwrap()))
});
group.finish();
}
fn bench_scalar_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p521/scalar");
let mut rng = bench_rng();
let (scalar1, _) = p521::generate_keypair(&mut rng).unwrap();
let (scalar2, _) = p521::generate_keypair(&mut rng).unwrap();
group.bench_function("add_mod_n", |b| {
b.iter(|| black_box(scalar1.add_mod_n(&scalar2).unwrap()))
});
group.bench_function("sub_mod_n", |b| {
b.iter(|| black_box(scalar1.sub_mod_n(&scalar2).unwrap()))
});
group.bench_function("mul_mod_n", |b| {
b.iter(|| black_box(scalar1.mul_mod_n(&scalar2).unwrap()))
});
group.bench_function("inv_mod_n", |b| {
b.iter(|| black_box(scalar1.inv_mod_n().unwrap()))
});
group.bench_function("negate", |b| b.iter(|| black_box(scalar1.negate())));
group.finish();
}
fn bench_point_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p521/point");
let g = p521::base_point_g();
let g2 = g.double();
group.bench_function("add", |b| b.iter(|| black_box(g.add(&g2))));
group.bench_function("double", |b| b.iter(|| black_box(g.double())));
let mut small_scalar_bytes = [0u8; 66];
small_scalar_bytes[65] = 42;
let small_scalar = Scalar::new(small_scalar_bytes).unwrap();
group.bench_function("scalar_mul_small", |b| {
b.iter(|| black_box(g.mul(&small_scalar).unwrap()))
});
let mut rng = bench_rng();
let (full_scalar, _) = p521::generate_keypair(&mut rng).unwrap();
group.bench_function("scalar_mul_full", |b| {
b.iter(|| black_box(g.mul(&full_scalar).unwrap()))
});
group.bench_function("scalar_mult_base_g", |b| {
b.iter(|| black_box(p521::scalar_mult_base_g(&full_scalar).unwrap()))
});
group.finish();
}
fn bench_point_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("p521/serialization");
let g = p521::base_point_g();
group.bench_function("serialize_uncompressed", |b| {
b.iter(|| black_box(g.serialize_uncompressed()))
});
let uncompressed = g.serialize_uncompressed();
group.bench_function("deserialize_uncompressed", |b| {
b.iter(|| black_box(Point::deserialize_uncompressed(&uncompressed).unwrap()))
});
group.bench_function("serialize_compressed", |b| {
b.iter(|| black_box(g.serialize_compressed()))
});
let compressed = g.serialize_compressed();
group.bench_function("deserialize_compressed", |b| {
b.iter(|| black_box(Point::deserialize_compressed(&compressed).unwrap()))
});
group.finish();
}
fn bench_key_generation(c: &mut Criterion) {
let mut group = c.benchmark_group("p521/keygen");
group.bench_function("generate_keypair", |b| {
let mut rng = bench_rng();
b.iter(|| black_box(p521::generate_keypair(&mut rng).unwrap()))
});
group.finish();
}
fn bench_ecdh_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p521/ecdh");
let mut rng = bench_rng();
let (private_a, _) = p521::generate_keypair(&mut rng).unwrap();
let (_, public_b) = p521::generate_keypair(&mut rng).unwrap();
group.bench_function("shared_secret", |b| {
b.iter(|| {
black_box(p521::scalar_mult(&private_a, &public_b).unwrap())
})
});
let shared_point = p521::scalar_mult(&private_a, &public_b).unwrap();
let shared_x = shared_point.x_coordinate_bytes();
group.bench_function("kdf_hkdf_sha512", |b| {
b.iter(|| black_box(p521::kdf_hkdf_sha512_for_ecdh_kem(&shared_x, None).unwrap()))
});
group.finish();
}
fn bench_scalar_mult_sizes(c: &mut Criterion) {
let mut group = c.benchmark_group("p521/scalar_mult_sizes");
let g = p521::base_point_g();
let bit_sizes = vec![8, 16, 32, 64, 128, 256, 521];
for bits in bit_sizes {
let mut scalar_bytes = [0u8; 66];
let byte_idx = (521 - bits) / 8;
let bit_offset = (521 - bits) % 8;
if byte_idx < 66 {
scalar_bytes[byte_idx] = 1 << (7 - bit_offset);
for i in (byte_idx + 1)..66 {
scalar_bytes[i] = 0xFF;
}
}
let scalar = Scalar::new(scalar_bytes).unwrap();
group.bench_with_input(BenchmarkId::from_parameter(bits), &scalar, |b, scalar| {
b.iter(|| black_box(g.mul(scalar).unwrap()))
});
}
group.finish();
}
criterion_group!(
benches,
bench_field_operations,
bench_scalar_operations,
bench_point_operations,
bench_point_serialization,
bench_key_generation,
bench_ecdh_operations,
bench_scalar_mult_sizes
);
criterion_main!(benches);