Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Risc0 Acceleration for Curve25519 #1

Merged
merged 27 commits into from
Sep 15, 2023
Merged

Conversation

jjtny1
Copy link

@jjtny1 jjtny1 commented Jul 27, 2023

Closes: risc0/risc0#721

Use Risc0's accelerated multiplication circuit in the Curve25519-dalek library. This speeds up Ed25519 signatures.

Majority of these changes live in src/backend/serial/risc0. This folder contains implementations of:

  1. FieldElement over 2**255 - 19
  2. Scalar implementation mod 2**252 + 27742317777372353535851937790883648493 (the order of the subgroup of points with base x = 9)
  3. Constants

Cargo uses these FieldElement, Scalar and Constant implementations from the risc0 folder when compiled for the zkvm.

Initial benchmarks using sample guest code results are:

Scalar operations:
add: 836 cycles

Scalar operations:
mul: 779 cycles

Scalar operations:
square: 779 cycles

Scalar operations:
negate: 500 cycles

Scalar operations:
invert: 84981 cycles

Group operations:
vartime_double_scalar_mul_basepoint: 963496 cycles

Without acceleration:
Scalar operations:
add: 786 cycles

Scalar operations:
mul: 3121 cycles

Scalar operations:
square: 4215 cycles

Scalar operations:
negate: 1812 cycles

Scalar operations:
invert: 318714 cycles

Group operations:
vartime_double_scalar_mul_basepoint: 2915045 cycles

And signature verification:

With Acceleration:
Verification: 1112088 cycles

Without Acceleration:
Verification: 3214543 cycles

How was this tested:

  1. Using @nategraf's branch, I ran CARGO=/Users/jobythundil/.cargo/bin/cargo cargo-risczero risczero test -- --release --no-default-features from the curve25519-dalek crate to run all the unit and integration tests

  2. Using this guest modeled after our ecdsa example to run basic group operations and signature verification: https://github.com/jjtny1/curve25519-dalek-test/tree/main/methods/guest/src/bin

use crate::edwards::EdwardsPoint;

use elliptic_curve::bigint::U256;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constants are taken from the u64/u32 existing implementation (https://github.com/risc0/curve25519-dalek/blob/main/curve25519-dalek/src/backend/serial/u64/constants.rs)

@jjtny1 jjtny1 requested review from mothran and Cardosaum July 27, 2023 20:29

[target.'cfg(target_arch = "x86_64")'.dependencies]
cpufeatures = "0.2.6"

[target.'cfg(curve25519_dalek_backend = "fiat")'.dependencies]
fiat-crypto = "0.1.19"

[target.'cfg(not(target_os = "zkvm"))'.dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This library does not build in the zkvm. It's only used for benchmark tests

@@ -0,0 +1,182 @@
//! Field arithmetic modulo \\(p = 2\^{255} - 19\\), using \\(32\\)-bit
//! limbs with \\(64\\)-bit products.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file implements a custom FieldElement type that uses risc0::mod_mul

Copy link
Collaborator

@nategraf nategraf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how simple the core of this PR is. It looks like the biggest lift is indeed in curve25519-dalek/src/backend/serial/risc0/field.rs.

As far as I can see, you are always using the normalized form of the modular multiplication functions, which is the right baseline. However, within the field implementation is should be possible to speed things up by using the denormalized form for multiplication and adjusting addition and other methdods to handle the fact that inputs may be greater than the modulus. (In particular, there will be exactly two representations of any given number).

The field implementation in the k256 PR can provide some idea of what I mean:

https://github.com/risc0/RustCrypto-elliptic-curves/blob/44b1fc2b317e76bb150636cf67d0fbdfcac39601/k256/src/arithmetic/field/field_8x32_risc0.rs#L137-L181

Additionally, if the benchmark cycles numbers you sent before are accurate, there is something that is not being accounted for that is driving up field arithmetic costs. As a reference, here is the results from cargo run --bin benchmark in the k256 ECDSA example. ("Field" below means the k256 base field and "Scalar" means the scalar field).

Field operations:
add: 169 cycles
mul: 39 cycles
mul_single: 1142 cycles
square: 39 cycles
negate: 31 cycles
invert: 9328 cycles

Scalar operations:
add: 225 cycles
mul: 147 cycles
square: 147 cycles
negate: 123 cycles
invert: 17200 cycles
invert_vartime: 17201 cycles

Group operations:
lincomb: 861568 cycles

I would expect the ed25519 base field performance to be at least as good as k256. (Note, not exactly sure why mul_single is over 1000 cycles. My guest is that there is a page-in that is not being removed from the cycle count)

Could you point me to your benchmark implementation so that I can try it out?

ed25519-dalek/tests/validationvectors.rs Outdated Show resolved Hide resolved
curve25519-dalek/Cargo.toml Outdated Show resolved Hide resolved
curve25519-dalek/src/backend/serial/curve_models/mod.rs Outdated Show resolved Hide resolved
ed25519-dalek/Cargo.toml Outdated Show resolved Hide resolved
curve25519-dalek/src/backend/serial/risc0/field.rs Outdated Show resolved Hide resolved
curve25519-dalek/src/backend/serial/risc0/field.rs Outdated Show resolved Hide resolved
@jjtny1 jjtny1 requested a review from nategraf August 21, 2023 16:47
@jjtny1
Copy link
Author

jjtny1 commented Aug 21, 2023

Could you point me to your benchmark implementation so that I can try it out?

Ive been using this: https://github.com/jjtny1/curve25519-dalek-test
and i run it with cargo run --bin benchmark @nategraf

@jjtny1 jjtny1 closed this Aug 21, 2023
@jjtny1 jjtny1 reopened this Aug 21, 2023
Copy link
Collaborator

@nategraf nategraf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this warning when compiling the guest. Can you add the RISC Zero target in build.rs to suppress this warning?

ed25519_verify: warning: "Defaulting to curve25519_dalek_bits=32: Unknown Rust target platform."

I also opened a PR against your branch with some fixes jjtny1#1

Comment on lines 54 to 55
crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] }
hex = "0.4.3"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you gate these behind a cfg(not(target_os = "zkvm"))? Goal being that building this fork for the host should be essentially the same as upstream

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean cfg(target_os = "zkvm")

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes that's what I meant

curve25519-dalek/src/backend/serial/risc0/field.rs Outdated Show resolved Hide resolved
curve25519-dalek/src/backend/serial/risc0/field.rs Outdated Show resolved Hide resolved
curve25519-dalek/src/backend/serial/risc0/scalar.rs Outdated Show resolved Hide resolved
curve25519-dalek/src/backend/serial/risc0/scalar.rs Outdated Show resolved Hide resolved
Fix normalization and overflow issues found during review
@jjtny1 jjtny1 changed the base branch from joby/curve25519-risc0-acceleration to risczero September 14, 2023 23:41
Copy link
Collaborator

@nategraf nategraf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ready to merge! Thanks for driving this

@jjtny1 jjtny1 merged commit 3ae00b9 into risc0:risczero Sep 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants