cardano_crypto/vrf/
draft03.rs

1//! VRF implementation following IETF draft-03 specification
2//!
3//! Implements **ECVRF-ED25519-SHA512-Elligator2** as defined in
4//! [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-03).
5//! This variant produces 80-byte proofs and provides full byte-for-byte compatibility
6//! with Cardano's libsodium VRF implementation.
7//!
8//! # Specification Details
9//!
10//! - **Suite**: ECVRF-ED25519-SHA512-Elligator2
11//! - **Curve**: Edwards25519 (Ed25519)
12//! - **Hash Function**: SHA-512
13//! - **Hash-to-Curve**: Elligator2 (for non-uniform hash-to-curve)
14//! - **Proof Size**: 80 bytes (Gamma point 32 bytes + challenge 16 bytes + response 32 bytes)
15//! - **Public Key Size**: 32 bytes
16//! - **Secret Key Size**: 64 bytes (32-byte seed || 32-byte public key, Ed25519 format)
17//! - **Output Size**: 64 bytes (SHA-512 hash)
18//!
19//! # Cardano Compatibility
20//!
21//! This implementation is **100% compatible** with Cardano's VRF as used in:
22//! - Block production (VRF-based leader selection)
23//! - Epoch nonce generation
24//! - Praos consensus protocol
25//!
26//! All proofs generated by this library can be verified by Cardano nodes and vice versa.
27//!
28//! # Security
29//!
30//! - Constant-time operations for secret key material
31//! - Automatic memory zeroization for sensitive data
32//! - No unsafe code
33//! - Extensively tested against official test vectors
34//!
35//! # Examples
36//!
37//! ```rust,ignore
38//! use cardano_crypto::vrf::VrfDraft03;
39//!
40//! // Generate keypair from seed
41//! let seed = [42u8; 32];
42//! let (secret_key, public_key) = VrfDraft03::keypair_from_seed(&seed);
43//!
44//! // Generate proof
45//! let message = b"Cardano block production";
46//! let proof = VrfDraft03::prove(&secret_key, message)?;
47//!
48//! // Verify proof and get VRF output
49//! let output = VrfDraft03::verify(&public_key, &proof, message)?;
50//!
51//! // Convert proof directly to hash (without verification)
52//! let hash = VrfDraft03::proof_to_hash(&proof)?;
53//! assert_eq!(&output[..], &hash[..]);
54//! ```
55//!
56//! # Performance
57//!
58//! Typical operation times on modern hardware:
59//! - Keypair generation: ~20μs
60//! - Proof generation: ~1.2ms
61//! - Proof verification: ~800μs
62//!
63//! # When to Use
64//!
65//! Use this variant when:
66//! - You need Cardano compatibility
67//! - Proof size (80 bytes) is acceptable
68//! - You're working with existing Cardano infrastructure
69
70use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
71use sha2::{Digest, Sha512};
72use zeroize::Zeroizing;
73
74use crate::common::{clamp_scalar, point_to_bytes, CryptoResult, SUITE_DRAFT03, THREE};
75use crate::vrf::cardano_compat::{cardano_vrf_prove, cardano_vrf_verify};
76
77/// VRF proof size for draft-03: 80 bytes
78///
79/// Structure: Gamma (32 bytes) || c (16 bytes) || s (32 bytes)
80/// - Gamma: Hashed message point on Edwards curve
81///
82/// # Example
83///
84/// ```rust
85/// use cardano_crypto::vrf::{VrfDraft03, draft03::PROOF_SIZE};
86///
87/// # fn main() -> cardano_crypto::common::Result<()> {
88/// // Generate a keypair
89/// let seed = [1u8; 32];
90/// let (sk, pk) = VrfDraft03::keypair_from_seed(&seed);
91///
92/// // Create a proof
93/// let message = b"test message";
94/// let proof = VrfDraft03::prove(&sk, message)?;
95///
96/// // Verify the proof length
97/// assert_eq!(proof.len(), PROOF_SIZE);
98/// # Ok(())
99/// # }
100/// ```
101/// - c: Challenge scalar (truncated to 16 bytes)
102/// - s: Response scalar
103pub const PROOF_SIZE: usize = 80;
104
105/// Ed25519 public key size: 32 bytes
106///
107/// Compressed Edwards curve point in standard Ed25519 format.
108///
109/// # Example
110///
111/// ```
112/// use cardano_crypto::vrf::draft03::PUBLIC_KEY_SIZE;
113///
114/// assert_eq!(PUBLIC_KEY_SIZE, 32);
115/// ```
116pub const PUBLIC_KEY_SIZE: usize = 32;
117
118/// Ed25519 secret key size: 64 bytes
119///
120/// Format: seed (32 bytes) || public_key (32 bytes)
121/// This follows the Ed25519 expanded secret key format used by Cardano.
122///
123/// # Example
124///
125/// ```
126/// use cardano_crypto::vrf::draft03::SECRET_KEY_SIZE;
127///
128/// assert_eq!(SECRET_KEY_SIZE, 64);
129/// ```
130pub const SECRET_KEY_SIZE: usize = 64;
131
132/// Random seed size for keypair generation: 32 bytes
133///
134/// High-quality randomness is critical for security. Use a
135/// cryptographically secure random number generator.
136///
137/// # Example
138///
139/// ```
140/// use cardano_crypto::vrf::draft03::SEED_SIZE;
141///
142/// assert_eq!(SEED_SIZE, 32);
143/// ```
144pub const SEED_SIZE: usize = 32;
145
146/// VRF output hash size: 64 bytes
147///
148/// SHA-512 hash of the VRF proof's Gamma point.
149///
150/// # Example
151///
152/// ```
153/// use cardano_crypto::vrf::draft03::OUTPUT_SIZE;
154///
155/// assert_eq!(OUTPUT_SIZE, 64);
156/// ```
157pub const OUTPUT_SIZE: usize = 64;
158
159/// VRF Draft-03 implementation (ECVRF-ED25519-SHA512-Elligator2)
160///
161/// Zero-sized type providing static methods for VRF operations following
162/// the draft-03 specification. All methods are stateless and thread-safe.
163///
164/// # Cloning
165///
166/// This type is `Clone` but contains no data - clones are identical.
167///
168/// # Examples
169///
170/// ```rust,ignore
171/// use cardano_crypto::vrf::VrfDraft03;
172///
173/// // All operations are static - no instance needed
174/// let seed = [0u8; 32];
175/// let (sk, pk) = VrfDraft03::keypair_from_seed(&seed);
176/// ```
177#[derive(Clone, Debug)]
178pub struct VrfDraft03;
179
180impl VrfDraft03 {
181    /// Generates a VRF proof for the given message using the secret key
182    ///
183    /// Produces an 80-byte proof that can be verified by anyone with the corresponding
184    /// public key. The proof binds the message to the secret key while maintaining
185    /// the randomness properties required for VRF applications.
186    ///
187    /// # Arguments
188    ///
189    /// * `secret_key` - 64-byte Ed25519 expanded secret key (seed || public_key)
190    /// * `message` - Arbitrary-length message to prove (typically a blockchain slot ID or similar)
191    ///
192    /// # Returns
193    ///
194    /// 80-byte proof on success containing (Gamma || c || s)
195    ///
196    /// # Errors
197    ///
198    /// Returns [`crate::common::error::CryptoError`] if:
199    /// - Secret key is malformed or invalid
200    /// - Internal cryptographic operations fail (extremely rare)
201    ///
202    /// # Security
203    ///
204    /// - Uses constant-time operations for secret key handling
205    /// - Automatically zeroizes sensitive intermediate values
206    /// - Safe against timing attacks
207    ///
208    /// # Examples
209    ///
210    /// ```rust,ignore
211    /// use cardano_crypto::vrf::VrfDraft03;
212    ///
213    /// let seed = [1u8; 32];
214    /// let (secret_key, public_key) = VrfDraft03::keypair_from_seed(&seed);
215    ///
216    /// let message = b"slot_12345";
217    /// let proof = VrfDraft03::prove(&secret_key, message)?;
218    ///
219    /// assert_eq!(proof.len(), 80);
220    /// ```
221    ///
222    /// # Panics
223    ///
224    /// May panic if internal cryptographic operations encounter invalid state
225    /// (extremely unlikely in correct usage).
226    pub fn prove(
227        secret_key: &[u8; SECRET_KEY_SIZE],
228        message: &[u8],
229    ) -> CryptoResult<[u8; PROOF_SIZE]> {
230        cardano_vrf_prove(secret_key, message)
231    }
232
233    /// Verifies a VRF proof and returns the deterministic VRF output
234    ///
235    /// Checks that the proof is cryptographically valid for the given public key
236    /// and message. If verification succeeds, returns the deterministic 64-byte
237    /// VRF output - a deterministic 64-byte value derived from the proof.
238    ///
239    /// # Arguments
240    ///
241    /// * `public_key` - 32-byte Ed25519 public key
242    /// * `proof` - 80-byte VRF proof to verify
243    /// * `message` - Message that was allegedly proven
244    ///
245    /// # Returns
246    ///
247    /// 64-byte VRF output (SHA-512 hash) on successful verification
248    ///
249    /// # Errors
250    ///
251    /// Returns [`crate::common::error::CryptoError`] if:
252    /// - `InvalidPublicKey`: Public key is malformed or not on the curve
253    /// - `InvalidProof`: Proof is malformed, wrong length, or contains invalid data
254    /// - `VerificationFailed`: Proof is well-formed but cryptographically invalid
255    ///
256    /// # Examples
257    ///
258    /// ```rust,ignore
259    /// use cardano_crypto::vrf::VrfDraft03;
260    ///
261    /// let seed = [2u8; 32];
262    /// let (secret_key, public_key) = VrfDraft03::keypair_from_seed(&seed);
263    ///
264    /// let message = b"verify_this_message";
265    /// let proof = VrfDraft03::prove(&secret_key, message)?;
266    ///
267    /// // Verify proof and get VRF output
268    /// let output = VrfDraft03::verify(&public_key, &proof, message)?;
269    /// assert_eq!(output.len(), 64);
270    ///
271    /// // Wrong message should fail verification
272    /// assert!(VrfDraft03::verify(&public_key, &proof, b"wrong").is_err());
273    /// ```
274    pub fn verify(
275        public_key: &[u8; PUBLIC_KEY_SIZE],
276        proof: &[u8; PROOF_SIZE],
277        message: &[u8],
278    ) -> CryptoResult<[u8; OUTPUT_SIZE]> {
279        cardano_vrf_verify(public_key, proof, message)
280    }
281
282    /// Extracts the VRF output hash directly from a proof without verification
283    ///
284    /// Computes the VRF output (SHA-512 hash of the Gamma point) from a proof
285    /// **without verifying** its validity. This is useful when the proof has
286    /// already been verified or when you need to extract the hash for other purposes.
287    ///
288    /// ⚠️ **WARNING**: This function does NOT verify the proof's authenticity.
289    /// Use [`verify`](Self::verify) if you need cryptographic assurance.
290    ///
291    /// # Arguments
292    ///
293    /// * `proof` - 80-byte VRF proof
294    ///
295    /// # Returns
296    ///
297    /// 64-byte VRF output hash (same as what `verify` would return)
298    ///
299    /// # Errors
300    ///
301    /// Returns [`crate::common::error::CryptoError::InvalidProof`] if the proof is malformed
302    ///
303    /// # Examples
304    ///
305    /// ```rust,ignore
306    /// use cardano_crypto::vrf::VrfDraft03;
307    ///
308    /// let seed = [3u8; 32];
309    /// let (secret_key, public_key) = VrfDraft03::keypair_from_seed(&seed);
310    ///
311    /// let message = b"extract_hash_example";
312    /// let proof = VrfDraft03::prove(&secret_key, message)?;
313    ///
314    /// // Extract hash without verification (fast)
315    /// let hash = VrfDraft03::proof_to_hash(&proof)?;
316    ///
317    /// // Verify that it matches the verified output
318    /// let verified_output = VrfDraft03::verify(&public_key, &proof, message)?;
319    /// assert_eq!(hash, verified_output);
320    /// ```
321    pub fn proof_to_hash(proof: &[u8; PROOF_SIZE]) -> CryptoResult<[u8; OUTPUT_SIZE]> {
322        use crate::common::bytes_to_point;
323        use crate::vrf::cardano_compat::cardano_clear_cofactor;
324
325        let gamma_bytes: [u8; 32] = proof[0..32]
326            .try_into()
327            .expect("proof gamma segment must be 32 bytes");
328
329        let gamma = bytes_to_point(&gamma_bytes)?;
330        let gamma_cleared = cardano_clear_cofactor(&gamma);
331
332        let mut hasher = Sha512::new();
333        hasher.update([SUITE_DRAFT03]);
334        hasher.update([THREE]);
335        hasher.update(point_to_bytes(&gamma_cleared));
336        let hash = hasher.finalize();
337
338        let mut output = [0u8; OUTPUT_SIZE];
339        output.copy_from_slice(&hash);
340        Ok(output)
341    }
342
343    /// Generate keypair from seed
344    ///
345    /// Derives a deterministic Ed25519 keypair from a 32-byte seed using SHA-512
346    /// and standard Ed25519 scalar clamping.
347    ///
348    /// # Arguments
349    ///
350    /// * `seed` - 32-byte random seed (must be cryptographically secure)
351    ///
352    /// # Returns
353    ///
354    /// Tuple of (secret_key, public_key):
355    /// - `secret_key`: 64 bytes (seed || public_key)
356    /// - `public_key`: 32 bytes (compressed Edwards point)
357    ///
358    /// # Examples
359    ///
360    /// ```rust,ignore
361    /// use cardano_crypto::vrf::VrfDraft03;
362    ///
363    /// let seed = [42u8; 32];
364    /// let (secret_key, public_key) = VrfDraft03::keypair_from_seed(&seed);
365    ///
366    /// assert_eq!(secret_key.len(), 64);
367    /// assert_eq!(public_key.len(), 32);
368    /// ```
369    #[must_use]
370    pub fn keypair_from_seed(
371        seed: &[u8; SEED_SIZE],
372    ) -> ([u8; SECRET_KEY_SIZE], [u8; PUBLIC_KEY_SIZE]) {
373        let mut hasher = Sha512::new();
374        hasher.update(seed);
375        let hash = hasher.finalize();
376
377        let mut secret_scalar = Zeroizing::new([0u8; 32]);
378        secret_scalar.copy_from_slice(&hash[0..32]);
379        *secret_scalar = clamp_scalar(*secret_scalar);
380
381        let scalar = Scalar::from_bytes_mod_order(*secret_scalar);
382        let public_point = ED25519_BASEPOINT_POINT * scalar;
383        let public_key = point_to_bytes(&public_point);
384
385        let mut secret_key = [0u8; SECRET_KEY_SIZE];
386        secret_key[0..32].copy_from_slice(seed);
387        secret_key[32..64].copy_from_slice(&public_key);
388
389        (secret_key, public_key)
390    }
391}
392
393// ============================================================================
394// VrfAlgorithm trait implementation
395// ============================================================================
396
397impl crate::vrf::VrfAlgorithm for VrfDraft03 {
398    type SecretKey = [u8; SECRET_KEY_SIZE];
399    type VerificationKey = [u8; PUBLIC_KEY_SIZE];
400    type Proof = [u8; PROOF_SIZE];
401    type Output = [u8; OUTPUT_SIZE];
402
403    const ALGORITHM_NAME: &'static str = "ECVRF-ED25519-SHA512-Elligator2-Draft03";
404    const SEED_SIZE: usize = SEED_SIZE;
405    const SECRET_KEY_SIZE: usize = SECRET_KEY_SIZE;
406    const VERIFICATION_KEY_SIZE: usize = PUBLIC_KEY_SIZE;
407    const PROOF_SIZE: usize = PROOF_SIZE;
408    const OUTPUT_SIZE: usize = OUTPUT_SIZE;
409
410    fn keypair_from_seed(seed: &[u8; 32]) -> (Self::SecretKey, Self::VerificationKey) {
411        VrfDraft03::keypair_from_seed(seed)
412    }
413
414    fn derive_verification_key(sk: &Self::SecretKey) -> Self::VerificationKey {
415        let mut pk = [0u8; PUBLIC_KEY_SIZE];
416        pk.copy_from_slice(&sk[32..64]);
417        pk
418    }
419
420    fn prove(sk: &Self::SecretKey, message: &[u8]) -> CryptoResult<Self::Proof> {
421        VrfDraft03::prove(sk, message)
422    }
423
424    fn verify(
425        vk: &Self::VerificationKey,
426        proof: &Self::Proof,
427        message: &[u8],
428    ) -> CryptoResult<Self::Output> {
429        VrfDraft03::verify(vk, proof, message)
430    }
431
432    fn proof_to_hash(proof: &Self::Proof) -> CryptoResult<Self::Output> {
433        VrfDraft03::proof_to_hash(proof)
434    }
435
436    fn raw_serialize_verification_key(vk: &Self::VerificationKey) -> &[u8] {
437        vk.as_slice()
438    }
439
440    fn raw_deserialize_verification_key(bytes: &[u8]) -> Option<Self::VerificationKey> {
441        if bytes.len() != PUBLIC_KEY_SIZE {
442            return None;
443        }
444        let mut vk = [0u8; PUBLIC_KEY_SIZE];
445        vk.copy_from_slice(bytes);
446        Some(vk)
447    }
448
449    fn raw_serialize_proof(proof: &Self::Proof) -> &[u8] {
450        proof.as_slice()
451    }
452
453    fn raw_deserialize_proof(bytes: &[u8]) -> Option<Self::Proof> {
454        if bytes.len() != PROOF_SIZE {
455            return None;
456        }
457        let mut proof = [0u8; PROOF_SIZE];
458        proof.copy_from_slice(bytes);
459        Some(proof)
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use super::*;
466
467    #[test]
468    fn test_prove_verify_roundtrip() {
469        let seed = [42u8; SEED_SIZE];
470        let (sk, pk) = VrfDraft03::keypair_from_seed(&seed);
471        let message = b"test message";
472
473        let proof = VrfDraft03::prove(&sk, message).expect("prove failed");
474        let output = VrfDraft03::verify(&pk, &proof, message).expect("verify failed");
475
476        assert_eq!(output.len(), OUTPUT_SIZE);
477    }
478
479    #[test]
480    fn test_verify_rejects_invalid_proof() {
481        let seed = [42u8; SEED_SIZE];
482        let (_sk, pk) = VrfDraft03::keypair_from_seed(&seed);
483        let message = b"test message";
484
485        let invalid_proof = [0u8; PROOF_SIZE];
486        let result = VrfDraft03::verify(&pk, &invalid_proof, message);
487
488        assert!(result.is_err());
489    }
490
491    #[test]
492    fn test_proof_to_hash_deterministic() {
493        let seed = [42u8; SEED_SIZE];
494        let (sk, _pk) = VrfDraft03::keypair_from_seed(&seed);
495        let message = b"test message";
496
497        let proof = VrfDraft03::prove(&sk, message).expect("prove failed");
498        let hash1 = VrfDraft03::proof_to_hash(&proof).expect("hash failed");
499        let hash2 = VrfDraft03::proof_to_hash(&proof).expect("hash failed");
500
501        assert_eq!(hash1, hash2);
502    }
503
504    #[test]
505    fn test_keypair_generation() {
506        let seed = [1u8; 32];
507        let (sk, pk) = VrfDraft03::keypair_from_seed(&seed);
508
509        // Check sizes
510        assert_eq!(sk.len(), 64);
511        assert_eq!(pk.len(), 32);
512
513        // Check that secret key contains seed and public key
514        assert_eq!(&sk[0..32], &seed);
515        assert_eq!(&sk[32..64], &pk);
516    }
517
518    #[test]
519    fn test_proof_sizes() {
520        assert_eq!(PROOF_SIZE, 80);
521        assert_eq!(SECRET_KEY_SIZE, 64);
522        assert_eq!(PUBLIC_KEY_SIZE, 32);
523        assert_eq!(OUTPUT_SIZE, 64);
524        assert_eq!(SEED_SIZE, 32);
525    }
526
527    #[test]
528    fn test_vrf_algorithm_trait() {
529        use crate::hash::Blake2b256;
530        use crate::vrf::VrfAlgorithm;
531
532        let seed = [42u8; 32];
533        let (sk, vk) = <VrfDraft03 as VrfAlgorithm>::keypair_from_seed(&seed);
534
535        // Test trait constants
536        assert_eq!(<VrfDraft03 as VrfAlgorithm>::SEED_SIZE, 32);
537        assert_eq!(<VrfDraft03 as VrfAlgorithm>::SECRET_KEY_SIZE, 64);
538        assert_eq!(<VrfDraft03 as VrfAlgorithm>::VERIFICATION_KEY_SIZE, 32);
539        assert_eq!(<VrfDraft03 as VrfAlgorithm>::PROOF_SIZE, 80);
540        assert_eq!(<VrfDraft03 as VrfAlgorithm>::OUTPUT_SIZE, 64);
541
542        // Test prove/verify through trait
543        let message = b"test message";
544        let proof = <VrfDraft03 as VrfAlgorithm>::prove(&sk, message).unwrap();
545        let output = <VrfDraft03 as VrfAlgorithm>::verify(&vk, &proof, message).unwrap();
546        assert_eq!(output.len(), 64);
547
548        // Test hash_verification_key
549        let hash = <VrfDraft03 as VrfAlgorithm>::hash_verification_key::<Blake2b256>(&vk);
550        assert_eq!(hash.len(), 32);
551
552        // Different keys should have different hashes
553        let seed2 = [43u8; 32];
554        let (_, vk2) = <VrfDraft03 as VrfAlgorithm>::keypair_from_seed(&seed2);
555        let hash2 = <VrfDraft03 as VrfAlgorithm>::hash_verification_key::<Blake2b256>(&vk2);
556        assert_ne!(hash, hash2);
557    }
558
559    #[test]
560    fn test_serialization_roundtrip() {
561        use crate::vrf::VrfAlgorithm;
562
563        let seed = [99u8; 32];
564        let (sk, vk) = VrfDraft03::keypair_from_seed(&seed);
565        let message = b"test";
566        let proof = VrfDraft03::prove(&sk, message).unwrap();
567
568        // Test verification key roundtrip
569        let vk_bytes = <VrfDraft03 as VrfAlgorithm>::raw_serialize_verification_key(&vk);
570        let vk_restored =
571            <VrfDraft03 as VrfAlgorithm>::raw_deserialize_verification_key(vk_bytes).unwrap();
572        assert_eq!(vk, vk_restored);
573
574        // Test proof roundtrip
575        let proof_bytes = <VrfDraft03 as VrfAlgorithm>::raw_serialize_proof(&proof);
576        let proof_restored =
577            <VrfDraft03 as VrfAlgorithm>::raw_deserialize_proof(proof_bytes).unwrap();
578        assert_eq!(proof, proof_restored);
579    }
580}