Skip to main content

libecvrf_k256/
ecvrf.rs

1use crate::{
2    error,
3    hash::{hash_points, hash_to_curve},
4};
5use k256::{
6    elliptic_curve::{
7        rand_core::OsRng,
8        sec1::{FromEncodedPoint, ToEncodedPoint},
9        subtle::ConditionallyNegatable,
10        Field, PrimeField,
11    },
12    AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, PublicKey, Scalar, SecretKey,
13};
14use serde::{Deserialize, Serialize};
15use tiny_keccak::{Hasher, Keccak};
16
17/// EC-VRF proof
18#[derive(Clone)]
19#[derive(Debug)]
20#[derive(Eq, PartialEq)]
21#[derive(Serialize, Deserialize)]
22pub struct ECVRFProof {
23    /// gamma
24    pub gamma: AffinePoint,
25    /// c
26    pub c: Scalar,
27    /// s
28    pub s: Scalar,
29    /// y is the result
30    pub y: Scalar,
31    /// Public key
32    pub pk: PublicKey,
33}
34
35/// ECVRF
36pub struct ECVRF {
37    secret_key: SecretKey,
38    public_key: PublicKey,
39}
40
41impl ECVRF {
42    /// Create new instance of ECVRF from a secret key
43    pub fn new(secret_key: SecretKey) -> Self {
44        let public_key = PublicKey::from_secret_scalar(&secret_key.to_nonzero_scalar());
45        ECVRF {
46            secret_key,
47            public_key,
48        }
49    }
50
51    pub fn new_from_bytes(secret_key: &[u8]) -> Result<Self, error::Error> {
52        let secret_key =
53            SecretKey::from_slice(secret_key).map_err(|_| error::Error::UnknowError)?;
54        let public_key = PublicKey::from_secret_scalar(&secret_key.to_nonzero_scalar());
55        Ok(ECVRF {
56            secret_key,
57            public_key,
58        })
59    }
60
61    /// Ordinary prover
62    pub fn prove(&self, alpha: &[u8]) -> Result<ECVRFProof, error::Error> {
63        let alpha = Self::generate_alpha(alpha);
64
65        let pub_affine: AffinePoint = self.public_key.into();
66        let secret_key: Scalar = *self.secret_key.to_nonzero_scalar();
67
68        // Hash to a point on curve
69        let h = hash_to_curve(&alpha, Some(&pub_affine));
70
71        // gamma = H * secret_key
72        let gamma = h * secret_key;
73
74        // k = random()
75        // We need to make sure that k < GROUP_ORDER
76        let k = Scalar::random(&mut OsRng);
77
78        // Calculate k * G <=> u
79        let kg = ProjectivePoint::GENERATOR * k;
80
81        // Calculate k * H <=> v
82        let kh = h * k;
83
84        // c = ECVRF_hash_points(G, H, public_key, gamma, k * G, k * H)
85        let c = hash_points(
86            &AffinePoint::GENERATOR,
87            &h,
88            &pub_affine,
89            &gamma.to_affine(),
90            &kg.to_affine(),
91            &kh.to_affine(),
92        );
93
94        // s = (k - c * secret_key) mod p
95        let mut neg_c = c;
96        neg_c.conditional_negate(1.into());
97        let s = k + neg_c * secret_key;
98
99        // y = keccak256(gama.encode())
100        let mut output = [0u8; 32];
101        let mut hasher = Keccak::v256();
102        hasher.update(gamma.to_encoded_point(true).as_bytes());
103        hasher.finalize(&mut output);
104        let bytes = FieldBytes::from_slice(&output);
105        let y = Scalar::from_repr(*bytes).unwrap();
106
107        Ok(ECVRFProof {
108            gamma: gamma.to_affine(),
109            c,
110            s,
111            y,
112            pk: self.public_key,
113        })
114    }
115
116    /// Verify proof
117    pub fn verify(alpha: &[u8], vrf_proof: &ECVRFProof, public_key: &[u8]) -> bool {
118        let alpha = Self::generate_alpha(alpha);
119        let pub_encoded = EncodedPoint::from_bytes(public_key).unwrap();
120        let pub_point = ProjectivePoint::from_encoded_point(&pub_encoded).unwrap();
121        let u = pub_point * vrf_proof.c + ProjectivePoint::GENERATOR * vrf_proof.s;
122
123        let h = hash_to_curve(&alpha, Some(&pub_point.to_affine()));
124
125        // Gamma witness: c * gamma
126        let witness_gamma = ProjectivePoint::from(vrf_proof.gamma) * vrf_proof.c;
127
128        // Hash witness: s * H
129        let witness_hash = ProjectivePoint::from(h) * vrf_proof.s;
130
131        // V = c * gamma + s * H
132        let v = witness_gamma + witness_hash;
133
134        // c_prime = ECVRF_hash_points(G, H, pk, gamma, U, V)
135        let computed_c = hash_points(
136            &AffinePoint::GENERATOR,
137            &h,
138            &pub_point.to_affine(),
139            &vrf_proof.gamma,
140            &u.to_affine(),
141            &v.to_affine(),
142        );
143
144        // y = keccak256(gamma.encode())
145        let mut output = [0u8; 32];
146        let mut hasher = Keccak::v256();
147        hasher.update(vrf_proof.gamma.to_encoded_point(true).as_bytes());
148        hasher.finalize(&mut output);
149
150        let bytes = FieldBytes::from_slice(&output);
151        let computed_y = Scalar::from_repr(*bytes).unwrap();
152
153        computed_c == vrf_proof.c && computed_y == vrf_proof.y
154    }
155
156    fn generate_alpha(alpha: &[u8]) -> Scalar {
157        let mut output = [0u8; 32];
158        let mut hasher = Keccak::v256();
159        hasher.update(alpha);
160        hasher.finalize(&mut output);
161
162        let bytes = FieldBytes::from_slice(&output);
163        Scalar::from_repr(*bytes).unwrap()
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use k256::{
170        elliptic_curve::{rand_core::OsRng, sec1::ToEncodedPoint},
171        PublicKey, SecretKey,
172    };
173
174    use crate::ECVRF;
175
176    const TEST_ALPHA: &[u8] = b"test alphaddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
177
178    #[test]
179    fn success_proof_and_verify() {
180        let secret_key = SecretKey::random(&mut OsRng);
181        let public_key = PublicKey::from_secret_scalar(&secret_key.to_nonzero_scalar());
182
183        let ecvrf = ECVRF::new(secret_key);
184        let proof = ecvrf.prove(TEST_ALPHA).unwrap();
185        assert_eq!(proof.pk, public_key);
186
187        let result = ECVRF::verify(
188            TEST_ALPHA,
189            &proof,
190            public_key.to_encoded_point(true).as_bytes(),
191        );
192        assert!(result);
193    }
194}