1use p256::{PublicKey, SecretKey};
2use sha2::Sha256;
3
4use crate::{
5 ec::util,
6 error::{VrfError, VrfResult},
7};
8
9const CHALLENGE_LEN: usize = 16;
10const Q_LEN: usize = 32;
11const PT_LEN: usize = 33;
12
13pub mod tai {
14 use sha2::Sha256;
15
16 use crate::{
17 Ciphersuite,
18 ec::p256::{
19 EcVrfProof,
20 internal::{EcVrfP256PublicKey, EcVrfP256SecretKey},
21 },
22 error::VrfResult,
23 };
24
25 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
26 pub struct EcVrfP256Tai;
27
28 impl crate::VRF for EcVrfP256Tai {
29 type Hash = Sha256;
30 type Proof = EcVrfProof;
31 type Verifier = EcVrfP256TaiPublicKey;
32 type Prover = EcVrfP256TaiSecretKey;
33
34 fn ciphersuite(&self) -> Ciphersuite {
35 Ciphersuite::ECVRF_P256_SHA256_TAI
36 }
37 }
38
39 #[derive(zeroize::ZeroizeOnDrop, PartialEq, Eq)]
40 #[repr(transparent)]
41 pub struct EcVrfP256TaiSecretKey(EcVrfP256SecretKey);
42
43 impl crate::Prover<sha2::Sha256> for EcVrfP256TaiSecretKey {
44 type Proof = EcVrfProof;
45
46 type Verifier = EcVrfP256TaiPublicKey;
47
48 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
49 where
50 Self: Sized,
51 {
52 Ok(Self(EcVrfP256SecretKey::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 EcVrfP256TaiPublicKey(self.0.public_key())
62 }
63
64 fn prove(&self, alpha: &[u8]) -> VrfResult<Self::Proof> {
65 self.0.prove(Ciphersuite::ECVRF_P256_SHA256_TAI, alpha)
66 }
67 }
68
69 #[derive(Debug, PartialEq, Eq)]
70 #[repr(transparent)]
71 pub struct EcVrfP256TaiPublicKey(EcVrfP256PublicKey);
72
73 impl crate::Verifier<Sha256> for EcVrfP256TaiPublicKey {
74 type Proof = EcVrfProof;
75
76 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
77 where
78 Self: Sized,
79 {
80 Ok(Self(EcVrfP256PublicKey::from_bytes(bytes)?))
81 }
82
83 fn verify(&self, alpha: &[u8], proof: Self::Proof) -> VrfResult<digest::Output<Sha256>> {
84 use crate::Proof as _;
85 self.0
86 .verify(Ciphersuite::ECVRF_P256_SHA256_TAI, alpha, proof)?
87 .proof_to_hash(Ciphersuite::ECVRF_P256_SHA256_TAI)
88 }
89 }
90}
91
92pub mod sswu {
93 use sha2::Sha256;
94
95 use crate::{
96 Ciphersuite,
97 ec::p256::{
98 EcVrfProof,
99 internal::{EcVrfP256PublicKey, EcVrfP256SecretKey},
100 },
101 error::VrfResult,
102 };
103
104 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
105 pub struct EcVrfP256Sswu;
106
107 impl crate::VRF for EcVrfP256Sswu {
108 type Hash = Sha256;
109 type Proof = EcVrfProof;
110 type Verifier = EcVrfP256SswuPublicKey;
111 type Prover = EcVrfP256SswuSecretKey;
112
113 fn ciphersuite(&self) -> Ciphersuite {
114 Ciphersuite::ECVRF_P256_SHA256_SSWU
115 }
116 }
117
118 #[derive(zeroize::ZeroizeOnDrop, PartialEq, Eq)]
119 #[repr(transparent)]
120 pub struct EcVrfP256SswuSecretKey(EcVrfP256SecretKey);
121
122 impl crate::Prover<sha2::Sha256> for EcVrfP256SswuSecretKey {
123 type Proof = EcVrfProof;
124
125 type Verifier = EcVrfP256SswuPublicKey;
126
127 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
128 where
129 Self: Sized,
130 {
131 Ok(Self(EcVrfP256SecretKey::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 EcVrfP256SswuPublicKey(self.0.public_key())
141 }
142
143 fn prove(&self, alpha: &[u8]) -> VrfResult<Self::Proof> {
144 self.0.prove(Ciphersuite::ECVRF_P256_SHA256_SSWU, alpha)
145 }
146 }
147
148 #[derive(Debug, PartialEq, Eq)]
149 #[repr(transparent)]
150 pub struct EcVrfP256SswuPublicKey(EcVrfP256PublicKey);
151
152 impl crate::Verifier<Sha256> for EcVrfP256SswuPublicKey {
153 type Proof = EcVrfProof;
154
155 fn from_slice(bytes: &[u8]) -> VrfResult<Self>
156 where
157 Self: Sized,
158 {
159 Ok(Self(EcVrfP256PublicKey::from_bytes(bytes)?))
160 }
161
162 fn verify(&self, alpha: &[u8], proof: Self::Proof) -> VrfResult<digest::Output<Sha256>> {
163 use crate::Proof as _;
164 self.0
165 .verify(Ciphersuite::ECVRF_P256_SHA256_SSWU, alpha, proof)?
166 .proof_to_hash(Ciphersuite::ECVRF_P256_SHA256_SSWU)
167 }
168 }
169}
170
171#[derive(Debug, PartialEq, Eq)]
172pub struct EcVrfProof {
173 gamma: PublicKey,
174 c: SecretKey,
175 s: SecretKey,
176}
177
178impl EcVrfProof {
179 #[inline]
180 fn compressed_gamma(&self) -> p256::EncodedPoint {
181 p256::EncodedPoint::from(self.gamma).compress()
182 }
183}
184
185impl crate::Proof<Sha256> for EcVrfProof {
186 const PROOF_LEN: usize = CHALLENGE_LEN + Q_LEN + PT_LEN;
187
188 fn decode_pi(pi: &[u8]) -> VrfResult<Self>
189 where
190 Self: Sized,
191 {
192 if pi.len() != Self::PROOF_LEN {
193 return Err(VrfError::IncorrectPiLength {
194 expected: Self::PROOF_LEN,
195 actual: pi.len(),
196 });
197 }
198
199 let gamma_string = &pi[..PT_LEN];
200 let c_string = &pi[PT_LEN..PT_LEN + CHALLENGE_LEN];
201 let s_string = &pi[PT_LEN + CHALLENGE_LEN..];
202 debug_assert_eq!(s_string.len(), Q_LEN);
203 let gamma = PublicKey::from_sec1_bytes(gamma_string)?;
204
205 let c = internal::sk_from_slice(c_string)?;
206 let s = internal::sk_from_slice(s_string)?;
207
208 Ok(EcVrfProof { gamma, c, s })
209 }
210
211 fn encode_to_pi(&self) -> Vec<u8> {
212 let c_bytes = self.c.to_bytes();
213 let c_slice = &c_bytes[c_bytes.len() - CHALLENGE_LEN..];
214 let ret = [
215 self.compressed_gamma().as_bytes(),
216 c_slice,
217 &self.s.to_bytes(),
218 ]
219 .concat();
220
221 debug_assert_eq!(ret.len(), Self::PROOF_LEN);
222
223 ret
224 }
225
226 fn proof_to_hash(&self, suite: crate::Ciphersuite) -> VrfResult<digest::Output<Sha256>> {
227 Ok(util::proof_to_hash::<Sha256>(
228 suite,
229 self.compressed_gamma().as_bytes(),
230 ))
231 }
232}
233
234mod internal {
235 use p256::{
236 EncodedPoint, NistP256, PublicKey, SecretKey,
237 elliptic_curve::{ScalarValue, bigint::ArrayEncoding},
238 };
239 use sha2::{Digest as _, Sha256};
240
241 use crate::{
242 Ciphersuite,
243 consts::ecvrf::{ciphersuites::ECVRF_P256_SHA256_SSWU, e2c::ECVRF_E2C_H2C_DST},
244 ec::{
245 p256::{CHALLENGE_LEN, EcVrfProof, PT_LEN, Q_LEN},
246 util,
247 },
248 error::{VrfError, VrfResult},
249 };
250
251 pub(super) fn sk_from_slice(slice: &[u8]) -> VrfResult<SecretKey> {
252 assert!(slice.len() <= Q_LEN);
253 let offset = Q_LEN.saturating_sub(slice.len());
254 let mut c_scalar_bytes: p256::FieldBytes = Default::default();
255 c_scalar_bytes[offset..].copy_from_slice(slice);
256 Ok(SecretKey::from_bytes(&c_scalar_bytes)?)
257 }
258
259 #[derive(zeroize::ZeroizeOnDrop, PartialEq, Eq)]
260 pub struct EcVrfP256SecretKey(SecretKey);
261
262 impl EcVrfP256SecretKey {
263 pub(super) fn from_sk(sk: [u8; 32]) -> VrfResult<Self> {
264 Ok(SecretKey::from_bytes(&sk.into()).map(Self)?)
265 }
266
267 #[cfg(feature = "hazmat")]
268 pub fn x_equals(&self, value: &[u8]) -> bool {
269 use subtle::ConstantTimeEq as _;
270 self.0.to_bytes().as_slice().ct_eq(value).into()
271 }
272
273 pub(super) fn public_key(&self) -> EcVrfP256PublicKey {
274 let point = self.0.public_key();
275 let compressed = EncodedPoint::from(point).compress();
276 EcVrfP256PublicKey { point, compressed }
277 }
278
279 fn generate_nonce(&self, h_string: &[u8]) -> VrfResult<SecretKey> {
280 let q = ScalarValue::<NistP256>::MODULUS.to_be_byte_array();
281 let h = Sha256::digest(h_string);
282 let k = rfc6979::generate_k::<Sha256, _>(&self.0.to_bytes(), &q, &h, b"");
283
284 Ok(SecretKey::from_bytes(&k)?)
285 }
286
287 pub(super) fn prove(&self, suite: Ciphersuite, alpha: &[u8]) -> VrfResult<EcVrfProof> {
288 let y = self.public_key();
290 let h = y.encode_to_curve(suite, alpha)?;
292
293 let x = self.0.to_nonzero_scalar();
294 let gamma = h.point.to_projective() * x.as_ref();
296 let gamma = PublicKey::from_affine(gamma.to_affine())?;
297
298 let k = self.generate_nonce(h.encode_to_curve_salt())?;
300 let k_nz = k.to_nonzero_scalar();
301 let k_h =
303 PublicKey::from_affine((h.point.to_projective() * k_nz.as_ref()).to_affine())?;
304 let c =
305 EcChallenge::generate(suite, &[&y.point, &h.point, &gamma, &k.public_key(), &k_h])?;
306
307 let c_x = c.to_nonzero_scalar() * x;
309 let Some(s_scalar) =
310 p256::NonZeroScalar::new(k_nz.as_ref() + c_x.as_ref()).into_option()
311 else {
312 unreachable!()
313 };
314 let s = SecretKey::from(s_scalar);
315
316 Ok(EcVrfProof { gamma, c, s })
319 }
320 }
321
322 #[derive(Debug, Clone, PartialEq, Eq)]
323 pub struct EcVrfP256PublicKey {
324 point: PublicKey,
325 compressed: EncodedPoint,
326 }
327
328 impl EcVrfP256PublicKey {
329 pub(super) fn from_bytes(bytes: &[u8]) -> VrfResult<Self> {
330 let point = PublicKey::from_sec1_bytes(bytes)?;
331 if point.as_affine().is_identity().into() {
332 return Err(VrfError::InvalidEcPoint);
333 }
334
335 let compressed = EncodedPoint::from(point).compress();
336
337 Ok(Self { point, compressed })
338 }
339
340 fn encode_to_curve_salt(&self) -> &[u8] {
341 self.compressed.as_bytes()
342 }
343
344 fn encode_to_curve(&self, suite: Ciphersuite, alpha: &[u8]) -> VrfResult<Self> {
345 Ok(match suite {
346 Ciphersuite::ECVRF_P256_SHA256_TAI => self.encode_to_curve_tai(alpha)?,
347 Ciphersuite::ECVRF_P256_SHA256_SSWU => self.h2c_encode_to_curve_sswu(alpha)?,
348 _ => return Err(VrfError::UnsupportedCiphersuite(suite)),
349 })
350 }
351
352 fn encode_to_curve_tai(&self, alpha: &[u8]) -> VrfResult<Self> {
353 let salt = self.encode_to_curve_salt();
354
355 for candidate in util::encode_to_curve_tai_generator::<Sha256>(
356 Ciphersuite::ECVRF_P256_SHA256_TAI,
357 salt,
358 alpha,
359 ) {
360 let mut point = [0u8; PT_LEN];
364 point[0] = 0x02;
365 point[1..].copy_from_slice(&candidate[..32]);
366
367 let Ok(pk) = Self::from_bytes(&point) else {
368 continue;
369 };
370
371 return Ok(pk);
373 }
374
375 Err(VrfError::TryAndIncrementNoCandidatesFound)
376 }
377
378 fn h2c_encode_to_curve_sswu(&self, alpha: &[u8]) -> VrfResult<Self> {
379 use hash2curve::GroupDigest as _;
380 let string_to_be_hashed = [self.encode_to_curve_salt(), alpha].concat();
382 let dst = [
384 &ECVRF_E2C_H2C_DST[..],
385 p256::NistP256::ENCODE_TO_CURVE_ID,
386 &[ECVRF_P256_SHA256_SSWU],
387 ]
388 .concat();
389
390 let point = <p256::NistP256 as hash2curve::GroupDigest>::encode_from_bytes(
391 &string_to_be_hashed,
392 &dst,
393 )?
394 .to_affine();
395
396 let point = PublicKey::from_affine(point)?;
397 let compressed = EncodedPoint::from(point).compress();
398
399 Ok(Self { compressed, point })
400 }
401
402 pub(super) fn verify(
403 &self,
404 suite: Ciphersuite,
405 alpha: &[u8],
406 proof: EcVrfProof,
407 ) -> VrfResult<EcVrfProof> {
408 let EcVrfProof { gamma, c, s } = proof;
411 let h = self.encode_to_curve(suite, alpha)?;
413
414 let c_nz = c.to_nonzero_scalar();
415 let s_nz = s.to_nonzero_scalar();
416
417 let u = s.public_key().to_projective() - (self.point.to_projective() * c_nz.as_ref());
419 let u = PublicKey::from_affine(u.to_affine())?;
420 let v = h.point.to_projective() * s_nz.as_ref() - gamma.to_projective() * c_nz.as_ref();
422 let v = PublicKey::from_affine(v.to_affine())?;
423 let c_prime = EcChallenge::generate(suite, &[&self.point, &h.point, &gamma, &u, &v])?;
425
426 if c_prime != c {
428 return Err(VrfError::ProofVerificationFailure);
429 }
430
431 Ok(EcVrfProof { gamma, c, s })
432 }
433 }
434
435 pub struct EcChallenge;
436
437 impl EcChallenge {
438 fn from_challenge_bytes(c_string: &[u8; CHALLENGE_LEN]) -> VrfResult<SecretKey> {
439 sk_from_slice(&c_string[..])
440 }
441
442 fn generate(suite: Ciphersuite, points: &[&PublicKey; 5]) -> VrfResult<SecretKey> {
443 let compressed = points.map(|pk| EncodedPoint::from(pk).compress());
444
445 Self::from_challenge_bytes(&util::challenge_bytes::<CHALLENGE_LEN, Sha256>(
446 suite,
447 compressed.iter().map(|c| c.as_bytes()),
448 ))
449 }
450 }
451}