mirror of
https://github.com/arnaucube/babyjubjub-ark.git
synced 2026-01-13 17:21:29 +01:00
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
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "babyjubjub-rs"
|
||||
version = "0.0.4"
|
||||
version = "0.0.5"
|
||||
authors = ["arnaucube <root@arnaucube.com>"]
|
||||
edition = "2018"
|
||||
license = "GPL-3.0"
|
||||
@@ -15,7 +15,7 @@ rand6 = {package="rand", version="0.6.5"}
|
||||
num = "0.2.0"
|
||||
num-bigint = {version = "0.2.2", features = ["rand"]}
|
||||
num-traits = "0.2.8"
|
||||
blake2 = "0.8"
|
||||
blake-hash = "0.4.0"
|
||||
generic-array = "0.13.2"
|
||||
tiny-keccak = "1.5"
|
||||
rustc-hex = "1.0.0"
|
||||
|
||||
206
src/lib.rs
206
src/lib.rs
@@ -1,5 +1,3 @@
|
||||
// WARNING still updating the code, it works, but is still in process the refactor.
|
||||
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate ff;
|
||||
@@ -18,11 +16,15 @@ extern crate num_traits;
|
||||
extern crate rand6;
|
||||
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 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;
|
||||
|
||||
@@ -39,38 +41,29 @@ lazy_static! {
|
||||
pub static ref Q: BigInt = BigInt::parse_bytes(
|
||||
b"21888242871839275222246405745257275088548364400416034343698204186575808495617",10
|
||||
)
|
||||
.unwrap();
|
||||
// pub static ref Q: Fr = Fr::from_str(
|
||||
// "21888242871839275222246405745257275088548364400416034343698204186575808495617"
|
||||
// )
|
||||
// .unwrap();
|
||||
.unwrap();
|
||||
static ref B8: Point = Point {
|
||||
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(
|
||||
"21888242871839275222246405745257275088614511777268538073601725287587578984328",
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
|
||||
// SUBORDER = ORDER >> 3
|
||||
// static ref SUBORDER: Fr = Fr::from_str(
|
||||
// "i2736030358979909402780800718157159386076813972158567259200215660948447373041",
|
||||
// )
|
||||
// .unwrap();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -90,7 +83,7 @@ impl PointProjective {
|
||||
};
|
||||
}
|
||||
|
||||
let mut zinv = self.z.inverse().unwrap();
|
||||
let zinv = self.z.inverse().unwrap();
|
||||
let mut x = self.x;
|
||||
x.mul_assign(&zinv);
|
||||
let mut y = self.y;
|
||||
@@ -274,13 +267,40 @@ pub fn decompress_signature(b: &[u8; 64]) -> Result<Signature, String> {
|
||||
}
|
||||
|
||||
pub struct PrivateKey {
|
||||
key: BigInt,
|
||||
key: [u8; 32],
|
||||
}
|
||||
|
||||
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> {
|
||||
// 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())
|
||||
}
|
||||
|
||||
@@ -288,28 +308,32 @@ impl PrivateKey {
|
||||
if msg > Q.clone() {
|
||||
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 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);
|
||||
let r8: Point = B8.mul_scalar(&r)?;
|
||||
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 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 = s % &SUBORDER.clone();
|
||||
|
||||
@@ -332,7 +356,8 @@ impl PrivateKey {
|
||||
let h = schnorr_hash(&pk, m, &r)?;
|
||||
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
@@ -341,12 +366,11 @@ pub fn schnorr_hash(pk: &Point, msg: BigInt, c: &Point) -> Result<BigInt, String
|
||||
if msg > Q.clone() {
|
||||
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)?;
|
||||
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> {
|
||||
@@ -365,32 +389,21 @@ pub fn new_key() -> PrivateKey {
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.5
|
||||
let mut rng = rand6::thread_rng();
|
||||
let sk_raw = rng.gen_biguint(1024).to_bigint().unwrap();
|
||||
|
||||
let mut hasher = Blake2b::new();
|
||||
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 {
|
||||
if msg > Q.clone() {
|
||||
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![
|
||||
sig.r_b8.x.clone(),
|
||||
sig.r_b8.y.clone(),
|
||||
pk.x.clone(),
|
||||
pk.y.clone(),
|
||||
msgFr,
|
||||
msg_fr,
|
||||
];
|
||||
let hm = match poseidon.hash(hm_input) {
|
||||
Result::Err(_) => return false,
|
||||
@@ -400,9 +413,9 @@ pub fn verify(pk: Point, sig: Signature, msg: BigInt) -> bool {
|
||||
Result::Err(_) => return false,
|
||||
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(
|
||||
&pk.mul_scalar(&(8.to_bigint().unwrap() * hmB))
|
||||
&pk.mul_scalar(&(8.to_bigint().unwrap() * hm_b))
|
||||
.unwrap()
|
||||
.projective(),
|
||||
) {
|
||||
@@ -637,10 +650,8 @@ mod tests {
|
||||
for _ in 0..5 {
|
||||
let random_bytes = rand6::thread_rng().gen::<[u8; 32]>();
|
||||
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();
|
||||
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[31] = h[31] & 0x7F;
|
||||
@@ -684,10 +695,59 @@ mod tests {
|
||||
|
||||
let msg = BigInt::parse_bytes(b"123456789012345678901234567890", 10).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();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user