Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add docs and tests #100

Merged
merged 5 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions rust-arkworks/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ pub fn test_k256_affine_to_arkworks_secp256k1_affine() {
}

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 num_field_bits = 320;
let mut sk_bytes_vec = vec![0u8; num_field_bits];
let mut sk_bytes = hex::decode(hex).unwrap();

sk_bytes.reverse();
Expand Down Expand Up @@ -245,7 +245,7 @@ pub fn test_against_zk_nullifier_sig_c_and_s() {
let sig =
PlumeSignature::sign_with_r(&pp, (&keypair.0, &keypair.1), message, r, PlumeVersion::V2)
.unwrap();

assert_eq!(
coord_to_hex(sig.c.into()),
"00000000000000003dbfb717705010d4f44a70720c95e74b475bd3a783ab0b9e8a6b3b363434eb96"
Expand Down
13 changes: 7 additions & 6 deletions rust-k256/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "plume_rustcrypto"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT"
description = "Implementation of PLUME: nullifier friendly signature scheme on ECDSA; using the k256 library"
Expand All @@ -11,11 +11,12 @@ keywords = ["nullifier", "zero-knowledge", "ECDSA", "PLUME"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand_core = "0.6.3"
hash2field = "0.4.0"
num-bigint = "0.4.3"
num-integer = "0.1.45"
k256 = {version = "0.13.2", features = ["arithmetic", "hash2curve", "expose-field", "sha2"]}
rand_core = "~0.6.3"
# hash2field = "0.4.0"
num-bigint = "~0.4.3"
num-integer = "~0.1.45"
k256 = {version = "~0.13.3", features = ["arithmetic", "hash2curve", "expose-field", "sha2"]}
signature = "^2.2.0"

[dev-dependencies]
hex = "0.4.3"
Expand Down
177 changes: 84 additions & 93 deletions rust-k256/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,84 @@
// #![feature(generic_const_expr)]
// #![allow(incomplete_features)]

//! A library for generating (coming [soon](https://github.com/plume-sig/zk-nullifier-sig/issues/84)) and verifying PLUME signatures.
//! A library for generating and verifying PLUME signatures.
//!
//! See <https://blog.aayushg.com/nullifier> for more information.
//!
// Find `arkworks-rs` crate as `plume_arkworks`.
//
// # Examples
// For V2 just set `v1` to `None`
// ```rust
// # fn main() {
// let sig_good = PlumeSignature<'a>{
// message: &b"An example app message string",
// pk: ProjectivePoint::GENERATOR * Scalar::from_repr(hex!("519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464").into()).unwrap(),
// ...
// };
// # }
// ```

use k256::{
elliptic_curve::ops::ReduceNonZero,
elliptic_curve::{bigint::ArrayEncoding, group::ff::PrimeField},
FieldBytes, U256,
}; // requires 'getrandom' feature
//! # Examples
//! If you want more control or to be more generic on traits `use` [`PlumeSigner`] from [`randomizedsigner`]
//! ```rust
//! use plume_rustcrypto::{PlumeSignature, SecretKey};
//! use rand_core::OsRng;
//! # fn main() {
//! # let sk = SecretKey::random(&mut OsRng);
//! #
//! let sig_v1 = PlumeSignature::sign_v1(
//! &sk, b"ZK nullifier signature", &mut OsRng
//! );
//! assert!(sig_v1.verify());
//!
//! let sig_v2 = PlumeSignature::sign_v2(
//! &sk, b"ZK nullifier signature", &mut OsRng
//! );
//! assert!(sig_v2.verify());
//! # }
//! ```

use k256::elliptic_curve::bigint::ArrayEncoding;
use k256::elliptic_curve::ops::Reduce;
use k256::sha2::{digest::Output, Digest, Sha256}; // requires 'getrandom' feature
use k256::Scalar;
use k256::U256;
use signature::RandomizedSigner;
// TODO
pub use k256::ProjectivePoint;
/// Re-exports the [`Scalar`] type, [`Sha256`] hash function, and [`Output`] type
/// from the [`k256`] crate's [`sha2`] module. This allows them to be used
/// from the current module.
pub use k256::{
sha2::{digest::Output, Digest, Sha256},
Scalar,
};
use std::panic;
/// Re-exports the `NonZeroScalar` and `SecretKey` types from the `k256` crate.
/// These are used for generating secret keys and non-zero scalars for signing.
pub use k256::{NonZeroScalar, SecretKey};
/// Re-exports the [`CryptoRngCore`] trait from the [`rand_core`] crate.
/// This allows it to be used from the current module.
pub use rand_core::CryptoRngCore;

mod utils;
// not published due to use of `Projective...`; these utils can be found in other crates
use utils::*;

/// Provides the [`RandomizedSigner`] trait implementation over [`PlumeSignature`].
pub mod randomizedsigner;
use randomizedsigner::PlumeSigner;

/// The domain separation tag used for hashing to the `secp256k1` curve
pub const DST: &[u8] = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_"; // Hash to curve algorithm

/// Struct holding signature data for a PLUME signature.
///
/// `v1` field differintiate whether V1 or V2 protocol will be used.
pub struct PlumeSignature<'a> {
/// `v1specific` field differintiate whether V1 or V2 protocol will be used.
pub struct PlumeSignature {
/// The message that was signed.
pub message: &'a [u8],
pub message: Vec<u8>,
/// The public key used to verify the signature.
pub pk: &'a ProjectivePoint,
pub pk: ProjectivePoint,
/// The nullifier.
pub nullifier: &'a ProjectivePoint,
/// Part of the signature data.
pub c: &'a [u8],
pub nullifier: ProjectivePoint,
/// Part of the signature data. SHA-256 interpreted as a scalar.
pub c: NonZeroScalar,
/// Part of the signature data, a scalar value.
pub s: &'a Scalar,
pub s: NonZeroScalar,
/// Optional signature data for variant 1 signatures.
pub v1: Option<PlumeSignatureV1Fields<'a>>,
pub v1specific: Option<PlumeSignatureV1Fields>,
}
/// Nested struct holding additional signature data used in variant 1 of the protocol.
#[derive(Debug)]
pub struct PlumeSignatureV1Fields<'a> {
pub struct PlumeSignatureV1Fields {
/// Part of the signature data, a curve point.
pub r_point: &'a ProjectivePoint,
pub r_point: ProjectivePoint,
/// Part of the signature data, a curve point.
pub hashed_to_curve_r: &'a ProjectivePoint,
pub hashed_to_curve_r: ProjectivePoint,
}
impl PlumeSignature<'_> {
impl PlumeSignature {
/// Verifies a PLUME signature.
/// Returns `true` if the signature is valid.
pub fn verify(&self) -> bool {
Expand All @@ -76,56 +87,64 @@ 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)

// don't forget to check `c` is `Output<Sha256>` in the #API
let c = panic::catch_unwind(|| Output::<Sha256>::from_slice(self.c));
if c.is_err() {
return false;
}
let c = c.unwrap();
let c_scalar = *self.c;

// TODO should we allow `c` input greater than BaseField::MODULUS?
// TODO `reduce_nonzero` doesn't seems to be correct here. `NonZeroScalar` should be appropriate.
let c_scalar = &Scalar::reduce_nonzero(U256::from_be_byte_array(c.to_owned()));
let r_point = (ProjectivePoint::GENERATOR * *self.s) - (self.pk * (c_scalar));

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 = hash_to_curve(&self.message, &self.pk);
if hashed_to_curve.is_err() {
return false;
}
let hashed_to_curve = hashed_to_curve.unwrap();

let hashed_to_curve_r = hashed_to_curve * self.s - self.nullifier * c_scalar;
let hashed_to_curve_r = hashed_to_curve * *self.s - self.nullifier * (c_scalar);

if let Some(PlumeSignatureV1Fields {
r_point: sig_r_point,
hashed_to_curve_r: sig_hashed_to_curve_r,
}) = self.v1
}) = self.v1specific
{
// Check whether g^r equals g^s * pk^{-c}
if &r_point != sig_r_point {
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 {
if hashed_to_curve_r != sig_hashed_to_curve_r {
return false;
}

// Check if the given hash matches
c == &c_sha256_vec_signal(vec![
&ProjectivePoint::GENERATOR,
self.pk,
&hashed_to_curve,
self.nullifier,
&r_point,
&hashed_to_curve_r,
])
c_scalar
== Scalar::reduce(U256::from_be_byte_array(c_sha256_vec_signal(vec![
&ProjectivePoint::GENERATOR,
&self.pk,
&hashed_to_curve,
&self.nullifier,
&r_point,
&hashed_to_curve_r,
])))
} else {
// Check if the given hash matches
c == &c_sha256_vec_signal(vec![self.nullifier, &r_point, &hashed_to_curve_r])
c_scalar
== Scalar::reduce(U256::from_be_byte_array(c_sha256_vec_signal(vec![
&self.nullifier,
&r_point,
&hashed_to_curve_r,
])))
}
}

/// Yields the signature with `None` for `v1specific`. Same as using [`RandomizedSigner`] with [`PlumeSigner`];
/// use it when you don't want to `use` PlumeSigner and the trait in your code.
pub fn sign_v1(secret_key: &SecretKey, msg: &[u8], rng: &mut impl CryptoRngCore) -> Self {
PlumeSigner::new(secret_key, true).sign_with_rng(rng, msg)
}
/// Yields the signature with `Some` for `v1specific`. Same as using [`RandomizedSigner`] with [`PlumeSigner`];
/// use it when you don't want to `use` PlumeSigner and the trait in your code.
pub fn sign_v2(secret_key: &SecretKey, msg: &[u8], rng: &mut impl CryptoRngCore) -> Self {
PlumeSigner::new(secret_key, false).sign_with_rng(rng, msg)
}
}

fn c_sha256_vec_signal(values: Vec<&ProjectivePoint>) -> Output<Sha256> {
Expand All @@ -139,34 +158,6 @@ fn c_sha256_vec_signal(values: Vec<&ProjectivePoint>) -> Output<Sha256> {
sha256_hasher.finalize()
}

// Withhold removing this before implementing `sign`
fn sha256hash6signals(
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);
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();

//println!("c_preimage_vec: {:?}", c_preimage_vec);

let mut sha256_hasher = Sha256::new();
sha256_hasher.update(c_preimage_vec.as_slice());
let sha512_hasher_result = sha256_hasher.finalize(); //512 bit hash

let c_bytes = FieldBytes::from_iter(sha512_hasher_result.iter().copied());
Scalar::from_repr(c_bytes).unwrap()
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -200,7 +191,7 @@ mod tests {
fn test_byte_array_to_scalar() {
let scalar = byte_array_to_scalar(&hex!(
"c6a7fc2c926ddbaf20731a479fb6566f2daa5514baae5223fe3b32edbce83254"
)); // TODO this `fn` looks suspicious as in reproducing const time ops
));
assert_eq!(
hex::encode(scalar.to_bytes()),
"c6a7fc2c926ddbaf20731a479fb6566f2daa5514baae5223fe3b32edbce83254"
Expand Down
Loading
Loading