From f11928cc0d494076ddbbb55fd6c2dcca60c12071 Mon Sep 17 00:00:00 2001 From: skaunov Date: Sun, 22 Oct 2023 03:13:38 +0300 Subject: [PATCH 1/9] Basic solution; unpolished --- rust-k256/Cargo.toml | 2 +- rust-k256/src/lib.rs | 191 +++++++++++++++++++------------------------ 2 files changed, 85 insertions(+), 108 deletions(-) diff --git a/rust-k256/Cargo.toml b/rust-k256/Cargo.toml index 1f5c70c..9c5426a 100644 --- a/rust-k256/Cargo.toml +++ b/rust-k256/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] crate = "0.0.2" -digest = "0.10.3" +# digest = "0.10.3" ring = "0.16.20" rand_core = "0.6.3" hex-literal = "0.3.4" diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 3cf94a8..7d857fd 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -1,9 +1,6 @@ -#![allow(dead_code)] -#![allow(unused_variables)] // #![feature(generic_const_expr)] // #![allow(incomplete_features)] -use digest::Output; use elliptic_curve::bigint::ArrayEncoding; use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}; use elliptic_curve::ops::Reduce; @@ -12,7 +9,7 @@ use k256::U256; use k256::{ // ecdsa::{signature::Signer, Signature, SigningKey}, elliptic_curve::group::ff::PrimeField, - sha2::{Digest, Sha256}, + sha2::{Digest, Sha256, digest::Output}, FieldBytes, ProjectivePoint, Scalar, @@ -34,10 +31,10 @@ fn print_type_of(_: &T) { println!("{}", std::any::type_name::()); } -fn c_sha256_vec_signal(values: &[ProjectivePoint]) -> Output { +fn c_sha256_vec_signal(values: Vec<&ProjectivePoint>) -> Output { let preimage_vec = values - .iter() - .map(|value| encode_pt(*value).unwrap()) + .into_iter() + .map(|value| encode_pt(value).unwrap()) .collect::>() .concat(); let mut sha256_hasher = Sha256::new(); @@ -53,12 +50,12 @@ fn sha256hash6signals( 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).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(); @@ -75,7 +72,7 @@ fn sha256hash6signals( // Hashes two values to the curve fn hash_to_curve(m: &[u8], pk: &ProjectivePoint) -> ProjectivePoint { let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>( - &[[m, &encode_pt(*pk).unwrap()].concat().as_slice()], + &[[m, &encode_pt(pk).unwrap()].concat().as_slice()], //b"CURVE_XMD:SHA-256_SSWU_RO_", DST, ) @@ -88,82 +85,74 @@ enum PlumeVersion { V2, } -// 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( - message: &[u8], - pk: &ProjectivePoint, - nullifier: &ProjectivePoint, - c: &Output, - s: &Scalar, - r_point_optional: &Option, - hashed_to_curve_optional: &Option, - version: PlumeVersion, -) -> bool { - let mut verified: bool = true; // looks like antipattern to @skaunov; also see #22 - - // The base point or generator of the curve. - let g = &ProjectivePoint::GENERATOR; - - // hash[m, pk] - let hashed_to_curve_computed = &hash_to_curve(message, pk); - - // Check whether g^r equals g^s * pk^{-c} - let r_point_computed: ProjectivePoint; - // TODO should we use non-zero `Scalar`? - let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(*c)); - match *r_point_optional { - Some(_g_r_value) => { - if (g * s - pk * c_scalar) != _g_r_value { - verified = false; - } - } - None => println!("g^r not provided, check skipped"), - } - r_point_computed = g * s - pk * c_scalar; - - // Check whether h^r equals h^{r + sk * c} * nullifier^{-c} - let hashed_to_curve_r_computed: ProjectivePoint; - match *hashed_to_curve_optional { - Some(_hash_m_pk_pow_r_value) => { - if (hashed_to_curve_computed * s - nullifier * c_scalar) != _hash_m_pk_pow_r_value { - verified = false; - } - } - None => println!("hash_m_pk_pow_r not provided, check skipped"), - } - hashed_to_curve_r_computed = hashed_to_curve_computed * s - nullifier * c_scalar; - - // Check if the given hash matches - match version { - PlumeVersion::V1 => { - if c_sha256_vec_signal(&[ - *g, - *pk, - *hashed_to_curve_computed, - *nullifier, - r_point_computed, - hashed_to_curve_r_computed, - ]) != *c - { - verified = false; - } +/* currently seems to right place for this `struct` declaration; +should be moved (to the beginning of the file?) during refactoring for proper order of the items */ +/* while no consistent API is present here it's completely `pub`; +when API will be designed it should include this `struct` */ +pub struct PlumeSignature<'a>{ + pub message: &'a [u8], + pub pk: &'a ProjectivePoint, + pub nullifier: &'a ProjectivePoint, + pub c: &'a [u8], + pub s: &'a Scalar, + pub v2: Option> + // version: PlumeVersion, #removeMe +} +struct PlumeSignatureV2Fields<'a>{ + sig_r_point: &'a ProjectivePoint, + sig_hashed_to_curve_r: &'a ProjectivePoint, +} +impl PlumeSignature<'_>{ + // 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(&self) -> bool { + // TODO check `c` is `Output`; actually check should be in the API + + // use TryFrom; + // as GenericArray<>>::try_from(self.c); + // if panic::catch_unwind(|| Output::::from_slice(self.c)).is_err() {return false} + // let _ : Output = self.c.try_into().unwrap(); + + let c = Output::::from_slice(self.c)/* .to_owned() */; + // TODO should we allow `c` input greater than BaseField::MODULUS? + let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.to_owned())); + // @skaunov would be glad to discuss with @Divide-By-0 excessive of the following check. Though I should notice that it at least doesn't breaking anything. + if c_scalar.is_zero().into() {return false} + + let r_point = ProjectivePoint::GENERATOR * self.s - self.pk * &c_scalar; + let hashed_to_curve = hash_to_curve(self.message, self.pk); + let hashed_to_curve_r = &hashed_to_curve * self.s - self.nullifier * &c_scalar; + + if let Some(PlumeSignatureV2Fields { sig_r_point, sig_hashed_to_curve_r }) = self.v2 { + // Check whether g^r equals g^s * pk^{-c} + if &r_point != sig_r_point {return false} + + // Check whether h^r equals h^{r + sk * c} * nullifier^{-c} + if &hashed_to_curve_r != sig_hashed_to_curve_r {return false} + + // Check if the given hash matches + if &c_sha256_vec_signal(vec![ + &ProjectivePoint::GENERATOR, + self.pk, + &hashed_to_curve, + self.nullifier, + &r_point, + &hashed_to_curve_r, + ]) != c {return false} } - PlumeVersion::V2 => { - if c_sha256_vec_signal(&[*nullifier, r_point_computed, hashed_to_curve_r_computed]) - != *c - { - verified = false; - } + else { + // Check if the given hash matches + if &c_sha256_vec_signal(vec![self.nullifier, &r_point, &hashed_to_curve_r]) != c {return false} } + + true } - verified } /// Encodes the point by compressing it to 33 bytes -fn encode_pt(point: ProjectivePoint) -> Result, Error> { +fn encode_pt(point: &ProjectivePoint) -> Result, Error> { let encoded = point.to_encoded_point(true); Ok(encoded.to_bytes().to_vec()) } @@ -292,9 +281,9 @@ mod tests { // The Fiat-Shamir type step. let c = match version { PlumeVersion::V1 => { - c_sha256_vec_signal(&[g, pk, hash_m_pk, nullifier, g_r, hash_m_pk_pow_r]) + c_sha256_vec_signal(vec![&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r]) } - PlumeVersion::V2 => c_sha256_vec_signal(&[nullifier, g_r, hash_m_pk_pow_r]), + PlumeVersion::V2 => c_sha256_vec_signal(vec![&nullifier, &g_r, &hash_m_pk_pow_r]), }; let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.clone())); @@ -326,16 +315,11 @@ mod tests { // 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, - PlumeVersion::V1, - ); + let verified = PlumeSignature{ + message: m, pk: &pk, nullifier: &nullifier, c: &c, s: &r_sk_c, v2: Some(PlumeSignatureV2Fields{ + sig_r_point: &g_r.unwrap(), sig_hashed_to_curve_r: &hash_m_pk_pow_r.unwrap() + }) + }.verify_signals(); println!("Verified: {}", verified); // Print nullifier @@ -401,7 +385,7 @@ mod tests { ); // Test encode_pt() - let g_as_bytes = encode_pt(g).unwrap(); + let g_as_bytes = encode_pt(&g).unwrap(); assert_eq!( hex::encode(g_as_bytes), "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" @@ -447,16 +431,9 @@ mod tests { // 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, - PlumeVersion::V2, - ); + let verified = PlumeSignature{ + message: m, pk: &pk, nullifier: &nullifier, c: &c, s: &r_sk_c, v2: None + }.verify_signals(); assert!(verified) } } From 0c2f7512c4570bc9fbfbf42a16da2ef2b26371c2 Mon Sep 17 00:00:00 2001 From: skaunov Date: Sun, 22 Oct 2023 03:18:53 +0300 Subject: [PATCH 2/9] `fmt` --- rust-k256/src/lib.rs | 102 ++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 7d857fd..66cb923 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -9,7 +9,7 @@ use k256::U256; use k256::{ // ecdsa::{signature::Signer, Signature, SigningKey}, elliptic_curve::group::ff::PrimeField, - sha2::{Digest, Sha256, digest::Output}, + sha2::{digest::Output, Digest, Sha256}, FieldBytes, ProjectivePoint, Scalar, @@ -85,52 +85,62 @@ enum PlumeVersion { V2, } -/* currently seems to right place for this `struct` declaration; +/* currently seems to right place for this `struct` declaration; should be moved (to the beginning of the file?) during refactoring for proper order of the items */ -/* while no consistent API is present here it's completely `pub`; +/* while no consistent API is present here it's completely `pub`; when API will be designed it should include this `struct` */ -pub struct PlumeSignature<'a>{ +pub struct PlumeSignature<'a> { pub message: &'a [u8], pub pk: &'a ProjectivePoint, pub nullifier: &'a ProjectivePoint, pub c: &'a [u8], pub s: &'a Scalar, - pub v2: Option> - // version: PlumeVersion, #removeMe + pub v2: Option>, } -struct PlumeSignatureV2Fields<'a>{ - sig_r_point: &'a ProjectivePoint, - sig_hashed_to_curve_r: &'a ProjectivePoint, +pub struct PlumeSignatureV2Fields<'a> { + pub sig_r_point: &'a ProjectivePoint, + pub sig_hashed_to_curve_r: &'a ProjectivePoint, } -impl PlumeSignature<'_>{ +impl PlumeSignature<'_> { // 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(&self) -> bool { // TODO check `c` is `Output`; actually check should be in the API - + // use TryFrom; // as GenericArray<>>::try_from(self.c); // if panic::catch_unwind(|| Output::::from_slice(self.c)).is_err() {return false} // let _ : Output = self.c.try_into().unwrap(); - let c = Output::::from_slice(self.c)/* .to_owned() */; - // TODO should we allow `c` input greater than BaseField::MODULUS? + let c = Output::::from_slice(self.c); + // TODO should we allow `c` input greater than BaseField::MODULUS? let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.to_owned())); - // @skaunov would be glad to discuss with @Divide-By-0 excessive of the following check. Though I should notice that it at least doesn't breaking anything. - if c_scalar.is_zero().into() {return false} + /* @skaunov would be glad to discuss with @Divide-By-0 excessive of the following check. + Though I should notice that it at least doesn't breaking anything. */ + if c_scalar.is_zero().into() { + return false; + } let r_point = ProjectivePoint::GENERATOR * self.s - self.pk * &c_scalar; let hashed_to_curve = hash_to_curve(self.message, self.pk); let hashed_to_curve_r = &hashed_to_curve * self.s - self.nullifier * &c_scalar; - - if let Some(PlumeSignatureV2Fields { sig_r_point, sig_hashed_to_curve_r }) = self.v2 { + + if let Some(PlumeSignatureV2Fields { + sig_r_point, + sig_hashed_to_curve_r, + }) = self.v2 + { // Check whether g^r equals g^s * pk^{-c} - if &r_point != sig_r_point {return false} - + if &r_point != sig_r_point { + return false; + } + // Check whether h^r equals h^{r + sk * c} * nullifier^{-c} - if &hashed_to_curve_r != sig_hashed_to_curve_r {return false} + if &hashed_to_curve_r != sig_hashed_to_curve_r { + return false; + } // Check if the given hash matches if &c_sha256_vec_signal(vec![ @@ -140,11 +150,15 @@ impl PlumeSignature<'_>{ self.nullifier, &r_point, &hashed_to_curve_r, - ]) != c {return false} - } - else { + ]) != c + { + return false; + } + } else { // Check if the given hash matches - if &c_sha256_vec_signal(vec![self.nullifier, &r_point, &hashed_to_curve_r]) != c {return false} + if &c_sha256_vec_signal(vec![self.nullifier, &r_point, &hashed_to_curve_r]) != c { + return false; + } } true @@ -280,9 +294,14 @@ mod tests { // The Fiat-Shamir type step. let c = match version { - PlumeVersion::V1 => { - c_sha256_vec_signal(vec![&g, &pk, &hash_m_pk, &nullifier, &g_r, &hash_m_pk_pow_r]) - } + PlumeVersion::V1 => c_sha256_vec_signal(vec![ + &g, + &pk, + &hash_m_pk, + &nullifier, + &g_r, + &hash_m_pk_pow_r, + ]), PlumeVersion::V2 => c_sha256_vec_signal(vec![&nullifier, &g_r, &hash_m_pk_pow_r]), }; @@ -315,11 +334,18 @@ mod tests { // 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 = PlumeSignature{ - message: m, pk: &pk, nullifier: &nullifier, c: &c, s: &r_sk_c, v2: Some(PlumeSignatureV2Fields{ - sig_r_point: &g_r.unwrap(), sig_hashed_to_curve_r: &hash_m_pk_pow_r.unwrap() - }) - }.verify_signals(); + let verified = PlumeSignature { + message: m, + pk: &pk, + nullifier: &nullifier, + c: &c, + s: &r_sk_c, + v2: Some(PlumeSignatureV2Fields { + sig_r_point: &g_r.unwrap(), + sig_hashed_to_curve_r: &hash_m_pk_pow_r.unwrap(), + }), + } + .verify_signals(); println!("Verified: {}", verified); // Print nullifier @@ -431,9 +457,15 @@ mod tests { // 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 = PlumeSignature{ - message: m, pk: &pk, nullifier: &nullifier, c: &c, s: &r_sk_c, v2: None - }.verify_signals(); + let verified = PlumeSignature { + message: m, + pk: &pk, + nullifier: &nullifier, + c: &c, + s: &r_sk_c, + v2: None, + } + .verify_signals(); assert!(verified) } } From f35494f06cfed21db742d6a03d1d2a86240863b8 Mon Sep 17 00:00:00 2001 From: skaunov Date: Sun, 22 Oct 2023 13:15:26 +0300 Subject: [PATCH 3/9] Correct additional fields scheme naming --- rust-k256/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 66cb923..aa20fd4 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -95,9 +95,9 @@ pub struct PlumeSignature<'a> { pub nullifier: &'a ProjectivePoint, pub c: &'a [u8], pub s: &'a Scalar, - pub v2: Option>, + pub v2: Option>, } -pub struct PlumeSignatureV2Fields<'a> { +pub struct PlumeSignatureV1Fields<'a> { pub sig_r_point: &'a ProjectivePoint, pub sig_hashed_to_curve_r: &'a ProjectivePoint, } @@ -127,7 +127,7 @@ impl PlumeSignature<'_> { let hashed_to_curve = hash_to_curve(self.message, self.pk); let hashed_to_curve_r = &hashed_to_curve * self.s - self.nullifier * &c_scalar; - if let Some(PlumeSignatureV2Fields { + if let Some(PlumeSignatureV1Fields { sig_r_point, sig_hashed_to_curve_r, }) = self.v2 @@ -340,7 +340,7 @@ mod tests { nullifier: &nullifier, c: &c, s: &r_sk_c, - v2: Some(PlumeSignatureV2Fields { + v2: Some(PlumeSignatureV1Fields { sig_r_point: &g_r.unwrap(), sig_hashed_to_curve_r: &hash_m_pk_pow_r.unwrap(), }), From e3c849ae0a766198b4ba0813bf9a8aa8dd863bfd Mon Sep 17 00:00:00 2001 From: skaunov Date: Mon, 23 Oct 2023 00:10:49 +0300 Subject: [PATCH 4/9] Manually cleaned --- rust-k256/src/lib.rs | 50 +++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index aa20fd4..73261e0 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -5,7 +5,6 @@ use elliptic_curve::bigint::ArrayEncoding; use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}; use elliptic_curve::ops::Reduce; use elliptic_curve::sec1::ToEncodedPoint; -use k256::U256; use k256::{ // ecdsa::{signature::Signer, Signature, SigningKey}, elliptic_curve::group::ff::PrimeField, @@ -13,14 +12,13 @@ use k256::{ FieldBytes, ProjectivePoint, Scalar, - Secp256k1, + Secp256k1, U256 }; // 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 -const DEFAULT_VERSION: PlumeVersion = PlumeVersion::V1; #[derive(Debug, PartialEq)] pub enum Error { @@ -80,26 +78,21 @@ fn hash_to_curve(m: &[u8], pk: &ProjectivePoint) -> ProjectivePoint { pt } -enum PlumeVersion { - V1, - V2, -} - /* currently seems to right place for this `struct` declaration; should be moved (to the beginning of the file?) during refactoring for proper order of the items */ -/* while no consistent API is present here it's completely `pub`; -when API will be designed it should include this `struct` */ +/* while no consistent #API is present here it's completely `pub`; +when API will be designed it should include this `struct` (and it also probably will hold values instead of references) */ pub struct PlumeSignature<'a> { pub message: &'a [u8], pub pk: &'a ProjectivePoint, pub nullifier: &'a ProjectivePoint, pub c: &'a [u8], pub s: &'a Scalar, - pub v2: Option>, + pub v1: Option>, } pub struct PlumeSignatureV1Fields<'a> { - pub sig_r_point: &'a ProjectivePoint, - pub sig_hashed_to_curve_r: &'a ProjectivePoint, + pub r_point: &'a ProjectivePoint, + pub hashed_to_curve_r: &'a ProjectivePoint, } impl PlumeSignature<'_> { // Verifier check in SNARK: @@ -107,13 +100,7 @@ impl PlumeSignature<'_> { // 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(&self) -> bool { - // TODO check `c` is `Output`; actually check should be in the API - - // use TryFrom; - // as GenericArray<>>::try_from(self.c); - // if panic::catch_unwind(|| Output::::from_slice(self.c)).is_err() {return false} - // let _ : Output = self.c.try_into().unwrap(); - + // don't forget to check `c` is `Output` in the #API let c = Output::::from_slice(self.c); // TODO should we allow `c` input greater than BaseField::MODULUS? let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.to_owned())); @@ -128,9 +115,9 @@ impl PlumeSignature<'_> { let hashed_to_curve_r = &hashed_to_curve * self.s - self.nullifier * &c_scalar; if let Some(PlumeSignatureV1Fields { - sig_r_point, - sig_hashed_to_curve_r, - }) = self.v2 + r_point: sig_r_point, + hashed_to_curve_r: sig_hashed_to_curve_r, + }) = self.v1 { // Check whether g^r equals g^s * pk^{-c} if &r_point != sig_r_point { @@ -188,11 +175,17 @@ fn byte_array_to_scalar(bytes: &[u8]) -> Scalar { mod tests { use super::*; - use helpers::{gen_test_scalar_sk, hash_to_secp, test_gen_signals}; + use helpers::{gen_test_scalar_sk, hash_to_secp, test_gen_signals, PlumeVersion}; mod helpers { use super::*; use hex_literal::hex; + #[derive(Debug)] + pub enum PlumeVersion { + V1, + V2, + } + // Generates a deterministic secret key for deterministic testing. Should be replaced by random oracle in production deployments. pub fn gen_test_scalar_sk() -> Scalar { Scalar::from_repr( @@ -304,6 +297,7 @@ mod tests { ]), PlumeVersion::V2 => c_sha256_vec_signal(vec![&nullifier, &g_r, &hash_m_pk_pow_r]), }; + dbg!(&c, version); let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.clone())); // This value is part of the discrete log equivalence (DLEQ) proof. @@ -340,9 +334,9 @@ mod tests { nullifier: &nullifier, c: &c, s: &r_sk_c, - v2: Some(PlumeSignatureV1Fields { - sig_r_point: &g_r.unwrap(), - sig_hashed_to_curve_r: &hash_m_pk_pow_r.unwrap(), + v1: Some(PlumeSignatureV1Fields { + r_point: &g_r.unwrap(), + hashed_to_curve_r: &hash_m_pk_pow_r.unwrap(), }), } .verify_signals(); @@ -463,7 +457,7 @@ mod tests { nullifier: &nullifier, c: &c, s: &r_sk_c, - v2: None, + v1: None, } .verify_signals(); assert!(verified) From 15f5de2cc32115b8e3e93e6ed706a02f96b8d53f Mon Sep 17 00:00:00 2001 From: skaunov Date: Mon, 23 Oct 2023 00:12:11 +0300 Subject: [PATCH 5/9] Linting --- rust-k256/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 73261e0..070aa2f 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -12,7 +12,8 @@ use k256::{ FieldBytes, ProjectivePoint, Scalar, - Secp256k1, U256 + Secp256k1, + U256, }; // requires 'getrandom' feature const L: usize = 48; From c32efefaf44013dec78c9bfe9b766d4e56adef24 Mon Sep 17 00:00:00 2001 From: skaunov Date: Mon, 23 Oct 2023 14:33:22 +0300 Subject: [PATCH 6/9] Add warning about `c` check --- rust-k256/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 070aa2f..5e913e9 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -96,6 +96,8 @@ pub struct PlumeSignatureV1Fields<'a> { pub hashed_to_curve_r: &'a ProjectivePoint, } impl PlumeSignature<'_> { + /// WARNING: panics when `self.c` isn't an `Output::`. + /// So catch it if it's a possible case for you. // 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 From ef93f16f9d0adf6b6c90f4842295b4fade89a996 Mon Sep 17 00:00:00 2001 From: Sergey Kaunov Date: Mon, 23 Oct 2023 21:07:24 +0300 Subject: [PATCH 7/9] Improve dependencies `Digest` had been switched to more robust re-exported version. `ring` seems to be excessive, commented out for now --- rust-k256/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust-k256/Cargo.toml b/rust-k256/Cargo.toml index 9c5426a..87165e9 100644 --- a/rust-k256/Cargo.toml +++ b/rust-k256/Cargo.toml @@ -7,8 +7,7 @@ edition = "2021" [dependencies] crate = "0.0.2" -# digest = "0.10.3" -ring = "0.16.20" +# ring = "0.16.20" rand_core = "0.6.3" hex-literal = "0.3.4" hash2field = "0.4.0" From daa7c04b47d4e2ad7912a6d3b8a08da9d69337b7 Mon Sep 17 00:00:00 2001 From: skaunov Date: Fri, 27 Oct 2023 12:31:42 +0300 Subject: [PATCH 8/9] Style --- rust-k256/src/lib.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 5e913e9..744bd97 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -96,7 +96,7 @@ pub struct PlumeSignatureV1Fields<'a> { pub hashed_to_curve_r: &'a ProjectivePoint, } impl PlumeSignature<'_> { - /// WARNING: panics when `self.c` isn't an `Output::`. + /// WARNING: panics when `self.c` isn't an `Output::`. /// So catch it if it's a possible case for you. // Verifier check in SNARK: // g^[r + sk * c] / (g^sk)^c = g^r @@ -117,6 +117,14 @@ impl PlumeSignature<'_> { let hashed_to_curve = hash_to_curve(self.message, self.pk); let hashed_to_curve_r = &hashed_to_curve * self.s - self.nullifier * &c_scalar; + let result = |components: Vec<&ProjectivePoint>| -> bool { + if &c_sha256_vec_signal(components) == c { + true + } else { + false + } + }; + if let Some(PlumeSignatureV1Fields { r_point: sig_r_point, hashed_to_curve_r: sig_hashed_to_curve_r, @@ -133,25 +141,18 @@ impl PlumeSignature<'_> { } // Check if the given hash matches - if &c_sha256_vec_signal(vec![ + result(vec![ &ProjectivePoint::GENERATOR, self.pk, &hashed_to_curve, self.nullifier, &r_point, &hashed_to_curve_r, - ]) != c - { - return false; - } + ]) } else { // Check if the given hash matches - if &c_sha256_vec_signal(vec![self.nullifier, &r_point, &hashed_to_curve_r]) != c { - return false; - } + result(vec![self.nullifier, &r_point, &hashed_to_curve_r]) } - - true } } From 1cfa817c589d3ff50b00b4bac04834629cd1e3f9 Mon Sep 17 00:00:00 2001 From: skaunov Date: Fri, 27 Oct 2023 12:32:40 +0300 Subject: [PATCH 9/9] Style --- rust-k256/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 744bd97..a92153d 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -117,6 +117,7 @@ impl PlumeSignature<'_> { let hashed_to_curve = hash_to_curve(self.message, self.pk); let hashed_to_curve_r = &hashed_to_curve * self.s - self.nullifier * &c_scalar; + // Check if the given hash matches let result = |components: Vec<&ProjectivePoint>| -> bool { if &c_sha256_vec_signal(components) == c { true @@ -124,7 +125,7 @@ impl PlumeSignature<'_> { false } }; - + if let Some(PlumeSignatureV1Fields { r_point: sig_r_point, hashed_to_curve_r: sig_hashed_to_curve_r, @@ -140,7 +141,6 @@ impl PlumeSignature<'_> { return false; } - // Check if the given hash matches result(vec![ &ProjectivePoint::GENERATOR, self.pk, @@ -150,7 +150,6 @@ impl PlumeSignature<'_> { &hashed_to_curve_r, ]) } else { - // Check if the given hash matches result(vec![self.nullifier, &r_point, &hashed_to_curve_r]) } }