moved arkworks impl to this repo

This commit is contained in:
Koh Wei Jie
2022-09-15 11:29:12 -07:00
parent c94b80eb92
commit 832ea2dad9
6 changed files with 636 additions and 0 deletions

30
rust-arkworks/Cargo.toml Normal file
View File

@@ -0,0 +1,30 @@
[package]
name = "sig"
version = "0.1.0"
edition = "2021"
# 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"
thiserror = "1.0.30"
secp256k1 = { git = "https://github.com/geometryresearch/ark-secp256k1.git" }
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"
[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" }

View File

@@ -0,0 +1,11 @@
use thiserror::Error;
/// This is an error that could occur when running a cryptograhic primitive
#[derive(Error, Debug, PartialEq)]
pub enum CryptoError {
#[error("Cannot hash to curve")]
CannotHashToCurve,
#[error("Cannot encode a point not on the curve")]
PointNotOnCurve,
}

View File

@@ -0,0 +1,89 @@
use crate::error::CryptoError;
use ark_ec::{AffineCurve, ProjectiveCurve};
use tiny_keccak::{Hasher, Shake, Xof};
use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
use k256::{AffinePoint};
use k256::sha2::Sha256;
use elliptic_curve::sec1::ToEncodedPoint;
use ark_ec::short_weierstrass_jacobian::GroupAffine;
use k256::{ProjectivePoint, Secp256k1};
use ark_ff::FromBytes;
use secp256k1::Sec1EncodePoint;
pub fn hash_to_curve<
Fp: ark_ff::PrimeField,
P: ark_ec::SWModelParameters,
>(
msg: &[u8],
pk: &GroupAffine<P>,
) -> GroupAffine<P> {
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.as_slice();
let pt: ProjectivePoint = Secp256k1::hash_from_bytes::<ExpandMsgXmd<Sha256>>(
&[x],
b"CURVE_XMD:SHA-256_SSWU_RO_"
).unwrap();
let pt_affine = pt.to_affine();
k256_affine_to_arkworks_secp256k1_affine::<Fp, P>(pt_affine)
}
pub fn k256_affine_to_arkworks_secp256k1_affine<
Fp: ark_ff::PrimeField,
P: ark_ec::SWModelParameters
>(
k_pt: AffinePoint,
) -> GroupAffine<P> {
let encoded_pt = k_pt.to_encoded_point(false);
let num_field_bytes = 320;
// extract k_pt.x
let k_pt_x_bytes = encoded_pt.x().unwrap();
// 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 reader = std::io::BufReader::new(k_pt_x_bytes_vec.as_slice());
let g_x = P::BaseField::read(reader).unwrap();
// extract k_pt.y
let k_pt_y_bytes = encoded_pt.y().unwrap();
// 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 reader = std::io::BufReader::new(k_pt_y_bytes_vec.as_slice());
let g_y = P::BaseField::read(reader).unwrap();
GroupAffine::<P>::new(g_x, g_y, false)
}
/// Kobi's hash_to_curve function, here for reference only
pub fn _try_and_increment<C: ProjectiveCurve>(msg: &[u8]) -> Result<C::Affine, CryptoError> {
for nonce in 0u8..=255 {
let mut h = Shake::v128();
h.update(&[nonce]);
h.update(msg.as_ref());
let output_size = C::zero().serialized_size();
let mut output = vec![0u8; output_size];
h.squeeze(&mut output);
if let Some(p) = C::Affine::from_random_bytes(&output) {
return Ok(p);
}
}
Err(CryptoError::CannotHashToCurve)
}

256
rust-arkworks/src/lib.rs Normal file
View File

@@ -0,0 +1,256 @@
mod error;
mod hash_to_curve;
#[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_ff::{PrimeField, ToBytes};
use ark_std::{
marker::PhantomData,
UniformRand,
rand::Rng,
};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError, Read, Write};
use sha2::{Sha512, Digest};
use secp256k1::sec1::Sec1EncodePoint;
pub struct DeterministicNullifierSignatureScheme<'a, C: ProjectiveCurve, Fq: ark_ff::PrimeField, P: ark_ec::SWModelParameters> {
_group: PhantomData<C>,
_field: PhantomData<Fq>,
_parameters: PhantomData<P>,
_message_lifetime: PhantomData<&'a ()>,
}
pub fn affine_to_bytes<P: SWModelParameters>(
point: &GroupAffine<P>
) -> Vec::<u8> {
let encoded = point.to_encoded_point(true);
let b = hex::decode(encoded).unwrap();
b.to_vec()
}
fn compute_h<'a, C: ProjectiveCurve, Fq: PrimeField, P: SWModelParameters>(
pk: &GroupAffine<P>,
message: &'a [u8],
) -> Result<GroupAffine<P>, CryptoError> {
//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())
Ok(hash_to_curve::hash_to_curve::<Fq, P>(message, pk))
}
fn compute_c<P: SWModelParameters>(
g: &GroupAffine<P>,
pk: &GroupAffine<P>,
h: &GroupAffine<P>,
nul: &GroupAffine<P>,
g_r: &GroupAffine<P>,
z: &GroupAffine<P>,
) -> P::ScalarField {
// Compute c = sha512([g, pk, h, nul, g^r, z])
let g_bytes = affine_to_bytes::<P>(g);
let pk_bytes = affine_to_bytes::<P>(pk);
let h_bytes = affine_to_bytes::<P>(h);
let nul_bytes = affine_to_bytes::<P>(nul);
let g_r_bytes = affine_to_bytes::<P>(g_r);
let z_bytes = affine_to_bytes::<P>(z);
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());
let sha512_hasher_result = sha512_hasher.finalize();
// Take the first 32 bytes
let mut first_32 = Vec::<u8>::with_capacity(32);
for i in 0..32 {
first_32.push(sha512_hasher_result[i]);
}
// 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)
}
pub trait VerifiableUnpredictableFunction {
type Message: ToBytes;
type Parameters: CanonicalSerialize + CanonicalDeserialize;
type PublicKey: CanonicalSerialize + CanonicalDeserialize;
type SecretKey: CanonicalSerialize + CanonicalDeserialize;
type Signature: CanonicalSerialize + CanonicalDeserialize;
/// Generate a public key and a private key.
fn keygen<R: Rng>(
pp: &Self::Parameters,
rng: &mut R,
) -> Result<(Self::PublicKey, Self::SecretKey), CryptoError>;
/// Sign a message.
fn sign<R: Rng>(
pp: &Self::Parameters,
rng: &mut R,
keypair: (&Self::PublicKey, &Self::SecretKey),
message: Self::Message,
) -> Result<Self::Signature, CryptoError>;
/// Sign a message using an specified r value
fn sign_with_r(
pp: &Self::Parameters,
keypair: (&Self::PublicKey, &Self::SecretKey),
message: Self::Message,
r: Self::SecretKey,
) -> Result<Self::Signature, CryptoError>;
fn verify_non_zk(
pp: &Self::Parameters,
pk: &Self::PublicKey,
sig: &Self::Signature,
message: Self::Message,
) -> Result<bool, CryptoError>;
}
#[derive(Copy, Clone, ark_serialize_derive::CanonicalSerialize, ark_serialize_derive::CanonicalDeserialize)]
pub struct Parameters<P: SWModelParameters> {
pub g: GroupAffine<P>,
}
#[derive(Copy, Clone, ark_serialize_derive::CanonicalSerialize, ark_serialize_derive::CanonicalDeserialize)]
pub struct Signature<P: SWModelParameters> {
pub z: GroupAffine<P>,
pub g_r: GroupAffine<P>,
pub s: P::ScalarField,
pub c: P::ScalarField,
pub nul: GroupAffine<P>,
}
impl<'a, C: ProjectiveCurve, Fq: PrimeField, P: SWModelParameters> VerifiableUnpredictableFunction for DeterministicNullifierSignatureScheme<'a, C, Fq, P> {
type Message = &'a [u8];
type Parameters = Parameters<P>;
type PublicKey = GroupAffine<P>;
type SecretKey = P::ScalarField;
type Signature = Signature<P>;
fn keygen<R: Rng>(
pp: &Self::Parameters,
rng: &mut R,
) -> Result<(Self::PublicKey, Self::SecretKey), CryptoError> {
let secret_key = Self::SecretKey::rand(rng).into();
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),
message: Self::Message,
r: P::ScalarField,
) -> Result<Self::Signature, CryptoError> {
let g = pp.g;
let g_r = g.mul(r).into_affine();
// Compute h = htc([m, pk])
let h = compute_h::<C, Fq, P>(&keypair.0, &message).unwrap();
// Compute z = h^r
let z = h.mul(r).into_affine();
// Compute nul = h^sk
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::<P>(
&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();
let s = r.into_repr().into() + sk_c;
let s_scalar = P::ScalarField::from(s);
let signature = Signature {
z,
s: s_scalar,
g_r,
c: c_scalar,
nul
};
Ok(signature)
}
fn sign<R: Rng>(
pp: &Self::Parameters,
rng: &mut R,
keypair: (&Self::PublicKey, &Self::SecretKey),
message: Self::Message,
) -> Result<Self::Signature, CryptoError> {
// Pick a random r from Fp
let r: P::ScalarField = Self::SecretKey::rand(rng).into();
Self::sign_with_r(pp, keypair, message, r)
}
fn verify_non_zk(
pp: &Self::Parameters,
pk: &Self::PublicKey,
sig: &Self::Signature,
message: Self::Message,
) -> Result<bool, CryptoError> {
// Compute h = htc([m, pk])
let h = compute_h::<C, Fq, P>(pk, message).unwrap();
// Compute c' = sha512([g, pk, h, nul, g^r, z])
let c_scalar: P::ScalarField = compute_c::<P>(
&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);
let pk_c = pk.mul(sig.c);
let g_s_pk_c = g_s - pk_c;
if sig.g_r != g_s_pk_c {
return Ok(false);
}
// Reject if h^s ⋅ nul^{-c} = z
let h_s = h.mul(sig.s);
let nul_c = sig.nul.mul(sig.c);
let h_s_nul_c = h_s - nul_c;
if sig.z != h_s_nul_c {
return Ok(false)
}
// Reject if c != c'
if c_scalar != sig.c {
return Ok(false);
}
Ok(true)
}
}
}

