Skip to content

Commit

Permalink
feat(curves): add secp256k1
Browse files Browse the repository at this point in the history
  • Loading branch information
rymnc committed May 21, 2024
1 parent 2aadb38 commit 962775c
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 9 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ crate-type = ["staticlib"]
ffi = []
bls12_381 = []
bls12_377 = []
secp256k1 = []
bn254 = []
default = ["ffi"]

Expand All @@ -26,6 +27,7 @@ ark-ff = "0.4.1"
ark-bn254 = "0.4.0"
ark-bls12-381 = "0.4.0"
ark-bls12-377 = "0.4.0"
ark-secp256k1 = "0.4.0"
tiny-keccak = { version = "=2.0.2", features = ["keccak"] }
ark-ec = "0.4.1"
ark-serialize = "0.4.1"
Expand Down
19 changes: 13 additions & 6 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::ffi::CErrorCode::{
NoError, SerializationErrorInvalidData, SerializationErrorIoError,
SerializationErrorNotEnoughSpace, SerializationErrorUnexpectedFlags,
};
use crate::stealth_commitments::{StealthAddressOnCurve};
use crate::stealth_commitments::StealthAddressOnCurve;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError};
use num_traits::Zero;
use std::ops::Add;
Expand All @@ -16,6 +16,11 @@ cfg_if::cfg_if! {
use ark_bls12_377::{Bls12_377, Fr, G1Projective};
type Curve = Bls12_377;
const PROJECTIVE_SIZE: usize = 48;
} else if #[cfg(feature = "secp256k1")] {
use crate::secp256k1::Secp256k1;
use ark_secp256k1::{Fr, Projective as G1Projective};
type Curve = Secp256k1;
const PROJECTIVE_SIZE: usize = 33;
} else if #[cfg(feature = "bn254")] {
use ark_bn254::{Bn254, Fr, G1Projective};
// we import this to prevent using multiple static libs
Expand All @@ -24,12 +29,10 @@ cfg_if::cfg_if! {
type Curve = Bn254;
const PROJECTIVE_SIZE: usize = 32;
} else {
compile_error!("Enable one curve in features: [bn254, bls12_381, bls12_377]");
compile_error!("Enable one curve in features: [bn254, bls12_381, bls12_377, secp256k1]");
}
}



