1use curve25519_dalek::{EdwardsPoint, Scalar};
2use sha2::Sha512;
3
4use crate::{
5 Ciphersuite,
6 ec::util,
7 error::{VrfError, VrfResult},
8};
9
10const CHALLENGE_LEN: usize = 16;
11const Q_LEN: usize = 32;
12const PT_LEN: usize = 32;
13
14pub mod tai {
15 use sha2::Sha512;
16
17 use crate::{
18 Ciphersuite,
19 ec::edwards25519::{
20 EdVrfProof,
21 internal::{EdVrfEdwards25519PublicKey, EdVrfEdwards25519SecretKey},
22 },
23 error::VrfResult,
24 };
25
26 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
27 pub struct EdVrfEdwards25519Tai;
28
29 impl crate::VRF for EdVrfEdwards25519Tai {
30 type Hash = Sha512;
31 type Proof = EdVrfProof;
32 type Verifier = EdVrfEdwards25519TaiPublicKey;
33 type Prover = EdVrfEdwards25519TaiSecretKey;
34
35 fn ciphersuite(&self) -> Ciphersuite {
36 Ciphersuite::ECVRF_EDWARDS25519_SHA512_TAI
37 }
38 }
39
40 #[derive(zeroize::ZeroizeOnDrop, PartialEq, Eq)]
41 #[repr(transparent)]
42 pub struct EdVrfEdwards25519TaiSecretKey(EdVrfEdwards25519SecretKey);
43
44 impl crate::Prover<Sha512> for EdVrfEdwards25519TaiSecretKey {
45 type Proof = EdVrfProof;
46 type Verifier = EdVrfEdwards25519TaiPublicKey;
47
48 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
49 where
50 Self: Sized,
51 {
52 Ok(Self(EdVrfEdwards25519SecretKey::from_sk(bytes.try_into()?)))
53 }
54
55 #[cfg(feature = "hazmat")]
56 fn x_equals(&self, value: &[u8]) -> bool {
57 self.0.x_equals(value)
58 }
59
60 fn verifier(&self) -> Self::Verifier {
61 EdVrfEdwards25519TaiPublicKey(self.0.public_key())
62 }
63
64 fn prove(&self, alpha: &[u8]) -> VrfResult<Self::Proof> {
65 self.0
66 .prove(Ciphersuite::ECVRF_EDWARDS25519_SHA512_TAI, alpha)
67 }
68 }
69
70 #[derive(Debug, PartialEq, Eq)]
71 #[repr(transparent)]
72 pub struct EdVrfEdwards25519TaiPublicKey(EdVrfEdwards25519PublicKey);
73
74 impl crate::Verifier<Sha512> for EdVrfEdwards25519TaiPublicKey {
75 type Proof = EdVrfProof;
76
77 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
78 where
79 Self: Sized,
80 {
81 Ok(Self(EdVrfEdwards25519PublicKey::from_bytes(bytes)?))
82 }
83
84 fn verify(&self, alpha: &[u8], proof: Self::Proof) -> VrfResult<digest::Output<Sha512>> {
85 use crate::Proof as _;
86 self.0
87 .verify(Ciphersuite::ECVRF_EDWARDS25519_SHA512_TAI, alpha, proof)?
88 .proof_to_hash(Ciphersuite::ECVRF_EDWARDS25519_SHA512_TAI)
89 }
90 }
91}
92
93pub mod elligator2 {
94 use sha2::Sha512;
95
96 use crate::{
97 Ciphersuite,
98 ec::edwards25519::{
99 EdVrfProof,
100 internal::{EdVrfEdwards25519PublicKey, EdVrfEdwards25519SecretKey},
101 },
102 error::VrfResult,
103 };
104
105 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
106 pub struct EdVrfEdwards25519Ell2;
107
108 impl crate::VRF for EdVrfEdwards25519Ell2 {
109 type Hash = Sha512;
110 type Proof = EdVrfProof;
111 type Verifier = EdVrfEdwards25519Ell2PublicKey;
112 type Prover = EdVrfEdwards25519Ell2SecretKey;
113
114 fn ciphersuite(&self) -> Ciphersuite {
115 Ciphersuite::ECVRF_EDWARDS25519_SHA512_ELL2
116 }
117 }
118
119 #[derive(zeroize::ZeroizeOnDrop, PartialEq, Eq)]
120 #[repr(transparent)]
121 pub struct EdVrfEdwards25519Ell2SecretKey(EdVrfEdwards25519SecretKey);
122
123 impl crate::Prover<Sha512> for EdVrfEdwards25519Ell2SecretKey {
124 type Proof = EdVrfProof;
125 type Verifier = EdVrfEdwards25519Ell2PublicKey;
126
127 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
128 where
129 Self: Sized,
130 {
131 Ok(Self(EdVrfEdwards25519SecretKey::from_sk(bytes.try_into()?)))
132 }
133
134 #[cfg(feature = "hazmat")]
135 fn x_equals(&self, value: &[u8]) -> bool {
136 self.0.x_equals(value)
137 }
138
139 fn verifier(&self) -> Self::Verifier {
140 EdVrfEdwards25519Ell2PublicKey(self.0.public_key())
141 }
142
143 fn prove(&self, alpha: &[u8]) -> VrfResult<Self::Proof> {
144 self.0
145 .prove(Ciphersuite::ECVRF_EDWARDS25519_SHA512_ELL2, alpha)
146 }
147 }
148
149 #[derive(Debug, PartialEq, Eq)]
150 #[repr(transparent)]
151 pub struct EdVrfEdwards25519Ell2PublicKey(EdVrfEdwards25519PublicKey);
152
153 impl crate::Verifier<Sha512> for EdVrfEdwards25519Ell2PublicKey {
154 type Proof = EdVrfProof;
155
156 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
157 where
158 Self: Sized,
159 {
160 Ok(Self(EdVrfEdwards25519PublicKey::from_bytes(bytes)?))
161 }
162
163 fn verify(&self, alpha: &[u8], proof: Self::Proof) -> VrfResult<digest::Output<Sha512>> {
164 use crate::Proof as _;
165 self.0
166 .verify(Ciphersuite::ECVRF_EDWARDS25519_SHA512_ELL2, alpha, proof)?
167 .proof_to_hash(Ciphersuite::ECVRF_EDWARDS25519_SHA512_ELL2)
168 }
169 }
170}
171
172#[derive(Debug, PartialEq, Eq, zeroize::ZeroizeOnDrop)]
173pub struct EdVrfProof {
174 gamma: EdwardsPoint,
175 c: Scalar,
176 s: Scalar,
177}
178
179impl crate::Proof<Sha512> for EdVrfProof {
180 const PROOF_LEN: usize = CHALLENGE_LEN + Q_LEN + PT_LEN;
181
182 fn decode_pi(pi: &[u8]) -> VrfResult<Self> {
183 if pi.len() != Self::PROOF_LEN {
184 return Err(VrfError::IncorrectPiLength {
185 expected: Self::PROOF_LEN,
186 actual: pi.len(),
187 });
188 }
189
190 let gamma_string = &pi[..PT_LEN];
191 let c_string = &pi[PT_LEN..PT_LEN + CHALLENGE_LEN];
192 let s_string = &pi[PT_LEN + CHALLENGE_LEN..];
193 debug_assert_eq!(s_string.len(), Q_LEN);
194 let gamma = curve25519_dalek::edwards::CompressedEdwardsY(gamma_string.try_into()?);
195 let gamma = gamma.decompress().ok_or(VrfError::InvalidEcPoint)?;
196
197 let mut c_bytes = [0u8; 32];
199 c_bytes[..c_string.len()].copy_from_slice(c_string);
200
201 let c = Scalar::from_bytes_mod_order(c_bytes);
202 let s = Scalar::from_bytes_mod_order(s_string.try_into()?);
203
204 Ok(EdVrfProof { gamma, c, s })
205 }
206
207 fn encode_to_pi(&self) -> Vec<u8> {
208 let ret = [
209 self.gamma.compress().as_bytes(),
210 &self.c.as_bytes()[..CHALLENGE_LEN],
211 self.s.as_bytes(),
212 ]
213 .concat();
214
215 debug_assert_eq!(ret.len(), Self::PROOF_LEN);
216
217 ret
218 }
219
220 fn proof_to_hash(&self, suite: Ciphersuite) -> VrfResult<digest::Output<Sha512>> {
221 Ok(util::proof_to_hash::<Sha512>(
222 suite,
223 self.gamma.mul_by_cofactor().compress().as_bytes(),
224 ))
225 }
226}
227
228mod internal {
229 use curve25519_dalek::{
230 EdwardsPoint, Scalar, edwards::CompressedEdwardsY, scalar::clamp_integer,
231 };
232 use digest::Digest as _;
233 use sha2::Sha512;
234 use subtle::ConstantTimeEq;
235
236 use crate::{
237 Ciphersuite,
238 consts::ecvrf::{
239 ciphersuites::ECVRF_EDWARDS25519_SHA512_ELL2,
240 e2c::{ECVRF_E2C_H2C_DST, ECVRF_EDWARDS25519_ELL2_DST},
241 },
242 ec::{
243 edwards25519::{CHALLENGE_LEN, EdVrfProof},
244 util,
245 },
246 error::{VrfError, VrfResult},
247 };
248
249 #[derive(zeroize::ZeroizeOnDrop)]
250 pub struct EdVrfEdwards25519SecretKey {
251 pub(super) x: Scalar,
252 hash_prefix: [u8; 32],
253 }
254
255 impl std::cmp::PartialEq for EdVrfEdwards25519SecretKey {
256 fn eq(&self, other: &Self) -> bool {
257 (self.hash_prefix.ct_eq(&other.hash_prefix) & self.x.ct_eq(&other.x)).into()
258 }
259 }
260
261 impl std::cmp::Eq for EdVrfEdwards25519SecretKey {}
262
263 impl EdVrfEdwards25519SecretKey {
264 pub fn from_sk(sk: [u8; 32]) -> Self {
265 let hashed = Sha512::digest(sk);
266 let mut x_bytes = [0u8; 32];
267 x_bytes.copy_from_slice(&hashed[..32]);
268 let mut hash_prefix = [0u8; 32];
269 hash_prefix.copy_from_slice(&hashed[32..]);
270 #[allow(deprecated)]
271 let x = Scalar::from_bits(clamp_integer(x_bytes));
272 Self { x, hash_prefix }
273 }
274
275 #[cfg(feature = "hazmat")]
276 pub fn x_equals(&self, value: &[u8]) -> bool {
277 self.x.as_bytes().as_slice().ct_eq(value).into()
278 }
279
280 pub fn public_key(&self) -> EdVrfEdwards25519PublicKey {
281 let point = EdwardsPoint::mul_base(&self.x);
282 EdVrfEdwards25519PublicKey {
283 compressed: point.compress(),
284 point,
285 }
286 }
287
288 fn generate_nonce(&self, h_string: &[u8]) -> Scalar {
289 let k_string = Sha512::new()
290 .chain_update(self.hash_prefix)
291 .chain_update(h_string)
292 .finalize();
293
294 Scalar::from_bytes_mod_order_wide(&k_string.into())
295 }
296
297 pub(super) fn prove(&self, suite: Ciphersuite, alpha: &[u8]) -> VrfResult<EdVrfProof> {
298 let y = self.public_key();
300 let h = y.encode_to_curve(suite, alpha)?;
302 let gamma = self.x * h.point;
304
305 let k = self.generate_nonce(h.encode_to_curve_salt());
307 let c = EdChallenge::generate(
309 suite,
310 &[
311 &y.point,
312 &h.point,
313 &gamma,
314 &EdwardsPoint::mul_base(&k),
315 &(k * h.point),
316 ],
317 );
318
319 let s = k + c * self.x;
322
323 Ok(EdVrfProof { gamma, c, s })
326 }
327 }
328
329 #[derive(Debug, Clone, PartialEq, Eq)]
330 pub struct EdVrfEdwards25519PublicKey {
331 compressed: CompressedEdwardsY,
332 point: EdwardsPoint,
333 }
334
335 impl EdVrfEdwards25519PublicKey {
336 pub fn from_bytes(bytes: &[u8]) -> VrfResult<Self> {
337 let compressed = CompressedEdwardsY::from_slice(bytes)?;
338 let point = compressed.decompress().ok_or(VrfError::InvalidEcPoint)?;
339 if point.is_small_order() {
340 return Err(VrfError::InvalidEcPoint);
341 }
342
343 Ok(Self { compressed, point })
344 }
345
346 pub fn encode_to_curve(&self, suite: Ciphersuite, alpha: &[u8]) -> VrfResult<Self> {
347 Ok(match suite {
348 Ciphersuite::ECVRF_EDWARDS25519_SHA512_TAI => self.encode_to_curve_tai(alpha)?,
349 Ciphersuite::ECVRF_EDWARDS25519_SHA512_ELL2 => self.h2c_encode_to_curve_ell2(alpha),
350 _ => return Err(VrfError::UnsupportedCiphersuite(suite)),
351 })
352 }
353
354 pub fn encode_to_curve_salt(&self) -> &[u8] {
355 self.compressed.as_bytes().as_slice()
356 }
357
358 fn encode_to_curve_tai(&self, alpha: &[u8]) -> VrfResult<Self> {
359 let salt = self.encode_to_curve_salt();
360
361 for candidate in util::encode_to_curve_tai_generator::<Sha512>(
362 Ciphersuite::ECVRF_EDWARDS25519_SHA512_TAI,
363 salt,
364 alpha,
365 ) {
366 let Ok(mut pk) = Self::from_bytes(&candidate[..32]) else {
370 continue;
371 };
372
373 pk.point = pk.point.mul_by_cofactor();
375 pk.compressed = pk.point.compress();
376 return Ok(pk);
377 }
378
379 Err(VrfError::TryAndIncrementNoCandidatesFound)
380 }
381
382 fn h2c_encode_to_curve_ell2(&self, alpha: &[u8]) -> Self {
383 let string_to_be_hashed = [self.encode_to_curve_salt(), alpha].concat();
385 let dst = [
387 &ECVRF_E2C_H2C_DST[..],
388 &ECVRF_EDWARDS25519_ELL2_DST[..],
389 &[ECVRF_EDWARDS25519_SHA512_ELL2],
390 ]
391 .concat();
392
393 let point =
394 EdwardsPoint::encode_to_curve::<sha2::Sha512>(&[&string_to_be_hashed], &[&dst]);
395
396 Self {
400 compressed: point.compress(),
401 point,
402 }
403 }
404
405 pub(super) fn verify(
406 &self,
407 suite: Ciphersuite,
408 alpha: &[u8],
409 proof: EdVrfProof,
410 ) -> VrfResult<EdVrfProof> {
411 let EdVrfProof { gamma, c, s } = proof;
414 let h = self.encode_to_curve(suite, alpha)?;
416
417 let u = EdwardsPoint::mul_base(&s) - c * self.point;
419 let v = s * h.point - c * gamma;
421 let c_prime = EdChallenge::generate(suite, &[&self.point, &h.point, &gamma, &u, &v]);
423
424 if c_prime != c {
426 return Err(VrfError::ProofVerificationFailure);
427 }
428
429 Ok(EdVrfProof { gamma, c, s })
430 }
431 }
432
433 pub struct EdChallenge;
434
435 impl EdChallenge {
436 pub fn from_challenge_bytes(c_string: &[u8; CHALLENGE_LEN]) -> Scalar {
437 let mut c_scalar_bytes = [0u8; 32];
438 c_scalar_bytes[..CHALLENGE_LEN].copy_from_slice(c_string);
439 Scalar::from_bytes_mod_order(c_scalar_bytes)
440 }
441
442 pub fn generate(suite: Ciphersuite, points: &[&EdwardsPoint; 5]) -> Scalar {
443 let compressed = points.map(EdwardsPoint::compress);
444
445 Self::from_challenge_bytes(&util::challenge_bytes::<CHALLENGE_LEN, Sha512>(
446 suite,
447 compressed.iter().map(|c| c.as_bytes().as_slice()),
448 ))
449 }
450 }
451}