Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ dashmap = "6.1.0"
dashu = "0.4.2"
derive_more = { version = "2.0", features = ["constructor"] }
elf = "0.7.4"
elliptic-curve = "0.13.8"
elliptic-curve = { version = "0.13.8", features = ["ecdh", "hazmat", "sec1"] }
env_logger = "0.11.6"
eyre = "0.6.12"
ff = { version = "0.13", features = ["derive", "derive_bits"] }
Expand Down
2 changes: 1 addition & 1 deletion sdk/cli/src/subcommand/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build]
target = ["riscv32im-risc0-zkvm-elf"]
target = ["riscv64im-pico-zkvm-elf"]
extended = true
tools = ["cargo", "cargo-clippy", "clippy", "rustfmt"]
configure-args = []
Expand Down
2 changes: 1 addition & 1 deletion sdk/cli/src/subcommand/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl ProveCmd {
.parent()
.unwrap()
.join(DEFAULT_ELF_DIR)
.join("riscv32im-pico-zkvm-elf")
.join("riscv64im-pico-zkvm-elf")
}
};
let elf: Vec<u8> = std::fs::read(elf_path)?;
Expand Down
1 change: 1 addition & 0 deletions sdk/patch-libs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ categories = { workspace = true }

[dependencies]
bincode.workspace = true
elliptic-curve.workspace = true
serde.workspace = true
69 changes: 49 additions & 20 deletions sdk/patch-libs/src/bls12381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use crate::{
};

/// The number of limbs in [Bls12381AffinePoint].
pub const N: usize = 24;
pub const N: usize = 12;

/// A point on the BLS12-381 curve.
#[derive(Copy, Clone)]
#[repr(align(4))]
#[repr(align(8))]
pub struct Bls12381Point(pub WeierstrassPoint<N>);

