use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
use dcrypt_algorithms::ec::p256::{
self, FieldElement, Point, Scalar, P256_FIELD_ELEMENT_SIZE, P256_SCALAR_SIZE,
};
use rand::{rngs::OsRng, RngCore};
fn random_field_element() -> FieldElement {
let mut bytes = [0u8; P256_FIELD_ELEMENT_SIZE];
loop {
OsRng.fill_bytes(&mut bytes);
if let Ok(fe) = FieldElement::from_bytes(&bytes) {
return fe;
}
}
}
fn random_scalar() -> Scalar {
let mut bytes = [0u8; P256_SCALAR_SIZE];
loop {
OsRng.fill_bytes(&mut bytes);
if let Ok(scalar) = Scalar::new(bytes) {
return scalar;
}
}
}
fn random_point() -> Point {
let scalar = random_scalar();
p256::scalar_mult_base_g(&scalar).unwrap()
}
fn bench_field_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p256-field");
group.bench_function("addition", |b| {
b.iter_batched(
|| (random_field_element(), random_field_element()),
|(a, b)| black_box(a.add(&b)),
BatchSize::SmallInput,
)
});
group.bench_function("subtraction", |b| {
b.iter_batched(
|| (random_field_element(), random_field_element()),
|(a, b)| black_box(a.sub(&b)),
BatchSize::SmallInput,
)
});
group.bench_function("multiplication", |b| {
b.iter_batched(
|| (random_field_element(), random_field_element()),
|(a, b)| black_box(a.mul(&b)),
BatchSize::SmallInput,
)
});
group.bench_function("squaring", |b| {
b.iter_batched(
|| random_field_element(),
|a| black_box(a.square()),
BatchSize::SmallInput,
)
});
group.bench_function("inversion", |b| {
b.iter_batched(
|| random_field_element(),
|a| black_box(a.invert().unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("sqrt", |b| {
b.iter_batched(
|| {
let x = random_field_element();
x.square()
},
|a| black_box(a.sqrt()),
BatchSize::SmallInput,
)
});
group.bench_function("to_bytes", |b| {
b.iter_batched(
|| random_field_element(),
|a| black_box(a.to_bytes()),
BatchSize::SmallInput,
)
});
group.bench_function("from_bytes", |b| {
b.iter_batched(
|| random_field_element().to_bytes(),
|bytes| black_box(FieldElement::from_bytes(&bytes).unwrap()),
BatchSize::SmallInput,
)
});
group.finish();
}
fn bench_point_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p256-point");
group.bench_function("addition", |b| {
b.iter_batched(
|| (random_point(), random_point()),
|(p1, p2)| black_box(p1.add(&p2)),
BatchSize::SmallInput,
)
});
group.bench_function("doubling", |b| {
b.iter_batched(
|| random_point(),
|p| black_box(p.double()),
BatchSize::SmallInput,
)
});
group.bench_function("scalar_mult", |b| {
b.iter_batched(
|| (random_point(), random_scalar()),
|(p, s)| black_box(p.mul(&s).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("scalar_mult_base", |b| {
b.iter_batched(
|| random_scalar(),
|s| black_box(p256::scalar_mult_base_g(&s).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("validation", |b| {
b.iter_batched(
|| {
let p = random_point();
(p.x_coordinate_bytes(), p.y_coordinate_bytes())
},
|(x, y)| black_box(Point::new_uncompressed(&x, &y)),
BatchSize::SmallInput,
)
});
group.finish();
}
fn bench_scalar_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p256-scalar");
group.bench_function("add_mod_n", |b| {
b.iter_batched(
|| (random_scalar(), random_scalar()),
|(a, b)| black_box(a.add_mod_n(&b).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("sub_mod_n", |b| {
b.iter_batched(
|| (random_scalar(), random_scalar()),
|(a, b)| black_box(a.sub_mod_n(&b).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("mul_mod_n", |b| {
b.iter_batched(
|| (random_scalar(), random_scalar()),
|(a, b)| black_box(a.mul_mod_n(&b).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("inv_mod_n", |b| {
b.iter_batched(
|| random_scalar(),
|a| black_box(a.inv_mod_n().unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("negate", |b| {
b.iter_batched(
|| random_scalar(),
|a| black_box(a.negate()),
BatchSize::SmallInput,
)
});
group.bench_function("new", |b| {
b.iter_batched(
|| {
let mut bytes = [0u8; P256_SCALAR_SIZE];
OsRng.fill_bytes(&mut bytes);
bytes
},
|bytes| black_box(Scalar::new(bytes)),
BatchSize::SmallInput,
)
});
group.finish();
}
fn bench_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("p256-serialization");
group.bench_function("serialize_uncompressed", |b| {
b.iter_batched(
|| random_point(),
|p| black_box(p.serialize_uncompressed()),
BatchSize::SmallInput,
)
});
group.bench_function("deserialize_uncompressed", |b| {
b.iter_batched(
|| random_point().serialize_uncompressed(),
|bytes| black_box(Point::deserialize_uncompressed(&bytes).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("serialize_compressed", |b| {
b.iter_batched(
|| random_point(),
|p| black_box(p.serialize_compressed()),
BatchSize::SmallInput,
)
});
group.bench_function("deserialize_compressed", |b| {
b.iter_batched(
|| random_point().serialize_compressed(),
|bytes| black_box(Point::deserialize_compressed(&bytes).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("detect_format", |b| {
b.iter_batched(
|| random_point().serialize_uncompressed(),
|bytes| black_box(Point::detect_format(&bytes).unwrap()),
BatchSize::SmallInput,
)
});
group.finish();
}
fn bench_crypto_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("p256-crypto");
group.bench_function("generate_keypair", |b| {
let mut rng = OsRng;
b.iter(|| black_box(p256::generate_keypair(&mut rng).unwrap()))
});
group.bench_function("ecdh_raw", |b| {
b.iter_batched(
|| {
let mut rng = OsRng;
let (priv_a, pub_a) = p256::generate_keypair(&mut rng).unwrap();
let (priv_b, pub_b) = p256::generate_keypair(&mut rng).unwrap();
(priv_a, pub_b)
},
|(priv_key, pub_key)| black_box(p256::scalar_mult(&priv_key, &pub_key).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("kdf_hkdf_sha256", |b| {
b.iter_batched(
|| {
let mut ikm = [0u8; 32];
OsRng.fill_bytes(&mut ikm);
ikm
},
|ikm| black_box(p256::kdf_hkdf_sha256_for_ecdh_kem(&ikm, Some(b"test info")).unwrap()),
BatchSize::SmallInput,
)
});
group.finish();
}
fn bench_ecdh_workflow(c: &mut Criterion) {
let mut group = c.benchmark_group("p256-ecdh-workflow");
group.bench_function("complete", |b| {
b.iter_batched(
|| {
let mut rng = OsRng;
let (priv_a, _) = p256::generate_keypair(&mut rng).unwrap();
let (_, pub_b) = p256::generate_keypair(&mut rng).unwrap();
(priv_a, pub_b)
},
|(priv_key, pub_key)| {
let shared_point = p256::scalar_mult(&priv_key, &pub_key).unwrap();
let shared_x = shared_point.x_coordinate_bytes();
black_box(p256::kdf_hkdf_sha256_for_ecdh_kem(&shared_x, Some(b"ECDH")).unwrap())
},
BatchSize::SmallInput,
)
});
group.finish();
}
fn bench_scalar_mult_sizes(c: &mut Criterion) {
let mut group = c.benchmark_group("p256-scalar-mult-sizes");
group.bench_function("small_scalar", |b| {
b.iter_batched(
|| {
let mut bytes = [0u8; P256_SCALAR_SIZE];
bytes[31] = 0xFF; (p256::base_point_g(), Scalar::new(bytes).unwrap())
},
|(p, s)| black_box(p.mul(&s).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("medium_scalar", |b| {
b.iter_batched(
|| {
let mut bytes = [0u8; P256_SCALAR_SIZE];
for i in 16..32 {
bytes[i] = 0xFF;
}
(p256::base_point_g(), Scalar::new(bytes).unwrap())
},
|(p, s)| black_box(p.mul(&s).unwrap()),
BatchSize::SmallInput,
)
});
group.bench_function("large_scalar", |b| {
b.iter_batched(
|| {
let mut bytes = [0xFF; P256_SCALAR_SIZE];
bytes[0] = 0x00; (p256::base_point_g(), Scalar::new(bytes).unwrap())
},
|(p, s)| black_box(p.mul(&s).unwrap()),
BatchSize::SmallInput,
)
});
group.finish();
}
criterion_group!(
benches,
bench_field_operations,
bench_point_operations,
bench_scalar_operations,
bench_serialization,
bench_crypto_operations,
bench_ecdh_workflow,
bench_scalar_mult_sizes
);
criterion_main!(benches);