vrf_rfc9381/ec/
p256.rs

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            // 1. Use SK to derive the VRF secret scalar x and the VRF public key Y = x*B
289            let y = self.public_key();
290            // 2. H = ECVRF_encode_to_curve(encode_to_curve_salt, alpha_string) (see Section 5.4.1)
291            let h = y.encode_to_curve(suite, alpha)?;
292
293            let x = self.0.to_nonzero_scalar();
294            // 3. Gamma = x*H
295            let gamma = h.point.to_projective() * x.as_ref();
296            let gamma = PublicKey::from_affine(gamma.to_affine())?;
297
298            // 4. k = ECVRF_nonce_generation(SK, h_string) (see Section 5.4.2)
299            let k = self.generate_nonce(h.encode_to_curve_salt())?;
300            let k_nz = k.to_nonzero_scalar();
301            // 5. c = ECVRF_challenge_generation(Y, H, Gamma, k*B, k*H) (see Section 5.4.3)
302            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            // 7. s = (k + c*x) mod q
308            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            // 8. pi_string = point_to_string(Gamma) || int_to_string(c, cLen) || int_to_string(s, qLen)
317            // EdProof here can be encoded into pi_string at any point so we return the struct as-is
318            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                // 5. While H is "INVALID" or H is the identity element of the elliptic curve group:
361                // H = interpret_hash_value_as_a_point(hash_string)
362                // Addendum: "interpret_hash_value_as_a_point(s) = string_to_point(0x02 || s)."
363                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                // 6. Output H
372                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            // string_to_be_hashed = encode_to_curve_salt || alpha_string
381            let string_to_be_hashed = [self.encode_to_curve_salt(), alpha].concat();
382            // "ECVRF_" || h2c_suite_ID_string || suite_string
383            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            // 4. D = ECVRF_decode_proof(pi_string) (see Section 5.4.4)
409            // 6. (Gamma, c, s) = D
410            let EcVrfProof { gamma, c, s } = proof;
411            // 7. H = ECVRF_encode_to_curve(encode_to_curve_salt, alpha_string) (see Section 5.4.1)
412            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            // 8. U = s*B - c*Y
418            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            // 9. V = s*H - c*Gamma
421            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            // 10. c' = ECVRF_challenge_generation(Y, H, Gamma, U, V) (see Section 5.4.3)
424            let c_prime = EcChallenge::generate(suite, &[&self.point, &h.point, &gamma, &u, &v])?;
425
426            // 11.  If c and c' are equal, output ("VALID", ECVRF_proof_to_hash(pi_string)); else output "INVALID"
427            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}