use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
use sha2::{Digest, Sha512};
use zeroize::Zeroizing;
use crate::common::{clamp_scalar, point_to_bytes, CryptoResult, SUITE_DRAFT03, THREE};
use crate::vrf::cardano_compat::{cardano_vrf_prove, cardano_vrf_verify};
pub const PROOF_SIZE: usize = 80;
pub const PUBLIC_KEY_SIZE: usize = 32;
pub const SECRET_KEY_SIZE: usize = 64;
pub const SEED_SIZE: usize = 32;
pub const OUTPUT_SIZE: usize = 64;
#[derive(Clone, Debug)]
pub struct VrfDraft03;
impl VrfDraft03 {
pub fn prove(
secret_key: &[u8; SECRET_KEY_SIZE],
message: &[u8],
) -> CryptoResult<[u8; PROOF_SIZE]> {
cardano_vrf_prove(secret_key, message)
}
pub fn verify(
public_key: &[u8; PUBLIC_KEY_SIZE],
proof: &[u8; PROOF_SIZE],
message: &[u8],
) -> CryptoResult<[u8; OUTPUT_SIZE]> {
cardano_vrf_verify(public_key, proof, message)
}
pub fn proof_to_hash(proof: &[u8; PROOF_SIZE]) -> CryptoResult<[u8; OUTPUT_SIZE]> {
use crate::common::bytes_to_point;
use crate::vrf::cardano_compat::cardano_clear_cofactor;
let gamma_bytes: [u8; 32] = proof[0..32]
.try_into()
.expect("proof gamma segment must be 32 bytes");
let gamma = bytes_to_point(&gamma_bytes)?;
let gamma_cleared = cardano_clear_cofactor(&gamma);
let mut hasher = Sha512::new();
hasher.update([SUITE_DRAFT03]);
hasher.update([THREE]);
hasher.update(point_to_bytes(&gamma_cleared));
let hash = hasher.finalize();
let mut output = [0u8; OUTPUT_SIZE];
output.copy_from_slice(&hash);
Ok(output)
}
#[must_use]
pub fn keypair_from_seed(
seed: &[u8; SEED_SIZE],
) -> ([u8; SECRET_KEY_SIZE], [u8; PUBLIC_KEY_SIZE]) {
let mut hasher = Sha512::new();
hasher.update(seed);
let hash = hasher.finalize();
let mut secret_scalar = Zeroizing::new([0u8; 32]);
secret_scalar.copy_from_slice(&hash[0..32]);
*secret_scalar = clamp_scalar(*secret_scalar);
let scalar = Scalar::from_bytes_mod_order(*secret_scalar);
let public_point = ED25519_BASEPOINT_POINT * scalar;
let public_key = point_to_bytes(&public_point);
let mut secret_key = [0u8; SECRET_KEY_SIZE];
secret_key[0..32].copy_from_slice(seed);
secret_key[32..64].copy_from_slice(&public_key);
(secret_key, public_key)
}
}
impl crate::vrf::VrfAlgorithm for VrfDraft03 {
type SecretKey = [u8; SECRET_KEY_SIZE];
type VerificationKey = [u8; PUBLIC_KEY_SIZE];
type Proof = [u8; PROOF_SIZE];
type Output = [u8; OUTPUT_SIZE];
const ALGORITHM_NAME: &'static str = "ECVRF-ED25519-SHA512-Elligator2-Draft03";
const SEED_SIZE: usize = SEED_SIZE;
const SECRET_KEY_SIZE: usize = SECRET_KEY_SIZE;
const VERIFICATION_KEY_SIZE: usize = PUBLIC_KEY_SIZE;
const PROOF_SIZE: usize = PROOF_SIZE;
const OUTPUT_SIZE: usize = OUTPUT_SIZE;
fn keypair_from_seed(seed: &[u8; 32]) -> (Self::SecretKey, Self::VerificationKey) {
VrfDraft03::keypair_from_seed(seed)
}
fn derive_verification_key(sk: &Self::SecretKey) -> Self::VerificationKey {
let mut pk = [0u8; PUBLIC_KEY_SIZE];
pk.copy_from_slice(&sk[32..64]);
pk
}
fn prove(sk: &Self::SecretKey, message: &[u8]) -> CryptoResult<Self::Proof> {
VrfDraft03::prove(sk, message)
}
fn verify(
vk: &Self::VerificationKey,
proof: &Self::Proof,
message: &[u8],
) -> CryptoResult<Self::Output> {
VrfDraft03::verify(vk, proof, message)
}
fn proof_to_hash(proof: &Self::Proof) -> CryptoResult<Self::Output> {
VrfDraft03::proof_to_hash(proof)
}
fn raw_serialize_verification_key(vk: &Self::VerificationKey) -> &[u8] {
vk.as_slice()
}
fn raw_deserialize_verification_key(bytes: &[u8]) -> Option<Self::VerificationKey> {
if bytes.len() != PUBLIC_KEY_SIZE {
return None;
}
let mut vk = [0u8; PUBLIC_KEY_SIZE];
vk.copy_from_slice(bytes);
Some(vk)
}
fn raw_serialize_proof(proof: &Self::Proof) -> &[u8] {
proof.as_slice()
}
fn raw_deserialize_proof(bytes: &[u8]) -> Option<Self::Proof> {
if bytes.len() != PROOF_SIZE {
return None;
}
let mut proof = [0u8; PROOF_SIZE];
proof.copy_from_slice(bytes);
Some(proof)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prove_verify_roundtrip() {
let seed = [42u8; SEED_SIZE];
let (sk, pk) = VrfDraft03::keypair_from_seed(&seed);
let message = b"test message";
let proof = VrfDraft03::prove(&sk, message).expect("prove failed");
let output = VrfDraft03::verify(&pk, &proof, message).expect("verify failed");
assert_eq!(output.len(), OUTPUT_SIZE);
}
#[test]
fn test_verify_rejects_invalid_proof() {
let seed = [42u8; SEED_SIZE];
let (_sk, pk) = VrfDraft03::keypair_from_seed(&seed);
let message = b"test message";
let invalid_proof = [0u8; PROOF_SIZE];
let result = VrfDraft03::verify(&pk, &invalid_proof, message);
assert!(result.is_err());
}
#[test]
fn test_proof_to_hash_deterministic() {
let seed = [42u8; SEED_SIZE];
let (sk, _pk) = VrfDraft03::keypair_from_seed(&seed);
let message = b"test message";
let proof = VrfDraft03::prove(&sk, message).expect("prove failed");
let hash1 = VrfDraft03::proof_to_hash(&proof).expect("hash failed");
let hash2 = VrfDraft03::proof_to_hash(&proof).expect("hash failed");
assert_eq!(hash1, hash2);
}
#[test]
fn test_keypair_generation() {
let seed = [1u8; 32];
let (sk, pk) = VrfDraft03::keypair_from_seed(&seed);
assert_eq!(sk.len(), 64);
assert_eq!(pk.len(), 32);
assert_eq!(&sk[0..32], &seed);
assert_eq!(&sk[32..64], &pk);
}
#[test]
fn test_proof_sizes() {
assert_eq!(PROOF_SIZE, 80);
assert_eq!(SECRET_KEY_SIZE, 64);
assert_eq!(PUBLIC_KEY_SIZE, 32);
assert_eq!(OUTPUT_SIZE, 64);
assert_eq!(SEED_SIZE, 32);
}
#[test]
fn test_vrf_algorithm_trait() {
use crate::hash::Blake2b256;
use crate::vrf::VrfAlgorithm;
let seed = [42u8; 32];
let (sk, vk) = <VrfDraft03 as VrfAlgorithm>::keypair_from_seed(&seed);
assert_eq!(<VrfDraft03 as VrfAlgorithm>::SEED_SIZE, 32);
assert_eq!(<VrfDraft03 as VrfAlgorithm>::SECRET_KEY_SIZE, 64);
assert_eq!(<VrfDraft03 as VrfAlgorithm>::VERIFICATION_KEY_SIZE, 32);
assert_eq!(<VrfDraft03 as VrfAlgorithm>::PROOF_SIZE, 80);
assert_eq!(<VrfDraft03 as VrfAlgorithm>::OUTPUT_SIZE, 64);
let message = b"test message";
let proof = <VrfDraft03 as VrfAlgorithm>::prove(&sk, message).unwrap();
let output = <VrfDraft03 as VrfAlgorithm>::verify(&vk, &proof, message).unwrap();
assert_eq!(output.len(), 64);
let hash = <VrfDraft03 as VrfAlgorithm>::hash_verification_key::<Blake2b256>(&vk);
assert_eq!(hash.len(), 32);
let seed2 = [43u8; 32];
let (_, vk2) = <VrfDraft03 as VrfAlgorithm>::keypair_from_seed(&seed2);
let hash2 = <VrfDraft03 as VrfAlgorithm>::hash_verification_key::<Blake2b256>(&vk2);
assert_ne!(hash, hash2);
}
#[test]
fn test_serialization_roundtrip() {
use crate::vrf::VrfAlgorithm;
let seed = [99u8; 32];
let (sk, vk) = VrfDraft03::keypair_from_seed(&seed);
let message = b"test";
let proof = VrfDraft03::prove(&sk, message).unwrap();
let vk_bytes = <VrfDraft03 as VrfAlgorithm>::raw_serialize_verification_key(&vk);
let vk_restored =
<VrfDraft03 as VrfAlgorithm>::raw_deserialize_verification_key(vk_bytes).unwrap();
assert_eq!(vk, vk_restored);
let proof_bytes = <VrfDraft03 as VrfAlgorithm>::raw_serialize_proof(&proof);
let proof_restored =
<VrfDraft03 as VrfAlgorithm>::raw_deserialize_proof(proof_bytes).unwrap();
assert_eq!(proof, proof_restored);
}
}