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#[derive(Clone)]
19#[derive(Debug)]
20#[derive(Eq, PartialEq)]
21#[derive(Serialize, Deserialize)]
22pub struct ECVRFProof {
23 pub gamma: AffinePoint,
25 pub c: Scalar,
27 pub s: Scalar,
29 pub y: Scalar,
31 pub pk: PublicKey,
33}
34
35pub struct ECVRF {
37 secret_key: SecretKey,
38 public_key: PublicKey,
39}
40
41impl ECVRF {
42 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 prove(&self, alpha: &[u8]) -> Result<ECVRFProof, error::Error> {
53 let alpha = Self::generate_alpha(alpha);
54
55 let pub_affine: AffinePoint = self.public_key.into();
56 let secret_key: Scalar = *self.secret_key.to_nonzero_scalar();
57
58 let h = hash_to_curve(&alpha, Some(&pub_affine));
60
61 let gamma = h * secret_key;
63
64 let k = Scalar::random(&mut OsRng);
67
68 let kg = ProjectivePoint::GENERATOR * k;
70
71 let kh = h * k;
73
74 let c = hash_points(
76 &AffinePoint::GENERATOR,
77 &h,
78 &pub_affine,
79 &gamma.to_affine(),
80 &kg.to_affine(),
81 &kh.to_affine(),
82 );
83
84 let mut neg_c = c;
86 neg_c.conditional_negate(1.into());
87 let s = k + neg_c * secret_key;
88
89 let mut output = [0u8; 32];
91 let mut hasher = Keccak::v256();
92 hasher.update(gamma.to_encoded_point(true).as_bytes());
93 hasher.finalize(&mut output);
94 let bytes = FieldBytes::from_slice(&output);
95 let y = Scalar::from_repr(*bytes).unwrap();
96
97 Ok(ECVRFProof {
98 gamma: gamma.to_affine(),
99 c,
100 s,
101 y,
102 pk: self.public_key,
103 })
104 }
105
106 pub fn verify(alpha: &[u8], vrf_proof: &ECVRFProof, public_key: &[u8]) -> bool {
108 let alpha = Self::generate_alpha(alpha);
109 let pub_encoded = EncodedPoint::from_bytes(public_key).unwrap();
110 let pub_point = ProjectivePoint::from_encoded_point(&pub_encoded).unwrap();
111 let u = pub_point * vrf_proof.c + ProjectivePoint::GENERATOR * vrf_proof.s;
112
113 let h = hash_to_curve(&alpha, Some(&pub_point.to_affine()));
114
115 let witness_gamma = ProjectivePoint::from(vrf_proof.gamma) * vrf_proof.c;
117
118 let witness_hash = ProjectivePoint::from(h) * vrf_proof.s;
120
121 let v = witness_gamma + witness_hash;
123
124 let computed_c = hash_points(
126 &AffinePoint::GENERATOR,
127 &h,
128 &pub_point.to_affine(),
129 &vrf_proof.gamma,
130 &u.to_affine(),
131 &v.to_affine(),
132 );
133
134 let mut output = [0u8; 32];
136 let mut hasher = Keccak::v256();
137 hasher.update(vrf_proof.gamma.to_encoded_point(true).as_bytes());
138 hasher.finalize(&mut output);
139
140 let bytes = FieldBytes::from_slice(&output);
141 let computed_y = Scalar::from_repr(*bytes).unwrap();
142
143 computed_c == vrf_proof.c && computed_y == vrf_proof.y
144 }
145
146 fn generate_alpha(alpha: &[u8]) -> Scalar {
147 let mut output = [0u8; 32];
148 let mut hasher = Keccak::v256();
149 hasher.update(alpha);
150 hasher.finalize(&mut output);
151
152 let bytes = FieldBytes::from_slice(&output);
153 Scalar::from_repr(*bytes).unwrap()
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use k256::{
160 elliptic_curve::{rand_core::OsRng, sec1::ToEncodedPoint},
161 PublicKey, SecretKey,
162 };
163
164 use crate::ECVRF;
165
166 const TEST_ALPHA: &[u8] = b"test alphaddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
167
168 #[test]
169 fn success_proof_and_verify() {
170 let secret_key = SecretKey::random(&mut OsRng);
171 let public_key = PublicKey::from_secret_scalar(&secret_key.to_nonzero_scalar());
172
173 let ecvrf = ECVRF::new(secret_key);
174 let proof = ecvrf.prove(TEST_ALPHA).unwrap();
175 assert_eq!(proof.pk, public_key);
176
177 let result = ECVRF::verify(
178 TEST_ALPHA,
179 &proof,
180 public_key.to_encoded_point(true).as_bytes(),
181 );
182 assert!(result);
183 }
184}