Browse Source

Update EdDSA to last circomlib version

- Update EdDSA to last circomlib version
	- Compatible with circomlib version 0.3.0 at
	  5935ac69df
	- Use blake hash precandidate (non-blake2b) for circomlib
	  compatibility
	- Add circomlib testvector
- Change PrivateKey from BigInt to [u8;32], which lead to faster
signature computation:
	(Benchmarks on a Intel(R) Core(TM) i7-8705G CPU @ 3.10GHz, with
	32 GB of RAM)
	- Old:
	    ```
	    sign                    time:   [559.84 us 568.41 us 576.26 us]
	    verify                  time:   [376.59 us 376.68 us 376.78 us]
	    ```
	- New:
	    ```
	    sign                    time:   [383.85 us 390.31 us 396.44 us]
	    verify                  time:   [371.33 us 376.06 us 381.19 us]
	    ```
- crate version v0.0.5
main
arnaucube 4 years ago
parent
commit
005019d684
2 changed files with 135 additions and 75 deletions
  1. +2
    -2
      Cargo.toml
  2. +133
    -73
      src/lib.rs

+ 2
- 2
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "babyjubjub-rs" name = "babyjubjub-rs"
version = "0.0.4"
version = "0.0.5"
authors = ["arnaucube <root@arnaucube.com>"] authors = ["arnaucube <root@arnaucube.com>"]
edition = "2018" edition = "2018"
license = "GPL-3.0" license = "GPL-3.0"
@ -15,7 +15,7 @@ rand6 = {package="rand", version="0.6.5"}
num = "0.2.0" num = "0.2.0"
num-bigint = {version = "0.2.2", features = ["rand"]} num-bigint = {version = "0.2.2", features = ["rand"]}
num-traits = "0.2.8" num-traits = "0.2.8"
blake2 = "0.8"
blake-hash = "0.4.0"
generic-array = "0.13.2" generic-array = "0.13.2"
tiny-keccak = "1.5" tiny-keccak = "1.5"
rustc-hex = "1.0.0" rustc-hex = "1.0.0"

+ 133
- 73
src/lib.rs

