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}