impl WeierstrassAffinePoint<N> for Bls12381Point {
Expand All @@ -24,38 +24,53 @@ impl WeierstrassAffinePoint<N> for Bls12381Point {
}

impl AffinePoint<N> for Bls12381Point {
const GENERATOR: [u64; N] = [
18103045581585958587,
7806400890582735599,
11623291730934869080,
14080658508445169925,
2780237799254240271,
1725392847304644500,
912580534683953121,
15005087156090211044,
61670280795567085,
18227722000993880822,
11573741888802228964,
627113611842199793,
];

/// The generator was taken from "py_ecc" python library by the Ethereum Foundation:
///
/// https://github.com/ethereum/py_ecc/blob/7b9e1b3/py_ecc/bls12_381/bls12_381_curve.py#L38-L45
const GENERATOR: [u32; N] = [
3676489403, 4214943754, 4185529071, 1817569343, 387689560, 2706258495, 2541009157,
3278408783, 1336519695, 647324556, 832034708, 401724327, 1187375073, 212476713, 2726857444,
3493644100, 738505709, 14358731, 3587181302, 4243972245, 1948093156, 2694721773,
3819610353, 146011265,
];
#[allow(deprecated)]
const GENERATOR_T: Self = Self(WeierstrassPoint::Affine(Self::GENERATOR));

fn new(limbs: [u32; N]) -> Self {
fn new(limbs: [u64; N]) -> Self {
Self(WeierstrassPoint::Affine(limbs))
}

fn limbs_ref(&self) -> &[u32; N] {
fn identity() -> Self {
Self::infinity()
}

fn is_identity(&self) -> bool {
self.is_infinity()
}

fn limbs_ref(&self) -> &[u64; N] {
match &self.0 {
WeierstrassPoint::Infinity => panic!("Infinity point has no limbs"),
WeierstrassPoint::Affine(limbs) => limbs,
}
}

fn limbs_mut(&mut self) -> &mut [u32; N] {
fn limbs_mut(&mut self) -> &mut [u64; N] {
match &mut self.0 {
WeierstrassPoint::Infinity => panic!("Infinity point has no limbs"),
WeierstrassPoint::Affine(limbs) => limbs,
}
}

fn complete_add_assign(&mut self, other: &Self) {
self.weierstrass_add_assign(other);
}

fn add_assign(&mut self, other: &Self) {
let a = self.limbs_mut();
let b = other.limbs_ref();
Expand All @@ -64,6 +79,10 @@ impl AffinePoint<N> for Bls12381Point {
}
}

fn complete_add_assign(&mut self, other: &Self) {
self.weierstrass_add_assign(other);
}

fn double(&mut self) {
let a = self.limbs_mut();
unsafe {
Expand All @@ -73,12 +92,22 @@ impl AffinePoint<N> for Bls12381Point {
}

/// Decompresses a compressed public key using bls12381_decompress precompile.
pub fn decompress_pubkey(compressed_key: &[u8; 48]) -> Result<[u8; 96], ErrorKind> {
let mut decompressed_key = [0u8; 96];
decompressed_key[..48].copy_from_slice(compressed_key);
pub fn decompress_pubkey(compressed_key: &[u64; 6]) -> Result<[u64; 12], ErrorKind> {
let mut decompressed_key = [0u64; 12];
decompressed_key[..6].copy_from_slice(compressed_key);

// The sign bit is stored in the first byte, so we have to access it like this.
let mut decompressed_key = decompressed_key.map(u64::to_ne_bytes);

// The sign bit is the third most significant bit (beginning the count at "first").
const SIGN_OFFSET: u32 = 3;
const SIGN_MASK: u8 = 1u8 << (u8::BITS - SIGN_OFFSET);
let sign_bit = (decompressed_key[0][0] & SIGN_MASK) != 0;
decompressed_key[0][0] <<= SIGN_OFFSET;
decompressed_key[0][0] >>= SIGN_OFFSET;

let mut decompressed_key = decompressed_key.map(u64::from_ne_bytes);

let sign_bit = ((decompressed_key[0] & 0b_0010_0000) >> 5) == 1;
decompressed_key[0] &= 0b_0001_1111;
unsafe {
syscall_bls12381_decompress(&mut decompressed_key, sign_bit);
}
Expand Down
31 changes: 21 additions & 10 deletions sdk/patch-libs/src/bn254.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use crate::{
};

/// The number of limbs in [Bn254AffinePoint].
pub const N: usize = 16;
pub const N: usize = 8;

/// A point on the Bn254 curve.
#[derive(Copy, Clone)]
#[repr(align(4))]
#[repr(align(8))]
pub struct Bn254Point(pub WeierstrassPoint<N>);

impl WeierstrassAffinePoint<N> for Bn254Point {
Expand All @@ -22,33 +22,40 @@ impl WeierstrassAffinePoint<N> for Bn254Point {
}

impl AffinePoint<N> for Bn254Point {
const GENERATOR: [u64; N] = [1, 0, 0, 0, 2, 0, 0, 0];

#[allow(deprecated)]
/// The generator has been taken from py_pairing python library by the Ethereum Foundation:
///
/// https://github.com/ethereum/py_pairing/blob/5f609da/py_ecc/bn128/bn128_field_elements.py
const GENERATOR: [u32; N] = [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0];
const GENERATOR_T: Self = Self(WeierstrassPoint::Affine(Self::GENERATOR));

fn new(limbs: [u32; N]) -> Self {
fn new(limbs: [u64; N]) -> Self {
Self(WeierstrassPoint::Affine(limbs))
}

fn limbs_ref(&self) -> &[u32; N] {
fn identity() -> Self {
Self::infinity()
}

fn is_identity(&self) -> bool {
self.is_infinity()
}

fn limbs_ref(&self) -> &[u64; N] {
match &self.0 {
WeierstrassPoint::Infinity => panic!("Infinity point has no limbs"),
WeierstrassPoint::Affine(limbs) => limbs,
}
}

fn limbs_mut(&mut self) -> &mut [u32; N] {
fn limbs_mut(&mut self) -> &mut [u64; N] {
match &mut self.0 {
WeierstrassPoint::Infinity => panic!("Infinity point has no limbs"),
WeierstrassPoint::Affine(limbs) => limbs,
}
}

fn complete_add_assign(&mut self, other: &Self) {
self.weierstrass_add_assign(other);
}

fn add_assign(&mut self, other: &Self) {
let a = self.limbs_mut();
let b = other.limbs_ref();
Expand All @@ -57,6 +64,10 @@ impl AffinePoint<N> for Bn254Point {
}
}

fn complete_add_assign(&mut self, other: &Self) {
self.weierstrass_add_assign(other);
}

fn double(&mut self) {
let a = self.limbs_mut();
unsafe {
Expand Down
111 changes: 111 additions & 0 deletions sdk/patch-libs/src/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//! An implementation of the types needed for [`CurveArithmetic`].
//!
//! [`CurveArithmetic`] is a trait that is used in [RustCryptos ECDSA](https://github.com/RustCrypto/signatures).
//!
//! [`CurveArithmetic`] contains all the types needed to implement the ECDSA algorithm over some
//! curve.
//!
//! This implementation is specifically for use inside of Pico zkVM, and internally uses Pico's Weierstrass
//! precompiles.
//!
//! In summary, Pico overrides curve arithmetic entirely, and patches upstream field operations
//! to be more efficient in the VM, such as `sqrt` or `inverse`.
use crate::utils::AffinePoint as PicoAffinePointTrait;

use elliptic_curve::{
ff, generic_array::typenum::consts::U32, subtle::CtOption, CurveArithmetic, FieldBytes,
};
use std::{fmt::Debug, ops::Neg};

/// The affine point type for Pico.
pub mod affine;
pub use affine::AffinePoint;

/// The projective point type for Pico.
pub mod projective;
pub use projective::ProjectivePoint;

/// NOTE: The only supported ECDSA curves are secp256k1 and secp256r1, which both
/// have 8 limbs in their field elements.
const POINT_LIMBS: usize = 4 * 2;

/// The number of bytes in a field element as an [`usize`].
const FIELD_BYTES_SIZE_USIZE: usize = 32;

/// The number of bytes in a field element as an [`elliptic_curve::generic_array::U32`].
#[allow(non_camel_case_types)]
type FIELD_BYTES_SIZE = U32;

/// A [`CurveArithmetic`] extension for Pico acceleration.
///
/// Patched crates implement this trait to take advantage of Pico-specific acceleration in the zkVM
/// context.
///
/// Note: This trait only supports 32 byte base field curves.
pub trait ECDSACurve
where
Self: CurveArithmetic<
FieldBytesSize = FIELD_BYTES_SIZE,
AffinePoint = AffinePoint<Self>,
ProjectivePoint = ProjectivePoint<Self>,
>,
{
type FieldElement: Field<Self> + Neg<Output = Self::FieldElement>;

/// The underlying [`PicoAffinePointTrait`] implementation.
type PicoAffinePoint: ECDSAPoint;

/// The `a` coefficient in the curve equation.
const EQUATION_A: Self::FieldElement;

/// The `b` coefficient in the curve equation.
const EQUATION_B: Self::FieldElement;
}

/// Alias trait for the [`ff::PrimeField`] with 32 byte field elements.
///
/// Note: All bytes should be considered to be in big-endian format.
pub trait Field<C: ECDSACurve>: ff::PrimeField {
/// Create an instance of self from a FieldBytes.
fn from_bytes(bytes: &FieldBytes<C>) -> CtOption<Self>;

/// Convert self to a FieldBytes.
///
/// Note: Implementers should ensure these methods normalize first.
fn to_bytes(self) -> FieldBytes<C>;

/// Ensure the field element is normalized.
fn normalize(self) -> Self;
}

pub type FieldElement<C> = <C as ECDSACurve>::FieldElement;

/// Alias trait for the [`PicoAffinePointTrait`] with 32 byte field elements.
pub trait ECDSAPoint:
PicoAffinePointTrait<POINT_LIMBS> + Clone + Copy + Debug + Send + Sync
{
#[inline]
fn from(x: &[u8], y: &[u8]) -> Self {
<Self as PicoAffinePointTrait<POINT_LIMBS>>::from(x, y)
}
}

impl<P> ECDSAPoint for P where
P: PicoAffinePointTrait<POINT_LIMBS> + Clone + Copy + Debug + Send + Sync
{
}

pub mod ecdh {
pub use elliptic_curve::ecdh::{diffie_hellman, EphemeralSecret, SharedSecret};

use super::{AffinePoint, ECDSACurve, Field};

impl<C: ECDSACurve> From<&AffinePoint<C>> for SharedSecret<C> {
fn from(affine: &AffinePoint<C>) -> SharedSecret<C> {
let (x, _) = affine.field_elements();

x.to_bytes().into()
}
}
}
Loading
Loading