-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
ecvrf.rs
323 lines (283 loc) · 10.1 KB
/
ecvrf.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0
//! This module implements an instantiation of a verifiable random function known as
//! [ECVRF-ED25519-SHA512-TAI](https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04).
//!
//! # Examples
//!
//! ```
//! use nextgen_crypto::{traits::Uniform, vrf::ecvrf::*};
//! use rand::{rngs::StdRng, SeedableRng};
//!
//! let message = b"Test message";
//! let mut rng: StdRng = SeedableRng::from_seed([0; 32]);
//! let private_key = VRFPrivateKey::generate_for_testing(&mut rng);
//! let public_key: VRFPublicKey = (&private_key).into();
//! ```
//! **Note**: The above example generates a private key using a private function intended only for
//! testing purposes. Production code should find an alternate means for secure key generation.
//!
//! Produce a proof for a message from a `VRFPrivateKey`, and verify the proof and message
//! using a `VRFPublicKey`:
//!
//! ```
//! # use nextgen_crypto::{traits::Uniform, vrf::ecvrf::*};
//! # use rand::{rngs::StdRng, SeedableRng};
//! # let message = b"Test message";
//! # let mut rng: StdRng = SeedableRng::from_seed([0; 32]);
//! # let private_key = VRFPrivateKey::generate_for_testing(&mut rng);
//! # let public_key: VRFPublicKey = (&private_key).into();
//! let proof = private_key.prove(message);
//! assert!(public_key.verify(&proof, message).is_ok());
//! ```
//!
//! Produce a pseudorandom output from a `Proof`:
//!
//! ```
//! # use nextgen_crypto::{traits::Uniform, vrf::ecvrf::*};
//! # use rand::{rngs::StdRng, SeedableRng};
//! # let message = b"Test message";
//! # let mut rng: StdRng = SeedableRng::from_seed([0; 32]);
//! # let private_key = VRFPrivateKey::generate_for_testing(&mut rng);
//! # let public_key: VRFPublicKey = (&private_key).into();
//! # let proof = private_key.prove(message);
//! let output: Output = (&proof).into();
//! ```
use crate::traits::*;
use core::convert::TryFrom;
use curve25519_dalek::{
constants::ED25519_BASEPOINT_POINT,
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar as ed25519_Scalar,
};
use derive_deref::Deref;
use ed25519_dalek::{
self, Digest, PublicKey as ed25519_PublicKey, SecretKey as ed25519_PrivateKey, Sha512,
};
use failure::prelude::*;
use serde::{Deserialize, Serialize};
const SUITE: u8 = 0x03;
const ONE: u8 = 0x01;
const TWO: u8 = 0x02;
const THREE: u8 = 0x03;
/// The number of bytes of [`Output`]
pub const OUTPUT_LENGTH: usize = 64;
/// The number of bytes of [`Proof`]
pub const PROOF_LENGTH: usize = 80;
/// An ECVRF private key
#[derive(Serialize, Deserialize, Deref, Debug)]
pub struct VRFPrivateKey(ed25519_PrivateKey);
/// An ECVRF public key
#[derive(Serialize, Deserialize, Deref, Debug, PartialEq, Eq)]
pub struct VRFPublicKey(ed25519_PublicKey);
/// A longer private key which is slightly optimized for proof generation.
///
/// This is similar in structure to ed25519_dalek::ExpandedSecretKey. It can be produced from
/// a VRFPrivateKey.
pub struct VRFExpandedPrivateKey {
pub(super) key: ed25519_Scalar,
pub(super) nonce: [u8; 32],
}
impl VRFPrivateKey {
/// Produces a proof for an input (using the private key)
pub fn prove(&self, alpha: &[u8]) -> Proof {
VRFExpandedPrivateKey::from(self).prove(&VRFPublicKey((&self.0).into()), alpha)
}
}
impl VRFExpandedPrivateKey {
/// Produces a proof for an input (using the expanded private key)
pub fn prove(&self, pk: &VRFPublicKey, alpha: &[u8]) -> Proof {
let h_point = pk.hash_to_curve(alpha);
let k_scalar =
ed25519_Scalar::from_bytes_mod_order_wide(&nonce_generation_bytes(self.nonce, h_point));
let gamma = h_point * self.key;
let c_scalar = hash_points(&[
h_point,
gamma,
ED25519_BASEPOINT_POINT * k_scalar,
h_point * k_scalar,
]);
Proof {
gamma,
c: c_scalar,
s: k_scalar + c_scalar * self.key,
}
}
}
impl Uniform for VRFPrivateKey {
fn generate_for_testing<R>(rng: &mut R) -> Self
where
R: SeedableCryptoRng,
{
VRFPrivateKey(ed25519_PrivateKey::generate(rng))
}
}
impl TryFrom<&[u8]> for VRFPrivateKey {
type Error = CryptoMaterialError;
fn try_from(bytes: &[u8]) -> std::result::Result<VRFPrivateKey, CryptoMaterialError> {
Ok(VRFPrivateKey(
ed25519_PrivateKey::from_bytes(bytes).unwrap(),
))
}
}
impl TryFrom<&[u8]> for VRFPublicKey {
type Error = CryptoMaterialError;
fn try_from(bytes: &[u8]) -> std::result::Result<VRFPublicKey, CryptoMaterialError> {
if bytes.len() != ed25519_dalek::PUBLIC_KEY_LENGTH {
return Err(CryptoMaterialError::WrongLengthError);
}
let mut bits: [u8; 32] = [0u8; 32];
bits.copy_from_slice(&bytes[..32]);
let compressed = curve25519_dalek::edwards::CompressedEdwardsY(bits);
let point = compressed
.decompress()
.ok_or(CryptoMaterialError::DeserializationError)?;
// Check if the point lies on a small subgroup. This is required
// when using curves with a small cofactor (in ed25519, cofactor = 8).
if point.is_small_order() {
return Err(CryptoMaterialError::SmallSubgroupError);
}
Ok(VRFPublicKey(ed25519_PublicKey::from_bytes(bytes).unwrap()))
}
}
impl VRFPublicKey {
/// Given a [`Proof`] and an input, returns whether or not the proof is valid for the input
/// and public key
pub fn verify(&self, proof: &Proof, alpha: &[u8]) -> Result<()> {
let h_point = self.hash_to_curve(alpha);
let pk_point = CompressedEdwardsY::from_slice(self.as_bytes())
.decompress()
.unwrap();
let cprime = hash_points(&[
h_point,
proof.gamma,
ED25519_BASEPOINT_POINT * proof.s - pk_point * proof.c,
h_point * proof.s - proof.gamma * proof.c,
]);
if proof.c == cprime {
Ok(())
} else {
bail!("The proof failed to verify for this public key")
}
}
pub(super) fn hash_to_curve(&self, alpha: &[u8]) -> EdwardsPoint {
let mut result = [0u8; 32];
let mut counter = 0;
let mut wrapped_point: Option<EdwardsPoint> = None;
while wrapped_point.is_none() {
result.copy_from_slice(
&Sha512::new()
.chain(&[SUITE, ONE])
.chain(self.as_bytes())
.chain(&alpha)
.chain(&[counter])
.result()[..32],
);
wrapped_point = CompressedEdwardsY::from_slice(&result).decompress();
counter += 1;
}
wrapped_point.unwrap().mul_by_cofactor()
}
}
impl<'a> From<&'a VRFPrivateKey> for VRFPublicKey {
fn from(private_key: &'a VRFPrivateKey) -> Self {
let secret: &ed25519_PrivateKey = private_key;
let public: ed25519_PublicKey = secret.into();
VRFPublicKey(public)
}
}
impl<'a> From<&'a VRFPrivateKey> for VRFExpandedPrivateKey {
fn from(private_key: &'a VRFPrivateKey) -> Self {
let mut h: Sha512 = Sha512::default();
let mut hash: [u8; 64] = [0u8; 64];
let mut lower: [u8; 32] = [0u8; 32];
let mut upper: [u8; 32] = [0u8; 32];
h.input(private_key.to_bytes());
hash.copy_from_slice(h.result().as_slice());
lower.copy_from_slice(&hash[00..32]);
upper.copy_from_slice(&hash[32..64]);
lower[0] &= 248;
lower[31] &= 63;
lower[31] |= 64;
VRFExpandedPrivateKey {
key: ed25519_Scalar::from_bits(lower),
nonce: upper,
}
}
}
/// A VRF proof that can be used to validate an input with a public key
pub struct Proof {
gamma: EdwardsPoint,
c: ed25519_Scalar,
s: ed25519_Scalar,
}
impl Proof {
/// Produces a new Proof struct from its fields
pub fn new(gamma: EdwardsPoint, c: ed25519_Scalar, s: ed25519_Scalar) -> Proof {
Proof { gamma, c, s }
}
/// Converts a Proof into bytes
pub fn to_bytes(&self) -> [u8; PROOF_LENGTH] {
let mut ret = [0u8; PROOF_LENGTH];
ret[..32].copy_from_slice(&self.gamma.compress().to_bytes()[..]);
ret[32..48].copy_from_slice(&self.c.to_bytes()[..16]);
ret[48..].copy_from_slice(&self.s.to_bytes()[..]);
ret
}
}
impl TryFrom<&[u8]> for Proof {
type Error = CryptoMaterialError;
fn try_from(bytes: &[u8]) -> std::result::Result<Proof, CryptoMaterialError> {
let mut c_buf = [0u8; 32];
c_buf[..16].copy_from_slice(&bytes[32..48]);
let mut s_buf = [0u8; 32];
s_buf.copy_from_slice(&bytes[48..]);
Ok(Proof {
gamma: CompressedEdwardsY::from_slice(&bytes[..32])
.decompress()
.unwrap(),
c: ed25519_Scalar::from_bits(c_buf),
s: ed25519_Scalar::from_bits(s_buf),
})
}
}
/// The ECVRF output produced from the proof
pub struct Output([u8; OUTPUT_LENGTH]);
impl Output {
/// Converts an Output into bytes
#[inline]
pub fn to_bytes(&self) -> [u8; OUTPUT_LENGTH] {
self.0
}
}
impl<'a> From<&'a Proof> for Output {
fn from(proof: &'a Proof) -> Output {
let mut output = [0u8; OUTPUT_LENGTH];
output.copy_from_slice(
&Sha512::new()
.chain(&[SUITE, THREE])
.chain(&proof.gamma.mul_by_cofactor().compress().to_bytes()[..])
.result()[..],
);
Output(output)
}
}
pub(super) fn nonce_generation_bytes(nonce: [u8; 32], h_point: EdwardsPoint) -> [u8; 64] {
let mut k_buf = [0u8; 64];
k_buf.copy_from_slice(
&Sha512::new()
.chain(nonce)
.chain(h_point.compress().as_bytes())
.result()[..],
);
k_buf
}
pub(super) fn hash_points(points: &[EdwardsPoint]) -> ed25519_Scalar {
let mut result = [0u8; 32];
let mut hash = Sha512::new().chain(&[SUITE, TWO]);
for point in points.iter() {
hash = hash.chain(point.compress().to_bytes());
}
result[..16].copy_from_slice(&hash.result()[..16]);
ed25519_Scalar::from_bits(result)
}