update EC-VRF algorithm to IETF's draft 4

This commit is contained in:
Geoffroy Couprie 2019-02-22 17:52:32 +01:00
parent 2bec1d22a2
commit 0149babd71
7 changed files with 158 additions and 159 deletions

View File

@ -361,9 +361,9 @@ Example of library this can be implemented with:
proposed by @KellerFuchs
https://tools.ietf.org/html/draft-goldbe-vrf-01
https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04
Using the primitives defined in https://tools.ietf.org/html/draft-goldbe-vrf-01#section-5 :
Using the primitives defined in https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04#section-5 :
```
F - finite field
@ -397,26 +397,26 @@ creating a proof pi = ECVRF_prove(pk, sk, message):
- h = ECVRF_hash_to_curve(pk, message)
- gamma = h^sk
- choose a random integer nonce k from [0, q-1]
- c = ECVRF_hash_points(g, h, pk, gamma, g^k, h^k)
- s = k - c * sk mod q
- k = ECVRF_nonce(pk, h)
- c = ECVRF_hash_points(h, gamma, g^k, h^k)
- s = k + c * sk mod q
- pi = (gamma, c, s)
Verify(pk, pi, message) for one message and its signature:
- (gamma, c, s) = pi
```
u = pk^c * g^s
= g^(sk*c)*g^(k - c*sk)
u = pk^-c * g^s
= g^(sk*-c)*g^(k + c*sk)
= g^k
```
- h = ECVRF_hash_to_curve(pk, message)
```
v = gamma^c * h^s
= h^(sk*c)*h^(k - c*sk)
v = gamma^-c * h^s
= h^(sk*-c)*h^(k + c*sk)
= h^k
```
- c' = ECVRF_hash_points(g, h, pk, gamma, u, v)
- c' = ECVRF_hash_points(h, gamma, u, v)
- return c == c'
#### Aggregating signatures
@ -426,9 +426,9 @@ Sign:
First block: Sign0(pk, sk, message)
- `h = ECVRF_hash_to_curve(pk, message)`
- `gamma = h^sk`
- `choose a random integer nonce k from [0, q-1]`
- `c = ECVRF_hash_points(g, h, pk, gamma, g^k, h^k)`
- `s = k - c * sk mod q`
- `k = ECVRF_nonce(pk, h)`
- `c = ECVRF_hash_points(h, gamma, g^k, h^k)`
- `s = k + c * sk mod q`
- `W = 1`
- `S = s`
- `PI_0 = (gamma, c, S, W)`
@ -455,18 +455,18 @@ Aggregate(pk', pi', [pk], PI) with [pk] list of public keys and PI aggregated si
- `([gamma], [c], S, W, C) = PI`
- check that `n = |[pk]| == |[message]| == |[gamma]| == |[c]|`
```
U = pk_0^c_0 * .. * pk_n^c_n * g^S
= g^(sk_0*c_0) * .. * g^(sk_n*c_n) * g^(k_0 - sk0*c_0 + .. + k_n - sk_n*c_n)
U = pk_0^-c_0 * .. * pk_n^-c_n * g^S
= g^(sk_0*-c_0) * .. * g^(sk_n*-c_n) * g^(k_0 + sk0*c_0 + .. + k_n + sk_n*c_n)
= g^(k_0 + .. + k_n)
```
```
V = W* gamma_0^c_0 * h_0^S * .. * gamma_n^c_n * h_n^S
= h_0^(s_0 - S) * .. * h_n^(s_0 - S) * h_0^(sk_0*c_0 + S) * .. * h_n^(sk_n*c_n + S)
= h_0^(k_0 - sk_0*c_0 - S + sk_0*c_0 + S) * .. * h_n^(k_n - sk_n*c_n - S + sk_n*c_n + S)
V = W* gamma_0^-c_0 * h_0^S * .. * gamma_n^-c_n * h_n^S
= h_0^(s_0 - S) * .. * h_n^(s_0 - S) * h_0^(sk_0*-c_0 + S) * .. * h_n^(sk_n*-c_n + S)
= h_0^(k_0 + sk_0*c_0 - S - sk_0*c_0 + S) * .. * h_n^(k_n + sk_n*c_n - S - sk_n*c_n + S)
= h_0^k_0 * .. * h_n^k_n
```
- `C = ECVRF_hash_points(g, h_n, pk0 * .. * pk_n, gamma_0 * .. * gamma_n, U, V)`
- `C = ECVRF_hash_points(h_n, gamma_0 * .. * gamma_n, U, V)`
- verify that `C == c_n`
Note: we could probably store the product of gamma points instead

View File

@ -48,7 +48,7 @@ Pairing based crypto libraries are not frequent, so it might be hard to implemen
### Verifiable random functions
By reusing primitives from https://tools.ietf.org/html/draft-goldbe-vrf-01#section-5 , we can generate
By reusing primitives from https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04#section-5 , we can generate
aggregated non interactive proof of discrete logarithms, that match our requirements.
We have an example that uses the curve25519-dalek Rust crate, with the Ristretto group.
@ -60,23 +60,23 @@ Here are some benchmarks for this approach:
The "first" benchmark uses the scheme described in the `DESIGN.md` document.
```
test bench::sign_first_block ... bench: 281,512 ns/iter (+/- 51,906)
test bench::sign_second_block ... bench: 737,981 ns/iter (+/- 190,380)
test bench::sign_third_block ... bench: 888,010 ns/iter (+/- 129,310)
test bench::verify_one_block ... bench: 334,755 ns/iter (+/- 25,783)
test bench::verify_three_blocks ... bench: 846,658 ns/iter (+/- 101,685)
test bench::verify_two_blocks ... bench: 568,526 ns/iter (+/- 106,227)
test bench::sign_first_block ... bench: 254,468 ns/iter (+/- 20,833)
test bench::sign_second_block ... bench: 690,781 ns/iter (+/- 140,195)
test bench::sign_third_block ... bench: 844,560 ns/iter (+/- 44,068)
test bench::verify_one_block ... bench: 322,904 ns/iter (+/- 27,904)
test bench::verify_two_blocks ... bench: 548,263 ns/iter (+/- 73,312)
test bench::verify_three_blocks ... bench: 748,755 ns/iter (+/- 95,676)
```
The "second" benchmark modifies that scheme to precalculate some point additions.
```
test bench::sign_first_block ... bench: 333,231 ns/iter (+/- 111,969)
test bench::sign_second_block ... bench: 715,324 ns/iter (+/- 163,933)
test bench::sign_third_block ... bench: 818,048 ns/iter (+/- 220,139)
test bench::verify_one_block ... bench: 264,095 ns/iter (+/- 103,451)
test bench::verify_three_blocks ... bench: 434,528 ns/iter (+/- 28,993)
test bench::verify_two_blocks ... bench: 345,233 ns/iter (+/- 22,881)
test bench::sign_first_block ... bench: 325,743 ns/iter (+/- 34,561)
test bench::sign_second_block ... bench: 678,686 ns/iter (+/- 147,267)
test bench::sign_third_block ... bench: 866,091 ns/iter (+/- 282,052)
test bench::verify_one_block ... bench: 264,231 ns/iter (+/- 54,111)
test bench::verify_two_blocks ... bench: 322,503 ns/iter (+/- 17,924)
test bench::verify_three_blocks ... bench: 418,594 ns/iter (+/- 37,085)
```
There's probably a lot of low hanging fruit in optimizing those, but
@ -182,8 +182,8 @@ It has som slight differences in behaviour with the other methods, though:
| | 1 block | 2 blocks | 3 blocks |
| --------- | ------- | -------- | -------- |
| pairing | 2932 μs | 3234 μs | |
| VRF 1 | 281 μs | 737 μs | 888 μs |
| VRF 2 | 333 μs | 715 μs | 818 μs |
| VRF 1 | 254 μs | 690 μs | 844 μs |
| VRF 2 | 325 μs | 678 μs | 866 μs |
| challenge | 325 μs | 402 μs | 405 μs |
### Verifying
@ -191,8 +191,8 @@ It has som slight differences in behaviour with the other methods, though:
| | 1 block | 2 blocks | 3 blocks |
| --------- | -------- | -------- | -------- |
| pairing | 15170 μs | 22570 μs | 30657 μs |
| VRF 1 | 334 μs | 846 μs | 568 μs |
| VRF 2 | 264 μs | 434 μs | 345 μs |
| VRF 1 | 322 μs | 548 μs | 748 μs |
| VRF 2 | 264 μs | 322 μs | 418 μs |
| challenge | 308 μs | 472 μs | 624 μs |
### Size overhead

View File

@ -8,3 +8,4 @@ edition = "2018"
curve25519-dalek = "^1.0"
rand = "^0.6"
sha2 = "^0.8"
hmac = "^0.7"

View File

@ -4,9 +4,8 @@ extern crate test;
extern crate biscuit_vrf;
extern crate rand;
use rand::{Rng, SeedableRng, XorShiftRng, OsRng};
use rand::rngs::OsRng;
use biscuit_vrf::{KeyPair, Token, TokenSignature};
use test::Bencher;
mod bench {
use super::*;
@ -19,12 +18,12 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
b.iter(||{
Token::new(&mut rng, &keypair1, &message1[..])
Token::new(&keypair1, &message1[..])
});
}
@ -35,7 +34,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -44,12 +43,12 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
b.iter(||{
token1.append(&mut rng, &keypair2, &message2[..])
token1.append(&keypair2, &message2[..])
});
}
@ -60,7 +59,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -69,7 +68,7 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -78,12 +77,12 @@ mod bench {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");
b.iter(||{
token2.append(&mut rng, &keypair3, &message3[..])
token2.append(&keypair3, &message3[..])
});
}
@ -94,7 +93,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -110,7 +109,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -119,7 +118,7 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -135,7 +134,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -144,7 +143,7 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -153,7 +152,7 @@ mod bench {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");

View File

@ -4,8 +4,8 @@ extern crate test;
extern crate biscuit_vrf;
extern crate rand;
use rand::{Rng, SeedableRng, XorShiftRng, OsRng};
use biscuit_vrf::second::{KeyPair, Token, TokenSignature};
use rand::rngs::OsRng;
use biscuit_vrf::second::{KeyPair, Token};
use test::Bencher;
mod bench {
@ -19,12 +19,12 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
b.iter(||{
Token::new(&mut rng, &keypair1, &message1[..])
Token::new(&keypair1, &message1[..])
});
}
@ -35,7 +35,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -44,12 +44,12 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
b.iter(||{
token1.append(&mut rng, &keypair2, &message2[..])
token1.append(&keypair2, &message2[..])
});
}
@ -60,7 +60,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -69,7 +69,7 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -78,12 +78,12 @@ mod bench {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");
b.iter(||{
token2.append(&mut rng, &keypair3, &message3[..])
token2.append(&keypair3, &message3[..])
});
}
@ -94,7 +94,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -110,7 +110,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -119,7 +119,7 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -135,7 +135,7 @@ mod bench {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -144,7 +144,7 @@ mod bench {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -153,7 +153,7 @@ mod bench {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");

View File

@ -1,11 +1,15 @@
#![allow(non_snake_case)]
extern crate curve25519_dalek;
extern crate rand;
extern crate sha2;
extern crate hmac;
pub mod second;
use sha2::{Digest, Sha512};
use rand::{Rng, CryptoRng, OsRng};
use hmac::{Hmac, Mac};
use rand::prelude::*;
use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{RistrettoPoint},
@ -14,6 +18,9 @@ use curve25519_dalek::{
};
use std::ops::{Deref, Neg};
type HmacSha512 = Hmac<Sha512>;
pub struct KeyPair {
private: Scalar,
public: RistrettoPoint,
@ -35,8 +42,8 @@ pub struct Token {
}
impl Token {
pub fn new<T: Rng + CryptoRng>(rng: &mut T, keypair: &KeyPair, message: &[u8]) -> Self {
let signature = TokenSignature::new(rng, keypair, message);
pub fn new(keypair: &KeyPair, message: &[u8]) -> Self {
let signature = TokenSignature::new(keypair, message);
Token {
messages: vec![message.to_owned()],
@ -45,8 +52,8 @@ impl Token {
}
}
pub fn append<T: Rng + CryptoRng>(&self, rng: &mut T, keypair: &KeyPair, message: &[u8]) -> Self {
let signature = self.signature.sign(rng, &self.keys, &self.messages, keypair, message);
pub fn append(&self, keypair: &KeyPair, message: &[u8]) -> Self {
let signature = self.signature.sign(&self.keys, &self.messages, keypair, message);
let mut t = Token {
messages: self.messages.clone(),
@ -73,14 +80,14 @@ pub struct TokenSignature {
}
impl TokenSignature {
pub fn new<T: Rng + CryptoRng>(rng: &mut T, keypair: &KeyPair, message: &[u8]) -> Self {
pub fn new(keypair: &KeyPair, message: &[u8]) -> Self {
let h = ECVRF_hash_to_curve(keypair.public, message);
let gamma = keypair.private * h;
let k = Scalar::random(rng);
let c = ECVRF_hash_points(&[RISTRETTO_BASEPOINT_POINT, h, keypair.public, gamma,
k* RISTRETTO_BASEPOINT_POINT, k*h]);
let s = (k - c * keypair.private).reduce();
let k = ECVRF_nonce(keypair.private, h);
let c = ECVRF_hash_points(&[h, gamma, k* RISTRETTO_BASEPOINT_POINT, k*h]);
let s = (k + c * keypair.private).reduce();
// W = h^(s0 - S) * .. * hn^(sn - S)
let w = RistrettoPoint::identity();
TokenSignature {
@ -91,13 +98,13 @@ impl TokenSignature {
}
}
pub fn sign<T: Rng + CryptoRng, M: Deref<Target=[u8]>>(&self, rng: &mut T, public_keys: &[RistrettoPoint],
pub fn sign<M: Deref<Target=[u8]>>(&self, public_keys: &[RistrettoPoint],
messages: &[M], keypair: &KeyPair, message: &[u8]) -> Self {
let h = ECVRF_hash_to_curve(keypair.public, message);
let gamma = keypair.private * h;
let k = Scalar::random(rng);
let k = ECVRF_nonce(keypair.private, h);
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*c).collect::<Vec<_>>();
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*(c.neg())).collect::<Vec<_>>();
// u = g^(k0 + k1 + ... + kn)
let u = add_points(&pc) + (self.s * RISTRETTO_BASEPOINT_POINT) + (k * RISTRETTO_BASEPOINT_POINT);
@ -106,15 +113,14 @@ impl TokenSignature {
// v = h0^k0 * h1^k1 * .. * hn^k^n
let v = self.gamma.iter().zip(self.c.iter()).fold(self.w, |acc, (gamma, c)| {
(c * gamma) + acc
(c.neg() * gamma) + acc
}) + (self.s * hashes_sum) + (k * h);
let p = add_points(public_keys);
let gammas = add_points(&self.gamma);
let c = ECVRF_hash_points(&[RISTRETTO_BASEPOINT_POINT, h, p + keypair.public, gammas + gamma, u, v]);
let c = ECVRF_hash_points(&[h, gammas + gamma, u, v]);
let s = (k - c * keypair.private).reduce();
let s = (k + c * keypair.private).reduce();
let agg_s = (self.s + s).reduce();
let hs = hashes_sum * s.neg();
@ -140,7 +146,7 @@ impl TokenSignature {
return false;
}
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*c).collect::<Vec<_>>();
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*(c.neg())).collect::<Vec<_>>();
// u = g^(k0 + k1 + ... + kn)
let u = add_points(&pc) + (self.s *RISTRETTO_BASEPOINT_POINT);
@ -148,13 +154,12 @@ impl TokenSignature {
// v = h0^k0 * h1^k1 * .. * hn^k^n
let v = self.gamma.iter().zip(self.c.iter()).zip(hashes.iter()).fold(self.w, |acc, ((gamma, c), h)| {
(c * gamma) + (self.s * h) + acc
(c.neg() * gamma) + (self.s * h) + acc
});
let p = add_points(public_keys);
let gammas = add_points(&self.gamma);
let c = ECVRF_hash_points(&[RISTRETTO_BASEPOINT_POINT, *hashes.last().unwrap(), p, gammas, u, v]);
let c = ECVRF_hash_points(&[*hashes.last().unwrap(), gammas, u, v]);
c == *self.c.last().unwrap()
}
@ -193,10 +198,38 @@ pub fn add_points(points: &[RistrettoPoint]) -> RistrettoPoint {
}
}
pub fn ECVRF_nonce(sk: Scalar, point: RistrettoPoint) -> Scalar {
let k = [0u8; 64];
let v = [1u8; 64];
let mut mac = HmacSha512::new_varkey(&k[..]).unwrap();
mac.input(&v[..]);
mac.input(&[0]);
mac.input(&sk.as_bytes()[..]);
mac.input(point.compress().as_bytes());
let k = mac.result().code();
let mut mac = HmacSha512::new_varkey(&k[..]).unwrap();
mac.input(&v[..]);
mac.input(&[1]);
mac.input(&sk.as_bytes()[..]);
mac.input(point.compress().as_bytes());
let k = mac.result().code();
// the process in RFC 6979 is a bit ore complex than that
let mut h = Sha512::new();
h.input(k);
Scalar::from_hash(h)
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{OsRng,SeedableRng,StdRng};
#[test]
fn three_messages() {
@ -207,7 +240,7 @@ mod tests {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -216,7 +249,7 @@ mod tests {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -225,7 +258,7 @@ mod tests {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");
}
@ -239,7 +272,7 @@ mod tests {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -248,7 +281,7 @@ mod tests {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let mut token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let mut token2 = token1.append(&keypair2, &message2[..]);
token2.messages[1] = Vec::from(&b"you"[..]);
@ -259,7 +292,7 @@ mod tests {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");
}

View File

@ -1,8 +1,8 @@
//! same solution as in src/lib.rs, but preaggregating the 'gamma' points
//! note: now the gamma points are not added into the 'c' hash calculation
#![allow(non_snake_case)]
use sha2::{Digest, Sha512};
use rand::{Rng, CryptoRng, OsRng};
use rand::prelude::*;
use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{RistrettoPoint},
@ -10,6 +10,7 @@ use curve25519_dalek::{
traits::Identity
};
use std::ops::{Deref, Neg};
use super::{ECVRF_hash_to_curve, ECVRF_hash_points, ECVRF_nonce, add_points};
pub struct KeyPair {
private: Scalar,
@ -32,8 +33,8 @@ pub struct Token {
}
impl Token {
pub fn new<T: Rng + CryptoRng>(rng: &mut T, keypair: &KeyPair, message: &[u8]) -> Self {
let signature = TokenSignature::new(rng, keypair, message);
pub fn new(keypair: &KeyPair, message: &[u8]) -> Self {
let signature = TokenSignature::new(keypair, message);
Token {
messages: vec![message.to_owned()],
@ -42,8 +43,8 @@ impl Token {
}
}
pub fn append<T: Rng + CryptoRng>(&self, rng: &mut T, keypair: &KeyPair, message: &[u8]) -> Self {
let signature = self.signature.sign(rng, &self.keys, &self.messages, keypair, message);
pub fn append(&self, keypair: &KeyPair, message: &[u8]) -> Self {
let signature = self.signature.sign(&self.keys, &self.messages, keypair, message);
let mut t = Token {
messages: self.messages.clone(),
@ -70,31 +71,31 @@ pub struct TokenSignature {
}
impl TokenSignature {
pub fn new<T: Rng + CryptoRng>(rng: &mut T, keypair: &KeyPair, message: &[u8]) -> Self {
pub fn new(keypair: &KeyPair, message: &[u8]) -> Self {
let h = ECVRF_hash_to_curve(keypair.public, message);
let gamma = keypair.private * h;
let k = Scalar::random(rng);
let c = ECVRF_hash_points(&[RISTRETTO_BASEPOINT_POINT, h, keypair.public,
k* RISTRETTO_BASEPOINT_POINT, k*h]);
let s = (k - c * keypair.private).reduce();
let k = ECVRF_nonce(keypair.private, h);
let c = ECVRF_hash_points(&[h, keypair.public, k* RISTRETTO_BASEPOINT_POINT, k*h]);
let s = (k + c * keypair.private).reduce();
// W = h^(s0 - S) * .. * hn^(sn - S)
let w = RistrettoPoint::identity();
TokenSignature {
gamma_agg: c * gamma,
gamma_agg: c.neg() * gamma,
c: vec![c],
w,
s
}
}
pub fn sign<T: Rng + CryptoRng, M: Deref<Target=[u8]>>(&self, rng: &mut T, public_keys: &[RistrettoPoint],
pub fn sign<M: Deref<Target=[u8]>>(&self, public_keys: &[RistrettoPoint],
messages: &[M], keypair: &KeyPair, message: &[u8]) -> Self {
let h = ECVRF_hash_to_curve(keypair.public, message);
let gamma = keypair.private * h;
let k = Scalar::random(rng);
let k = ECVRF_nonce(keypair.private, h);
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*c).collect::<Vec<_>>();
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*(c.neg())).collect::<Vec<_>>();
// u = g^(k0 + k1 + ... + kn)
let u = add_points(&pc) + (self.s * RISTRETTO_BASEPOINT_POINT) + (k * RISTRETTO_BASEPOINT_POINT);
@ -106,17 +107,16 @@ impl TokenSignature {
let p = add_points(public_keys);
let c = ECVRF_hash_points(&[RISTRETTO_BASEPOINT_POINT, h, p + keypair.public,
u, v]);
let c = ECVRF_hash_points(&[h, p + keypair.public, u, v]);
let s = (k - c * keypair.private).reduce();
let s = (k + c * keypair.private).reduce();
let agg_s = (self.s + s).reduce();
let hs = hashes_sum * s.neg();
let w = self.w + hs + h * self.s.neg();
let mut res = TokenSignature {
gamma_agg: self.gamma_agg + c * gamma,
gamma_agg: self.gamma_agg + c.neg() * gamma,
c: self.c.clone(),
w,
s: agg_s
@ -133,7 +133,7 @@ impl TokenSignature {
return false;
}
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*c).collect::<Vec<_>>();
let pc = public_keys.iter().zip(self.c.iter()).map(|(p, c)| p*c.neg()).collect::<Vec<_>>();
// u = g^(k0 + k1 + ... + kn)
let u = add_points(&pc) + (self.s *RISTRETTO_BASEPOINT_POINT);
@ -144,50 +144,16 @@ impl TokenSignature {
let p = add_points(public_keys);
let c = ECVRF_hash_points(&[RISTRETTO_BASEPOINT_POINT, *hashes.last().unwrap(), p,
u, v]);
let c = ECVRF_hash_points(&[*hashes.last().unwrap(), p, u, v]);
c == *self.c.last().unwrap()
}
}
//FIXME: the ECVRF_hash_to_curve1 looks like a hash and pray, but since
// curve25519-dalek already has a hash to curve function, we can reuse it instead?
pub fn ECVRF_hash_to_curve(point: RistrettoPoint, data: &[u8]) -> RistrettoPoint {
let h = Sha512::new()
.chain(point.compress().as_bytes())
.chain(data);
RistrettoPoint::from_hash(h)
}
//FIXME: is the output value in the right set?
pub fn ECVRF_hash_points(points: &[RistrettoPoint]) -> Scalar {
let mut h = Sha512::new();
for point in points.iter() {
h.input(point.compress().as_bytes());
}
Scalar::from_hash(h)
}
pub fn add_points(points: &[RistrettoPoint]) -> RistrettoPoint {
assert!(points.len() > 0);
if points.len() == 1 {
points[0]
} else {
let mut it = points.iter();
let first = it.next().unwrap();
it.fold(*first, |acc, pk| acc + pk)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{OsRng,SeedableRng,StdRng};
#[test]
fn three_messages() {
@ -198,7 +164,7 @@ mod tests {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -207,7 +173,7 @@ mod tests {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let token2 = token1.append(&keypair2, &message2[..]);
assert!(token2.verify(), "cannot verify second token");
@ -216,7 +182,7 @@ mod tests {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");
}
@ -230,7 +196,7 @@ mod tests {
let message1 = b"hello";
let keypair1 = KeyPair::new(&mut rng);
let token1 = Token::new(&mut rng, &keypair1, &message1[..]);
let token1 = Token::new(&keypair1, &message1[..]);
assert!(token1.verify(), "cannot verify first token");
@ -239,7 +205,7 @@ mod tests {
let message2 = b"world";
let keypair2 = KeyPair::new(&mut rng);
let mut token2 = token1.append(&mut rng, &keypair2, &message2[..]);
let mut token2 = token1.append(&keypair2, &message2[..]);
token2.messages[1] = Vec::from(&b"you"[..]);
@ -250,7 +216,7 @@ mod tests {
let message3 = b"!!!";
let keypair3 = KeyPair::new(&mut rng);
let token3 = token2.append(&mut rng, &keypair3, &message3[..]);
let token3 = token2.append(&keypair3, &message3[..]);
assert!(token3.verify(), "cannot verify third token");
}