mirror of
https://github.com/biscuit-auth/biscuit.git
synced 2024-09-11 06:15:30 +03:00
update EC-VRF algorithm to IETF's draft 4
This commit is contained in:
parent
2bec1d22a2
commit
0149babd71
38
DESIGN.md
38
DESIGN.md
@ -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
|
||||
|
@ -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
|
||||
|
@ -8,3 +8,4 @@ edition = "2018"
|
||||
curve25519-dalek = "^1.0"
|
||||
rand = "^0.6"
|
||||
sha2 = "^0.8"
|
||||
hmac = "^0.7"
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user