Skip to content
Open
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
3 changes: 3 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions crates/accelerators/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ openvm-keccak256.workspace = true
openvm-sha2.workspace = true

# crypto
ark-bls12-381 = { version = "0.5", default-features = false, features = ["curve"] }
ark-ec = { version = "0.5", default-features = false }
ark-serialize = { version = "0.5", default-features = false }
aurora-engine-modexp = { version = "1.2.0", default-features = false }
ripemd = { version = "0.1.3", default-features = false }

Expand Down
56 changes: 53 additions & 3 deletions crates/accelerators/src/ffi/bls12_381.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! C ABI for the BLS12-381 add/MSM accelerators (EIP-2537).
//! C ABI for the BLS12-381 add/MSM/map accelerators (EIP-2537).

use crate::{
ops,
types::{
ZkvmBls12381G1MsmPair, ZkvmBls12381G1Point, ZkvmBls12381G2MsmPair, ZkvmBls12381G2Point,
ZkvmBls12381PairingPair, ZkvmStatus,
ZkvmBls12381Fp, ZkvmBls12381Fp2, ZkvmBls12381G1MsmPair, ZkvmBls12381G1Point,
ZkvmBls12381G2MsmPair, ZkvmBls12381G2Point, ZkvmBls12381PairingPair, ZkvmStatus,
},
};

Expand Down Expand Up @@ -147,3 +147,53 @@ pub unsafe extern "C" fn zkvm_bls12_pairing(
Err(_) => ZkvmStatus::Fail,
}
}

/// BLS12-381 map field element to G1 (precompile 0x10, EIP-2537).
///
/// Returns [`ZkvmStatus::Fail`] if either pointer is NULL or the field element
/// is not canonical.
///
/// # Safety
///
/// - `field_element`, if non-NULL, must be valid for reads of 48 bytes.
/// - `result`, if non-NULL, must be valid for writes of 96 bytes.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zkvm_bls12_map_fp_to_g1(
field_element: *const ZkvmBls12381Fp,
result: *mut ZkvmBls12381G1Point,
) -> ZkvmStatus {
if field_element.is_null() || result.is_null() {
return ZkvmStatus::Fail;
}
// SAFETY: non-NULL checked above; validity is guaranteed by the caller.
let (field_element, result) = unsafe { (&*field_element, &mut *result) };
match ops::bls12_381_map_fp_to_g1(field_element, result) {
Ok(()) => ZkvmStatus::Ok,
Err(_) => ZkvmStatus::Fail,
}
}

/// BLS12-381 map field element to G2 (precompile 0x11, EIP-2537).
///
/// Returns [`ZkvmStatus::Fail`] if either pointer is NULL or either half of
/// the field element is not canonical.
///
/// # Safety
///
/// - `field_element`, if non-NULL, must be valid for reads of 96 bytes.
/// - `result`, if non-NULL, must be valid for writes of 192 bytes.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn zkvm_bls12_map_fp2_to_g2(
field_element: *const ZkvmBls12381Fp2,
result: *mut ZkvmBls12381G2Point,
) -> ZkvmStatus {
if field_element.is_null() || result.is_null() {
return ZkvmStatus::Fail;
}
// SAFETY: non-NULL checked above; validity is guaranteed by the caller.
let (field_element, result) = unsafe { (&*field_element, &mut *result) };
match ops::bls12_381_map_fp2_to_g2(field_element, result) {
Ok(()) => ZkvmStatus::Ok,
Err(_) => ZkvmStatus::Fail,
}
}
3 changes: 1 addition & 2 deletions crates/accelerators/src/ops/bls12_381/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ use openvm_curve_utils::SubgroupCheck;
use openvm_ecc_guest::{algebra::IntMod, weierstrass::WeierstrassPoint, Group};
use openvm_pairing::bls12_381 as bls;

use super::BLS_FP_LEN;
use crate::{
ops::Error,
types::{ZkvmBls12381G1Point, ZkvmBls12381G2Point, ZkvmBls12381Scalar},
};

const BLS_FP_LEN: usize = 48;

#[inline]
fn read_bls_fp(input: &[u8]) -> Result<bls::Fp, Error> {
bls::Fp::from_be_bytes(input).ok_or(Error::FieldElementInvalid)
Expand Down
83 changes: 83 additions & 0 deletions crates/accelerators/src/ops/bls12_381/map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! BLS12-381 map-to-curve (EIP-2537).
//!
//! Implemented using arkworks.

use ark_bls12_381::{Fq, Fq2, G1Affine, G2Affine};
use ark_ec::{
hashing::{curve_maps::wb::WBMap, map_to_curve_hasher::MapToCurve},
AffineRepr,
};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};

