use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
use sha2::{Digest, Sha512};
use zeroize::Zeroizing;
use super::point::cardano_hash_to_curve;
use crate::common::{point_to_bytes, CryptoResult, SUITE_DRAFT03, TWO};
pub fn cardano_vrf_prove(secret_key: &[u8; 64], message: &[u8]) -> CryptoResult<[u8; 80]> {
let mut az = Zeroizing::new([0u8; 64]);
let mut hasher = Sha512::new();
hasher.update(&secret_key[0..32]);
let hash = hasher.finalize();
az.copy_from_slice(&hash);
az[0] &= 248;
az[31] &= 127;
az[31] |= 64;
let secret_scalar_bytes: [u8; 32] = az[0..32]
.try_into()
.expect("secret key slice must be 32 bytes");
let x = Scalar::from_bytes_mod_order(secret_scalar_bytes);
let pk = &secret_key[32..64];
let (h_point, h_string) = cardano_hash_to_curve(pk, message)?;
let gamma = h_point * x;
let gamma_bytes = point_to_bytes(&gamma);
let mut nonce_hasher = Sha512::new();
nonce_hasher.update(&az[32..64]);
nonce_hasher.update(h_string);
let nonce_hash = nonce_hasher.finalize();
let nonce_hash_bytes: [u8; 64] = nonce_hash.into();
let k = Scalar::from_bytes_mod_order_wide(&nonce_hash_bytes);
let k_b = ED25519_BASEPOINT_POINT * k;
let k_h = h_point * k;
let k_b_bytes = point_to_bytes(&k_b);
let k_h_bytes = point_to_bytes(&k_h);
let mut c_hasher = Sha512::new();
c_hasher.update([SUITE_DRAFT03]);
c_hasher.update([TWO]);
c_hasher.update(h_string);
c_hasher.update(gamma_bytes);
c_hasher.update(k_b_bytes);
c_hasher.update(k_h_bytes);
let c_hash = c_hasher.finalize();
let c_bytes_short: [u8; 16] = c_hash[0..16].try_into().unwrap();
let mut c_bytes = [0u8; 32];
c_bytes[0..16].copy_from_slice(&c_bytes_short);
let c = Scalar::from_bytes_mod_order(c_bytes);
let s = k + (c * x);
let s_bytes = s.to_bytes();
let mut proof = [0u8; 80];
proof[0..32].copy_from_slice(&gamma_bytes);
proof[32..48].copy_from_slice(&c_bytes_short);
proof[48..80].copy_from_slice(&s_bytes);
Ok(proof)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prove_deterministic() {
let mut sk = [0u8; 64];
sk[0..32].fill(1);
sk[32..64].copy_from_slice(&[2u8; 32]);
let message = b"test";
let proof1 = cardano_vrf_prove(&sk, message).expect("prove failed");
let proof2 = cardano_vrf_prove(&sk, message).expect("prove failed");
assert_eq!(proof1, proof2);
}
}