mirror of
https://github.com/vacp2p/stealth-address-kit.git
synced 2026-01-09 13:38:01 -05:00
feat(curves): add secp256k1
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
19
src/ffi.rs
19
src/ffi.rs
@@ -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(),
|
||||
|
||||
23
src/lib.rs
23
src/lib.rs
@@ -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
120
src/secp256k1.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user