(
msg: &[u8],
pk: &GroupAffine,
) -> GroupAffine
{
-
let pk_encoded = pk.to_encoded_point(true);
let b = hex::decode(pk_encoded).unwrap();
let x = [msg, b.as_slice()];
@@ -26,8 +22,9 @@ pub fn hash_to_curve<
let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>(
&[x],
- b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_"
- ).unwrap();
+ b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_",
+ )
+ .unwrap();
let pt_affine = pt.to_affine();
@@ -36,7 +33,7 @@ pub fn hash_to_curve<
pub fn k256_affine_to_arkworks_secp256k1_affine<
Fp: ark_ff::PrimeField,
- P: ark_ec::SWModelParameters
+ P: ark_ec::SWModelParameters,
>(
k_pt: AffinePoint,
) -> GroupAffine {
@@ -50,7 +47,10 @@ pub fn k256_affine_to_arkworks_secp256k1_affine<
// pad x bytes
let mut k_pt_x_bytes_vec = vec![0u8; num_field_bytes];
for (i, _) in k_pt_x_bytes.clone().iter().enumerate() {
- let _ = std::mem::replace(&mut k_pt_x_bytes_vec[i], k_pt_x_bytes[k_pt_x_bytes.len() - 1 - i]);
+ let _ = std::mem::replace(
+ &mut k_pt_x_bytes_vec[i],
+ k_pt_x_bytes[k_pt_x_bytes.len() - 1 - i],
+ );
}
let reader = std::io::BufReader::new(k_pt_x_bytes_vec.as_slice());
let g_x = P::BaseField::read(reader).unwrap();
@@ -61,7 +61,10 @@ pub fn k256_affine_to_arkworks_secp256k1_affine<
// pad y bytes
let mut k_pt_y_bytes_vec = vec![0u8; num_field_bytes];
for (i, _) in k_pt_y_bytes.clone().iter().enumerate() {
- let _ = std::mem::replace(&mut k_pt_y_bytes_vec[i], k_pt_y_bytes[k_pt_y_bytes.len() - 1 - i]);
+ let _ = std::mem::replace(
+ &mut k_pt_y_bytes_vec[i],
+ k_pt_y_bytes[k_pt_y_bytes.len() - 1 - i],
+ );
}
let reader = std::io::BufReader::new(k_pt_y_bytes_vec.as_slice());
diff --git a/rust-arkworks/src/lib.rs b/rust-arkworks/src/lib.rs
index f974701..20b7609 100644
--- a/rust-arkworks/src/lib.rs
+++ b/rust-arkworks/src/lib.rs
@@ -1,33 +1,35 @@
mod error;
mod hash_to_curve;
+mod wasm;
#[cfg(test)]
mod tests;
pub mod sig {
use crate::error::CryptoError;
use crate::hash_to_curve;
- use ark_ec::{AffineCurve, ProjectiveCurve, models::SWModelParameters};
use ark_ec::short_weierstrass_jacobian::GroupAffine;
+ use ark_ec::{models::SWModelParameters, AffineCurve, ProjectiveCurve};
use ark_ff::{PrimeField, ToBytes};
- use ark_std::{
- marker::PhantomData,
- UniformRand,
- rand::Rng,
+ use ark_serialize::{
+ CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write,
};
- use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError, Read, Write};
- use sha2::{Sha512, Digest};
+ use ark_std::{marker::PhantomData, rand::Rng, UniformRand};
use secp256k1::sec1::Sec1EncodePoint;
-
- pub struct DeterministicNullifierSignatureScheme<'a, C: ProjectiveCurve, Fq: ark_ff::PrimeField, P: ark_ec::SWModelParameters> {
+ use sha2::{Digest, Sha512};
+
+ pub struct DeterministicNullifierSignatureScheme<
+ 'a,
+ C: ProjectiveCurve,
+ Fq: ark_ff::PrimeField,
+ P: ark_ec::SWModelParameters,
+ > {
_group: PhantomData,
_field: PhantomData,
_parameters: PhantomData,
_message_lifetime: PhantomData<&'a ()>,
}
- pub fn affine_to_bytes(
- point: &GroupAffine
- ) -> Vec:: {
+ pub fn affine_to_bytes(point: &GroupAffine) -> Vec {
let encoded = point.to_encoded_point(true);
let b = hex::decode(encoded).unwrap();
b.to_vec()
@@ -59,14 +61,7 @@ pub mod sig {
let g_r_bytes = affine_to_bytes::(g_r);
let z_bytes = affine_to_bytes::
(z);
- let c_preimage_vec = [
- g_bytes,
- pk_bytes,
- h_bytes,
- nul_bytes,
- g_r_bytes,
- z_bytes,
- ].concat();
+ let c_preimage_vec = [g_bytes, pk_bytes, h_bytes, nul_bytes, g_r_bytes, z_bytes].concat();
let mut sha512_hasher = Sha512::new();
sha512_hasher.update(c_preimage_vec.as_slice());
@@ -122,12 +117,22 @@ pub mod sig {
) -> Result;
}
- #[derive(Copy, Clone, ark_serialize_derive::CanonicalSerialize, ark_serialize_derive::CanonicalDeserialize)]
+ #[derive(
+ Copy,
+ Clone,
+ ark_serialize_derive::CanonicalSerialize,
+ ark_serialize_derive::CanonicalDeserialize,
+ )]
pub struct Parameters {
pub g: GroupAffine,
}
- #[derive(Copy, Clone, ark_serialize_derive::CanonicalSerialize, ark_serialize_derive::CanonicalDeserialize)]
+ #[derive(
+ Copy,
+ Clone,
+ ark_serialize_derive::CanonicalSerialize,
+ ark_serialize_derive::CanonicalDeserialize,
+ )]
pub struct Signature {
pub z: GroupAffine,
pub g_r: GroupAffine
,
@@ -136,7 +141,9 @@ pub mod sig {
pub nul: GroupAffine
,
}
- impl<'a, C: ProjectiveCurve, Fq: PrimeField, P: SWModelParameters> VerifiableUnpredictableFunction for DeterministicNullifierSignatureScheme<'a, C, Fq, P> {
+ impl<'a, C: ProjectiveCurve, Fq: PrimeField, P: SWModelParameters>
+ VerifiableUnpredictableFunction for DeterministicNullifierSignatureScheme<'a, C, Fq, P>
+ {
type Message = &'a [u8];
type Parameters = Parameters
;
type PublicKey = GroupAffine
;
@@ -151,7 +158,7 @@ pub mod sig {
let public_key = pp.g.mul(secret_key).into();
Ok((public_key, secret_key))
}
-
+
fn sign_with_r(
pp: &Self::Parameters,
keypair: (&Self::PublicKey, &Self::SecretKey),
@@ -163,7 +170,7 @@ pub mod sig {
// Compute h = htc([m, pk])
let h = compute_h::(&keypair.0, &message).unwrap();
-
+
// Compute z = h^r
let z = h.mul(r).into_affine();
@@ -171,14 +178,7 @@ pub mod sig {
let nul = h.mul(*keypair.1).into_affine();
// Compute c = sha512([g, pk, h, nul, g^r, z])
- let c_scalar: P::ScalarField = compute_c::(
- &g,
- keypair.0,
- &h,
- &nul,
- &g_r,
- &z,
- );
+ let c_scalar: P::ScalarField = compute_c::
(&g, keypair.0, &h, &nul, &g_r, &z);
// Compute s = r + sk ⋅ c
let sk_c = keypair.1.into_repr().into() * c_scalar.into_repr().into();
@@ -191,7 +191,7 @@ pub mod sig {
s: s_scalar,
g_r,
c: c_scalar,
- nul
+ nul,
};
Ok(signature)
}
@@ -218,14 +218,8 @@ pub mod sig {
let h = compute_h::(pk, message).unwrap();
// Compute c' = sha512([g, pk, h, nul, g^r, z])
- let c_scalar: P::ScalarField = compute_c::(
- &pp.g,
- pk,
- &h,
- &sig.nul,
- &sig.g_r,
- &sig.z,
- );
+ let c_scalar: P::ScalarField =
+ compute_c::
(&pp.g, pk, &h, &sig.nul, &sig.g_r, &sig.z);
// Reject if g^s ⋅ pk^{-c} != g^r
let g_s = pp.g.mul(sig.s);
@@ -242,14 +236,14 @@ pub mod sig {
let h_s_nul_c = h_s - nul_c;
if sig.z != h_s_nul_c {
- return Ok(false)
+ return Ok(false);
}
// Reject if c != c'
if c_scalar != sig.c {
return Ok(false);
}
-
+
Ok(true)
}
}
diff --git a/rust-arkworks/src/tests.rs b/rust-arkworks/src/tests.rs
index e23f550..0749939 100644
--- a/rust-arkworks/src/tests.rs
+++ b/rust-arkworks/src/tests.rs
@@ -1,19 +1,16 @@
-use secp256k1::curves::Affine;
-use secp256k1::curves::Secp256k1Parameters;
-use secp256k1::fields::Fq;
-use crate::sig::VerifiableUnpredictableFunction;
-use crate::hash_to_curve::{
- hash_to_curve,
- k256_affine_to_arkworks_secp256k1_affine,
-};
-use ark_std::rand;
+use crate::hash_to_curve::{hash_to_curve, k256_affine_to_arkworks_secp256k1_affine};
use crate::sig::DeterministicNullifierSignatureScheme;
-use ark_ec::{AffineCurve, ProjectiveCurve};
+use crate::sig::VerifiableUnpredictableFunction;
use ark_ec::models::short_weierstrass_jacobian::GroupAffine;
-use ark_ff::bytes::{ToBytes, FromBytes};
+use ark_ec::{AffineCurve, ProjectiveCurve};
use ark_ff::biginteger;
-use rand::{prelude::ThreadRng, thread_rng};
+use ark_ff::bytes::{FromBytes, ToBytes};
+use ark_std::rand;
use k256::{ProjectivePoint, Scalar};
+use rand::{prelude::ThreadRng, thread_rng};
+use secp256k1::curves::Affine;
+use secp256k1::curves::Secp256k1Parameters;
+use secp256k1::fields::Fq;
type Parameters = crate::sig::Parameters;
@@ -24,7 +21,8 @@ fn test_template() -> (ThreadRng, Affine) {
(rng, g)
}
-type Scheme<'a> = DeterministicNullifierSignatureScheme::<'a, secp256k1::Projective, Fq, Secp256k1Parameters>;
+type Scheme<'a> =
+ DeterministicNullifierSignatureScheme<'a, secp256k1::Projective, Fq, Secp256k1Parameters>;
#[test]
pub fn test_k256_affine_to_arkworks_secp256k1_affine() {
@@ -40,7 +38,7 @@ pub fn test_k256_affine_to_arkworks_secp256k1_affine() {
// Convert k256_pt to an arkworks point
let converted_pt = k256_affine_to_arkworks_secp256k1_affine::<
secp256k1::fields::Fq,
- Secp256k1Parameters
+ Secp256k1Parameters,
>(k256_pt.to_affine());
// The points should match
@@ -48,9 +46,7 @@ pub fn test_k256_affine_to_arkworks_secp256k1_affine() {
}
}
-fn hex_to_fr(
- hex: &str,
-) -> secp256k1::fields::Fr {
+fn hex_to_fr(hex: &str) -> secp256k1::fields::Fr {
let num_field_bytes = 320;
let mut sk_bytes_vec = vec![0u8; num_field_bytes];
let mut sk_bytes = hex::decode(hex).unwrap();
@@ -64,9 +60,7 @@ fn hex_to_fr(
secp256k1::fields::Fr::read(sk_bytes_vec.as_slice()).unwrap()
}
-fn coord_to_hex(
- coord: biginteger::BigInteger320
-) -> String {
+fn coord_to_hex(coord: biginteger::BigInteger320) -> String {
let mut coord_bytes = vec![];
let _ = coord.write(&mut coord_bytes);
coord_bytes.reverse();
@@ -75,11 +69,11 @@ fn coord_to_hex(
}
fn hardcoded_sk() -> String {
- "519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464".to_string()
+ "519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464".to_string()
}
fn hardcoded_r() -> String {
- "93b9323b629f251b8f3fc2dd11f4672c5544e8230d493eceea98a90bda789808".to_string()
+ "93b9323b629f251b8f3fc2dd11f4672c5544e8230d493eceea98a90bda789808".to_string()
}
pub fn hardcoded_msg() -> String {
@@ -89,7 +83,7 @@ pub fn hardcoded_msg() -> String {
#[test]
pub fn test_keygen() {
let (mut rng, g) = test_template();
- let pp = Parameters{ g };
+ let pp = Parameters { g };
let (pk, sk) = Scheme::keygen(&pp, &mut rng).unwrap();
@@ -100,31 +94,21 @@ pub fn test_keygen() {
#[test]
pub fn test_sign_and_verify() {
let (mut rng, g) = test_template();
- let pp = Parameters{ g };
+ let pp = Parameters { g };
let message = b"Message";
let keypair = Scheme::keygen(&pp, &mut rng).unwrap();
- let sig = Scheme::sign(
- &pp,
- &mut rng,
- (&keypair.0, &keypair.1),
- message
- ).unwrap();
-
- let is_valid = Scheme::verify_non_zk(
- &pp,
- &keypair.0,
- &sig,
- message,
- );
+ let sig = Scheme::sign(&pp, &mut rng, (&keypair.0, &keypair.1), message).unwrap();
+
+ let is_valid = Scheme::verify_non_zk(&pp, &keypair.0, &sig, message);
assert!(is_valid.unwrap());
}
-pub fn compute_h() -> GroupAffine:: {
+pub fn compute_h() -> GroupAffine {
let msg = hardcoded_msg();
let message = msg.as_bytes();
-
+
let sk = hex_to_fr(&hardcoded_sk());
let (_, g) = test_template();
let pk_projective = g.mul(sk);
@@ -142,9 +126,15 @@ pub fn test_against_zk_nullifier_sig_pk() {
let (_, g) = test_template();
let pk_projective = g.mul(sk);
let pk = GroupAffine::::from(pk_projective);
-
- assert_eq!(coord_to_hex(pk.x.into()), "00000000000000000cec028ee08d09e02672a68310814354f9eabfff0de6dacc1cd3a774496076ae");
- assert_eq!(coord_to_hex(pk.y.into()), "0000000000000000eff471fba0409897b6a48e8801ad12f95d0009b753cf8f51c128bf6b0bd27fbd");
+
+ assert_eq!(
+ coord_to_hex(pk.x.into()),
+ "00000000000000000cec028ee08d09e02672a68310814354f9eabfff0de6dacc1cd3a774496076ae"
+ );
+ assert_eq!(
+ coord_to_hex(pk.y.into()),
+ "0000000000000000eff471fba0409897b6a48e8801ad12f95d0009b753cf8f51c128bf6b0bd27fbd"
+ );
}
#[test]
@@ -154,8 +144,14 @@ pub fn test_against_zk_nullifier_sig_g_r() {
let (_, g) = test_template();
let g_r_projective = g.mul(r);
let g_r = GroupAffine::::from(g_r_projective);
- assert_eq!(coord_to_hex(g_r.x.into()), "00000000000000009d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804");
- assert_eq!(coord_to_hex(g_r.y.into()), "0000000000000000ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1");
+ assert_eq!(
+ coord_to_hex(g_r.x.into()),
+ "00000000000000009d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804"
+ );
+ assert_eq!(
+ coord_to_hex(g_r.y.into()),
+ "0000000000000000ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1"
+ );
}
//TODO: add test vectors for hash_to_curve
@@ -163,8 +159,14 @@ pub fn test_against_zk_nullifier_sig_g_r() {
pub fn test_against_zk_nullifier_sig_h() {
let h = compute_h();
- assert_eq!(coord_to_hex(h.x.into()), "0000000000000000bcac2d0e12679f23c218889395abcdc01f2affbc49c54d1136a2190db0800b65");
- assert_eq!(coord_to_hex(h.y.into()), "00000000000000003bcfb339c974c0e757d348081f90a123b0a91a53e32b3752145d87f0cd70966e");
+ assert_eq!(
+ coord_to_hex(h.x.into()),
+ "0000000000000000bcac2d0e12679f23c218889395abcdc01f2affbc49c54d1136a2190db0800b65"
+ );
+ assert_eq!(
+ coord_to_hex(h.y.into()),
+ "00000000000000003bcfb339c974c0e757d348081f90a123b0a91a53e32b3752145d87f0cd70966e"
+ );
}
#[test]
@@ -175,8 +177,14 @@ pub fn test_against_zk_nullifier_sig_h_r() {
let r = secp256k1::fields::Fr::from(hex_to_fr(&hardcoded_r()));
let h_r_projective = h.mul(r);
let h_r = GroupAffine::::from(h_r_projective);
- assert_eq!(coord_to_hex(h_r.x.into()), "00000000000000006d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c");
- assert_eq!(coord_to_hex(h_r.y.into()), "0000000000000000586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed");
+ assert_eq!(
+ coord_to_hex(h_r.x.into()),
+ "00000000000000006d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c"
+ );
+ assert_eq!(
+ coord_to_hex(h_r.y.into()),
+ "0000000000000000586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed"
+ );
}
#[test]
@@ -187,8 +195,14 @@ pub fn test_against_zk_nullifier_sig_h_sk() {
// Test h^r using the hardcoded sk
let h_sk_projective = h.mul(sk);
let h_sk = GroupAffine::::from(h_sk_projective);
- assert_eq!(coord_to_hex(h_sk.x.into()), "000000000000000057bc3ed28172ef8adde4b9e0c2cce745fcc5a66473a45c1e626f1d0c67e55830");
- assert_eq!(coord_to_hex(h_sk.y.into()), "00000000000000006a2f41488d58f33ae46edd2188e111609f9f3ae67ea38fa891d6087fe59ecb73");
+ assert_eq!(
+ coord_to_hex(h_sk.x.into()),
+ "000000000000000057bc3ed28172ef8adde4b9e0c2cce745fcc5a66473a45c1e626f1d0c67e55830"
+ );
+ assert_eq!(
+ coord_to_hex(h_sk.y.into()),
+ "00000000000000006a2f41488d58f33ae46edd2188e111609f9f3ae67ea38fa891d6087fe59ecb73"
+ );
}
#[test]
@@ -198,18 +212,19 @@ pub fn test_against_zk_nullifier_sig_c_and_s() {
let message = message.as_bytes();
let sk = hex_to_fr(&hardcoded_sk());
let (_, g) = test_template();
- let pp = Parameters{ g };
+ let pp = Parameters { g };
let pk_projective = g.mul(sk);
let pk = GroupAffine::::from(pk_projective);
let keypair = (pk, sk);
- let sig = Scheme::sign_with_r(
- &pp,
- (&keypair.0, &keypair.1),
- message,
- r
- ).unwrap();
-
- assert_eq!(coord_to_hex(sig.c.into()), "00000000000000007da1ad3f63c6180beefd0d6a8e3c87620b54f1b1d2c8287d104da9e53b6b5524");
- assert_eq!(coord_to_hex(sig.s.into()), "0000000000000000638330fea277e97ad407b32c9dc4d522454f5483abd903e6710a59d14f6fbdf2");
+ let sig = Scheme::sign_with_r(&pp, (&keypair.0, &keypair.1), message, r).unwrap();
+
+ assert_eq!(
+ coord_to_hex(sig.c.into()),
+ "00000000000000007da1ad3f63c6180beefd0d6a8e3c87620b54f1b1d2c8287d104da9e53b6b5524"
+ );
+ assert_eq!(
+ coord_to_hex(sig.s.into()),
+ "0000000000000000638330fea277e97ad407b32c9dc4d522454f5483abd903e6710a59d14f6fbdf2"
+ );
}
diff --git a/rust-arkworks/src/wasm.rs b/rust-arkworks/src/wasm.rs
new file mode 100644
index 0000000..257c79f
--- /dev/null
+++ b/rust-arkworks/src/wasm.rs
@@ -0,0 +1,49 @@
+use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
+use js_sys::Error;
+
+
+fn compute_h(message: String, sk_hex: String) -> GroupAffine:: {
+ let sk = hex_to_fr(&sk_hex);
+ let g = Affine::prime_subgroup_generator();
+ let pk_projective = g.mul(sk);
+ let pk = GroupAffine::::from(pk_projective);
+
+ let h = hash_to_curve::(message.to_bytes(), &pk);
+ h
+}
+
+fn hex_to_fr(
+ hex: &str,
+) -> secp256k1::fields::Fr {
+ let num_field_bytes = 320;
+ let mut sk_bytes_vec = vec![0u8; num_field_bytes];
+ let mut sk_bytes = hex::decode(hex).unwrap();
+
+ sk_bytes.reverse();
+
+ for (i, _) in sk_bytes.clone().iter().enumerate() {
+ let _ = std::mem::replace(&mut sk_bytes_vec[i], sk_bytes[i]);
+ }
+
+ secp256k1::fields::Fr::read(sk_bytes_vec.as_slice()).unwrap()
+}
+
+fn coord_to_hex(coord: biginteger::BigInteger320) -> String {
+ let mut coord_bytes = vec![];
+ let _ = coord.write(&mut coord_bytes);
+ coord_bytes.reverse();
+
+ String::from(hex::encode(coord_bytes))
+}
+
+
+#[wasm_bindgen]
+pub fn make_nullifier(sk_hex: String, message: String) -> String {
+ let h = compute_h(&message);
+ let sk = hex_to_fr(&sk_hex);
+
+ let h_sk_projective = h.mul(sk);
+ let h_sk = GroupAffine::::from(h_sk_projective);
+ let h_sk_hex = coord_to_hex(h_sk.x.into());
+ h_sk_hex
+}
\ No newline at end of file
diff --git a/rust-k256/Cargo.lock b/rust-k256/Cargo.lock
index af6ad00..c7a9dff 100644
--- a/rust-k256/Cargo.lock
+++ b/rust-k256/Cargo.lock
@@ -47,6 +47,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
[[package]]
name = "const-oid"
version = "0.7.1"
@@ -233,8 +243,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
]
[[package]]
@@ -293,9 +305,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.58"
+version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
@@ -312,12 +324,6 @@ dependencies = [
"sha2",
]
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
[[package]]
name = "libc"
version = "0.2.126"
@@ -379,6 +385,12 @@ dependencies = [
"spki",
]
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
[[package]]
name = "proc-macro2"
version = "1.0.40"
@@ -397,6 +409,27 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
[[package]]
name = "rand_core"
version = "0.6.3"
@@ -532,9 +565,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.81"
+version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -542,13 +575,13 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.81"
+version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
- "lazy_static",
"log",
+ "once_cell",
"proc-macro2",
"quote",
"syn",
@@ -557,9 +590,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.81"
+version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -567,9 +600,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.81"
+version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
@@ -580,9 +613,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.81"
+version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
@@ -626,15 +659,20 @@ checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
name = "zk-nullifier"
version = "0.1.0"
dependencies = [
+ "console_error_panic_hook",
"crate",
"digest 0.10.3",
"elliptic-curve 0.12.2",
+ "getrandom",
"hash2field",
"hex",
"hex-literal",
+ "js-sys",
"k256",
"num-bigint",
"num-integer",
+ "rand",
"rand_core",
"ring",
+ "wasm-bindgen",
]
diff --git a/rust-k256/Cargo.toml b/rust-k256/Cargo.toml
index 28b0d8d..5efc2d3 100644
--- a/rust-k256/Cargo.toml
+++ b/rust-k256/Cargo.toml
@@ -5,6 +5,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[lib]
+name = "zk_nullifier"
+path = "src/lib.rs"
+crate-type = ["cdylib", "rlib"]
+
[dependencies]
crate = "0.0.2"
digest = "0.10.3"
@@ -17,3 +22,10 @@ num-integer = "0.1.45"
k256 = {version = "0.11.3", features = ["arithmetic", "hash2curve", "expose-field", "sha2"]}
elliptic-curve = { version = "0.12.2", features = ["arithmetic"]}
hex = "0.4.3"
+
+# TODO: Move those to 'wasm' feature
+wasm-bindgen = "0.2.83"
+js-sys = "0.3.60"
+console_error_panic_hook = "0.1.7"
+getrandom = { version = "0.2", features = ["js"] }
+rand = "0.8.5"
diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs
new file mode 100644
index 0000000..06f5282
--- /dev/null
+++ b/rust-k256/src/lib.rs
@@ -0,0 +1,10 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+// #![feature(generic_const_expr)]
+// #![allow(incomplete_features)]
+
+mod nullifier;
+mod serialize;
+#[cfg(test)]
+mod tests;
+mod wasm;
diff --git a/rust-k256/src/main.rs b/rust-k256/src/main.rs
deleted file mode 100644
index 556e65d..0000000
--- a/rust-k256/src/main.rs
+++ /dev/null
@@ -1,297 +0,0 @@
-#![allow(dead_code)]
-#![allow(unused_variables)]
-// #![feature(generic_const_expr)]
-// #![allow(incomplete_features)]
-
-use elliptic_curve::sec1::ToEncodedPoint;
-use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
-use hex_literal::hex;
-use k256::{
- // ecdsa::{signature::Signer, Signature, SigningKey},
- elliptic_curve::group::ff::PrimeField,
- sha2::{Digest, Sha256, Sha512},
- FieldBytes,
- ProjectivePoint,
- Scalar,
- Secp256k1,
-}; // requires 'getrandom' feature
-
-const L: usize = 48;
-const COUNT: usize = 2;
-const OUT: usize = L * COUNT;
-const DST: &[u8] = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_"; // Hash to curve algorithm
-
-#[derive(Debug, PartialEq)]
-pub enum Error {
- IsPointAtInfinityError,
-}
-
-
-fn print_type_of(_: &T) {
- println!("{}", std::any::type_name::());
-}
-
-// Generates a deterministic secret key for us temporarily. Can be replaced by random oracle anytime.
-fn gen_test_scalar_x() -> Scalar {
- Scalar::from_repr(
- hex!("519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464").into(),
- )
- .unwrap()
-}
-
-// Generates a deterministic r for us temporarily. Can be replaced by random oracle anytime.
-fn gen_test_scalar_r() -> Scalar {
- Scalar::from_repr(
- hex!("93b9323b629f251b8f3fc2dd11f4672c5544e8230d493eceea98a90bda789808").into(),
- )
- .unwrap()
-}
-
-// These generate test signals as if it were passed from a secure enclave to wallet. Note that leaking these signals would leak pk, but not sk.
-// Outputs these 6 signals, in this order
-// g^sk (private)
-// hash[m, pk]^sk public nullifier
-// c = hash2(g, pk, hash[m, pk], hash[m, pk]^sk, gr, hash[m, pk]^r) (public or private)
-// r + sk * c (public or private)
-// g^r (private, optional)
-// hash[m, pk]^r (private, optional)
-fn test_gen_signals(
- m: &[u8],
-) -> (
- ProjectivePoint,
- ProjectivePoint,
- Scalar,
- Scalar,
- Option,
- Option,
-) {
- // The base point or generator of the curve.
- let g = ProjectivePoint::GENERATOR;
-
- // The signer's secret key. It is only accessed within the secure enclave.
- let sk = gen_test_scalar_x();
-
- // A random value r. It is only accessed within the secure enclave.
- let r = gen_test_scalar_r();
-
- // The user's public key: g^sk.
- let pk = &g * &sk;
-
- // The generator exponentiated by r: g^r.
- let g_r = &g * &r;
-
- // hash[m, pk]
- let hash_m_pk = hash_m_pk_to_secp(m, &pk);
-
- println!("h.x: {:?}", hex::encode(hash_m_pk.to_affine().to_encoded_point(false).x().unwrap()));
- println!("h.y: {:?}", hex::encode(hash_m_pk.to_affine().to_encoded_point(false).y().unwrap()));
-
- // hash[m, pk]^r
- let hash_m_pk_pow_r = &hash_m_pk * &r;
- println!("hash_m_pk_pow_r.x: {:?}", hex::encode(hash_m_pk_pow_r.to_affine().to_encoded_point(false).x().unwrap()));
- println!("hash_m_pk_pow_r.y: {:?}", hex::encode(hash_m_pk_pow_r.to_affine().to_encoded_point(false).y().unwrap()));
-
- // The public nullifier: hash[m, pk]^sk.
- let nullifier = &hash_m_pk * &sk;
-
- // The Fiat-Shamir type step.
- let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
-
- // This value is part of the discrete log equivalence (DLEQ) proof.
- let r_sk_c = r + sk * c;
-
- // Return the signature.
- (pk, nullifier, c, r_sk_c, Some(g_r), Some(hash_m_pk_pow_r))
-}
-
-fn sha512hash6signals(
- g: &ProjectivePoint,
- pk: &ProjectivePoint,
- hash_m_pk: &ProjectivePoint,
- nullifier: &ProjectivePoint,
- g_r: &ProjectivePoint,
- hash_m_pk_pow_r: &ProjectivePoint,
-) -> Scalar {
- let g_bytes = encode_pt(*g).unwrap();
- let pk_bytes = encode_pt(*pk).unwrap();
- let h_bytes = encode_pt(*hash_m_pk).unwrap();
- let nul_bytes = encode_pt(*nullifier).unwrap();
- let g_r_bytes = encode_pt(*g_r).unwrap();
- let z_bytes = encode_pt(*hash_m_pk_pow_r).unwrap();
-
- let c_preimage_vec = [
- g_bytes,
- pk_bytes,
- h_bytes,
- nul_bytes,
- g_r_bytes,
- z_bytes,
- ].concat();
-
- //println!("c_preimage_vec: {:?}", c_preimage_vec);
-
- let mut sha512_hasher = Sha512::new();
- sha512_hasher.update(c_preimage_vec.as_slice());
- let sha512_hasher_result = sha512_hasher.finalize(); //512 bit hash
-
- let c_bytes = FieldBytes::from_iter(sha512_hasher_result.iter().copied());
- let c_scalar = Scalar::from_repr(c_bytes).unwrap();
- c_scalar
-}
-
-// Calls the hash to curve function for secp256k1, and returns the result as a ProjectivePoint
-fn hash_to_secp(s: &[u8]) -> ProjectivePoint {
- let pt: ProjectivePoint =
- Secp256k1::hash_from_bytes::>(
- &[s],
- //b"CURVE_XMD:SHA-256_SSWU_RO_"
- DST
- ).unwrap();
- pt
-}
-
-// Hashes two values to the curve
-fn hash_m_pk_to_secp(m: &[u8], pk: &ProjectivePoint) -> ProjectivePoint {
- let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>(
- &[[m, &encode_pt(*pk).unwrap()].concat().as_slice()],
- //b"CURVE_XMD:SHA-256_SSWU_RO_",
- DST
- )
- .unwrap();
- pt
-}
-
-// Verifier check in SNARK:
-// g^[r + sk * c] / (g^sk)^c = g^r
-// hash[m, gsk]^[r + sk * c] / (hash[m, pk]^sk)^c = hash[m, pk]^r
-// c = hash2(g, g^sk, hash[m, g^sk], hash[m, pk]^sk, gr, hash[m, pk]^r)
-fn verify_signals(
- m: &[u8],
- pk: &ProjectivePoint,
- nullifier: &ProjectivePoint,
- c: &Scalar,
- r_sk_c: &Scalar,
- g_r_option: &Option,
- hash_m_pk_pow_r_option: &Option,
-) -> bool {
- let mut verified: bool = true;
-
- // The base point or generator of the curve.
- let g = &ProjectivePoint::GENERATOR;
-
- // hash[m, pk]
- let hash_m_pk = &hash_m_pk_to_secp(m, pk);
-
- // Check whether g^r equals g^s * pk^{-c}
- let g_r: ProjectivePoint;
- match *g_r_option {
- Some(_g_r_value) => {
- if (g * r_sk_c - pk * c) != _g_r_value {
- verified = false;
- }
- }
- None => println!("g^r not provided, check skipped"),
- }
- g_r = g * r_sk_c - pk * c;
-
- // Check whether h^r equals h^{r + sk * c} * nullifier^{-c}
- let hash_m_pk_pow_r: ProjectivePoint;
- match *hash_m_pk_pow_r_option {
- Some(_hash_m_pk_pow_r_value) => {
- if (hash_m_pk * r_sk_c - nullifier * c) != _hash_m_pk_pow_r_value {
- verified = false;
- }
- }
- None => println!("hash_m_pk_pow_r not provided, check skipped"),
- }
- hash_m_pk_pow_r = hash_m_pk * r_sk_c - nullifier * c;
-
- // Check if the given hash matches
- if (sha512hash6signals(g, pk, hash_m_pk, nullifier, &g_r, &hash_m_pk_pow_r)) != *c {
- verified = false;
- }
- verified
-}
-
-// NOTE: MAKE SURE TO HAVE RUST-ANALYZER ENABLED IN VSCODE EXTENSIONS TO FILL IN INFERRED TYPES
-fn main() -> Result<(), ()> {
- let g = ProjectivePoint::GENERATOR;
-
- let m = b"An example app message string";
-
- // Fixed key nullifier, secret key, and random value for testing
- // Normally a secure enclave would generate these values, and output to a wallet implementation
- let (pk, nullifier, c, r_sk_c, g_r, hash_m_pk_pow_r) = test_gen_signals(m);
-
- // The signer's secret key. It is only accessed within the secure enclave.
- let sk = gen_test_scalar_x();
-
- // The user's public key: g^sk.
- let pk = &g * &sk;
-
- // Verify the signals, normally this would happen in ZK with only the nullifier public, which would have a zk verifier instead
- // The wallet should probably run this prior to snarkify-ing as a sanity check
- // m and nullifier should be public, so we can verify that they are correct
- let verified = verify_signals(m, &pk, &nullifier, &c, &r_sk_c, &g_r, &hash_m_pk_pow_r);
- println!("Verified: {}", verified);
-
- // Print nullifier
- println!("nullifier.x: {:?}", hex::encode(nullifier.to_affine().to_encoded_point(false).x().unwrap()));
- println!("nullifier.y: {:?}", hex::encode(nullifier.to_affine().to_encoded_point(false).y().unwrap()));
-
- // Print c
- println!("c: {:?}", hex::encode(&c.to_bytes()));
-
- // Print r_sk_c
- println!("r_sk_c: {:?}", hex::encode(r_sk_c.to_bytes()));
-
- // Print g_r
- println!("g_r.x: {:?}", hex::encode(g_r.unwrap().to_affine().to_encoded_point(false).x().unwrap()));
- println!("g_r.y: {:?}", hex::encode(g_r.unwrap().to_affine().to_encoded_point(false).y().unwrap()));
-
- // Print hash_m_pk_pow_r
- println!("hash_m_pk_pow_r.x: {:?}", hex::encode(hash_m_pk_pow_r.unwrap().to_affine().to_encoded_point(false).x().unwrap()));
- println!("hash_m_pk_pow_r.y: {:?}", hex::encode(hash_m_pk_pow_r.unwrap().to_affine().to_encoded_point(false).y().unwrap()));
-
- // Test encode_pt()
- let g_as_bytes = encode_pt(g).unwrap();
- assert_eq!(hex::encode(g_as_bytes), "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798");
-
- // Test byte_array_to_scalar()
- let bytes_to_convert = c.to_bytes();
- let scalar = byte_array_to_scalar(&bytes_to_convert);
- assert_eq!(hex::encode(scalar.to_bytes()), "7da1ad3f63c6180beefd0d6a8e3c87620b54f1b1d2c8287d104da9e53b6b5524");
-
- // Test the hash-to-curve algorithm
- let h = hash_to_secp(b"abc");
- assert_eq!(hex::encode(h.to_affine().to_encoded_point(false).x().unwrap()), "3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b");
- assert_eq!(hex::encode(h.to_affine().to_encoded_point(false).y().unwrap()), "7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6");
-
-
- Ok(())
-}
-
-/// Format a ProjectivePoint to 64 bytes - the concatenation of the x and y values. We use 64
-/// bytes instead of SEC1 encoding as our arkworks secp256k1 implementation doesn't support SEC1
-/// encoding yet.
-fn encode_pt(
- point: ProjectivePoint
-) -> Result, Error> {
- let encoded = point.to_encoded_point(true);
- Ok(encoded.to_bytes().to_vec())
-}
-
-/// Convert a 32-byte array to a scalar
-fn byte_array_to_scalar(
- bytes: &[u8],
-) -> Scalar {
- // From https://docs.rs/ark-ff/0.3.0/src/ark_ff/fields/mod.rs.html#371-393
- assert!(bytes.len() == 32);
- let mut res = Scalar::from(0u64);
- let window_size = Scalar::from(256u64);
- for byte in bytes.iter() {
- res *= window_size;
- res += Scalar::from(*byte as u64);
- }
- res
-}
diff --git a/rust-k256/src/nullifier.rs b/rust-k256/src/nullifier.rs
new file mode 100644
index 0000000..47f5e02
--- /dev/null
+++ b/rust-k256/src/nullifier.rs
@@ -0,0 +1,129 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+// #![feature(generic_const_expr)]
+// #![allow(incomplete_features)]
+
+use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
+use k256::{
+ // ecdsa::{signature::Signer, Signature, SigningKey},
+ elliptic_curve::group::ff::PrimeField,
+ sha2::{Digest, Sha256, Sha512},
+ FieldBytes,
+ ProjectivePoint,
+ Scalar,
+ Secp256k1,
+}; // requires 'getrandom' feature
+
+use crate::serialize::encode_pt;
+
+const L: usize = 48;
+const COUNT: usize = 2;
+const OUT: usize = L * COUNT;
+pub const DST: &[u8] = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_"; // Hash to curve algorithm
+
+#[derive(Debug, PartialEq)]
+pub enum Error {
+ IsPointAtInfinityError,
+}
+
+pub fn sha512hash6signals(
+ g: &ProjectivePoint,
+ pk: &ProjectivePoint,
+ hash_m_pk: &ProjectivePoint,
+ nullifier: &ProjectivePoint,
+ g_r: &ProjectivePoint,
+ hash_m_pk_pow_r: &ProjectivePoint,
+) -> Scalar {
+ let g_bytes = encode_pt(*g).unwrap();
+ let pk_bytes = encode_pt(*pk).unwrap();
+ let h_bytes = encode_pt(*hash_m_pk).unwrap();
+ let nul_bytes = encode_pt(*nullifier).unwrap();
+ let g_r_bytes = encode_pt(*g_r).unwrap();
+ let z_bytes = encode_pt(*hash_m_pk_pow_r).unwrap();
+
+ let c_preimage_vec = [g_bytes, pk_bytes, h_bytes, nul_bytes, g_r_bytes, z_bytes].concat();
+
+ //println!("c_preimage_vec: {:?}", c_preimage_vec);
+
+ let mut sha512_hasher = Sha512::new();
+ sha512_hasher.update(c_preimage_vec.as_slice());
+ let sha512_hasher_result = sha512_hasher.finalize(); //512 bit hash
+
+ let c_bytes = FieldBytes::from_iter(sha512_hasher_result.iter().copied());
+ let c_scalar = Scalar::from_repr(c_bytes).unwrap();
+ c_scalar
+}
+
+// Calls the hash to curve function for secp256k1, and returns the result as a ProjectivePoint
+pub fn hash_to_secp(s: &[u8]) -> ProjectivePoint {
+ let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>(
+ &[s],
+ //b"CURVE_XMD:SHA-256_SSWU_RO_"
+ DST,
+ )
+ .unwrap();
+ pt
+}
+
+// Hashes two values to the curve
+pub fn hash_m_pk_to_secp(m: &[u8], pk: &ProjectivePoint) -> ProjectivePoint {
+ let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>(
+ &[[m, &encode_pt(*pk).unwrap()].concat().as_slice()],
+ //b"CURVE_XMD:SHA-256_SSWU_RO_",
+ DST,
+ )
+ .unwrap();
+ pt
+}
+
+// Verifier check in SNARK:
+// g^[r + sk * c] / (g^sk)^c = g^r
+// hash[m, gsk]^[r + sk * c] / (hash[m, pk]^sk)^c = hash[m, pk]^r
+// c = hash2(g, g^sk, hash[m, g^sk], hash[m, pk]^sk, gr, hash[m, pk]^r)
+pub fn verify_signals(
+ m: &[u8],
+ pk: &ProjectivePoint,
+ nullifier: &ProjectivePoint,
+ c: &Scalar,
+ r_sk_c: &Scalar,
+ g_r_option: &Option,
+ hash_m_pk_pow_r_option: &Option,
+) -> bool {
+ let mut verified: bool = true;
+
+ // The base point or generator of the curve.
+ let g = &ProjectivePoint::GENERATOR;
+
+ // hash[m, pk]
+ let hash_m_pk = &hash_m_pk_to_secp(m, pk);
+
+ // Check whether g^r equals g^s * pk^{-c}
+ let g_r: ProjectivePoint;
+ match *g_r_option {
+ Some(_g_r_value) => {
+ if (g * r_sk_c - pk * c) != _g_r_value {
+ verified = false;
+ }
+ }
+ None => println!("g^r not provided, check skipped"),
+ }
+ g_r = g * r_sk_c - pk * c;
+
+ // Check whether h^r equals h^{r + sk * c} * nullifier^{-c}
+ let hash_m_pk_pow_r: ProjectivePoint;
+ match *hash_m_pk_pow_r_option {
+ Some(_hash_m_pk_pow_r_value) => {
+ if (hash_m_pk * r_sk_c - nullifier * c) != _hash_m_pk_pow_r_value {
+ verified = false;
+ }
+ }
+ None => println!("hash_m_pk_pow_r not provided, check skipped"),
+ }
+ hash_m_pk_pow_r = hash_m_pk * r_sk_c - nullifier * c;
+
+ // Check if the given hash matches
+ if (sha512hash6signals(g, pk, hash_m_pk, nullifier, &g_r, &hash_m_pk_pow_r)) != *c {
+ verified = false;
+ }
+ verified
+}
diff --git a/rust-k256/src/serialize.rs b/rust-k256/src/serialize.rs
new file mode 100644
index 0000000..6034fa6
--- /dev/null
+++ b/rust-k256/src/serialize.rs
@@ -0,0 +1,35 @@
+use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
+use elliptic_curve::sec1::ToEncodedPoint;
+use hex_literal::hex;
+use k256::{
+ // ecdsa::{signature::Signer, Signature, SigningKey},
+ elliptic_curve::group::ff::PrimeField,
+ sha2::{Digest, Sha256, Sha512},
+ FieldBytes,
+ ProjectivePoint,
+ Scalar,
+ Secp256k1,
+};
+
+use crate::nullifier::Error;
+
+// Format a ProjectivePoint to 64 bytes - the concatenation of the x and y values. We use 64
+/// bytes instead of SEC1 encoding as our arkworks secp256k1 implementation doesn't support SEC1
+/// encoding yet.
+pub fn encode_pt(point: ProjectivePoint) -> Result, Error> {
+ let encoded = point.to_encoded_point(true);
+ Ok(encoded.to_bytes().to_vec())
+}
+
+/// Convert a 32-byte array to a scalar
+pub fn byte_array_to_scalar(bytes: &[u8]) -> Scalar {
+ // From https://docs.rs/ark-ff/0.3.0/src/ark_ff/fields/mod.rs.html#371-393
+ assert!(bytes.len() == 32);
+ let mut res = Scalar::from(0u64);
+ let window_size = Scalar::from(256u64);
+ for byte in bytes.iter() {
+ res *= window_size;
+ res += Scalar::from(*byte as u64);
+ }
+ res
+}
diff --git a/rust-k256/src/tests.rs b/rust-k256/src/tests.rs
new file mode 100644
index 0000000..c8589e0
--- /dev/null
+++ b/rust-k256/src/tests.rs
@@ -0,0 +1,215 @@
+use elliptic_curve::sec1::ToEncodedPoint;
+use hex_literal::hex;
+use k256::{elliptic_curve::group::ff::PrimeField, ProjectivePoint, Scalar}; // requires 'getrandom' feature
+
+use crate::nullifier::*;
+use crate::serialize::*;
+
+// Generates a deterministic secret key for us temporarily. Can be replaced by random oracle anytime.
+fn gen_test_scalar_x() -> Scalar {
+ Scalar::from_repr(
+ hex!("519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464").into(),
+ )
+ .unwrap()
+}
+
+// Generates a deterministic r for us temporarily. Can be replaced by random oracle anytime.
+fn gen_test_scalar_r() -> Scalar {
+ Scalar::from_repr(
+ hex!("93b9323b629f251b8f3fc2dd11f4672c5544e8230d493eceea98a90bda789808").into(),
+ )
+ .unwrap()
+}
+
+// These generate test signals as if it were passed from a secure enclave to wallet. Note that leaking these signals would leak pk, but not sk.
+// Outputs these 6 signals, in this order
+// g^sk (private)
+// hash[m, pk]^sk public nullifier
+// c = hash2(g, pk, hash[m, pk], hash[m, pk]^sk, gr, hash[m, pk]^r) (public or private)
+// r + sk * c (public or private)
+// g^r (private, optional)
+// hash[m, pk]^r (private, optional)
+fn test_gen_signals(
+ m: &[u8],
+) -> (
+ ProjectivePoint,
+ ProjectivePoint,
+ Scalar,
+ Scalar,
+ Option,
+ Option,
+) {
+ // The base point or generator of the curve.
+ let g = ProjectivePoint::GENERATOR;
+
+ // The signer's secret key. It is only accessed within the secure enclave.
+ let sk = gen_test_scalar_x();
+
+ // A random value r. It is only accessed within the secure enclave.
+ let r = gen_test_scalar_r();
+
+ // The user's public key: g^sk.
+ let pk = &g * &sk;
+
+ // The generator exponentiated by r: g^r.
+ let g_r = &g * &r;
+
+ // hash[m, pk]
+ let hash_m_pk = hash_m_pk_to_secp(m, &pk);
+
+ println!(
+ "h.x: {:?}",
+ hex::encode(hash_m_pk.to_affine().to_encoded_point(false).x().unwrap())
+ );
+ println!(
+ "h.y: {:?}",
+ hex::encode(hash_m_pk.to_affine().to_encoded_point(false).y().unwrap())
+ );
+
+ // hash[m, pk]^r
+ let hash_m_pk_pow_r = &hash_m_pk * &r;
+ println!(
+ "hash_m_pk_pow_r.x: {:?}",
+ hex::encode(
+ hash_m_pk_pow_r
+ .to_affine()
+ .to_encoded_point(false)
+ .x()
+ .unwrap()
+ )
+ );
+ println!(
+ "hash_m_pk_pow_r.y: {:?}",
+ hex::encode(
+ hash_m_pk_pow_r
+ .to_affine()
+ .to_encoded_point(false)
+ .y()
+ .unwrap()
+ )
+ );
+
+ // The public nullifier: hash[m, pk]^sk.
+ let nullifier = &hash_m_pk * &sk;
+
+ // The Fiat-Shamir type step.
+ let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
+
+ // This value is part of the discrete log equivalence (DLEQ) proof.
+ let r_sk_c = r + sk * c;
+
+ // Return the signature.
+ (pk, nullifier, c, r_sk_c, Some(g_r), Some(hash_m_pk_pow_r))
+}
+
+// NOTE: MAKE SURE TO HAVE RUST-ANALYZER ENABLED IN VSCODE EXTENSIONS TO FILL IN INFERRED TYPES
+#[test]
+fn main() {
+ let g = ProjectivePoint::GENERATOR;
+
+ let m = b"An example app message string";
+
+ // Fixed key nullifier, secret key, and random value for testing
+ // Normally a secure enclave would generate these values, and output to a wallet implementation
+ let (pk, nullifier, c, r_sk_c, g_r, hash_m_pk_pow_r) = test_gen_signals(m);
+
+ // The signer's secret key. It is only accessed within the secure enclave.
+ let sk = gen_test_scalar_x();
+
+ // The user's public key: g^sk.
+ let pk = &g * &sk;
+
+ // Verify the signals, normally this would happen in ZK with only the nullifier public, which would have a zk verifier instead
+ // The wallet should probably run this prior to snarkify-ing as a sanity check
+ // m and nullifier should be public, so we can verify that they are correct
+ let verified = verify_signals(m, &pk, &nullifier, &c, &r_sk_c, &g_r, &hash_m_pk_pow_r);
+ println!("Verified: {}", verified);
+
+ // Print nullifier
+ println!(
+ "nullifier.x: {:?}",
+ hex::encode(nullifier.to_affine().to_encoded_point(false).x().unwrap())
+ );
+ println!(
+ "nullifier.y: {:?}",
+ hex::encode(nullifier.to_affine().to_encoded_point(false).y().unwrap())
+ );
+
+ // Print c
+ println!("c: {:?}", hex::encode(&c.to_bytes()));
+
+ // Print r_sk_c
+ println!("r_sk_c: {:?}", hex::encode(r_sk_c.to_bytes()));
+
+ // Print g_r
+ println!(
+ "g_r.x: {:?}",
+ hex::encode(
+ g_r.unwrap()
+ .to_affine()
+ .to_encoded_point(false)
+ .x()
+ .unwrap()
+ )
+ );
+ println!(
+ "g_r.y: {:?}",
+ hex::encode(
+ g_r.unwrap()
+ .to_affine()
+ .to_encoded_point(false)
+ .y()
+ .unwrap()
+ )
+ );
+
+ // Print hash_m_pk_pow_r
+ println!(
+ "hash_m_pk_pow_r.x: {:?}",
+ hex::encode(
+ hash_m_pk_pow_r
+ .unwrap()
+ .to_affine()
+ .to_encoded_point(false)
+ .x()
+ .unwrap()
+ )
+ );
+ println!(
+ "hash_m_pk_pow_r.y: {:?}",
+ hex::encode(
+ hash_m_pk_pow_r
+ .unwrap()
+ .to_affine()
+ .to_encoded_point(false)
+ .y()
+ .unwrap()
+ )
+ );
+
+ // Test encode_pt()
+ let g_as_bytes = encode_pt(g).unwrap();
+ assert_eq!(
+ hex::encode(g_as_bytes),
+ "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+ );
+
+ // Test byte_array_to_scalar()
+ let bytes_to_convert = c.to_bytes();
+ let scalar = byte_array_to_scalar(&bytes_to_convert);
+ assert_eq!(
+ hex::encode(scalar.to_bytes()),
+ "7da1ad3f63c6180beefd0d6a8e3c87620b54f1b1d2c8287d104da9e53b6b5524"
+ );
+
+ // Test the hash-to-curve algorithm
+ let h = hash_to_secp(b"abc");
+ assert_eq!(
+ hex::encode(h.to_affine().to_encoded_point(false).x().unwrap()),
+ "3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b"
+ );
+ assert_eq!(
+ hex::encode(h.to_affine().to_encoded_point(false).y().unwrap()),
+ "7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6"
+ );
+}
diff --git a/rust-k256/src/wasm.rs b/rust-k256/src/wasm.rs
new file mode 100644
index 0000000..0c5421b
--- /dev/null
+++ b/rust-k256/src/wasm.rs
@@ -0,0 +1,50 @@
+use k256::{elliptic_curve::group::ff::PrimeField, ProjectivePoint, Scalar};
+use rand::{rngs::StdRng, Rng, SeedableRng};
+use wasm_bindgen::prelude::{wasm_bindgen}; // requires 'getrandom' feature
+
+use crate::nullifier::{hash_m_pk_to_secp};
+use crate::serialize::{byte_array_to_scalar, encode_pt};
+
+#[wasm_bindgen]
+pub fn make_nullifier(sk_hex: String, message: String, rng_seed: &[u8]) -> String {
+ console_error_panic_hook::set_once();
+
+ let sk = hex::decode(sk_hex).unwrap();
+ let sk = byte_array_to_scalar(&sk);
+
+ // The base point or generator of the curve.
+ let g = ProjectivePoint::GENERATOR;
+
+ let rng_seed: [u8; 32] = rng_seed.try_into().unwrap();
+ let rng = &mut StdRng::from_seed(rng_seed);
+ let r_bytes = rng.gen::<[u8; 32]>();
+ // The signer's secret key. It is only accessed within the secure enclave.
+ let r = Scalar::from_repr(r_bytes.into()).unwrap();
+
+ // The user's public key: g^sk.
+ let pk = &g * &sk;
+
+ // The generator exponentiated by r: g^r.
+ let g_r = &g * &r;
+
+ // hash[m, pk]
+ let hash_m_pk = hash_m_pk_to_secp(message.as_bytes(), &pk);
+
+ // hash[m, pk]^r
+ let hash_m_pk_pow_r = &hash_m_pk * &r;
+
+ // The public nullifier: hash[m, pk]^sk.
+ let nullifier = &hash_m_pk * &sk;
+
+ // // The Fiat-Shamir type step.
+ // let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
+
+ // // This value is part of the discrete log equivalence (DLEQ) proof.
+ // let r_sk_c = r + sk * c;
+
+ // // Return the signature.
+ // (pk, nullifier, c, r_sk_c, g_r, hash_m_pk_pow_r)
+
+ let nullifier = encode_pt(nullifier).unwrap();
+ hex::encode(nullifier)
+}
\ No newline at end of file
diff --git a/rust-k256/wasm-pack.sh b/rust-k256/wasm-pack.sh
new file mode 100755
index 0000000..6c5a026
--- /dev/null
+++ b/rust-k256/wasm-pack.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+wasm-pack build --target web --release
\ No newline at end of file
From 455551b15af37702e634de077042c943981af94e Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Sun, 9 Oct 2022 01:31:43 +0200
Subject: [PATCH 2/4] make signature
---
rust-k256/src/wasm.rs | 76 +++++++++++++++++++++++++++++++++++++------
1 file changed, 66 insertions(+), 10 deletions(-)
diff --git a/rust-k256/src/wasm.rs b/rust-k256/src/wasm.rs
index 0c5421b..a73918e 100644
--- a/rust-k256/src/wasm.rs
+++ b/rust-k256/src/wasm.rs
@@ -1,8 +1,8 @@
use k256::{elliptic_curve::group::ff::PrimeField, ProjectivePoint, Scalar};
use rand::{rngs::StdRng, Rng, SeedableRng};
-use wasm_bindgen::prelude::{wasm_bindgen}; // requires 'getrandom' feature
+use wasm_bindgen::prelude::wasm_bindgen; // requires 'getrandom' feature
-use crate::nullifier::{hash_m_pk_to_secp};
+use crate::nullifier::{hash_m_pk_to_secp, sha512hash6signals};
use crate::serialize::{byte_array_to_scalar, encode_pt};
#[wasm_bindgen]
@@ -36,15 +36,71 @@ pub fn make_nullifier(sk_hex: String, message: String, rng_seed: &[u8]) -> Strin
// The public nullifier: hash[m, pk]^sk.
let nullifier = &hash_m_pk * &sk;
- // // The Fiat-Shamir type step.
- // let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
+ let nullifier = encode_pt(nullifier).unwrap();
+ hex::encode(nullifier)
+}
+
+#[wasm_bindgen]
+pub struct Signature {
+ pk: Vec,
+ nullifier: Vec,
+ c: Vec,
+ r_sk_c: Vec,
+ g_r: Vec,
+ hash_m_pk_pow_r: Vec,
+}
+
+#[wasm_bindgen]
+pub fn make_signature(sk_hex: String, message: String, rng_seed: &[u8]) -> Signature {
+ console_error_panic_hook::set_once();
+
+ let sk = hex::decode(sk_hex).unwrap();
+ let sk = byte_array_to_scalar(&sk);
+
+ // The base point or generator of the curve.
+ let g = ProjectivePoint::GENERATOR;
+
+ let rng_seed: [u8; 32] = rng_seed.try_into().unwrap();
+ let rng = &mut StdRng::from_seed(rng_seed);
+ let r_bytes = rng.gen::<[u8; 32]>();
+ // The signer's secret key. It is only accessed within the secure enclave.
+ let r = Scalar::from_repr(r_bytes.into()).unwrap();
+
+ // The user's public key: g^sk.
+ let pk = &g * &sk;
+
+ // The generator exponentiated by r: g^r.
+ let g_r = &g * &r;
+
+ // hash[m, pk]
+ let hash_m_pk = hash_m_pk_to_secp(message.as_bytes(), &pk);
- // // This value is part of the discrete log equivalence (DLEQ) proof.
- // let r_sk_c = r + sk * c;
+ // hash[m, pk]^r
+ let hash_m_pk_pow_r = &hash_m_pk * &r;
- // // Return the signature.
- // (pk, nullifier, c, r_sk_c, g_r, hash_m_pk_pow_r)
+ // The public nullifier: hash[m, pk]^sk.
+ let nullifier = &hash_m_pk * &sk;
+ // The Fiat-Shamir type step.
+ let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
+
+ // This value is part of the discrete log equivalence (DLEQ) proof.
+ let r_sk_c = r + sk * c;
+
+ let pk = encode_pt(pk).unwrap();
let nullifier = encode_pt(nullifier).unwrap();
- hex::encode(nullifier)
-}
\ No newline at end of file
+ let c = c.to_bytes().to_vec();
+ let r_sk_c = r_sk_c.to_bytes().to_vec();
+ let g_r = encode_pt(g_r).unwrap();
+ let hash_m_pk_pow_r = encode_pt(hash_m_pk_pow_r).unwrap();
+
+ // Return the signature.
+ Signature {
+ pk,
+ nullifier,
+ c,
+ r_sk_c,
+ g_r,
+ hash_m_pk_pow_r,
+ }
+}
From fb31b5a8920f6dc5a483f516a5d5eefa654569f9 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Sun, 20 Nov 2022 18:59:47 +0100
Subject: [PATCH 3/4] redo wasm bindings for rust-k256
---
README.md | 9 +-
rust-arkworks/Cargo.toml | 3 +-
rust-arkworks/src/error.rs | 2 +-
rust-arkworks/src/hash_to_curve.rs | 2 +-
rust-arkworks/src/lib.rs | 15 +-
rust-arkworks/src/tests.rs | 11 +-
rust-arkworks/src/wasm.rs | 35 +--
rust-k256/Cargo.lock | 114 +++++++--
rust-k256/Cargo.toml | 11 +-
.../{wasm-pack.sh => scripts/build-wasm.sh} | 0
rust-k256/src/lib.rs | 1 +
rust-k256/src/nullifier.rs | 84 +++++--
rust-k256/src/serialize.rs | 13 +-
rust-k256/src/tests.rs | 12 +-
rust-k256/src/utils.rs | 10 +
rust-k256/src/wasm.rs | 219 +++++++++++-------
16 files changed, 379 insertions(+), 162 deletions(-)
rename rust-k256/{wasm-pack.sh => scripts/build-wasm.sh} (100%)
create mode 100644 rust-k256/src/utils.rs
diff --git a/README.md b/README.md
index 35b739b..c18133d 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,10 @@
- `rust-k256`: Rust, using the k256 library
- `rust-arkworks`: Rust, using arkworks
+## WASM bindings
+
+Currently, WASM bindings are only available for the `rust-k256` implementation.
+
## TODO
- zk verifier circuits (WIP Circom here: https://github.com/geometryresearch/secp256k1_hash_to_curve/tree/main/circuits)
@@ -16,14 +20,17 @@
## Resources
### Paper
+
https://aayushg.com/thesis.pdf
### Slides
+
https://docs.google.com/presentation/d/1mKtOI4XgKrWBEPpKFAYkRjxZsBomwhy6Cc2Ia87hAnY/edit#slide=id.g13e97fbcd2c_0_76
### Unpublished Blog Post
+
https://docs.google.com/document/d/1Q9nUNGaeiKoZYAiN9ndh4iE9e-_ql-Rf7MESTo7UB8s/edit
### Spec
-https://hackmd.io/uZQbMHrVSbOHvoI_HrJJlw
+https://hackmd.io/uZQbMHrVSbOHvoI_HrJJlw
diff --git a/rust-arkworks/Cargo.toml b/rust-arkworks/Cargo.toml
index 815c1ea..0120619 100644
--- a/rust-arkworks/Cargo.toml
+++ b/rust-arkworks/Cargo.toml
@@ -12,7 +12,7 @@ ark-std = "0.3.0"
ark-serialize = "0.3.0"
ark-serialize-derive = "0.3.0"
thiserror = "1.0.30"
-secp256k1 = { git = "https://github.com/geometryresearch/ark-secp256k1.git" }
+secp256k1 = { git = "https://github.com/geometryresearch/ark-secp256k1" }
rand_core = {version = "0.6", default-features=false, features = ["getrandom"] }
rand = "0.8.4"
tiny-keccak = { version = "2.0.2", features = [ "shake" ] }
@@ -24,6 +24,7 @@ hex = "0.4.3"
wasm-bindgen = "0.2.83"
js-sys = "0.3.60"
console_error_panic_hook = "0.1.7"
+serde = "1.0.147"
[patch.crates-io]
ark-ec = { git = "https://github.com/FindoraNetwork/ark-algebra" }
diff --git a/rust-arkworks/src/error.rs b/rust-arkworks/src/error.rs
index 85a7fbe..7b796ce 100644
--- a/rust-arkworks/src/error.rs
+++ b/rust-arkworks/src/error.rs
@@ -1,7 +1,7 @@
use thiserror::Error;
/// This is an error that could occur when running a cryptograhic primitive
-#[derive(Error, Debug, PartialEq)]
+#[derive(Error, Debug, PartialEq, Eq)]
pub enum CryptoError {
#[error("Cannot hash to curve")]
CannotHashToCurve,
diff --git a/rust-arkworks/src/hash_to_curve.rs b/rust-arkworks/src/hash_to_curve.rs
index 6b542c6..0d681d3 100644
--- a/rust-arkworks/src/hash_to_curve.rs
+++ b/rust-arkworks/src/hash_to_curve.rs
@@ -17,7 +17,7 @@ pub fn hash_to_curve(
let pk_encoded = pk.to_encoded_point(true);
let b = hex::decode(pk_encoded).unwrap();
let x = [msg, b.as_slice()];
- let x = x.concat().clone();
+ let x = x.concat();
let x = x.as_slice();
let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>(
diff --git a/rust-arkworks/src/lib.rs b/rust-arkworks/src/lib.rs
index 20b7609..3e4ccd6 100644
--- a/rust-arkworks/src/lib.rs
+++ b/rust-arkworks/src/lib.rs
@@ -1,8 +1,8 @@
mod error;
mod hash_to_curve;
-mod wasm;
#[cfg(test)]
mod tests;
+mod wasm;
pub mod sig {
use crate::error::CryptoError;
@@ -35,9 +35,9 @@ pub mod sig {
b.to_vec()
}
- fn compute_h<'a, C: ProjectiveCurve, Fq: PrimeField, P: SWModelParameters>(
+ fn compute_h(
pk: &GroupAffine,
- message: &'a [u8],
+ message: &[u8],
) -> Result, CryptoError> {
//let pk_affine_bytes_vec = affine_to_bytes::(pk);
//let m_pk = [message, pk_affine_bytes_vec.as_slice()].concat();
@@ -75,9 +75,8 @@ pub mod sig {
// Convert digest bytes to a scalar
let c = first_32.as_slice();
- let c_be = P::ScalarField::from_be_bytes_mod_order(c);
- P::ScalarField::from(c_be)
+ P::ScalarField::from_be_bytes_mod_order(c)
}
pub trait VerifiableUnpredictableFunction {
@@ -154,7 +153,7 @@ pub mod sig {
pp: &Self::Parameters,
rng: &mut R,
) -> Result<(Self::PublicKey, Self::SecretKey), CryptoError> {
- let secret_key = Self::SecretKey::rand(rng).into();
+ let secret_key = Self::SecretKey::rand(rng);
let public_key = pp.g.mul(secret_key).into();
Ok((public_key, secret_key))
}
@@ -169,7 +168,7 @@ pub mod sig {
let g_r = g.mul(r).into_affine();
// Compute h = htc([m, pk])
- let h = compute_h::(&keypair.0, &message).unwrap();
+ let h = compute_h::(keypair.0, message).unwrap();
// Compute z = h^r
let z = h.mul(r).into_affine();
@@ -203,7 +202,7 @@ pub mod sig {
message: Self::Message,
) -> Result {
// Pick a random r from Fp
- let r: P::ScalarField = Self::SecretKey::rand(rng).into();
+ let r: P::ScalarField = Self::SecretKey::rand(rng);
Self::sign_with_r(pp, keypair, message, r)
}
diff --git a/rust-arkworks/src/tests.rs b/rust-arkworks/src/tests.rs
index 0749939..462a838 100644
--- a/rust-arkworks/src/tests.rs
+++ b/rust-arkworks/src/tests.rs
@@ -65,7 +65,7 @@ fn coord_to_hex(coord: biginteger::BigInteger320) -> String {
let _ = coord.write(&mut coord_bytes);
coord_bytes.reverse();
- String::from(hex::encode(coord_bytes))
+ hex::encode(coord_bytes)
}
fn hardcoded_sk() -> String {
@@ -114,8 +114,7 @@ pub fn compute_h() -> GroupAffine {
let pk_projective = g.mul(sk);
let pk = GroupAffine::::from(pk_projective);
- let h = hash_to_curve::(message, &pk);
- h
+ hash_to_curve::(message, &pk)
}
#[test]
@@ -140,7 +139,7 @@ pub fn test_against_zk_nullifier_sig_pk() {
#[test]
pub fn test_against_zk_nullifier_sig_g_r() {
// Test g^r using the hardcoded r
- let r = secp256k1::fields::Fr::from(hex_to_fr(&hardcoded_r()));
+ let r = hex_to_fr(&hardcoded_r());
let (_, g) = test_template();
let g_r_projective = g.mul(r);
let g_r = GroupAffine::::from(g_r_projective);
@@ -174,7 +173,7 @@ pub fn test_against_zk_nullifier_sig_h_r() {
let h = compute_h();
// Test h^r using the hardcoded r
- let r = secp256k1::fields::Fr::from(hex_to_fr(&hardcoded_r()));
+ let r = hex_to_fr(&hardcoded_r());
let h_r_projective = h.mul(r);
let h_r = GroupAffine::::from(h_r_projective);
assert_eq!(
@@ -207,7 +206,7 @@ pub fn test_against_zk_nullifier_sig_h_sk() {
#[test]
pub fn test_against_zk_nullifier_sig_c_and_s() {
- let r = secp256k1::fields::Fr::from(hex_to_fr(&hardcoded_r()));
+ let r = hex_to_fr(&hardcoded_r());
let message = hardcoded_msg();
let message = message.as_bytes();
let sk = hex_to_fr(&hardcoded_sk());
diff --git a/rust-arkworks/src/wasm.rs b/rust-arkworks/src/wasm.rs
index 257c79f..d6bcd1b 100644
--- a/rust-arkworks/src/wasm.rs
+++ b/rust-arkworks/src/wasm.rs
@@ -1,20 +1,22 @@
-use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
-use js_sys::Error;
+use ark_ec::{short_weierstrass_jacobian::GroupAffine, AffineCurve};
+use ark_ff::{biginteger, ToBytes};
+use secp256k1::{Affine, Secp256k1Parameters};
+use wasm_bindgen::prelude::wasm_bindgen;
-fn compute_h(message: String, sk_hex: String) -> GroupAffine:: {
- let sk = hex_to_fr(&sk_hex);
+use crate::hash_to_curve::hash_to_curve;
+
+fn compute_h(message: &str, sk_hex: &str) -> GroupAffine {
+ let sk = hex_to_fr(sk_hex);
let g = Affine::prime_subgroup_generator();
let pk_projective = g.mul(sk);
let pk = GroupAffine::::from(pk_projective);
- let h = hash_to_curve::(message.to_bytes(), &pk);
+ let h = hash_to_curve::(message.as_bytes(), &pk);
h
}
-fn hex_to_fr(
- hex: &str,
-) -> secp256k1::fields::Fr {
+fn hex_to_fr(hex: &str) -> secp256k1::fields::Fr {
let num_field_bytes = 320;
let mut sk_bytes_vec = vec![0u8; num_field_bytes];
let mut sk_bytes = hex::decode(hex).unwrap();
@@ -25,7 +27,7 @@ fn hex_to_fr(
let _ = std::mem::replace(&mut sk_bytes_vec[i], sk_bytes[i]);
}
- secp256k1::fields::Fr::read(sk_bytes_vec.as_slice()).unwrap()
+ ::read(sk_bytes_vec.as_slice()).unwrap()
}
fn coord_to_hex(coord: biginteger::BigInteger320) -> String {
@@ -33,17 +35,16 @@ fn coord_to_hex(coord: biginteger::BigInteger320) -> String {
let _ = coord.write(&mut coord_bytes);
coord_bytes.reverse();
- String::from(hex::encode(coord_bytes))
+ hex::encode(coord_bytes)
}
-
#[wasm_bindgen]
-pub fn make_nullifier(sk_hex: String, message: String) -> String {
- let h = compute_h(&message);
- let sk = hex_to_fr(&sk_hex);
+pub fn make_nullifier(sk_hex: &str, message: &str) -> String {
+ let h = compute_h(message, sk_hex);
+ let sk = hex_to_fr(sk_hex);
let h_sk_projective = h.mul(sk);
let h_sk = GroupAffine::::from(h_sk_projective);
- let h_sk_hex = coord_to_hex(h_sk.x.into());
- h_sk_hex
-}
\ No newline at end of file
+
+ coord_to_hex(h_sk.x.into())
+}
diff --git a/rust-k256/Cargo.lock b/rust-k256/Cargo.lock
index c7a9dff..33d8ab3 100644
--- a/rust-k256/Cargo.lock
+++ b/rust-k256/Cargo.lock
@@ -20,6 +20,15 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "block-buffer"
version = "0.10.2"
@@ -159,13 +168,14 @@ dependencies = [
[[package]]
name = "ecdsa"
-version = "0.14.3"
+version = "0.14.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bd46e0c364655e5baf2f5e99b603e7a09905da9966d7928d7470af393b28670"
+checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
dependencies = [
"der 0.6.0",
- "elliptic-curve 0.12.2",
+ "elliptic-curve 0.12.3",
"rfc6979",
+ "serdect",
"signature",
]
@@ -188,9 +198,9 @@ dependencies = [
[[package]]
name = "elliptic-curve"
-version = "0.12.2"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c47abd0a791d2ac0c7aa1118715f85b83689e4522c4e3a244e159d4fc9848a8d"
+checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
dependencies = [
"base16ct",
"crypto-bigint 0.4.8",
@@ -202,6 +212,7 @@ dependencies = [
"pkcs8",
"rand_core",
"sec1",
+ "serdect",
"subtle",
"zeroize",
]
@@ -314,13 +325,14 @@ dependencies = [
[[package]]
name = "k256"
-version = "0.11.3"
+version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c8a5a96d92d849c4499d99461da81c9cdc1467418a8ed2aaeb407e8d85940ed"
+checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b"
dependencies = [
"cfg-if",
"ecdsa",
- "elliptic-curve 0.12.2",
+ "elliptic-curve 0.12.3",
+ "serdect",
"sha2",
]
@@ -432,9 +444,9 @@ dependencies = [
[[package]]
name = "rand_core"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
@@ -465,6 +477,12 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
[[package]]
name = "sec1"
version = "0.3.0"
@@ -475,10 +493,41 @@ dependencies = [
"der 0.6.0",
"generic-array",
"pkcs8",
+ "serdect",
"subtle",
"zeroize",
]
+[[package]]
+name = "serde"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serdect"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038fce1bf4d74b9b30ea7dcd59df75ba8ec669a5dcb3cc64fbfcef7334ced32c"
+dependencies = [
+ "base16ct",
+ "serde",
+]
+
[[package]]
name = "sha2"
version = "0.10.2"
@@ -492,9 +541,9 @@ dependencies = [
[[package]]
name = "signature"
-version = "1.5.0"
+version = "1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4"
+checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
dependencies = [
"digest 0.10.3",
"rand_core",
@@ -588,6 +637,18 @@ dependencies = [
"wasm-bindgen-shared",
]
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
@@ -617,6 +678,30 @@ version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+[[package]]
+name = "wasm-bindgen-test"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d"
+dependencies = [
+ "console_error_panic_hook",
+ "js-sys",
+ "scoped-tls",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-bindgen-test-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-test-macro"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
[[package]]
name = "web-sys"
version = "0.3.58"
@@ -659,10 +744,11 @@ checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
name = "zk-nullifier"
version = "0.1.0"
dependencies = [
+ "bincode",
"console_error_panic_hook",
"crate",
"digest 0.10.3",
- "elliptic-curve 0.12.2",
+ "elliptic-curve 0.12.3",
"getrandom",
"hash2field",
"hex",
@@ -674,5 +760,7 @@ dependencies = [
"rand",
"rand_core",
"ring",
+ "serde",
"wasm-bindgen",
+ "wasm-bindgen-test",
]
diff --git a/rust-k256/Cargo.toml b/rust-k256/Cargo.toml
index 5efc2d3..1444364 100644
--- a/rust-k256/Cargo.toml
+++ b/rust-k256/Cargo.toml
@@ -19,13 +19,20 @@ hex-literal = "0.3.4"
hash2field = "0.4.0"
num-bigint = "0.4.3"
num-integer = "0.1.45"
-k256 = {version = "0.11.3", features = ["arithmetic", "hash2curve", "expose-field", "sha2"]}
-elliptic-curve = { version = "0.12.2", features = ["arithmetic"]}
+k256 = {version = "0.11.3", features = ["arithmetic", "hash2curve", "expose-field", "sha2", "serde"]}
+elliptic-curve = { version = "0.12.3", features = ["arithmetic", "serde"]}
hex = "0.4.3"
+# TODO: Move to 'serde' feature
+bincode = "1.3.3"
+serde = { version = "1.0", features = ["derive"] }
+
# TODO: Move those to 'wasm' feature
wasm-bindgen = "0.2.83"
js-sys = "0.3.60"
console_error_panic_hook = "0.1.7"
getrandom = { version = "0.2", features = ["js"] }
rand = "0.8.5"
+
+[dev-dependencies]
+wasm-bindgen-test = "0.3.28"
\ No newline at end of file
diff --git a/rust-k256/wasm-pack.sh b/rust-k256/scripts/build-wasm.sh
similarity index 100%
rename from rust-k256/wasm-pack.sh
rename to rust-k256/scripts/build-wasm.sh
diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs
index 06f5282..3e5c6f7 100644
--- a/rust-k256/src/lib.rs
+++ b/rust-k256/src/lib.rs
@@ -7,4 +7,5 @@ mod nullifier;
mod serialize;
#[cfg(test)]
mod tests;
+mod utils;
mod wasm;
diff --git a/rust-k256/src/nullifier.rs b/rust-k256/src/nullifier.rs
index 47f5e02..df14d05 100644
--- a/rust-k256/src/nullifier.rs
+++ b/rust-k256/src/nullifier.rs
@@ -12,7 +12,8 @@ use k256::{
ProjectivePoint,
Scalar,
Secp256k1,
-}; // requires 'getrandom' feature
+};
+use rand::{rngs::StdRng, Rng}; // requires 'getrandom' feature
use crate::serialize::encode_pt;
@@ -21,7 +22,7 @@ const COUNT: usize = 2;
const OUT: usize = L * COUNT;
pub const DST: &[u8] = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_"; // Hash to curve algorithm
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Eq)]
pub enum Error {
IsPointAtInfinityError,
}
@@ -34,12 +35,12 @@ pub fn sha512hash6signals(
g_r: &ProjectivePoint,
hash_m_pk_pow_r: &ProjectivePoint,
) -> Scalar {
- let g_bytes = encode_pt(*g).unwrap();
- let pk_bytes = encode_pt(*pk).unwrap();
- let h_bytes = encode_pt(*hash_m_pk).unwrap();
- let nul_bytes = encode_pt(*nullifier).unwrap();
- let g_r_bytes = encode_pt(*g_r).unwrap();
- let z_bytes = encode_pt(*hash_m_pk_pow_r).unwrap();
+ let g_bytes = encode_pt(*g);
+ let pk_bytes = encode_pt(*pk);
+ let h_bytes = encode_pt(*hash_m_pk);
+ let nul_bytes = encode_pt(*nullifier);
+ let g_r_bytes = encode_pt(*g_r);
+ let z_bytes = encode_pt(*hash_m_pk_pow_r);
let c_preimage_vec = [g_bytes, pk_bytes, h_bytes, nul_bytes, g_r_bytes, z_bytes].concat();
@@ -50,8 +51,8 @@ pub fn sha512hash6signals(
let sha512_hasher_result = sha512_hasher.finalize(); //512 bit hash
let c_bytes = FieldBytes::from_iter(sha512_hasher_result.iter().copied());
- let c_scalar = Scalar::from_repr(c_bytes).unwrap();
- c_scalar
+
+ Scalar::from_repr(c_bytes).unwrap()
}
// Calls the hash to curve function for secp256k1, and returns the result as a ProjectivePoint
@@ -68,7 +69,7 @@ pub fn hash_to_secp(s: &[u8]) -> ProjectivePoint {
// Hashes two values to the curve
pub fn hash_m_pk_to_secp(m: &[u8], pk: &ProjectivePoint) -> ProjectivePoint {
let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>(
- &[[m, &encode_pt(*pk).unwrap()].concat().as_slice()],
+ &[[m, &encode_pt(*pk)].concat().as_slice()],
//b"CURVE_XMD:SHA-256_SSWU_RO_",
DST,
)
@@ -98,7 +99,7 @@ pub fn verify_signals(
let hash_m_pk = &hash_m_pk_to_secp(m, pk);
// Check whether g^r equals g^s * pk^{-c}
- let g_r: ProjectivePoint;
+
match *g_r_option {
Some(_g_r_value) => {
if (g * r_sk_c - pk * c) != _g_r_value {
@@ -107,10 +108,10 @@ pub fn verify_signals(
}
None => println!("g^r not provided, check skipped"),
}
- g_r = g * r_sk_c - pk * c;
+ let g_r: ProjectivePoint = g * r_sk_c - pk * c;
// Check whether h^r equals h^{r + sk * c} * nullifier^{-c}
- let hash_m_pk_pow_r: ProjectivePoint;
+
match *hash_m_pk_pow_r_option {
Some(_hash_m_pk_pow_r_value) => {
if (hash_m_pk * r_sk_c - nullifier * c) != _hash_m_pk_pow_r_value {
@@ -119,7 +120,7 @@ pub fn verify_signals(
}
None => println!("hash_m_pk_pow_r not provided, check skipped"),
}
- hash_m_pk_pow_r = hash_m_pk * r_sk_c - nullifier * c;
+ let hash_m_pk_pow_r: ProjectivePoint = hash_m_pk * r_sk_c - nullifier * c;
// Check if the given hash matches
if (sha512hash6signals(g, pk, hash_m_pk, nullifier, &g_r, &hash_m_pk_pow_r)) != *c {
@@ -127,3 +128,56 @@ pub fn verify_signals(
}
verified
}
+
+// Not using serde::{Serialize, Deserialize} because it doesn't work with ProjectivePoint
+// See this comment about serialization: https://github.com/axelarnetwork/tofn/issues/197#issuecomment-1090715311
+#[derive(Debug, Clone)]
+pub struct NullifierSignature {
+ pub pk: ProjectivePoint,
+ pub nullifier: ProjectivePoint,
+ pub c: Scalar,
+ pub r_sk_c: Scalar,
+ pub g_r: ProjectivePoint,
+ pub hash_m_pk_pow_r: ProjectivePoint,
+}
+
+impl NullifierSignature {
+ pub fn new(sk: &Scalar, message: &[u8], rng: &mut StdRng) -> Self {
+ // The base point or generator of the curve.
+ let g = ProjectivePoint::GENERATOR;
+
+ let r_bytes = rng.gen::<[u8; 32]>();
+ // The signer's secret key. It is only accessed within the secure enclave.
+ let r = Scalar::from_repr(r_bytes.into()).unwrap();
+
+ // The user's public key: g^sk.
+ let pk = g * sk;
+
+ // The generator exponentiated by r: g^r.
+ let g_r = g * r;
+
+ // hash[m, pk]
+ let hash_m_pk = hash_m_pk_to_secp(message, &pk);
+
+ // hash[m, pk]^r
+ let hash_m_pk_pow_r = hash_m_pk * r;
+
+ // The public nullifier: hash[m, pk]^sk.
+ let nullifier = hash_m_pk * sk;
+
+ // The Fiat-Shamir type step.
+ let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
+
+ // This value is part of the discrete log equivalence (DLEQ) proof.
+ let r_sk_c = r + sk * &c;
+
+ Self {
+ pk,
+ nullifier,
+ c,
+ r_sk_c,
+ g_r,
+ hash_m_pk_pow_r,
+ }
+ }
+}
diff --git a/rust-k256/src/serialize.rs b/rust-k256/src/serialize.rs
index 6034fa6..e71a818 100644
--- a/rust-k256/src/serialize.rs
+++ b/rust-k256/src/serialize.rs
@@ -1,24 +1,17 @@
-use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
use elliptic_curve::sec1::ToEncodedPoint;
-use hex_literal::hex;
+
use k256::{
// ecdsa::{signature::Signer, Signature, SigningKey},
- elliptic_curve::group::ff::PrimeField,
- sha2::{Digest, Sha256, Sha512},
- FieldBytes,
ProjectivePoint,
Scalar,
- Secp256k1,
};
-use crate::nullifier::Error;
-
// Format a ProjectivePoint to 64 bytes - the concatenation of the x and y values. We use 64
/// bytes instead of SEC1 encoding as our arkworks secp256k1 implementation doesn't support SEC1
/// encoding yet.
-pub fn encode_pt(point: ProjectivePoint) -> Result, Error> {
+pub fn encode_pt(point: ProjectivePoint) -> Vec {
let encoded = point.to_encoded_point(true);
- Ok(encoded.to_bytes().to_vec())
+ encoded.to_bytes().to_vec()
}
/// Convert a 32-byte array to a scalar
diff --git a/rust-k256/src/tests.rs b/rust-k256/src/tests.rs
index c8589e0..c716564 100644
--- a/rust-k256/src/tests.rs
+++ b/rust-k256/src/tests.rs
@@ -49,10 +49,10 @@ fn test_gen_signals(
let r = gen_test_scalar_r();
// The user's public key: g^sk.
- let pk = &g * &sk;
+ let pk = g * sk;
// The generator exponentiated by r: g^r.
- let g_r = &g * &r;
+ let g_r = g * r;
// hash[m, pk]
let hash_m_pk = hash_m_pk_to_secp(m, &pk);
@@ -67,7 +67,7 @@ fn test_gen_signals(
);
// hash[m, pk]^r
- let hash_m_pk_pow_r = &hash_m_pk * &r;
+ let hash_m_pk_pow_r = hash_m_pk * r;
println!(
"hash_m_pk_pow_r.x: {:?}",
hex::encode(
@@ -90,7 +90,7 @@ fn test_gen_signals(
);
// The public nullifier: hash[m, pk]^sk.
- let nullifier = &hash_m_pk * &sk;
+ let nullifier = hash_m_pk * sk;
// The Fiat-Shamir type step.
let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
@@ -117,7 +117,7 @@ fn main() {
let sk = gen_test_scalar_x();
// The user's public key: g^sk.
- let pk = &g * &sk;
+ let pk = g * sk;
// Verify the signals, normally this would happen in ZK with only the nullifier public, which would have a zk verifier instead
// The wallet should probably run this prior to snarkify-ing as a sanity check
@@ -188,7 +188,7 @@ fn main() {
);
// Test encode_pt()
- let g_as_bytes = encode_pt(g).unwrap();
+ let g_as_bytes = encode_pt(g);
assert_eq!(
hex::encode(g_as_bytes),
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
diff --git a/rust-k256/src/utils.rs b/rust-k256/src/utils.rs
new file mode 100644
index 0000000..b1d7929
--- /dev/null
+++ b/rust-k256/src/utils.rs
@@ -0,0 +1,10 @@
+pub fn set_panic_hook() {
+ // When the `console_error_panic_hook` feature is enabled, we can call the
+ // `set_panic_hook` function at least once during initialization, and then
+ // we will get better error messages if our code ever panics.
+ //
+ // For more details see
+ // https://github.com/rustwasm/console_error_panic_hook#readme
+ #[cfg(feature = "console_error_panic_hook")]
+ console_error_panic_hook::set_once();
+}
diff --git a/rust-k256/src/wasm.rs b/rust-k256/src/wasm.rs
index a73918e..8e798c5 100644
--- a/rust-k256/src/wasm.rs
+++ b/rust-k256/src/wasm.rs
@@ -1,106 +1,163 @@
-use k256::{elliptic_curve::group::ff::PrimeField, ProjectivePoint, Scalar};
-use rand::{rngs::StdRng, Rng, SeedableRng};
-use wasm_bindgen::prelude::wasm_bindgen; // requires 'getrandom' feature
-
-use crate::nullifier::{hash_m_pk_to_secp, sha512hash6signals};
-use crate::serialize::{byte_array_to_scalar, encode_pt};
-
+use digest::generic_array::GenericArray;
+use elliptic_curve::group::GroupEncoding;
+use k256::ProjectivePoint;
+use rand::{rngs::StdRng, SeedableRng};
+use rand_core::OsRng;
+use wasm_bindgen::prelude::wasm_bindgen;
+// requires 'getrandom' feature
+use wasm_bindgen::JsValue;
+
+use crate::{
+ serialize::{byte_array_to_scalar, encode_pt},
+ utils::set_panic_hook,
+};
+
+// TODO: Probaly have to use a "serde_as" annotation here to serialize the ProjectivePoint as a byte array
+// https://github.com/RustCrypto/elliptic-curves/issues/393
#[wasm_bindgen]
-pub fn make_nullifier(sk_hex: String, message: String, rng_seed: &[u8]) -> String {
- console_error_panic_hook::set_once();
+#[derive(Debug, Clone)]
+pub struct NullifierSignature(pub(crate) crate::nullifier::NullifierSignature);
- let sk = hex::decode(sk_hex).unwrap();
- let sk = byte_array_to_scalar(&sk);
+// const fields are not yet supported in wasm-bindgen, so placing them outside of the struct
+const PPOINT_LEN: usize = 33;
+const SCALAR_LEN: usize = 32;
- // The base point or generator of the curve.
- let g = ProjectivePoint::GENERATOR;
+#[wasm_bindgen]
+impl NullifierSignature {
+ #[wasm_bindgen(getter, js_name = "publicKey")]
+ pub fn public_key(&self) -> Vec {
+ encode_pt(self.0.pk)
+ }
- let rng_seed: [u8; 32] = rng_seed.try_into().unwrap();
- let rng = &mut StdRng::from_seed(rng_seed);
- let r_bytes = rng.gen::<[u8; 32]>();
- // The signer's secret key. It is only accessed within the secure enclave.
- let r = Scalar::from_repr(r_bytes.into()).unwrap();
+ #[wasm_bindgen(getter, js_name = "nullifier")]
+ pub fn nullifier(&self) -> Vec {
+ encode_pt(self.0.nullifier)
+ }
- // The user's public key: g^sk.
- let pk = &g * &sk;
+ #[wasm_bindgen(getter, js_name = "c")]
+ pub fn c(&self) -> Vec {
+ self.0.c.to_bytes().to_vec()
+ }
- // The generator exponentiated by r: g^r.
- let g_r = &g * &r;
+ #[wasm_bindgen(getter, js_name = "r_sk_c")]
+ pub fn r_sk_c(&self) -> Vec {
+ self.0.r_sk_c.to_bytes().to_vec()
+ }
- // hash[m, pk]
- let hash_m_pk = hash_m_pk_to_secp(message.as_bytes(), &pk);
+ #[wasm_bindgen(getter, js_name = "g_r")]
+ pub fn g_r(&self) -> Vec {
+ encode_pt(self.0.g_r)
+ }
- // hash[m, pk]^r
- let hash_m_pk_pow_r = &hash_m_pk * &r;
+ #[wasm_bindgen(getter, js_name = "hash_m_pk_pow_r")]
+ pub fn hash_m_pk_pow_r(&self) -> Vec {
+ encode_pt(self.0.hash_m_pk_pow_r)
+ }
- // The public nullifier: hash[m, pk]^sk.
- let nullifier = &hash_m_pk * &sk;
+ #[wasm_bindgen(js_name = "toBytes")]
+ pub fn to_bytes(&self) -> Vec {
+ let mut bytes = Vec::new();
+ bytes.extend_from_slice(self.public_key().as_slice());
+ bytes.extend_from_slice(self.nullifier().as_slice());
+ bytes.extend_from_slice(self.c().as_slice());
+ bytes.extend_from_slice(self.r_sk_c().as_slice());
+ bytes.extend_from_slice(self.g_r().as_slice());
+ bytes.extend_from_slice(self.hash_m_pk_pow_r().as_slice());
+ bytes
+ }
- let nullifier = encode_pt(nullifier).unwrap();
- hex::encode(nullifier)
-}
+ #[wasm_bindgen(js_name = "serializedLength")]
+ pub fn serialized_len() -> usize {
+ PPOINT_LEN * 4 + SCALAR_LEN * 2
+ }
-#[wasm_bindgen]
-pub struct Signature {
- pk: Vec,
- nullifier: Vec,
- c: Vec,
- r_sk_c: Vec,
- g_r: Vec,
- hash_m_pk_pow_r: Vec,
+ #[wasm_bindgen(js_name = "fromBytes")]
+ pub fn from_bytes(bytes: &[u8]) -> Result {
+ set_panic_hook();
+
+ if bytes.len() != Self::serialized_len() {
+ panic!("Invalid length for NullifierSignature");
+ }
+
+ let pk_last_idx = PPOINT_LEN;
+ let pk =
+ ProjectivePoint::from_bytes(GenericArray::from_slice(&bytes[0..pk_last_idx])).unwrap();
+
+ let nullifier_last_idx = pk_last_idx + PPOINT_LEN;
+ let nullifier = ProjectivePoint::from_bytes(GenericArray::from_slice(
+ &bytes[pk_last_idx..nullifier_last_idx],
+ ))
+ .unwrap();
+
+ let c_last_idx = nullifier_last_idx + SCALAR_LEN;
+ let c = byte_array_to_scalar(&bytes[nullifier_last_idx..c_last_idx]);
+
+ let r_sk_c_last_idx = c_last_idx + SCALAR_LEN;
+ let r_sk_c = byte_array_to_scalar(&bytes[c_last_idx..r_sk_c_last_idx]);
+
+ let g_r_last_idx = r_sk_c_last_idx + PPOINT_LEN;
+ let g_r = ProjectivePoint::from_bytes(GenericArray::from_slice(
+ &bytes[r_sk_c_last_idx..g_r_last_idx],
+ ))
+ .unwrap();
+
+ let hash_m_pk_pow_r =
+ ProjectivePoint::from_bytes(GenericArray::from_slice(&bytes[g_r_last_idx..])).unwrap();
+
+ Ok(NullifierSignature(crate::nullifier::NullifierSignature {
+ pk,
+ nullifier,
+ c,
+ r_sk_c,
+ g_r,
+ hash_m_pk_pow_r,
+ }))
+ }
}
#[wasm_bindgen]
-pub fn make_signature(sk_hex: String, message: String, rng_seed: &[u8]) -> Signature {
- console_error_panic_hook::set_once();
-
- let sk = hex::decode(sk_hex).unwrap();
- let sk = byte_array_to_scalar(&sk);
+impl NullifierSignature {
+ #[wasm_bindgen]
+ pub fn new(sk: &[u8], message: &str, rng_seed: Option>) -> Self {
+ set_panic_hook();
- // The base point or generator of the curve.
- let g = ProjectivePoint::GENERATOR;
+ if sk.len() != 32 {
+ panic!("Invalid secret key length");
+ }
- let rng_seed: [u8; 32] = rng_seed.try_into().unwrap();
- let rng = &mut StdRng::from_seed(rng_seed);
- let r_bytes = rng.gen::<[u8; 32]>();
- // The signer's secret key. It is only accessed within the secure enclave.
- let r = Scalar::from_repr(r_bytes.into()).unwrap();
+ let mut rng = match rng_seed {
+ Some(seed) => {
+ let seed: [u8; 32] = seed.as_ref().try_into().unwrap();
- // The user's public key: g^sk.
- let pk = &g * &sk;
+ StdRng::from_seed(seed)
+ }
+ None => StdRng::from_rng(OsRng).unwrap(),
+ };
+ let sk = byte_array_to_scalar(sk);
- // The generator exponentiated by r: g^r.
- let g_r = &g * &r;
+ let nullifier_signature =
+ crate::nullifier::NullifierSignature::new(&sk, message.as_bytes(), &mut rng);
- // hash[m, pk]
- let hash_m_pk = hash_m_pk_to_secp(message.as_bytes(), &pk);
-
- // hash[m, pk]^r
- let hash_m_pk_pow_r = &hash_m_pk * &r;
-
- // The public nullifier: hash[m, pk]^sk.
- let nullifier = &hash_m_pk * &sk;
+ Self(nullifier_signature)
+ }
+}
- // The Fiat-Shamir type step.
- let c = sha512hash6signals(&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r);
+#[cfg(test)]
+mod wasm_test {
+ use elliptic_curve::Field;
+ use wasm_bindgen_test::*;
- // This value is part of the discrete log equivalence (DLEQ) proof.
- let r_sk_c = r + sk * c;
+ #[test]
+ #[wasm_bindgen_test]
+ fn serializes_and_deserializes_nullifier() {
+ let rng = rand::thread_rng();
+ let sk = k256::Scalar::random(rng).to_bytes().to_vec();
+ let message = "Hello, world!".to_string();
- let pk = encode_pt(pk).unwrap();
- let nullifier = encode_pt(nullifier).unwrap();
- let c = c.to_bytes().to_vec();
- let r_sk_c = r_sk_c.to_bytes().to_vec();
- let g_r = encode_pt(g_r).unwrap();
- let hash_m_pk_pow_r = encode_pt(hash_m_pk_pow_r).unwrap();
+ let sig = super::NullifierSignature::new(&sk, &message, None);
+ let bytes = sig.to_bytes();
+ let sig2 = super::NullifierSignature::from_bytes(&bytes).unwrap();
- // Return the signature.
- Signature {
- pk,
- nullifier,
- c,
- r_sk_c,
- g_r,
- hash_m_pk_pow_r,
+ assert_eq!(sig.to_bytes(), sig2.to_bytes());
}
}
From d61f6ea75cd63fe8a173ed234be6dcfa8c639dcb Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Sun, 20 Nov 2022 19:43:49 +0100
Subject: [PATCH 4/4] add gh actions
---
.github/workflows/ci.yml | 93 ++++++++++++++++++++++++++++++++++++++++
Cargo.toml | 6 +++
2 files changed, 99 insertions(+)
create mode 100644 .github/workflows/ci.yml
create mode 100644 Cargo.toml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..460d539
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,93 @@
+name: Build & Test
+
+on:
+ pull_request:
+ paths-ignore:
+ - README.md
+ push:
+ branches:
+ - main
+ paths-ignore:
+ - README.md
+ tags:
+ - v*
+
+env:
+ CARGO_INCREMENTAL: 0
+ RUSTFLAGS: "-Dwarnings"
+
+jobs:
+ clippy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: actions-rs/toolchain@v1
+ with:
+ toolchain: stable
+ components: clippy
+ override: true
+ profile: minimal
+ - run: cargo clippy --all --all-features -- -D warnings
+
+ rustfmt:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v1
+
+ - name: Install stable toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: stable
+ components: rustfmt
+ profile: minimal
+ override: true
+
+ - name: Run cargo fmt
+ uses: actions-rs/cargo@v1
+ with:
+ command: fmt
+ args: --all -- --check
+
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - target: x86_64-unknown-linux-gnu
+ rust: 1.57 # MSRV
+ - target: x86_64-unknown-linux-gnu
+ rust: stable
+
+ steps:
+ - uses: actions/checkout@v1
+ - uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: ${{ matrix.rust }}
+ target: ${{ matrix.target }}
+ override: true
+ - run: ${{ matrix.deps }}
+ - run: cargo check --all-features
+ - run: cargo test --release --all-features
+
+ wasm-test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ rust:
+ - 1.57 # MSRV
+ - stable
+ target:
+ - wasm32-unknown-unknown
+
+ steps:
+ - uses: actions/checkout@v1
+ - uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: ${{ matrix.rust }}
+ target: ${{ matrix.target }}
+ override: true
+ - run: cargo install wasm-pack
+ - run: cd rust-k256 && wasm-pack test --node
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..b2bbde4
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+
+members = [
+ # "rust-arkworks", # Doesn't work yet
+ "rust-k256",
+]
\ No newline at end of file