use super::BLS_FP_LEN;
use crate::{
ops::Error,
types::{ZkvmBls12381Fp, ZkvmBls12381Fp2, ZkvmBls12381G1Point, ZkvmBls12381G2Point},
};

/// BLS12-381 map field element to G1 (precompile 0x10).
pub fn bls12_381_map_fp_to_g1(
fp: &ZkvmBls12381Fp,
output: &mut ZkvmBls12381G1Point,
) -> Result<(), Error> {
let fp = read_fq(&fp.data)?;
let point = WBMap::map_to_curve(fp).map_err(|_| Error::FieldElementInvalid)?.clear_cofactor();

encode_g1_point(&point, output);
Ok(())
}

/// BLS12-381 map field element to G2 (precompile 0x11). Input is `c0 || c1`.
pub fn bls12_381_map_fp2_to_g2(
fp2: &ZkvmBls12381Fp2,
output: &mut ZkvmBls12381G2Point,
) -> Result<(), Error> {
let c0 = read_fq(&fp2.data[..BLS_FP_LEN])?;
let c1 = read_fq(&fp2.data[BLS_FP_LEN..])?;
let point = WBMap::map_to_curve(Fq2::new(c0, c1))
.map_err(|_| Error::FieldElementInvalid)?
.clear_cofactor();

encode_g2_point(&point, output);
Ok(())
}

/// Reads a big-endian field element, rejecting non-canonical encodings.
fn read_fq(input_be: &[u8]) -> Result<Fq, Error> {
let mut input_le = [0u8; BLS_FP_LEN];
input_le.copy_from_slice(input_be);
input_le.reverse();

Fq::deserialize_uncompressed(&input_le[..]).map_err(|_| Error::FieldElementInvalid)
}

/// Writes a field element as big-endian bytes.
fn encode_fq(fq: &Fq, output: &mut [u8]) {
fq.serialize_uncompressed(&mut output[..]).expect("Failed to serialize field element");
output.reverse();
}

/// Writes a G1 point as `x || y`; the point at infinity encodes as zeros.
fn encode_g1_point(point: &G1Affine, output: &mut ZkvmBls12381G1Point) {
let Some((x, y)) = point.xy() else {
output.data.fill(0);
return;
};

encode_fq(&x, &mut output.data[..BLS_FP_LEN]);
encode_fq(&y, &mut output.data[BLS_FP_LEN..]);
}

/// Writes a G2 point as `x_c0 || x_c1 || y_c0 || y_c1`; the point at infinity
/// encodes as zeros.
fn encode_g2_point(point: &G2Affine, output: &mut ZkvmBls12381G2Point) {
let Some((x, y)) = point.xy() else {
output.data.fill(0);
return;
};

encode_fq(&x.c0, &mut output.data[..BLS_FP_LEN]);
encode_fq(&x.c1, &mut output.data[BLS_FP_LEN..2 * BLS_FP_LEN]);
encode_fq(&y.c0, &mut output.data[2 * BLS_FP_LEN..3 * BLS_FP_LEN]);
encode_fq(&y.c1, &mut output.data[3 * BLS_FP_LEN..]);
}
6 changes: 6 additions & 0 deletions crates/accelerators/src/ops/bls12_381/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! BLS12-381 group operations (EIP-2537).

mod codec;
mod map;

pub use map::{bls12_381_map_fp2_to_g2, bls12_381_map_fp_to_g1};

use alloc::vec::Vec;

Expand All @@ -23,6 +26,9 @@ use crate::{
},
};

/// The number of bytes needed to represent an element of the base field Fp.
const BLS_FP_LEN: usize = 48;

/// BLS12-381 G1 point addition (precompile 0x0b). Inputs are `x || y`.
///
/// Per EIP-2537 G1ADD, inputs are validated on-curve only, not for subgroup
Expand Down
3 changes: 2 additions & 1 deletion crates/accelerators/src/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ mod modexp;

pub use blake2::blake2f;
pub use bls12_381::{
bls12_381_g1_add, bls12_381_g1_msm, bls12_381_g2_add, bls12_381_g2_msm, bls12_381_pairing_check,
bls12_381_g1_add, bls12_381_g1_msm, bls12_381_g2_add, bls12_381_g2_msm,
bls12_381_map_fp2_to_g2, bls12_381_map_fp_to_g1, bls12_381_pairing_check,
};
pub use bn254::{bn254_g1_add, bn254_g1_mul, bn254_pairing_check};
pub use ecdsa::{secp256k1_ecrecover, secp256k1_verify, secp256r1_verify};
Expand Down
Loading
Loading