@ -1,5 +1,3 @@
// WARNING still updating the code, it works, but is still in process the refactor.
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate ff; extern crate ff;
@ -18,11 +16,15 @@ extern crate num_traits;
extern crate rand6; extern crate rand6;
use rand6::Rng; use rand6::Rng;
use blake2::{Blake2b, Digest};
// use blake2::{Blake2b, Digest};
extern crate blake_hash; // compatible version with Blake used at circomlib
#[macro_use]
use blake_hash::Digest;
use std::cmp::min; use std::cmp::min;
use num_bigint::{BigInt, RandBigInt, RandomBits, Sign, ToBigInt};
use num_traits::{One, Zero};
use num_bigint::{BigInt, RandBigInt, Sign, ToBigInt};
use num_traits::One;
use generic_array::GenericArray; use generic_array::GenericArray;
@ -39,38 +41,29 @@ lazy_static! {
pub static ref Q: BigInt = BigInt::parse_bytes( pub static ref Q: BigInt = BigInt::parse_bytes(
b"21888242871839275222246405745257275088548364400416034343698204186575808495617",10 b"21888242871839275222246405745257275088548364400416034343698204186575808495617",10
) )
.unwrap();
// pub static ref Q: Fr = Fr::from_str(
// "21888242871839275222246405745257275088548364400416034343698204186575808495617"
// )
// .unwrap();
.unwrap();
static ref B8: Point = Point { static ref B8: Point = Point {
x: Fr::from_str( x: Fr::from_str(
"5299619240641551281634865583518297030282874472190772894086521144482721001553",
)
.unwrap(),
y: Fr::from_str(
"16950150798460657717958625567821834550301663161624707787222815936182638968203",
)
.unwrap(),
// z: Fr::one(),
"5299619240641551281634865583518297030282874472190772894086521144482721001553",
)
.unwrap(),
y: Fr::from_str(
"16950150798460657717958625567821834550301663161624707787222815936182638968203",
)
.unwrap(),
}; };
static ref ORDER: Fr = Fr::from_str( static ref ORDER: Fr = Fr::from_str(
"21888242871839275222246405745257275088614511777268538073601725287587578984328", "21888242871839275222246405745257275088614511777268538073601725287587578984328",
) )
.unwrap();
.unwrap();
// SUBORDER = ORDER >> 3 // SUBORDER = ORDER >> 3
// static ref SUBORDER: Fr = Fr::from_str(
// "i2736030358979909402780800718157159386076813972158567259200215660948447373041",
// )
// .unwrap();
static ref SUBORDER: BigInt = &BigInt::parse_bytes( static ref SUBORDER: BigInt = &BigInt::parse_bytes(
b"21888242871839275222246405745257275088614511777268538073601725287587578984328",
10,
)
.unwrap()
>> 3;
b"21888242871839275222246405745257275088614511777268538073601725287587578984328",
10,
)
.unwrap()
>> 3;
static ref poseidon: poseidon_rs::Poseidon = Poseidon::new(); static ref poseidon: poseidon_rs::Poseidon = Poseidon::new();
} }
@ -90,7 +83,7 @@ impl PointProjective {
}; };
} }
let mut zinv = self.z.inverse().unwrap();
let zinv = self.z.inverse().unwrap();
let mut x = self.x; let mut x = self.x;
x.mul_assign(&zinv); x.mul_assign(&zinv);
let mut y = self.y; let mut y = self.y;
@ -274,13 +267,40 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result {
} }
pub struct PrivateKey { pub struct PrivateKey {
key: BigInt,
key: [u8; 32],
} }
impl PrivateKey { impl PrivateKey {
pub fn import(b: Vec<u8>) -> Result<PrivateKey, String> {
if b.len() != 32 {
return Err(String::from("imported key can not be bigger than 32 bytes"));
}
let mut sk: [u8; 32] = [0; 32];
sk.copy_from_slice(&b[..32]);
Ok(PrivateKey { key: sk })
}
pub fn scalar_key(&self) -> BigInt {
// not-compatible with circomlib implementation, but using Blake2b
// let mut hasher = Blake2b::new();
// hasher.update(sk_raw_bytes);
// let mut h = hasher.finalize();
// compatible with circomlib implementation
let hash = blake_hash::Blake512::digest(&self.key.to_vec());
let mut h: Vec<u8> = hash[..32].to_vec();
h[0] = h[0] & 0xF8;
h[31] = h[31] & 0x7F;
h[31] = h[31] | 0x40;
let sk = BigInt::from_bytes_le(Sign::Plus, &h[..]);
sk >> 3
}
pub fn public(&self) -> Result<Point, String> { pub fn public(&self) -> Result<Point, String> {
// https://tools.ietf.org/html/rfc8032#section-5.1.5 // https://tools.ietf.org/html/rfc8032#section-5.1.5
let pk = B8.mul_scalar(&self.key)?;
let pk = B8.mul_scalar(&self.scalar_key())?;
Ok(pk.clone()) Ok(pk.clone())
} }
@ -288,28 +308,32 @@ impl PrivateKey {
if msg > Q.clone() { if msg > Q.clone() {
return Err("msg outside the Finite Field".to_string()); return Err("msg outside the Finite Field".to_string());
} }
// https://tools.ietf.org/html/rfc8032#section-5.1.6
let mut hasher = Blake2b::new();
let (_, sk_bytes) = self.key.to_bytes_be();
hasher.input(sk_bytes);
let mut h = hasher.result(); // h: hash(sk)
// s: h[32:64]
let (_, msg_bytes) = msg.to_bytes_be();
let msgFr: Fr = Fr::from_str(&msg.to_string()).unwrap();
// let (_, sk_bytes) = self.key.to_bytes_le();
// let mut hasher = Blake2b::new();
// hasher.update(sk_bytes);
// let mut h = hasher.finalize(); // h: hash(sk), s: h[32:64]
let mut h = blake_hash::Blake512::digest(&self.key);
let (_, msg_bytes) = msg.to_bytes_le();
let mut msg32: [u8; 32] = [0; 32];
msg32[..msg_bytes.len()].copy_from_slice(&msg_bytes[..]);
let msg_fr: Fr = Fr::from_str(&msg.to_string()).unwrap();
// https://tools.ietf.org/html/rfc8032#section-5.1.6
let s = GenericArray::<u8, generic_array::typenum::U32>::from_mut_slice(&mut h[32..64]); let s = GenericArray::<u8, generic_array::typenum::U32>::from_mut_slice(&mut h[32..64]);
let r_bytes = utils::concatenate_arrays(s, &msg_bytes);
let mut r = BigInt::from_bytes_be(Sign::Plus, &r_bytes[..]);
let r_bytes = utils::concatenate_arrays(s, &msg32);
let r_hashed = blake_hash::Blake512::digest(&r_bytes);
let mut r = BigInt::from_bytes_le(Sign::Plus, &r_hashed[..]);
r = utils::modulus(&r, &SUBORDER); r = utils::modulus(&r, &SUBORDER);
let r8: Point = B8.mul_scalar(&r)?; let r8: Point = B8.mul_scalar(&r)?;
let a = &self.public()?; let a = &self.public()?;
let hm_input = vec![r8.x.clone(), r8.y.clone(), a.x.clone(), a.y.clone(), msgFr];
let hm_input = vec![r8.x.clone(), r8.y.clone(), a.x.clone(), a.y.clone(), msg_fr];
let hm = poseidon.hash(hm_input)?; let hm = poseidon.hash(hm_input)?;
let mut s = &self.key << 3;
let hmB = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap();
s = hmB * s;
let mut s = &self.scalar_key() << 3;
let hm_b = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap();
s = hm_b * s;
s = r + s; s = r + s;
s = s % &SUBORDER.clone(); s = s % &SUBORDER.clone();
@ -332,7 +356,8 @@ impl PrivateKey {
let h = schnorr_hash(&pk, m, &r)?; let h = schnorr_hash(&pk, m, &r)?;
// s= k+x·h // s= k+x·h
let s = k + &self.key * &h;
let sk_scalar = self.scalar_key();
let s = k + &sk_scalar * &h;
Ok((r, s)) Ok((r, s))
} }
} }
@ -341,12 +366,11 @@ pub fn schnorr_hash(pk: &Point, msg: BigInt, c: &Point) -> Result
if msg > Q.clone() { if msg > Q.clone() {
return Err("msg outside the Finite Field".to_string()); return Err("msg outside the Finite Field".to_string());
} }
let msgFr: Fr = Fr::from_str(&msg.to_string()).unwrap();
let hm_input = vec![pk.x.clone(), pk.y.clone(), c.x.clone(), c.y.clone(), msgFr];
let msg_fr: Fr = Fr::from_str(&msg.to_string()).unwrap();
let hm_input = vec![pk.x.clone(), pk.y.clone(), c.x.clone(), c.y.clone(), msg_fr];
let h = poseidon.hash(hm_input)?; let h = poseidon.hash(hm_input)?;
println!("h {:?}", h.to_string());
let hB = BigInt::parse_bytes(to_hex(&h).as_bytes(), 16).unwrap();
Ok(hB)
let h_b = BigInt::parse_bytes(to_hex(&h).as_bytes(), 16).unwrap();
Ok(h_b)
} }
pub fn verify_schnorr(pk: Point, m: BigInt, r: Point, s: BigInt) -> Result<bool, String> { pub fn verify_schnorr(pk: Point, m: BigInt, r: Point, s: BigInt) -> Result<bool, String> {
@ -365,32 +389,21 @@ pub fn new_key() -> PrivateKey {
// https://tools.ietf.org/html/rfc8032#section-5.1.5 // https://tools.ietf.org/html/rfc8032#section-5.1.5
let mut rng = rand6::thread_rng(); let mut rng = rand6::thread_rng();
let sk_raw = rng.gen_biguint(1024).to_bigint().unwrap(); let sk_raw = rng.gen_biguint(1024).to_bigint().unwrap();
let mut hasher = Blake2b::new();
let (_, sk_raw_bytes) = sk_raw.to_bytes_be(); let (_, sk_raw_bytes) = sk_raw.to_bytes_be();
hasher.input(sk_raw_bytes);
let mut h = hasher.result();
h[0] = h[0] & 0xF8;
h[31] = h[31] & 0x7F;
h[31] = h[31] | 0x40;
let sk = BigInt::from_bytes_le(Sign::Plus, &h[..]);
PrivateKey { key: sk }
PrivateKey::import(sk_raw_bytes[..32].to_vec()).unwrap()
} }
pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool { pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool {
if msg > Q.clone() { if msg > Q.clone() {
return false; return false;
} }
let msgFr: Fr = Fr::from_str(&msg.to_string()).unwrap();
let msg_fr: Fr = Fr::from_str(&msg.to_string()).unwrap();
let hm_input = vec![ let hm_input = vec![
sig.r_b8.x.clone(), sig.r_b8.x.clone(),
sig.r_b8.y.clone(), sig.r_b8.y.clone(),
pk.x.clone(), pk.x.clone(),
pk.y.clone(), pk.y.clone(),
msgFr,
msg_fr,
]; ];
let hm = match poseidon.hash(hm_input) { let hm = match poseidon.hash(hm_input) {
Result::Err(_) => return false, Result::Err(_) => return false,
@ -400,9 +413,9 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool {
Result::Err(_) => return false, Result::Err(_) => return false,
Result::Ok(l) => l, Result::Ok(l) => l,
}; };
let hmB = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap();
let hm_b = BigInt::parse_bytes(to_hex(&hm).as_bytes(), 16).unwrap();
let r = match sig.r_b8.projective().add( let r = match sig.r_b8.projective().add(
&pk.mul_scalar(&(8.to_bigint().unwrap() * hmB))
&pk.mul_scalar(&(8.to_bigint().unwrap() * hm_b))
.unwrap() .unwrap()
.projective(), .projective(),
) { ) {
@ -637,10 +650,8 @@ mod tests {
for _ in 0..5 { for _ in 0..5 {
let random_bytes = rand6::thread_rng().gen::<[u8; 32]>(); let random_bytes = rand6::thread_rng().gen::<[u8; 32]>();
let sk_raw: BigInt = BigInt::from_bytes_le(Sign::Plus, &random_bytes[..]); let sk_raw: BigInt = BigInt::from_bytes_le(Sign::Plus, &random_bytes[..]);
let mut hasher = Blake2b::new();
let (_, sk_raw_bytes) = sk_raw.to_bytes_be(); let (_, sk_raw_bytes) = sk_raw.to_bytes_be();
hasher.input(sk_raw_bytes);
let mut h = hasher.result();
let mut h = blake_hash::Blake512::digest(&sk_raw_bytes);
h[0] = h[0] & 0xF8; h[0] = h[0] & 0xF8;
h[31] = h[31] & 0x7F; h[31] = h[31] & 0x7F;
@ -684,10 +695,59 @@ mod tests {
let msg = BigInt::parse_bytes(b"123456789012345678901234567890", 10).unwrap(); let msg = BigInt::parse_bytes(b"123456789012345678901234567890", 10).unwrap();
let (s, e) = sk.sign_schnorr(msg.clone()).unwrap(); let (s, e) = sk.sign_schnorr(msg.clone()).unwrap();
println!("s {:?}", s.x.to_string());
println!("s {:?}", s.y.to_string());
println!("e {:?}", e.to_string());
let verification = verify_schnorr(pk, msg, s, e).unwrap(); let verification = verify_schnorr(pk, msg, s, e).unwrap();
assert_eq!(true, verification); assert_eq!(true, verification);
} }
#[test]
fn test_circomlib_testvector() {
let sk_raw_bytes =
hex::decode("0001020304050607080900010203040506070809000102030405060708090001")
.unwrap();
// test blake compatible with circomlib implementation
let h = blake_hash::Blake512::digest(&sk_raw_bytes);
assert_eq!(h.to_hex(), "c992db23d6290c70ffcc02f7abeb00b9d00fa8b43e55d7949c28ba6be7545d3253882a61bd004a236ef1cdba01b27ba0aedfb08eefdbfb7c19657c880b43ddf1");
// test private key
let sk = PrivateKey::import(
hex::decode("0001020304050607080900010203040506070809000102030405060708090001")
.unwrap(),
)
.unwrap();
assert_eq!(
sk.scalar_key().to_string(),
"6466070937662820620902051049739362987537906109895538826186780010858059362905"
);
// test public key
let pk = sk.public().unwrap();
assert_eq!(
pk.x.to_string(),
"Fr(0x1d5ac1f31407018b7d413a4f52c8f74463b30e6ac2238220ad8b254de4eaa3a2)"
);
assert_eq!(
pk.y.to_string(),
"Fr(0x1e1de8a908826c3f9ac2e0ceee929ecd0caf3b99b3ef24523aaab796a6f733c4)"
);
// test signature & verification
let msg = BigInt::from_bytes_le(Sign::Plus, &hex::decode("00010203040506070809").unwrap());
println!("msg {:?}", msg.to_string());
let sig = sk.sign(msg.clone()).unwrap();
assert_eq!(
sig.r_b8.x.to_string(),
"Fr(0x192b4e51adf302c8139d356d0e08e2404b5ace440ef41fc78f5c4f2428df0765)"
);
assert_eq!(
sig.r_b8.y.to_string(),
"Fr(0x2202bebcf57b820863e0acc88970b6ca7d987a0d513c2ddeb42e3f5d31b4eddf)"
);
assert_eq!(
sig.s.to_string(),
"1398758333392199195742243841591064350253744445503462896781493968760929513778"
);
let v = verify(pk, sig, msg);
assert_eq!(v, true);
}
} }

Loading…
Cancel
Save