Skip to content

Commit

Permalink
Merge pull request #117 from plume-sig/arkworks04
Browse files Browse the repository at this point in the history
Bump to the recent `arkworks` (`v0.5`)
skaunov authored Dec 15, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents c835718 + 1f52416 commit 5a5c11f
Showing 17 changed files with 1,424 additions and 332 deletions.
7 changes: 0 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -2,10 +2,3 @@
resolver = "2"

members = ["rust-arkworks", "rust-k256", "javascript"]

[patch.crates-io]
ark-ec = { git = "https://github.com/FindoraNetwork/ark-algebra" }
ark-ff = { git = "https://github.com/FindoraNetwork/ark-algebra" }
ark-serialize = { git = "https://github.com/FindoraNetwork/ark-algebra" }
ark-algebra-test-templates = { git = "https://github.com/FindoraNetwork/ark-algebra" }
ark-std = { git = "https://github.com/FindoraNetwork/ark-std" }
27 changes: 15 additions & 12 deletions rust-arkworks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -11,24 +11,27 @@ keywords = ["nullifier", "zero-knowledge", "ECDSA", "PLUME"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ark-ec = "~0.3.0"
ark-ff = "~0.3.0"
ark-std = "~0.3.0"
ark-serialize = "~0.3.0"
ark-serialize-derive = "~0.3.0"
secp256k1 = { git = "https://github.com/geometryresearch/ark-secp256k1.git", version = "0.1.0" }
ark-ec = "~0.5.0"
ark-ff = "~0.5.0"
ark-std = "~0.5.0"
ark-serialize = "~0.5.0"
ark-serialize-derive = "~0.5.0"
rand_core = { version = "0.6", default-features = false, features = [
"getrandom",
] }
rand = "0.8.4"
tiny-keccak = { version = "2.0.2", features = ["shake"] }
sha2 = "0.10.2"
elliptic-curve = { version = "0.12.2", features = ["arithmetic"] }
k256 = { version = "0.11.3", features = [
"arithmetic",
"hash2curve",
"expose-field",
"sha2",
] }
generic-array = { version = "0.14", default-features = false }
hex = "0.4.3"

# #standinDependencies
## the curve internalization
ark-ff-macros = "~0.5.0"
## field hasher fix
arrayvec = { version = "0.7", default-features = false }

[dev-dependencies]
num-bigint = "*"
num-traits = "*"
4 changes: 1 addition & 3 deletions rust-arkworks/README.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
https://github.com/plume-sig/zk-nullifier-sig/blob/main/README.md
# HAZMAT
Please note that until `v0.1.0` this is very much a preview crate which lets you have some preliminary feel of the structure and the reference implementation approach.
https://github.com/plume-sig/zk-nullifier-sig/blob/main/README.md
31 changes: 0 additions & 31 deletions rust-arkworks/src/error.rs

This file was deleted.

135 changes: 135 additions & 0 deletions rust-arkworks/src/fixed_hasher/expander.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// The below implementation is a rework of https://github.com/armfazh/h2c-rust-ref
// With some optimisations

use core::marker::PhantomData;

use ark_std::vec::*;

use arrayvec::ArrayVec;
use sha2::digest::{ExtendableOutput, FixedOutputReset, Update};

pub trait Expander {
fn expand(&self, msg: &[u8], length: usize) -> Vec<u8>;
}
const MAX_DST_LENGTH: usize = 255;

const LONG_DST_PREFIX: &[u8; 17] = b"H2C-OVERSIZE-DST-";

/// Implements section [5.3.3](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3)
/// "Using DSTs longer than 255 bytes" of the
/// [IRTF CFRG hash-to-curve draft #16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3).
pub struct DST(arrayvec::ArrayVec<u8, MAX_DST_LENGTH>);

impl DST {
pub fn new_xmd<H: FixedOutputReset + Default>(dst: &[u8]) -> DST {
let array = if dst.len() > MAX_DST_LENGTH {
let mut long = H::default();
long.update(&LONG_DST_PREFIX[..]);
long.update(&dst);
ArrayVec::try_from(long.finalize_fixed().as_ref()).unwrap()
} else {
ArrayVec::try_from(dst).unwrap()
};
DST(array)
}

#[allow(dead_code)]
pub fn new_xof<H: ExtendableOutput + Default>(dst: &[u8], k: usize) -> DST {
let array = if dst.len() > MAX_DST_LENGTH {
let mut long = H::default();
long.update(&LONG_DST_PREFIX[..]);
long.update(&dst);

let mut new_dst = [0u8; MAX_DST_LENGTH];
let new_dst = &mut new_dst[0..((2 * k + 7) >> 3)];
long.finalize_xof_into(new_dst);
ArrayVec::try_from(&*new_dst).unwrap()
} else {
ArrayVec::try_from(dst).unwrap()
};
DST(array)
}

pub fn update<H: Update>(&self, h: &mut H) {
h.update(self.0.as_ref());
// I2OSP(len,1) https://www.rfc-editor.org/rfc/rfc8017.txt
h.update(&[self.0.len() as u8]);
}
}

#[allow(dead_code)]
pub(super) struct ExpanderXof<H: ExtendableOutput + Clone + Default> {
pub(super) xofer: PhantomData<H>,
pub(super) dst: Vec<u8>,
pub(super) k: usize,
}

impl<H: ExtendableOutput + Clone + Default> Expander for ExpanderXof<H> {
fn expand(&self, msg: &[u8], n: usize) -> Vec<u8> {
let mut xofer = H::default();
xofer.update(msg);

// I2OSP(len,2) https://www.rfc-editor.org/rfc/rfc8017.txt
let lib_str = (n as u16).to_be_bytes();
xofer.update(&lib_str);

DST::new_xof::<H>(self.dst.as_ref(), self.k).update(&mut xofer);
xofer.finalize_boxed(n).into_vec()
}
}

pub(super) struct ExpanderXmd<H: FixedOutputReset + Default + Clone> {
pub(super) hasher: PhantomData<H>,
pub(super) dst: Vec<u8>,
pub(super) block_size: usize,
}

static Z_PAD: [u8; 256] = [0u8; 256];

impl<H: FixedOutputReset + Default + Clone> Expander for ExpanderXmd<H> {
fn expand(&self, msg: &[u8], n: usize) -> Vec<u8> {
use sha2::digest::typenum::Unsigned;
// output size of the hash function, e.g. 32 bytes = 256 bits for sha2::Sha256
let b_len = H::OutputSize::to_usize();
let ell = (n + (b_len - 1)) / b_len;
assert!(
ell <= 255,
"The ratio of desired output to the output size of hash function is too large!"
);

let dst_prime = DST::new_xmd::<H>(self.dst.as_ref());
// Represent `len_in_bytes` as a 2-byte array.
// As per I2OSP method outlined in https://tools.ietf.org/pdf/rfc8017.pdf,
// The program should abort if integer that we're trying to convert is too large.
assert!(n < (1 << 16), "Length should be smaller than 2^16");
let lib_str: [u8; 2] = (n as u16).to_be_bytes();

let mut hasher = H::default();
hasher.update(&Z_PAD[0..self.block_size]);
hasher.update(msg);
hasher.update(&lib_str);
hasher.update(&[0u8]);
dst_prime.update(&mut hasher);
let b0 = hasher.finalize_fixed_reset();

hasher.update(&b0);
hasher.update(&[1u8]);
dst_prime.update(&mut hasher);
let mut bi = hasher.finalize_fixed_reset();

let mut uniform_bytes: Vec<u8> = Vec::with_capacity(n);
uniform_bytes.extend_from_slice(&bi);
for i in 2..=ell {
// update the hasher with xor of b_0 and b_i elements
for (l, r) in b0.iter().zip(bi.iter()) {
hasher.update(&[*l ^ *r]);
}
hasher.update(&[i as u8]);
dst_prime.update(&mut hasher);
bi = hasher.finalize_fixed_reset();
uniform_bytes.extend_from_slice(&bi);
}
uniform_bytes.truncate(n);
uniform_bytes
}
}
62 changes: 62 additions & 0 deletions rust-arkworks/src/fixed_hasher/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
mod expander;
use ark_ff::{field_hashers::HashToField, Field, PrimeField};
use core::marker::PhantomData;
use expander::{Expander, ExpanderXmd};
use sha2::digest::{core_api::BlockSizeUser, FixedOutputReset};

pub struct FixedFieldHasher<H: FixedOutputReset + Default + Clone, const SEC_PARAM: usize = 128> {
expander: ExpanderXmd<H>,
len_per_base_elem: usize,
}

impl<F: Field, H: FixedOutputReset + BlockSizeUser + Default + Clone, const SEC_PARAM: usize>
HashToField<F> for FixedFieldHasher<H, SEC_PARAM>
{
fn new(dst: &[u8]) -> Self {
// The final output of `hash_to_field` will be an array of field
// elements from F::BaseField, each of size `len_per_elem`.
let len_per_base_elem = get_len_per_elem::<F, SEC_PARAM>();

let expander = ExpanderXmd {
hasher: PhantomData,
dst: dst.to_vec(),
block_size: H::block_size(),
};

FixedFieldHasher {
expander,
len_per_base_elem,
}
}

fn hash_to_field<const N: usize>(&self, message: &[u8]) -> [F; N] {
let m = F::extension_degree() as usize;

// The user requests `N` of elements of F_p^m to output per input msg,
// each field element comprising `m` BasePrimeField elements.
let len_in_bytes = N * m * self.len_per_base_elem;
let uniform_bytes = self.expander.expand(message, len_in_bytes);

let cb = |i| {
let base_prime_field_elem = |j| {
let elm_offset = self.len_per_base_elem * (j + i * m);
F::BasePrimeField::from_be_bytes_mod_order(
&uniform_bytes[elm_offset..][..self.len_per_base_elem],
)
};
F::from_base_prime_field_elems((0..m).map(base_prime_field_elem)).unwrap()
};
ark_std::array::from_fn::<F, N, _>(cb)
}
}

const fn get_len_per_elem<F: Field, const SEC_PARAM: usize>() -> usize {
// ceil(log(p))
let base_field_size_in_bits = F::BasePrimeField::MODULUS_BIT_SIZE as usize;
// ceil(log(p)) + security_parameter
let base_field_size_with_security_padding_in_bits = base_field_size_in_bits + SEC_PARAM;
// ceil( (ceil(log(p)) + security_parameter) / 8)
let bytes_per_base_field_elem =
((base_field_size_with_security_padding_in_bits + 7) / 8) as u64;
bytes_per_base_field_elem as usize
}
87 changes: 0 additions & 87 deletions rust-arkworks/src/hash_to_curve.rs

This file was deleted.

260 changes: 154 additions & 106 deletions rust-arkworks/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,74 @@
/// This crate provides the PLUME signature scheme.
//! This crate provides the PLUME signature scheme.
//!
//! See <https://blog.aayushg.com/nullifier> for more information.
//!
//! Find the crate to use with RustCrypto as `plume_rustcrypto`.
//!
//! # Examples
//! ```rust
//! use plume_arkworks::{PlumeSignature, PlumeVersion, SWCurveConfig};
//! use rand_core::OsRng;
//!
//! # fn main() {
//! let message_the = b"ZK nullifier signature";
//! let sk = PlumeSignature::keygen(&mut OsRng);
//!
//! let sig = PlumeSignature::sign(
//! &mut OsRng, (&sk.0, &sk.1), message_the.as_slice(), PlumeVersion::V1
//! );
//!
//! assert!(
//! sig.unwrap()
//! .verify_non_zk(
//! &plume_arkworks::Parameters{
//! g_point: plume_arkworks::secp256k1::Config::GENERATOR
//! },
//! &sk.0,
//! message_the,
//! PlumeVersion::V1
//! ).unwrap()
//! );
//! # }
//! ```
/// Stand-in solution until [the default hasher issue](https://github.com/arkworks-rs/algebra/issues/849) is fixed.
pub mod fixed_hasher; // #standinDependencies
/// Stand-in solution until [the curve hashing support](https://github.com/arkworks-rs/algebra/pull/863) is merged.
pub mod secp256k1; // #standinDependencies

/* /// Re-exports `SWCurveConfig` type from `models::short_weierstrass` of the `ark_ec` crate,
/// and `hashing` items.
///
/// See <https://blog.aayushg.com/nullifier> for more information.
///
/// Find RustCrypto crate as `plume_rustcrypto`.
pub use crate::error::HashToCurveError;
use crate::hash_to_curve::hash_to_curve;

/// Re-exports the `GroupAffine` and `SWModelParameters` types from the `ark_ec` crate.
/// `HashToCurve` is the trait needed for hashing to the curve.
/// `HashToCurveError` is the error type to process a `Result`.
///
/// `GroupAffine` represents an affine point on a short Weierstrass elliptic curve.
/// `SWModelParameters` contains the parameters defining a short Weierstrass curve.
pub use ark_ec::{models::SWModelParameters, short_weierstrass_jacobian::GroupAffine};
/// Re-exports the `Rng` trait from the `rand` crate in `ark_std`.
/// `SWCurveConfig` contains the parameters defining a short Weierstrass curve. */
pub use ark_ec::{
hashing::{HashToCurve, HashToCurveError},
models::short_weierstrass::SWCurveConfig,
};
use ark_ec::{short_weierstrass, AffineRepr, CurveGroup};
/// Re-exports the `Rng` trait from `rand` crate in `ark_std`.
///
/// `Rng` provides methods for generating random values.
pub use ark_std::rand::Rng;

use ark_ec::{AffineCurve, ProjectiveCurve};
use ark_ff::PrimeField;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
use ark_std::UniformRand;
use secp256k1::sec1::Sec1EncodePoint;
use sha2::digest::Output;
use sha2::{Digest, Sha256};
use ark_ff::{BigInteger, PrimeField};

mod error;
mod hash_to_curve;
/// Re-exports the `CanonicalDeserialize` and `CanonicalSerialize` traits from `ark_serialize` crate.
///
/// These traits provide methods for serializing and deserializing data in a canonical format.
pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::UniformRand;
use fixed_hasher::FixedFieldHasher;
use sha2::{digest::Output, Digest, Sha256};
use std::ops::Mul;

const EXPECT_MSG_DECODE: &str = "the value decoded have been generated by a function which is improbable to output a malformed hexstring (still a place for refactoring)";
/// Re-exports the `Affine` and `Fr` types from `secp256k1` module.
///
/// `Affine` represents an affine point on `secp256k1` elliptic curve.
/// `Fr` represents an element of the scalar field of the secp256k1 elliptic curve.
pub use secp256k1::{Affine, Fr};

/// An `enum` representing the variant of the PLUME protocol.
pub enum PlumeVersion {
@@ -38,54 +78,72 @@ pub enum PlumeVersion {

/// Converts an affine point on the curve to the byte representation.
///
/// Serializes the affine point to its SEC1 encoding and returns the raw bytes.
pub fn affine_to_bytes<P: SWModelParameters>(point: &GroupAffine<P>) -> Vec<u8> {
hex::decode(point.to_encoded_point(true))
.expect(EXPECT_MSG_DECODE)
.to_vec()
/// Serializes the affine point to its SEC1 compressed encoding and returns the raw bytes.
///
/// Note that the identity element is coded with the `Vec` of single zero byte.
pub fn affine_to_bytes(point: &Affine) -> Vec<u8> {
if point.is_zero() {
return [0].into();
}
let mut compressed_bytes = Vec::new();
point.serialize_compressed(&mut compressed_bytes).expect("it's actually infallible here because the `BaseField` is a proper `Fp` (no flags on serialization)");
compressed_bytes.reverse();
compressed_bytes[0] = if point
.y()
.expect("zero check have been done above")
.into_bigint()
.is_odd()
{
3
} else {
2
};
compressed_bytes
}

fn compute_h<'a, C: ProjectiveCurve, Fq: PrimeField, P: SWModelParameters>(
pk: &GroupAffine<P>,
message: &'a [u8],
) -> Result<GroupAffine<P>, HashToCurveError> {
//let pk_affine_bytes_vec = affine_to_bytes::<P>(pk);
//let m_pk = [message, pk_affine_bytes_vec.as_slice()].concat();
//hash_to_curve::try_and_increment::<C>(m_pk.as_slice())
hash_to_curve::<Fq, P>(message, pk)
fn hash_to_curve(message: &[u8], pk: &Affine) -> Result<Affine, HashToCurveError> {
ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::<
ark_ec::short_weierstrass::Projective<secp256k1::Config>,
FixedFieldHasher<Sha256>,
ark_ec::hashing::curve_maps::wb::WBMap<secp256k1::Config>,
>::new(b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_")?
.hash(
[message, affine_to_bytes(pk).as_slice()]
.concat()
.as_slice(),
)
}

fn compute_c_v1<P: SWModelParameters>(
g_point: &GroupAffine<P>,
pk: &GroupAffine<P>,
hashed_to_curve: &GroupAffine<P>,
nullifier: &GroupAffine<P>,
r_point: &GroupAffine<P>,
hashed_to_curve_r: &GroupAffine<P>,
fn compute_c_v1(
pk: &secp256k1::Affine,
hashed_to_curve: &secp256k1::Affine,
nullifier: &secp256k1::Affine,
r_point: &secp256k1::Affine,
hashed_to_curve_r: &secp256k1::Affine,
) -> Output<Sha256> {
// Compute c = sha512([g, pk, h, nul, g^r, z])
// Compute c = sha256([g, pk, h, nul, g^r, z])
let c_preimage_vec = [
affine_to_bytes::<P>(g_point),
affine_to_bytes::<P>(pk),
affine_to_bytes::<P>(hashed_to_curve),
affine_to_bytes::<P>(nullifier),
affine_to_bytes::<P>(r_point),
affine_to_bytes::<P>(hashed_to_curve_r),
affine_to_bytes(&secp256k1::Config::GENERATOR),
affine_to_bytes(pk),
affine_to_bytes(hashed_to_curve),
affine_to_bytes(nullifier),
affine_to_bytes(r_point),
affine_to_bytes(hashed_to_curve_r),
]
.concat();

Sha256::digest(c_preimage_vec.as_slice())
}

fn compute_c_v2<P: SWModelParameters>(
nullifier: &GroupAffine<P>,
r_point: &GroupAffine<P>,
hashed_to_curve_r: &GroupAffine<P>,
fn compute_c_v2(
nullifier: &secp256k1::Affine,
r_point: &secp256k1::Affine,
hashed_to_curve_r: &secp256k1::Affine,
) -> Output<Sha256> {
// Compute c = sha512([nul, g^r, z])
let nul_bytes = affine_to_bytes::<P>(nullifier);
let g_r_bytes = affine_to_bytes::<P>(r_point);
let z_bytes = affine_to_bytes::<P>(hashed_to_curve_r);
// Compute c = sha256([nul, g^r, z])
let nul_bytes = affine_to_bytes(nullifier);
let g_r_bytes = affine_to_bytes(r_point);
let z_bytes = affine_to_bytes(hashed_to_curve_r);

let c_preimage_vec = [nul_bytes, g_r_bytes, z_bytes].concat();

@@ -100,9 +158,9 @@ fn compute_c_v2<P: SWModelParameters>(
ark_serialize_derive::CanonicalSerialize,
ark_serialize_derive::CanonicalDeserialize,
)]
pub struct Parameters<P: SWModelParameters> {
pub struct Parameters<P: SWCurveConfig> {
/// The generator point for the SW model parameters.
pub g_point: GroupAffine<P>,
pub g_point: short_weierstrass::Affine<P>,
}

/// A struct containing the PLUME signature data
@@ -112,15 +170,15 @@ pub struct Parameters<P: SWModelParameters> {
ark_serialize_derive::CanonicalSerialize,
ark_serialize_derive::CanonicalDeserialize,
)]
pub struct PlumeSignature<P: SWModelParameters> {
pub struct PlumeSignature {
/// The hash-to-curve output multiplied by the random `r`.
pub hashed_to_curve_r: GroupAffine<P>,
pub hashed_to_curve_r: Affine,
/// The randomness `r` represented as the curve point.
pub r_point: GroupAffine<P>,
pub s: P::ScalarField,
pub c: P::ScalarField,
pub r_point: Affine,
pub s: Fr,
pub c: Fr,
/// The nullifier.
pub nullifier: GroupAffine<P>,
pub nullifier: Affine,
}

// These aliases should be gone in #88 . If they won't TODO pay attention to the warning about `trait` boundaries being not checked for aliases
@@ -129,45 +187,39 @@ pub struct PlumeSignature<P: SWModelParameters> {
/// A type alias for a byte slice reference, used for representing the message.
pub type Message<'a> = &'a [u8];
/// The public key.
pub type PublicKey<P: SWModelParameters> = GroupAffine<P>;
pub type PublicKey = Affine;
/// The scalar field element representing the secret key.
pub type SecretKeyMaterial<P: SWModelParameters> = P::ScalarField;
pub type SecretKeyMaterial = Fr;

impl<P: SWModelParameters> PlumeSignature<P> {
impl PlumeSignature {
/// Generate the public key and a private key.
/// # HAZMAT
/// No measures yet taken for the [`SecretKeyMaterial`] protection
pub fn keygen(pp: &Parameters<P>, rng: &mut impl Rng) -> (PublicKey<P>, SecretKeyMaterial<P>) {
let secret_key = SecretKeyMaterial::<P>::rand(rng);
let public_key = pp.g_point.mul(secret_key).into();
(public_key, secret_key)
pub fn keygen(rng: &mut impl Rng) -> (PublicKey, SecretKeyMaterial) {
let secret_key = SecretKeyMaterial::rand(rng);
let public_key = secp256k1::Config::GENERATOR * secret_key;
(public_key.into_affine(), secret_key)
}

/// Sign a message using the specified `r` value
fn sign_with_r(
pp: &Parameters<P>,
keypair: (&PublicKey<P>, &SecretKeyMaterial<P>),
pub fn sign_with_r(
keypair: (&PublicKey, &SecretKeyMaterial),
message: Message,
r_scalar: P::ScalarField,
r_scalar: Fr,
version: PlumeVersion,
) -> Result<Self, HashToCurveError> {
let g_point = pp.g_point;
let r_point = g_point.mul(r_scalar).into_affine();
let r_point = secp256k1::Config::GENERATOR.mul(r_scalar).into_affine();

// Compute h = htc([m, pk])
let hashed_to_curve =
compute_h::<secp256k1::Projective, secp256k1::fields::Fq, P>(&keypair.0, &message)?;
let hashed_to_curve: secp256k1::Affine = hash_to_curve(message, keypair.0)?;

// Compute z = h^r
let hashed_to_curve_r = hashed_to_curve.mul(r_scalar).into_affine();

// Compute nul = h^sk
let nullifier = hashed_to_curve.mul(*keypair.1).into_affine();

// Compute c = sha512([g, pk, h, nul, g^r, z])
// Compute c = sha256([g, pk, h, nul, g^r, z])
let c = match version {
PlumeVersion::V1 => compute_c_v1::<P>(
&g_point,
PlumeVersion::V1 => compute_c_v1(
keypair.0,
&hashed_to_curve,
&nullifier,
@@ -176,12 +228,12 @@ impl<P: SWModelParameters> PlumeSignature<P> {
),
PlumeVersion::V2 => compute_c_v2(&nullifier, &r_point, &hashed_to_curve_r),
};
let c_scalar = P::ScalarField::from_be_bytes_mod_order(c.as_ref());
let c_scalar = secp256k1::Fr::from_be_bytes_mod_order(c.as_ref());
// Compute s = r + sk ⋅ c
let sk_c = keypair.1.into_repr().into() * c_scalar.into_repr().into();
let s = r_scalar.into_repr().into() + sk_c;
let sk_c = keypair.1 * &c_scalar;
let s = r_scalar + sk_c;

let s_scalar = P::ScalarField::from(s);
let s_scalar = secp256k1::Fr::from(s);

let signature = PlumeSignature {
hashed_to_curve_r,
@@ -194,17 +246,16 @@ impl<P: SWModelParameters> PlumeSignature<P> {
}

/// Sign a message.
fn sign(
pp: &Parameters<P>,
pub fn sign(
rng: &mut impl Rng,
keypair: (&PublicKey<P>, &SecretKeyMaterial<P>),
keypair: (&PublicKey, &SecretKeyMaterial),
message: Message,
version: PlumeVersion,
) -> Result<Self, HashToCurveError> {
// Pick a random r from Fp
let r_scalar = P::ScalarField::rand(rng);
let r_scalar = secp256k1::Fr::rand(rng);

Self::sign_with_r(pp, keypair, message, r_scalar, version)
Self::sign_with_r(keypair, message, r_scalar, version)
}

/// Verifies a PLUME signature.
@@ -217,23 +268,20 @@ impl<P: SWModelParameters> PlumeSignature<P> {
/// - Confirm c = c'
///
/// Rejects if any check fails.
fn verify_non_zk(
pub fn verify_non_zk(
self,
pp: &Parameters<P>,
pk: &PublicKey<P>,
pp: &Parameters<secp256k1::Config>,
pk: &PublicKey,
message: Message,
version: PlumeVersion,
) -> Result<bool, HashToCurveError> {
// Compute h = htc([m, pk])
let hashed_to_curve =
compute_h::<secp256k1::Projective, secp256k1::fields::Fq, P>(pk, message)?;
let hashed_to_curve = hash_to_curve(message, pk)?;

// TODO [replace SHA-512](https://github.com/plume-sig/zk-nullifier-sig/issues/39#issuecomment-1732497672)
// Compute c' = sha512([g, pk, h, nul, g^r, z]) for v1
// c' = sha512([nul, g^r, z]) for v2
// Compute c' = sha256([g, pk, h, nul, g^r, z]) for v1
// c' = sha256([nul, g^r, z]) for v2
let c = match version {
PlumeVersion::V1 => compute_c_v1::<P>(
&pp.g_point,
PlumeVersion::V1 => compute_c_v1(
pk,
&hashed_to_curve,
&self.nullifier,
@@ -244,7 +292,7 @@ impl<P: SWModelParameters> PlumeSignature<P> {
compute_c_v2(&self.nullifier, &self.r_point, &self.hashed_to_curve_r)
}
};
let c_scalar = P::ScalarField::from_be_bytes_mod_order(c.as_ref());
let c_scalar = secp256k1::Fr::from_be_bytes_mod_order(c.as_ref());

// Reject if g^s ⋅ pk^{-c} != g^r
let g_s = pp.g_point.mul(self.s);
113 changes: 113 additions & 0 deletions rust-arkworks/src/secp256k1/curves/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use super::{fq::Fq, fr::Fr};
use ark_ec::{
hashing::curve_maps::{
swu::SWUConfig,
wb::{IsogenyMap, WBConfig},
},
models::CurveConfig,
short_weierstrass::{self as sw, SWCurveConfig},
};
use ark_ff::{AdditiveGroup, Field, MontFp, Zero};

#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct Secp256k1Parameters;

pub type Affine = sw::Affine<Config>;
// pub type Projective = sw::Projective<Config>;

// pub type BaseField = Fq;
// pub type ScalarField = Fr;

#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct Config;
impl CurveConfig for Config {
type BaseField = Fq;
type ScalarField = Fr;

/// COFACTOR = 1
const COFACTOR: &'static [u64] = &[0x1];

/// COFACTOR_INV = COFACTOR^{-1} mod r = 1
#[rustfmt::skip]
const COFACTOR_INV: Fr = Fr::ONE;
}
impl SWCurveConfig for Config {
/// COEFF_A = 0
const COEFF_A: Fq = Fq::ZERO;

/// COEFF_B = 7
const COEFF_B: Fq = MontFp!("7");

/// GENERATOR = (G_GENERATOR_X, G_GENERATOR_Y)
const GENERATOR: Affine = Affine::new_unchecked(G_GENERATOR_X, G_GENERATOR_Y);

#[inline(always)]
fn mul_by_a(_: Self::BaseField) -> Self::BaseField {
Self::BaseField::zero()
}
}

/// G_GENERATOR_X =
/// 55066263022277343669578718895168534326250603453777594175500187360389116729240
pub const G_GENERATOR_X: Fq =
MontFp!("55066263022277343669578718895168534326250603453777594175500187360389116729240");

/// G_GENERATOR_Y =
/// 32670510020758816978083085130507043184471273380659243275938904335757337482424
pub const G_GENERATOR_Y: Fq =
MontFp!("32670510020758816978083085130507043184471273380659243275938904335757337482424");

/// `secp256k1_XMD:SHA-256_SSWU_RO_` isogenous curve
pub struct ConfigIsogenous {}
impl CurveConfig for ConfigIsogenous {
type BaseField = <Config as CurveConfig>::BaseField;
type ScalarField = <Config as CurveConfig>::ScalarField;

const COFACTOR: &'static [u64] = Config::COFACTOR;
const COFACTOR_INV: Self::ScalarField = Config::COFACTOR_INV;
}
type TheIsoCurveAffine = sw::Affine<ConfigIsogenous>;
impl SWCurveConfig for ConfigIsogenous {
const COEFF_A: Self::BaseField =
MontFp!("0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533");
const COEFF_B: Self::BaseField = MontFp!("1771");
const GENERATOR: TheIsoCurveAffine = TheIsoCurveAffine::new_unchecked(
MontFp!("75295888890003590383366995344834012177557063699577440394299653383124903397514"),
MontFp!("82553647407850972504999846303729620951309077682374043495922869307182479212755"),
);
}
impl SWUConfig for ConfigIsogenous {
const ZETA: Self::BaseField = MontFp!("-11");
}

// Parameters from the [IETF draft v16, section E.3](https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-suites-for-secp256k1).
impl WBConfig for Config {
type IsogenousCurve = ConfigIsogenous;

const ISOGENY_MAP: IsogenyMap<'static, Self::IsogenousCurve, Self> = IsogenyMap {
x_map_numerator: &[
MontFp!("0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7"),
MontFp!("0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581"),
MontFp!("0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262"),
MontFp!("0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c"),
],
x_map_denominator: &[
MontFp!("0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b"),
MontFp!("0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14"),
MontFp!("1"),
MontFp!("0"),
],
y_map_numerator: &[
MontFp!("0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c"),
MontFp!("0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3"),
MontFp!("0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931"),
MontFp!("0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84"),
],
y_map_denominator: &[
MontFp!("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b"),
MontFp!("0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573"),
MontFp!("0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f"),
MontFp!("1"),
],
};
}
87 changes: 87 additions & 0 deletions rust-arkworks/src/secp256k1/fields/fq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use ark_ff::{
// biginteger::BigInteger320 as BigInteger,
fields::{* /* MontConfig */},
};
use ark_ff_macros::MontConfig;
/*
~~Supported BigInteger sizes:~~
~~256, 320, 384, 448, 768, 832~~
*/

#[derive(MontConfig)]
#[modulus = "115792089237316195423570985008687907853269984665640564039457584007908834671663"]
#[generator = "3"]
#[small_subgroup_base = "3"]
#[small_subgroup_power = "1"]
pub struct FqConfig;
pub type Fq = Fp256<MontBackend<FqConfig, 4>>;

// pub struct FqParameters;

/* impl Fp320Parameters for FqParameters {}
impl FftParameters for FqParameters {
type BigInt = BigInteger;
const TWO_ADICITY: u32 = 1;
const TWO_ADIC_ROOT_OF_UNITY: Self::BigInt = BigInteger::new([
0xfffffffefffffc2f, 0xfffffffefffffc2e, 0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000000,
]);
}
impl FpParameters for FqParameters {
#[rustfmt::skip]
const MODULUS: BigInteger = BigInteger::new([
0xfffffffefffffc2f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000000,
]);
const MODULUS_BITS: u32 = 256;
const CAPACITY: u32 = Self::MODULUS_BITS - 1;
/// The number of bits that must be shaved from the beginning of
/// the representation when randomly sampling.
const REPR_SHAVE_BITS: u32 = 64;
/// Let `M` be the power of 2^64 nearest to `Self::MODULUS_BITS`. Then
/// `R = M % Self::MODULUS`.
/// R = M % MODULUS
#[rustfmt::skip]
const R: BigInteger = BigInteger::new([
0x0000000000000000, 0x00000001000003d1, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
]);
/// R2 = R * R % MODULUS
#[rustfmt::skip]
const R2: BigInteger = BigInteger::new([
0x0000000000000000, 0x0000000000000000, 0x000007a2000e90a1, 0x0000000000000001, 0x0000000000000000,
]);
/// INV = -MODULUS^{-1} mod 2^64
const INV: u64 = 15580212934572586289;
/// A multiplicative generator of the field, in Montgomery form (g * R % modulus).
/// `Self::GENERATOR` is an element having multiplicative order
/// `Self::MODULUS - 1`. In other words, the generator is the lowest value such that
/// MultiplicativeOrder(generator, p) = p - 1 where p is the modulus.
#[rustfmt::skip]
const GENERATOR: BigInteger = BigInteger::new([
0x0000000000000000, 0x0000000300000b73, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
]);
#[rustfmt::skip]
const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger::new([
0xffffffff7ffffe17, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff, 0x0000000000000000,
]);
#[rustfmt::skip]
const T: BigInteger = BigInteger::new([
0xffffffff7ffffe17, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff, 0x0000000000000000,
]);
#[rustfmt::skip]
const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger::new([
0xffffffffbfffff0b, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffffff, 0x0000000000000000,
]);
} */
94 changes: 94 additions & 0 deletions rust-arkworks/src/secp256k1/fields/fr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use ark_ff::{
// biginteger::BigInteger320 as BigInteger,
fields::{
// FftParameters,
// Fp320,
// Fp320Parameters,
// FpParameters,
Fp256,
MontBackend,
MontConfig,
},
};
/*
~~Supported BigInteger sizes:~~
~~256, 320, 384, 448, 768, 832~~
*/

#[derive(MontConfig)]
#[modulus = "115792089237316195423570985008687907852837564279074904382605163141518161494337"]
#[generator = "7"]
#[small_subgroup_base = "3"]
#[small_subgroup_power = "1"]
pub struct FrConfig;
pub type Fr = Fp256<MontBackend<FrConfig, 4>>;

// pub struct FrParameters;

// impl Fp320Parameters for FrParameters {}

// impl FftParameters for FrParameters {
// type BigInt = BigInteger;

// const TWO_ADICITY: u32 = 6;

// const TWO_ADIC_ROOT_OF_UNITY: Self::BigInt = BigInteger::new([
// 0x0112cb0f605a214a, 0x92225daffb794500, 0x7e42003a6ccb6212, 0x55980b07bc222114, 0x0000000000000000,
// ]);
// }

// impl FpParameters for FrParameters {
// #[rustfmt::skip]
// const MODULUS: BigInteger = BigInteger::new([
// 0xbfd25e8cd0364141, 0xbaaedce6af48a03b, 0xfffffffffffffffe, 0xffffffffffffffff, 0x0000000000000000,
// ]);

// const MODULUS_BITS: u32 = 256;

// const CAPACITY: u32 = Self::MODULUS_BITS - 1;

// /// The number of bits that must be shaved from the beginning of
// /// the representation when randomly sampling.
// const REPR_SHAVE_BITS: u32 = 64;

// /// Let `M` be the power of 2^64 nearest to `Self::MODULUS_BITS`. Then
// /// `R = M % Self::MODULUS`.
// /// R = M % MODULUS
// #[rustfmt::skip]
// const R: BigInteger = BigInteger::new([
// 0x0000000000000000, 0x402da1732fc9bebf, 0x4551231950b75fc4, 0x0000000000000001, 0x0000000000000000,
// ]);

// /// R2 = R * R % MODULUS
// #[rustfmt::skip]
// const R2: BigInteger = BigInteger::new([
// 0x1e004f504dfd7f79, 0x08fcf59774a052ea, 0x27c4120fc94e1653, 0x3c1a6191e5702644, 0x0000000000000000,
// ]);

// /// INV = -MODULUS^{-1} mod 2^64
// const INV: u64 = 5408259542528602431;

// /// A multiplicative generator of the field, in Montgomery form (g * R % modulus).
// /// `Self::GENERATOR` is an element having multiplicative order
// /// `Self::MODULUS - 1`. In other words, the generator is the lowest value such that
// /// MultiplicativeOrder(generator, p) = p - 1 where p is the modulus.
// #[rustfmt::skip]
// const GENERATOR: BigInteger = BigInteger::new([
// 0x0000000000000000, 0xc13f6a264e843739, 0xe537f5b135039e5d, 0x0000000000000008, 0x0000000000000000,
// ]);

// #[rustfmt::skip]
// const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger::new([
// 0xdfe92f46681b20a0, 0x5d576e7357a4501d, 0xffffffffffffffff, 0x7fffffffffffffff, 0x0000000000000000,
// ]);

// #[rustfmt::skip]
// const T: BigInteger = BigInteger::new([
// 0xeeff497a3340d905, 0xfaeabb739abd2280, 0xffffffffffffffff, 0x03ffffffffffffff, 0x0000000000000000,
// ]);

// #[rustfmt::skip]
// const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger::new([
// 0x777fa4bd19a06c82, 0xfd755db9cd5e9140, 0xffffffffffffffff, 0x01ffffffffffffff, 0x0000000000000000,
// ]);
// }
4 changes: 4 additions & 0 deletions rust-arkworks/src/secp256k1/fields/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod fq;
pub mod fr;
pub use self::fr::*;
// pub use self::fq::*;
21 changes: 21 additions & 0 deletions rust-arkworks/src/secp256k1/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![deny(
warnings,
unused,
// future_incompatible,
nonstandard_style,
rust_2018_idioms
)]
#![allow(rustdoc::bare_urls)]
#![forbid(unsafe_code)]

pub mod curves;
pub use curves::*;

pub mod fields;
pub use fields::*;

// pub mod sec1;
// pub use sec1::*;

// pub mod test_vectors;
mod tests;
157 changes: 157 additions & 0 deletions rust-arkworks/src/secp256k1/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#![allow(unused_imports)]
use ark_ec::hashing::HashToCurve;
use ark_ec::AffineRepr;
use ark_ff::field_hashers::HashToField;
use ark_ff::BigInt;
// use crate::test_vectors;
// use crate::sec1::Sec1EncodePoint;
// use crate::fields::{Fr, Fq};
// use crate::curves::*;
use ark_std::{io::BufReader, rand::Rng, string::String, test_rng, vec::Vec};
// use ark_algebra_test_templates::{
// fields::*,
// curves::*,
// msm::test_var_base_msm,
// groups::group_test,
// };
// use ark_ec::{AffineCurve, ProjectiveCurve};
use ark_ff::{
biginteger::BigInteger320,
vec,
BigInteger,
PrimeField,
// ToBytes,
// FromBytes,
ToConstraintField,
};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::string::ToString;
use hex::ToHex;
use sha2::Sha256;

use crate::fixed_hasher::FixedFieldHasher;
use crate::secp256k1;

// #[test]
// fn test_fr() {
// let mut rng = test_rng();
// for _ in 0..5 {
// let a: Fr = rng.gen();

// sqrt_field_test(a);
// fft_field_test::<Fr>();
// primefield_test::<Fr>();

// let b: Fr = rng.gen();
// field_test::<Fr>(a, b);
// }
// }

// #[test]
// fn test_fq() {
// let mut rng = test_rng();
// for _ in 0..5 {
// let a: Fq = rng.gen();

// sqrt_field_test(a);
// fft_field_test::<Fq>();
// primefield_test::<Fq>();
// let b: Fq = rng.gen();
// field_test::<Fq>(a, b);
// }
// }

// #[test]
// fn test_secp256k1_curve() {
// let mut rng = ark_std::test_rng();
// let a: Projective = rng.gen();
// let b: Projective = rng.gen();
// group_test(a, b);

// curve_tests::<Projective>();

// test_var_base_msm::<Affine>();

// // Fails in arkworks 0.3.0 but the next version should have a fix
// sw_tests::<Secp256k1Parameters>();

// test_var_base_msm::<Affine>();
// }

#[test]
fn test_secp256k1_generator() {
let generator = super::Affine::generator();
assert!(generator.is_on_curve());
assert!(generator.is_in_correct_subgroup_assuming_on_curve());
}

#[test]
fn test_h2c() {
/* suite = secp256k1_XMD:SHA-256_SSWU_RO_
dst = QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_
msg =
P.x = c1cae290e291aee617ebaef1be6d73861479c48b841eaba9b7b5852ddfeb1346
C1CAE290E291AEE617EBAEF1BE6D73861479C48B841EABA9B7B5852DDFEB1346
P.y = 64fa678e07ae116126f08b022a94af6de15985c996c3a91b64c406a960e51067
64FA678E07AE116126F08B022A94AF6DE15985C996C3A91B64C406A960E51067
u[0] = 6b0f9910dd2ba71c78f2ee9f04d73b5f4c5f7fc773a701abea1e57
3cab002fb3
u[1] = 1ae6c212e08fe1a5937f6202f929a2cc8ef4ee5b9782db68b0d579
9fd8f09e16
Q0.x = 74519ef88b32b425a095e4ebcc84d81b64e9e2c2675340a720bb1a
1857b99f1e
Q0.y = c174fa322ab7c192e11748beed45b508e9fdb1ce046dee9c2cd3a2
a86b410936
Q1.x = 44548adb1b399263ded3510554d28b4bead34b8cf9a37b4bd0bd2b
a4db87ae63
Q1.y = 96eb8e2faf05e368efe5957c6167001760233e6dd2487516b46ae7
25c4cce0c6 */
use std::str::FromStr;

// assert_eq!(
// ExpanderXmd::expand([]),
// hex::decode("68a985b87eb6b46952128911f2a4412bbc302a9d759667f87f7a21d803f07235")
// );

let dst = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_";

let defhasher: FixedFieldHasher<Sha256> =
<FixedFieldHasher<Sha256> as HashToField<secp256k1::fq::Fq>>::new(dst);
let u: [secp256k1::fq::Fq; 2] = defhasher.hash_to_field::<2>(&[]);
println!("{}", u[0]);
assert_eq!(
u[0],
secp256k1::fq::Fq::new(
BigInt::from_str(
"48425033926223359121679389614872723077618800904285921194876400224709273202611"
)
.unwrap()
),
);

assert_eq!(
ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::<
ark_ec::short_weierstrass::Projective<secp256k1::Config>,
FixedFieldHasher<Sha256>,
ark_ec::hashing::curve_maps::wb::WBMap<secp256k1::Config>,
>::new(dst)
.unwrap()
.hash(&[])
.unwrap(),
secp256k1::Affine::new(
secp256k1::fq::Fq::new(
BigInt::from_str(
"87654846584422849836571930156466438379984710599888121545025567473301233275718"
)
.unwrap()
),
secp256k1::fq::Fq::new(
BigInt::from_str(
"45673711333516174500892987253036094404176536844955599116957274814081860440167"
)
.unwrap()
),
)
);
}
161 changes: 76 additions & 85 deletions rust-arkworks/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,27 @@
use crate::hash_to_curve::{hash_to_curve, k256_affine_to_arkworks_secp256k1_affine};
use crate::{PlumeSignature, PlumeVersion};
use ark_ec::models::short_weierstrass_jacobian::GroupAffine;
use ark_ec::{AffineCurve, ProjectiveCurve};
use ark_ff::biginteger;
use ark_ff::bytes::{FromBytes, ToBytes};
mod test_vectors;

use crate::secp256k1::{Affine, Config};
use crate::{secp256k1, PlumeSignature, PlumeVersion};
use ark_ec::AffineRepr;
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::Parameters<Secp256k1Parameters>;
use crate::secp256k1::fq::Fq;
use ark_ec::CurveGroup;
use ark_ff::PrimeField;
use ark_ff::{BigInt, BigInteger};
use std::ops::Mul;

type Parameters = crate::Parameters<Config>;

fn test_template() -> (ThreadRng, Affine) {
let rng = thread_rng();
let g = Affine::prime_subgroup_generator();
let g = Affine::generator();

(rng, g)
}

#[test]
pub fn test_k256_affine_to_arkworks_secp256k1_affine() {
for i in 1..50 {
let i_u64 = i as u64;
let k256_scalar = Scalar::from(i_u64);
let ark_scalar = Fq::from(i_u64);

// Compute g^i_u64
let k256_pt = ProjectivePoint::GENERATOR.to_affine() * k256_scalar;
let ark_pt = Affine::prime_subgroup_generator().mul(ark_scalar);

// Convert k256_pt to an arkworks point
let converted_pt =
k256_affine_to_arkworks_secp256k1_affine::<Secp256k1Parameters>(k256_pt.to_affine());

// The points should match
assert_eq!(ark_pt.into_affine(), converted_pt.unwrap());
}
}

fn hex_to_fr(hex: &str) -> secp256k1::fields::Fr {
fn hex_to_fr(hex: &str) -> crate::secp256k1::Fr {
let num_field_bits = 320;
let mut sk_bytes_vec = vec![0u8; num_field_bits];
let mut sk_bytes = hex::decode(hex).unwrap();
@@ -51,12 +32,11 @@ fn hex_to_fr(hex: &str) -> secp256k1::fields::Fr {
let _ = std::mem::replace(&mut sk_bytes_vec[i], sk_bytes[i]);
}

secp256k1::fields::Fr::read(sk_bytes_vec.as_slice()).unwrap()
crate::secp256k1::Fr::from_le_bytes_mod_order(sk_bytes_vec.as_slice())
}

fn coord_to_hex(coord: biginteger::BigInteger320) -> String {
let mut coord_bytes = vec![];
let _ = coord.write(&mut coord_bytes);
fn coord_to_hex(coord: Fq) -> String {
let mut coord_bytes = coord.into_bigint().to_bytes_le();
coord_bytes.reverse();

String::from(hex::encode(coord_bytes))
@@ -79,7 +59,7 @@ pub fn test_keygen() {
let (mut rng, g) = test_template();
let pp = Parameters { g_point: g };

let (pk, sk) = PlumeSignature::keygen(&pp, &mut rng);
let (pk, sk) = PlumeSignature::keygen(&mut rng);

let expected_pk = g.mul(sk);
assert_eq!(pk, expected_pk);
@@ -91,10 +71,9 @@ pub fn test_sign_and_verify() {
let pp = Parameters { g_point: g };

let message = b"Message";
let keypair = PlumeSignature::keygen(&pp, &mut rng);
let keypair = PlumeSignature::keygen(&mut rng);

let sig = PlumeSignature::sign(
&pp,
&mut rng,
(&keypair.0, &keypair.1),
message,
@@ -106,7 +85,6 @@ pub fn test_sign_and_verify() {
assert!(is_valid.unwrap());

let sig = PlumeSignature::sign(
&pp,
&mut rng,
(&keypair.0, &keypair.1),
message,
@@ -118,16 +96,15 @@ pub fn test_sign_and_verify() {
assert!(is_valid.unwrap());
}

pub fn compute_h() -> GroupAffine<Secp256k1Parameters> {
pub fn hash_to_curve_with_testvalues() -> Affine {
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);
let pk = GroupAffine::<Secp256k1Parameters>::from(pk_projective);
let pk_projective = g * sk;

hash_to_curve::<secp256k1::fields::Fq, Secp256k1Parameters>(message, &pk).unwrap()
super::hash_to_curve(message, &pk_projective.into_affine()).unwrap()
}

#[test]
@@ -137,121 +114,135 @@ pub fn test_against_zk_nullifier_sig_pk() {

let (_, g) = test_template();
let pk_projective = g.mul(sk);
let pk = GroupAffine::<Secp256k1Parameters>::from(pk_projective);
let pk = Affine::from(pk_projective);

assert_eq!(
coord_to_hex(pk.x.into()),
"00000000000000000cec028ee08d09e02672a68310814354f9eabfff0de6dacc1cd3a774496076ae"
"0cec028ee08d09e02672a68310814354f9eabfff0de6dacc1cd3a774496076ae"
);
assert_eq!(
coord_to_hex(pk.y.into()),
"0000000000000000eff471fba0409897b6a48e8801ad12f95d0009b753cf8f51c128bf6b0bd27fbd"
"eff471fba0409897b6a48e8801ad12f95d0009b753cf8f51c128bf6b0bd27fbd"
);
}

#[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 = crate::secp256k1::Fr::from(hex_to_fr(&hardcoded_r()));
let (_, g) = test_template();
let g_r_projective = g.mul(r);
let g_r = GroupAffine::<Secp256k1Parameters>::from(g_r_projective);
let g_r = Affine::from(g_r_projective);
assert_eq!(
coord_to_hex(g_r.x.into()),
"00000000000000009d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804"
"9d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804"
);
assert_eq!(
coord_to_hex(g_r.y.into()),
"0000000000000000ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1"
"ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1"
);
}

//TODO: add test vectors for hash_to_curve
#[test]
pub fn test_against_zk_nullifier_sig_h() {
let h = compute_h();
let h = hash_to_curve_with_testvalues();

assert_eq!(
coord_to_hex(h.x.into()),
"0000000000000000bcac2d0e12679f23c218889395abcdc01f2affbc49c54d1136a2190db0800b65"
"bcac2d0e12679f23c218889395abcdc01f2affbc49c54d1136a2190db0800b65"
);
assert_eq!(
coord_to_hex(h.y.into()),
"00000000000000003bcfb339c974c0e757d348081f90a123b0a91a53e32b3752145d87f0cd70966e"
"3bcfb339c974c0e757d348081f90a123b0a91a53e32b3752145d87f0cd70966e"
);
}

#[test]
pub fn test_against_zk_nullifier_sig_h_r() {
let h = compute_h();
let h = hash_to_curve_with_testvalues();

// Test h^r using the hardcoded r
let r = secp256k1::fields::Fr::from(hex_to_fr(&hardcoded_r()));
let h_r_projective = h.mul(r);
let h_r = GroupAffine::<Secp256k1Parameters>::from(h_r_projective);
let r = crate::secp256k1::Fr::from(hex_to_fr(&hardcoded_r()));
let h_r_projective = h * r;
let h_r = Affine::from(h_r_projective);
assert_eq!(
coord_to_hex(h_r.x.into()),
"00000000000000006d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c"
"6d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c"
);
assert_eq!(
coord_to_hex(h_r.y.into()),
"0000000000000000586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed"
"586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed"
);
}

#[test]
pub fn test_against_zk_nullifier_sig_h_sk() {
let h = compute_h();
let h = hash_to_curve_with_testvalues();
let sk = hex_to_fr(&hardcoded_sk());

// Test h^r using the hardcoded sk
let h_sk_projective = h.mul(sk);
let h_sk = GroupAffine::<Secp256k1Parameters>::from(h_sk_projective);
let h_sk_projective = h * sk;
let h_sk = h_sk_projective.into_affine();
assert_eq!(
coord_to_hex(h_sk.x.into()),
"000000000000000057bc3ed28172ef8adde4b9e0c2cce745fcc5a66473a45c1e626f1d0c67e55830"
"57bc3ed28172ef8adde4b9e0c2cce745fcc5a66473a45c1e626f1d0c67e55830"
);
assert_eq!(
coord_to_hex(h_sk.y.into()),
"00000000000000006a2f41488d58f33ae46edd2188e111609f9f3ae67ea38fa891d6087fe59ecb73"
"6a2f41488d58f33ae46edd2188e111609f9f3ae67ea38fa891d6087fe59ecb73"
);
}

#[test]
pub fn test_against_zk_nullifier_sig_c_and_s() {
let r = secp256k1::fields::Fr::from(hex_to_fr(&hardcoded_r()));
let r = crate::secp256k1::Fr::from(hex_to_fr(&hardcoded_r()));
let message = hardcoded_msg();
let message = message.as_bytes();
let sk = hex_to_fr(&hardcoded_sk());
let (_, g) = test_template();
let pp = Parameters { g_point: g };
let pk_projective = g.mul(sk);
let pk = GroupAffine::<Secp256k1Parameters>::from(pk_projective);
let pk_projective = g * sk;
let pk = Affine::from(pk_projective);

let keypair = (pk, sk);
let sig =
PlumeSignature::sign_with_r(&pp, (&keypair.0, &keypair.1), message, r, PlumeVersion::V1)
.unwrap();
let sig = PlumeSignature::sign_with_r((&keypair.0, &keypair.1), message, r, PlumeVersion::V1)
.unwrap();

assert_eq!(
coord_to_hex(sig.c.into()),
"0000000000000000c6a7fc2c926ddbaf20731a479fb6566f2daa5514baae5223fe3b32edbce83254"
sig.c.into_bigint(),
BigInt!("0xc6a7fc2c926ddbaf20731a479fb6566f2daa5514baae5223fe3b32edbce83254")
);
assert_eq!(
coord_to_hex(sig.s.into()),
"0000000000000000e69f027d84cb6fe5f761e333d12e975fb190d163e8ea132d7de0bd6079ba28ca"
sig.s.into_bigint(),
BigInt!("0xe69f027d84cb6fe5f761e333d12e975fb190d163e8ea132d7de0bd6079ba28ca")
);

let sig =
PlumeSignature::sign_with_r(&pp, (&keypair.0, &keypair.1), message, r, PlumeVersion::V2)
.unwrap();

let sig = PlumeSignature::sign_with_r((&keypair.0, &keypair.1), message, r, PlumeVersion::V2)
.unwrap();

assert_eq!(
coord_to_hex(sig.c.into()),
"00000000000000003dbfb717705010d4f44a70720c95e74b475bd3a783ab0b9e8a6b3b363434eb96"
sig.c.into_bigint(),
BigInt!("0x3dbfb717705010d4f44a70720c95e74b475bd3a783ab0b9e8a6b3b363434eb96")
);
assert_eq!(
coord_to_hex(sig.s.into()),
"0000000000000000528e8fbb6452f82200797b1a73b2947a92524bd611085a920f1177cb8098136b"
sig.s.into_bigint(),
BigInt!("0x528e8fbb6452f82200797b1a73b2947a92524bd611085a920f1177cb8098136b")
);
}

#[test]
fn test_point_sec1_encoding() {
let vectors = test_vectors::encoding_test_vectors();

let generator = secp256k1::Affine::generator();

for vector in vectors {
let k = vector.0;
let point = (generator * secp256k1::Fr::from(k)).into_affine();

assert_eq!(
super::affine_to_bytes(&point),
hex::decode(vector.1.as_bytes()).unwrap()
);
}
}
504 changes: 504 additions & 0 deletions rust-arkworks/src/tests/test_vectors.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion rust-k256/src/lib.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
//!
//! See <https://blog.aayushg.com/nullifier> for more information.
//!
// Find `arkworks-rs` crate as `plume_arkworks`.
//! Find the crate to use with `arkworks-rs` crate as `plume_arkworks`.
//
//! # Examples
//! If you want more control or to be more generic on traits `use` [`PlumeSigner`] from [`randomizedsigner`]

0 comments on commit 5a5c11f

Please sign in to comment.