215
rust-arkworks/src/tests.rs Normal file
View File

@@ -0,0 +1,215 @@
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::sig::DeterministicNullifierSignatureScheme;
use ark_ec::{AffineCurve, ProjectiveCurve};
use ark_ec::models::short_weierstrass_jacobian::GroupAffine;
use ark_ff::bytes::{ToBytes, FromBytes};
use ark_ff::biginteger;
use rand::{prelude::ThreadRng, thread_rng};
use k256::{ProjectivePoint, Scalar};
type Parameters = crate::sig::Parameters<Secp256k1Parameters>;
fn test_template() -> (ThreadRng, Affine) {
let rng = thread_rng();
let g = Affine::prime_subgroup_generator();
(rng, g)
}
type Scheme<'a> = DeterministicNullifierSignatureScheme::<'a, secp256k1::Projective, Fq, Secp256k1Parameters>;
#[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::<
secp256k1::fields::Fq,
Secp256k1Parameters
>(k256_pt.to_affine());
// The points should match
assert_eq!(ark_pt.into_affine(), converted_pt);
}
}
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))
}
fn hardcoded_sk() -> String {
"519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464".to_string()
}
fn hardcoded_r() -> String {
"93b9323b629f251b8f3fc2dd11f4672c5544e8230d493eceea98a90bda789808".to_string()
}
pub fn hardcoded_msg() -> String {
"An example app message string".to_string()
}
#[test]
pub fn test_keygen() {
let (mut rng, g) = test_template();
let pp = Parameters{ g };
let (pk, sk) = Scheme::keygen(&pp, &mut rng).unwrap();
let expected_pk = g.mul(sk);
assert_eq!(pk, expected_pk);
}
#[test]
pub fn test_sign_and_verify() {
let (mut rng, g) = test_template();
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,
);
assert!(is_valid.unwrap());
}
pub fn compute_h() -> GroupAffine::<Secp256k1Parameters> {
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 h = hash_to_curve::<secp256k1::fields::Fq, Secp256k1Parameters>(message, &pk);
h
}
#[test]
pub fn test_against_zk_nullifier_sig_pk() {
// Check the pubkey generated from the hardcoded secret key
let sk = hex_to_fr(&hardcoded_sk());
let (_, g) = test_template();
let pk_projective = g.mul(sk);
let pk = GroupAffine::<Secp256k1Parameters>::from(pk_projective);
assert_eq!(coord_to_hex(pk.x.into()), "00000000000000000cec028ee08d09e02672a68310814354f9eabfff0de6dacc1cd3a774496076ae");
assert_eq!(coord_to_hex(pk.y.into()), "0000000000000000eff471fba0409897b6a48e8801ad12f95d0009b753cf8f51c128bf6b0bd27fbd");
}
#[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 (_, g) = test_template();
let g_r_projective = g.mul(r);
let g_r = GroupAffine::<Secp256k1Parameters>::from(g_r_projective);
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
#[test]
pub fn test_against_zk_nullifier_sig_h() {
let h = compute_h();
assert_eq!(coord_to_hex(h.x.into()), "000000000000000027cdee7f388ba2981f4ef3a499abdd7506281bdc4f535109ec66e0e80824a37b");
assert_eq!(coord_to_hex(h.y.into()), "00000000000000008beb2fe7adeecadb3e99be05c3979bcf734c2caa768aaed09a26cb48d1236f42");
}
#[test]
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 h_r_projective = h.mul(r);
let h_r = GroupAffine::<Secp256k1Parameters>::from(h_r_projective);
assert_eq!(coord_to_hex(h_r.x.into()), "0000000000000000adf22a767a1f43b8dc4e77ce00c4eea54a63b10126e03e5f418d460e1fe1b2c2");
assert_eq!(coord_to_hex(h_r.y.into()), "0000000000000000d9bc5ce25d1fd63dd56fe6b7b2260747758c0bdda4b0e09a4028eed29a8049d8");
}
#[test]
pub fn test_against_zk_nullifier_sig_h_sk() {
let h = compute_h();
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);
assert_eq!(coord_to_hex(h_sk.x.into()), "000000000000000015db23237364493d346e7ecf367c65c3861ba088e53a757deb5e8eaaa3e24e3f");
assert_eq!(coord_to_hex(h_sk.y.into()), "0000000000000000c3623b03cd7d92136dba28f6077e28c8fb731cc585e61fcb26d5c8f0f3b83fd0");
}
#[test]
pub fn test_against_zk_nullifier_sig_c_and_s() {
let r = secp256k1::fields::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 };
let pk_projective = g.mul(sk);
let pk = GroupAffine::<Secp256k1Parameters>::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()), "00000000000000009de4daa951b8728db267eea9aa54ae48f8496bfde11387e91a39b261782a2b43");
assert_eq!(coord_to_hex(sig.s.into()), "0000000000000000b4e19b36312e7489b708e9e277280ae51ca0bbf350add3b93c897902040fdd76");
}