#[repr(C)]
#[derive(Debug)]
pub struct CFr([u8; 32]);
Expand Down Expand Up @@ -478,8 +481,12 @@ pub extern "C" fn ffi_generate_stealth_private_key(
}))
}
};
let stealth_private_key_opt =
Curve::generate_stealth_private_key(ephemeral_public_key, spending_key, viewing_key, *view_tag);
let stealth_private_key_opt = Curve::generate_stealth_private_key(
ephemeral_public_key,
spending_key,
viewing_key,
*view_tag,
);
if stealth_private_key_opt.is_none() {
return Box::into_raw(Box::new(CReturn {
value: CFr::zero(),
Expand Down
23 changes: 20 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
mod stealth_commitments;

#[cfg(feature = "bls12_377")]
mod bls12_377_impl;
#[cfg(feature = "bls12_381")]
mod bls12_381_impl;
#[cfg(feature = "bn254")]
mod bn254_impl;
#[cfg(feature = "bls12_377")]
mod bls12_377_impl;

#[cfg(all(feature = "bls12_381", feature = "bls12_377", feature = "bn254"))]
#[cfg(feature = "secp256k1")]
mod secp256k1;

#[cfg(all(
feature = "bls12_381",
feature = "bls12_377",
feature = "bn254",
feature = "secp256k1"
))]
compile_error!("Curves are mutually exclusive and cannot be enabled together");

#[cfg(all(feature = "bls12_377", feature = "bn254"))]
compile_error!("Curves are mutually exclusive and cannot be enabled together");

#[cfg(all(feature = "bls12_377", feature = "secp256k1"))]
compile_error!("Curves are mutually exclusive and cannot be enabled together");

#[cfg(all(feature = "bn254", feature = "secp256k1"))]
compile_error!("Curves are mutually exclusive and cannot be enabled together");

#[cfg(all(feature = "bls12_381", feature = "secp256k1"))]
compile_error!("Curves are mutually exclusive and cannot be enabled together");

#[cfg(all(feature = "bls12_377", feature = "bls12_381"))]
compile_error!("Curves are mutually exclusive and cannot be enabled together");

Expand Down
120 changes: 120 additions & 0 deletions src/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use crate::stealth_commitments::{AffineWrapper, RawFr, StealthAddressOnCurve};
use ark_ff::PrimeField;
use ark_secp256k1::{Affine as G1Affine, Fq, Fr, Projective as G1Projective};
use ark_secp256k1::{G_GENERATOR_X, G_GENERATOR_Y};
use tiny_keccak::{Hasher, Keccak};

#[allow(non_camel_case_types)]
pub struct Secp256k1_G1Affine(G1Affine);
impl AffineWrapper for Secp256k1_G1Affine {
type Fq = Fq;
fn new(x: Self::Fq, y: Self::Fq) -> Self {
Secp256k1_G1Affine(G1Affine::new(x, y))
}
}

impl From<Secp256k1_G1Affine> for G1Projective {
fn from(value: Secp256k1_G1Affine) -> Self {
G1Projective::from(value.0)
}
}

impl RawFr for Fr {
type Fr = Fr;
fn as_u64(&self) -> u64 {
self.0 .0[0]
}
}

pub struct Secp256k1;

impl StealthAddressOnCurve for Secp256k1 {
type Projective = G1Projective;
type Affine = Secp256k1_G1Affine;
type Fr = Fr;

fn derive_public_key(private_key: &Self::Fr) -> Self::Projective {
let g1_generator_affine = Self::Affine::new(G_GENERATOR_X, G_GENERATOR_Y);
(Self::Projective::from(g1_generator_affine)) * *private_key
}

fn hash_to_fr(input: &[u8]) -> Self::Fr {
let mut hash = [0; 32];
let mut hasher = Keccak::v256();
hasher.update(input);
hasher.finalize(&mut hash);

// We export the hash as a field element
Self::Fr::from_le_bytes_mod_order(hash.as_slice())
}
}

#[cfg(test)]
mod tests {
use super::*;
use ark_ec::CurveGroup;

type Curve = Secp256k1;

#[test]
fn test_random_keypair() {
let (key, pub_key) = Curve::random_keypair();
// Check the derived key matches the one generated from original key
assert_eq!(Curve::derive_public_key(&key), pub_key);
}

#[test]
fn test_hash_to_fr() {
// Test that hash_to_fr(input_1) != hash_to_fr(input_2) when input_1 != input_2
let input_1 = b"input_1";
let input_2 = b"input_2";
assert_ne!(Curve::hash_to_fr(input_1), Curve::hash_to_fr(input_2));
}

#[test]
fn test_compute_shared_point() {
// In a multiple participant scenario, any participant's public key
// combined with any other participant's private key should arrive at the same shared key
let (key1, pub_key1) = Curve::random_keypair();
let (key2, pub_key2) = Curve::random_keypair();

let shared1 = Curve::compute_shared_point(key1, pub_key2);
let shared2 = Curve::compute_shared_point(key2, pub_key1);

// Convert Projective to Affine for equality comparison
let shared1_affine = shared1.into_affine();
let shared2_affine = shared2.into_affine();

assert_eq!(shared1_affine.x, shared2_affine.x);
assert_eq!(shared1_affine.y, shared2_affine.y);
}

#[test]
fn test_stealth_commitment_generation() {
let (spending_key, spending_public_key) = Curve::random_keypair();
let (viewing_key, viewing_public_key) = Curve::random_keypair();

// generate ephemeral keypair
let (ephemeral_private_key, ephemeral_public_key) = Curve::random_keypair();

let (stealth_commitment, view_tag) = Curve::generate_stealth_commitment(
viewing_public_key,
spending_public_key,
ephemeral_private_key,
);

let stealth_private_key_opt = Curve::generate_stealth_private_key(
ephemeral_public_key,
viewing_key,
spending_key,
view_tag,
);

if stealth_private_key_opt.is_none() {
panic!("View tags did not match");
}

let derived_commitment = Curve::derive_public_key(&stealth_private_key_opt.unwrap());
assert_eq!(derived_commitment, stealth_commitment);
}
}

0 comments on commit 962775c

Please sign in to comment.