darkfi_sdk/crypto/
ecvrf.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19//! <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-04#section-5>
20#![allow(non_snake_case)]
21
22#[cfg(feature = "async")]
23use darkfi_serial::async_trait;
24use darkfi_serial::{SerialDecodable, SerialEncodable};
25use halo2_gadgets::ecc::chip::FixedPoint;
26use pasta_curves::{
27    arithmetic::CurveExt,
28    group::{
29        ff::{FromUniformBytes, PrimeField},
30        GroupEncoding,
31    },
32    pallas,
33};
34
35use super::{
36    constants::NullifierK,
37    util::{fp_mod_fv, hash_to_scalar},
38    PublicKey, SecretKey,
39};
40
41/// Prefix domain used for `hash_to_curve` calls
42const VRF_DOMAIN: &str = "DarkFi_ECVRF";
43
44/// VRF Proof
45#[derive(Copy, Clone, Debug, SerialEncodable, SerialDecodable)]
46pub struct VrfProof {
47    gamma: pallas::Point,
48    c: blake3::Hash,
49    s: pallas::Scalar,
50}
51
52impl VrfProof {
53    /// Execute the VRF function and create a proof given a `SecretKey`
54    /// and a seed input `alpha_string`.
55    pub fn prove(x: SecretKey, alpha_string: &[u8]) -> Self {
56        let Y = PublicKey::from_secret(x);
57
58        let mut message = vec![];
59        message.extend_from_slice(&Y.to_bytes());
60        message.extend_from_slice(alpha_string);
61        let H = pallas::Point::hash_to_curve(VRF_DOMAIN)(&message);
62
63        let gamma = H * fp_mod_fv(x.inner());
64
65        // Generate a determinnistic nonce
66        let k = hash_to_scalar(VRF_DOMAIN.as_bytes(), &[&x.inner().to_repr(), &H.to_bytes()]);
67
68        let mut hasher = blake3::Hasher::new();
69        hasher.update(&H.to_bytes());
70        hasher.update(&gamma.to_bytes());
71        // The paper's B generator we use is NullifierK as that's used for
72        // SecretKey -> PublicKey derivation.
73        hasher.update(&(NullifierK.generator() * k).to_bytes());
74        hasher.update(&(H * k).to_bytes());
75        let c = hasher.finalize();
76
77        let mut c_scalar = [0u8; 64];
78        c_scalar[..blake3::OUT_LEN].copy_from_slice(c.as_bytes());
79        let c_scalar = pallas::Scalar::from_uniform_bytes(&c_scalar);
80
81        let s = k + c_scalar * fp_mod_fv(x.inner());
82
83        Self { gamma, c, s }
84    }
85
86    /// Verify a `VrfProof` given a `Publickey` and a seed input `alpha_string`.
87    pub fn verify(&self, Y: PublicKey, alpha_string: &[u8]) -> bool {
88        let mut message = vec![];
89        message.extend_from_slice(&Y.to_bytes());
90        message.extend_from_slice(alpha_string);
91        let H = pallas::Point::hash_to_curve(VRF_DOMAIN)(&message);
92
93        let mut c = [0u8; 64];
94        c[..blake3::OUT_LEN].copy_from_slice(self.c.as_bytes());
95        let c_scalar = pallas::Scalar::from_uniform_bytes(&c);
96
97        let U = NullifierK.generator() * self.s - Y.inner() * c_scalar;
98        let V = H * self.s - self.gamma * c_scalar;
99
100        let mut hasher = blake3::Hasher::new();
101        hasher.update(&H.to_bytes());
102        hasher.update(&self.gamma.to_bytes());
103        hasher.update(&U.to_bytes());
104        hasher.update(&V.to_bytes());
105
106        hasher.finalize() == self.c
107    }
108
109    /// Returns the VRF output.
110    /// **It is necessary** to do `VrfProof::verify` first in order to trust this function's output.
111    /// TODO: FIXME: We should enforce verification before getting the output.
112    pub fn hash_output(&self) -> blake3::Hash {
113        let mut hasher = blake3::Hasher::new();
114        hasher.update(VRF_DOMAIN.as_bytes());
115        hasher.update(&[0x03]);
116        hasher.update(&self.gamma.to_bytes());
117        hasher.finalize()
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use rand::rngs::OsRng;
125
126    #[test]
127    fn ecvrf() {
128        // VRF secret key
129        let secret_key = SecretKey::random(&mut OsRng);
130        // VRF public key
131        let public_key = PublicKey::from_secret(secret_key);
132        // VRF input
133        let input = [0xde, 0xad, 0xbe, 0xef];
134
135        let proof = VrfProof::prove(secret_key, &input);
136        assert!(proof.verify(public_key, &input));
137
138        // Forged public key
139        let forged_public_key = PublicKey::from_secret(SecretKey::random(&mut OsRng));
140        assert!(!proof.verify(forged_public_key, &input));
141
142        // Forged input
143        let forged_input = [0xde, 0xad, 0xba, 0xbe];
144        assert!(!proof.verify(public_key, &forged_input));
145    }
146}