diff --git a/.github/workflows/p521.yml b/.github/workflows/p521.yml index fc9d5359..65da7b79 100644 --- a/.github/workflows/p521.yml +++ b/.github/workflows/p521.yml @@ -38,6 +38,20 @@ jobs: - run: cargo build --target ${{ matrix.target }} --release --no-default-features - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc + benches: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.65.0 # MSRV + - stable + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - run: cargo build --all-features --benches + test: runs-on: ubuntu-latest strategy: diff --git a/Cargo.lock b/Cargo.lock index 3eb489a9..4e3a4683 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,6 +799,7 @@ version = "0.13.3" dependencies = [ "base16ct", "blobby", + "criterion", "ecdsa", "elliptic-curve", "hex-literal", diff --git a/p521/Cargo.toml b/p521/Cargo.toml index 8aee7bf3..b6f19d98 100644 --- a/p521/Cargo.toml +++ b/p521/Cargo.toml @@ -34,6 +34,7 @@ hex-literal = "0.4" primeorder = { version = "0.13.3", features = ["dev"], path = "../primeorder" } proptest = "1.4" rand_core = { version = "0.6", features = ["getrandom"] } +criterion = "0.5.1" [features] default = ["arithmetic", "ecdsa", "getrandom", "pem", "std"] @@ -44,6 +45,7 @@ arithmetic = ["dep:primeorder"] digest = ["ecdsa-core/digest", "ecdsa-core/hazmat"] ecdh = ["arithmetic", "elliptic-curve/ecdh"] ecdsa = ["arithmetic", "ecdsa-core/signing", "ecdsa-core/verifying", "sha512"] +expose-field = ["arithmetic"] getrandom = ["rand_core/getrandom"] hash2curve = ["arithmetic", "elliptic-curve/hash2curve"] jwk = ["elliptic-curve/jwk"] @@ -57,3 +59,12 @@ voprf = ["elliptic-curve/voprf", "dep:sha2"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[[bench]] +name = "field" +harness = false +required-features = ["expose-field"] + +[[bench]] +name = "scalar" +harness = false diff --git a/p521/benches/field.rs b/p521/benches/field.rs new file mode 100644 index 00000000..2123836c --- /dev/null +++ b/p521/benches/field.rs @@ -0,0 +1,57 @@ +//! secp521r1 field element benchmarks + +use criterion::{ + black_box, criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, Criterion, +}; +use hex_literal::hex; +use p521::{FieldBytes, FieldElement}; + +fn test_field_element_x() -> FieldElement { + black_box(FieldElement::from_bytes( + &FieldBytes::clone_from_slice(&hex!("01a7596d38aac7868327ddc1ef5e8178cf052b7ebc512828e8a45955d85bef49494d15278198bbcc5454358c12a2af9a3874e7002e1a2f02fcb36ff3e3b4bc0c69e7")) + ) + .unwrap()) +} + +fn test_field_element_y() -> FieldElement { + black_box(FieldElement::from_bytes( + &FieldBytes::clone_from_slice(&hex!("0184902e515982bb225b8c84f245e61b327c08e94d41c07d0b4101a963e02fe52f6a9f33e8b1de2394e0cb74c40790b4e489b5500e6804cabed0fe8c192443d4027b")) + ) + .unwrap()) +} + +fn bench_field_element_mul<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_field_element_x(); + let y = test_field_element_y(); + group.bench_function("mul", |b| b.iter(|| &x * &y)); +} + +fn bench_field_element_square<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_field_element_x(); + group.bench_function("square", |b| b.iter(|| x.square())); +} + +fn bench_field_element_sqrt<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_field_element_x(); + group.bench_function("sqrt", |b| b.iter(|| x.sqrt())); +} + +fn bench_field_element_invert<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_field_element_x(); + group.bench_function("invert", |b| b.iter(|| x.invert())); +} + +fn bench_field_element(c: &mut Criterion) { + let mut group = c.benchmark_group("field element operations"); + group.sample_size(500); + group.measurement_time(std::time::Duration::from_secs(10)); + + bench_field_element_mul(&mut group); + bench_field_element_square(&mut group); + bench_field_element_invert(&mut group); + bench_field_element_sqrt(&mut group); + group.finish(); +} + +criterion_group!(benches, bench_field_element); +criterion_main!(benches); diff --git a/p521/benches/scalar.rs b/p521/benches/scalar.rs new file mode 100644 index 00000000..a1c87dc6 --- /dev/null +++ b/p521/benches/scalar.rs @@ -0,0 +1,76 @@ +//! secp521r1 scalar arithmetic benchmarks + +use criterion::{ + black_box, criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, Criterion, +}; +use hex_literal::hex; +use p521::{elliptic_curve::group::ff::PrimeField, FieldBytes, ProjectivePoint, Scalar}; + +fn test_scalar_x() -> Scalar { + black_box(Scalar::from_repr( + FieldBytes::clone_from_slice(&hex!("01d7bb864c5b5ecae019296cf9b5c63a166f5f1113942819b1933d889a96d12245777a99428f93de4fc9a18d709bf91889d7f8dddd522b4c364aeae13c983e9fae46")) + ).unwrap()) +} + +fn test_scalar_y() -> Scalar { + black_box(Scalar::from_repr( + FieldBytes::clone_from_slice(&hex!("017e49b8ea8f9d1b7c0378e378a7a42e68e12cf78779ed41dcd29a090ae7e0f883b0d0f2cbc8f0473c0ad6732bea40d371a7f363bc6537d075bd1a4c23e558b0bc73")) + ).unwrap()) +} + +fn bench_point_mul<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let p = ProjectivePoint::GENERATOR; + let m = test_scalar_x(); + let s = Scalar::from_repr(m.into()).unwrap(); + group.bench_function("point-scalar mul", |b| b.iter(|| &p * &s)); +} + +fn bench_scalar_sub<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_scalar_x(); + let y = test_scalar_y(); + group.bench_function("sub", |b| b.iter(|| &x - &y)); +} + +fn bench_scalar_add<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_scalar_x(); + let y = test_scalar_y(); + group.bench_function("add", |b| b.iter(|| &x + &y)); +} + +fn bench_scalar_mul<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_scalar_x(); + let y = test_scalar_y(); + group.bench_function("mul", |b| b.iter(|| &x * &y)); +} + +fn bench_scalar_negate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_scalar_x(); + group.bench_function("negate", |b| b.iter(|| -x)); +} + +fn bench_scalar_invert<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let x = test_scalar_x(); + group.bench_function("invert", |b| b.iter(|| x.invert())); +} + +fn bench_point(c: &mut Criterion) { + let mut group = c.benchmark_group("point operations"); + bench_point_mul(&mut group); + group.finish(); +} + +fn bench_scalar(c: &mut Criterion) { + let mut group = c.benchmark_group("scalar operations"); + group.sample_size(500); + group.measurement_time(std::time::Duration::from_secs(10)); + + bench_scalar_sub(&mut group); + bench_scalar_add(&mut group); + bench_scalar_mul(&mut group); + bench_scalar_negate(&mut group); + bench_scalar_invert(&mut group); + group.finish(); +} + +criterion_group!(benches, bench_point, bench_scalar); +criterion_main!(benches); diff --git a/p521/src/lib.rs b/p521/src/lib.rs index e0056a01..ecacd617 100644 --- a/p521/src/lib.rs +++ b/p521/src/lib.rs @@ -40,6 +40,9 @@ pub mod test_vectors; #[cfg(feature = "arithmetic")] pub use arithmetic::{scalar::Scalar, AffinePoint, ProjectivePoint}; +#[cfg(feature = "expose-field")] +pub use arithmetic::field::FieldElement; + pub use elliptic_curve::{self, bigint::U576}; #[cfg(feature = "pkcs8")]