feat(curves): add secp256k1

This commit is contained in:
rymnc
2024-05-21 06:02:00 +05:30
parent 2aadb38b7f
commit 962775c25a
5 changed files with 167 additions and 9 deletions

12
Cargo.lock generated
View File

@@ -233,6 +233,17 @@ dependencies = [
"tracing-subscriber 0.2.25",
]
[[package]]
name = "ark-secp256k1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c02e954eaeb4ddb29613fee20840c2bbc85ca4396d53e33837e11905363c5f2"
dependencies = [
"ark-ec",
"ark-ff",
"ark-std",
]
[[package]]
name = "ark-serialize"
version = "0.4.1"
@@ -764,6 +775,7 @@ dependencies = [
"ark-bn254",
"ark-ec",
"ark-ff",
"ark-secp256k1",
"ark-serialize",
"ark-std",
"cfg-if",

View File

@@ -12,6 +12,7 @@ crate-type = ["staticlib"]
ffi = []
bls12_381 = []
bls12_377 = []
secp256k1 = []
bn254 = []
default = ["ffi"]
@@ -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"

View File

@@ -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;
@@ -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
@@ -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]);
@@ -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(),

View File

@@ -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");

120
src/secp256k1.rs Normal file
View File

@@ -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);
}
}