-
Notifications
You must be signed in to change notification settings - Fork 4
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
Conversation
use crate::edwards::EdwardsPoint; | ||
|
||
use elliptic_curve::bigint::U256; | ||
|
There was a problem hiding this comment.
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)
|
||
[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"] } |
There was a problem hiding this comment.
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. | |||
|
There was a problem hiding this comment.
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
There was a problem hiding this 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:
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?
Ive been using this: https://github.com/jjtny1/curve25519-dalek-test |
There was a problem hiding this 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
curve25519-dalek/Cargo.toml
Outdated
crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] } | ||
hex = "0.4.3" |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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")
There was a problem hiding this comment.
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
Fix normalization and overflow issues found during review
…ebcf78f0db166362a Update to be based on v4.1.0 and fix the last remaining issues
There was a problem hiding this 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
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: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:
And signature verification:
How was this tested:
Using @nategraf's branch, I ran
CARGO=/Users/jobythundil/.cargo/bin/cargo cargo-risczero risczero test -- --release --no-default-features
from thecurve25519-dalek
crate to run all the unit and integration testsUsing 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