1use hopr_crypto_random::random_bytes;
2use hopr_primitive_types::prelude::*;
3use k256::{
4 AffinePoint, Scalar, Secp256k1,
5 elliptic_curve::{
6 ProjectivePoint,
7 hash2curve::{ExpandMsgXmd, GroupDigest},
8 sec1::ToEncodedPoint,
9 },
10};
11
12use crate::{
13 errors::{CryptoError::CalculationError, Result},
14 keypairs::{ChainKeypair, Keypair},
15 types::{PublicKey, affine_point_from_bytes},
16 utils::k256_scalar_from_bytes,
17};
18
19#[allow(non_snake_case)]
24#[derive(Clone, Copy, Default)]
25pub struct VrfParameters {
26 pub V: AffinePoint,
28 pub h: Scalar,
29 pub s: Scalar,
30}
31
32#[cfg(feature = "serde")]
33impl serde::Serialize for VrfParameters {
34 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
35 where
36 S: serde::Serializer,
37 {
38 let v: [u8; Self::SIZE] = (*self).into();
39 serializer.serialize_bytes(v.as_ref())
40 }
41}
42
43#[cfg(feature = "serde")]
44mod de {
45 use serde::de;
46
47 use super::*;
48
49 pub(super) struct VrfParametersVisitor {}
50
51 impl de::Visitor<'_> for VrfParametersVisitor {
52 type Value = VrfParameters;
53
54 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
55 formatter.write_fmt(format_args!("a byte-array with {} elements", VrfParameters::SIZE))
56 }
57
58 fn visit_bytes<E>(self, v: &[u8]) -> std::result::Result<Self::Value, E>
59 where
60 E: de::Error,
61 {
62 VrfParameters::try_from(v).map_err(|e| de::Error::custom(e.to_string()))
63 }
64 }
65}
66
67#[cfg(feature = "serde")]
69impl<'de> serde::Deserialize<'de> for VrfParameters {
70 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
71 where
72 D: serde::Deserializer<'de>,
73 {
74 deserializer.deserialize_bytes(de::VrfParametersVisitor {})
75 }
76}
77
78impl std::fmt::Debug for VrfParameters {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 f.debug_struct("VrfParameters")
81 .field("V", &hex::encode(self.V.to_encoded_point(true)))
82 .field("h", &hex::encode(self.h.to_bytes()))
83 .field("s", &hex::encode(self.s.to_bytes()))
84 .finish()
85 }
86}
87
88impl From<VrfParameters> for [u8; VRF_PARAMETERS_SIZE] {
89 fn from(value: VrfParameters) -> Self {
90 let mut ret = [0u8; VRF_PARAMETERS_SIZE];
91 ret[0..PublicKey::SIZE_COMPRESSED].copy_from_slice(value.V.to_encoded_point(true).as_bytes());
92 ret[PublicKey::SIZE_COMPRESSED..PublicKey::SIZE_COMPRESSED + 32].copy_from_slice(value.h.to_bytes().as_ref());
93 ret[PublicKey::SIZE_COMPRESSED + 32..PublicKey::SIZE_COMPRESSED + 64]
94 .copy_from_slice(value.s.to_bytes().as_ref());
95 ret
96 }
97}
98
99impl TryFrom<&[u8]> for VrfParameters {
100 type Error = GeneralError;
101
102 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
103 if value.len() == Self::SIZE {
104 let mut v = [0u8; PublicKey::SIZE_COMPRESSED];
105 v.copy_from_slice(&value[..PublicKey::SIZE_COMPRESSED]);
106 Ok(VrfParameters {
107 V: affine_point_from_bytes(&value[..PublicKey::SIZE_COMPRESSED])
108 .map_err(|_| GeneralError::ParseError("VrfParameters.V".into()))?,
109 h: k256_scalar_from_bytes(&value[PublicKey::SIZE_COMPRESSED..PublicKey::SIZE_COMPRESSED + 32])
110 .map_err(|_| GeneralError::ParseError("VrfParameters.h".into()))?,
111 s: k256_scalar_from_bytes(
112 &value[PublicKey::SIZE_COMPRESSED + 32..PublicKey::SIZE_COMPRESSED + 32 + 32],
113 )
114 .map_err(|_| GeneralError::ParseError("VrfParameters.s".into()))?,
115 })
116 } else {
117 Err(GeneralError::ParseError("VrfParameters.size".into()))
118 }
119 }
120}
121
122const VRF_PARAMETERS_SIZE: usize = PublicKey::SIZE_COMPRESSED + 32 + 32;
123impl BytesEncodable<VRF_PARAMETERS_SIZE> for VrfParameters {}
124
125impl VrfParameters {
126 #[allow(non_snake_case)]
129 pub fn verify<const T: usize>(&self, creator: &Address, msg: &[u8; T], dst: &[u8]) -> Result<()> {
130 let cap_B = self.get_encoded_payload(creator, msg, dst)?;
131 let v_proj = ProjectivePoint::<Secp256k1>::from(self.V);
132
133 let R_v: ProjectivePoint<Secp256k1> = cap_B * self.s - v_proj * self.h;
134
135 let h_check = Secp256k1::hash_to_scalar::<ExpandMsgXmd<sha3::Keccak256>>(
136 &[
137 creator.as_ref(),
138 &self.V.to_encoded_point(false).as_bytes()[1..],
139 &R_v.to_affine().to_encoded_point(false).as_bytes()[1..],
140 msg,
141 ],
142 &[dst],
143 )
144 .or(Err(CalculationError))?;
145
146 if h_check != self.h {
147 return Err(CalculationError);
148 }
149
150 Ok(())
151 }
152
153 pub fn get_v_encoded_point(&self) -> k256::EncodedPoint {
155 self.V.to_encoded_point(false)
156 }
157
158 pub fn get_h_v_witness(&self) -> k256::EncodedPoint {
163 (ProjectivePoint::<Secp256k1>::from(self.V) * self.h)
164 .to_affine()
165 .to_encoded_point(false)
166 }
167
168 pub fn get_s_b_witness<const T: usize>(
174 &self,
175 creator: &Address,
176 msg: &[u8; T],
177 dst: &[u8],
178 ) -> Result<k256::EncodedPoint> {
179 Ok((self.get_encoded_payload(creator, msg, dst)? * self.s)
180 .to_affine()
181 .to_encoded_point(false))
182 }
183
184 fn get_encoded_payload<const T: usize>(
191 &self,
192 creator: &Address,
193 msg: &[u8; T],
194 dst: &[u8],
195 ) -> Result<k256::ProjectivePoint> {
196 Secp256k1::hash_from_bytes::<ExpandMsgXmd<sha3::Keccak256>>(&[creator.as_ref(), msg], &[dst])
197 .or(Err(CalculationError))
198 }
199}
200
201#[cfg(feature = "rust-ecdsa")]
205#[allow(non_snake_case)]
206pub fn derive_vrf_parameters<T: AsRef<[u8]>>(
207 msg: T,
208 chain_keypair: &ChainKeypair,
209 dst: &[u8],
210) -> crate::errors::Result<VrfParameters> {
211 let chain_addr = chain_keypair.public().to_address();
212 let B = Secp256k1::hash_from_bytes::<ExpandMsgXmd<sha3::Keccak256>>(&[chain_addr.as_ref(), msg.as_ref()], &[dst])?;
213
214 let a: Scalar = chain_keypair.into();
215
216 let V = B * a;
217
218 let r = Secp256k1::hash_to_scalar::<ExpandMsgXmd<sha3::Keccak256>>(
219 &[
220 &a.to_bytes(),
221 &V.to_affine().to_encoded_point(false).as_bytes()[1..],
222 &random_bytes::<64>(),
223 ],
224 &[dst],
225 )?;
226
227 let R_v = B * r;
228
229 let h = Secp256k1::hash_to_scalar::<ExpandMsgXmd<sha3::Keccak256>>(
230 &[
231 chain_addr.as_ref(),
232 &V.to_affine().to_encoded_point(false).as_bytes()[1..],
233 &R_v.to_affine().to_encoded_point(false).as_bytes()[1..],
234 msg.as_ref(),
235 ],
236 &[dst],
237 )?;
238 let s = r + h * a;
239
240 Ok(VrfParameters { V: V.to_affine(), h, s })
241}
242
243#[cfg(not(feature = "rust-ecdsa"))]
247#[allow(non_snake_case)]
248pub fn derive_vrf_parameters<T: AsRef<[u8]>>(
249 msg: T,
250 chain_keypair: &ChainKeypair,
251 dst: &[u8],
252) -> Result<VrfParameters> {
253 let chain_addr = chain_keypair.public().to_address();
254 let B = Secp256k1::hash_from_bytes::<ExpandMsgXmd<sha3::Keccak256>>(&[chain_addr.as_ref(), msg.as_ref()], &[dst])?
255 .to_affine();
256
257 let a = secp256k1::Scalar::from_be_bytes(chain_keypair.secret().clone().into())
258 .map_err(|_| crate::errors::CryptoError::InvalidSecretScalar)?;
259
260 let B_pk = secp256k1::PublicKey::from_byte_array_uncompressed(
261 B.to_encoded_point(false)
262 .as_bytes()
263 .try_into()
264 .map_err(|_| crate::errors::CryptoError::InvalidPublicKey)?,
265 )
266 .map_err(|_| crate::errors::CryptoError::InvalidPublicKey)?;
267
268 let V = B_pk
269 .mul_tweak(secp256k1::global::SECP256K1, &a)
270 .map_err(|_| CalculationError)?;
271
272 let r = Secp256k1::hash_to_scalar::<ExpandMsgXmd<sha3::Keccak256>>(
273 &[
274 &a.to_be_bytes(),
275 &V.serialize_uncompressed()[1..],
276 &random_bytes::<64>(),
277 ],
278 &[dst],
279 )?;
280
281 let r_scalar = secp256k1::Scalar::from_be_bytes(r.to_bytes().into())
282 .map_err(|_| crate::errors::CryptoError::InvalidSecretScalar)?;
283
284 let R_v = B_pk
285 .mul_tweak(secp256k1::global::SECP256K1, &r_scalar)
286 .map_err(|_| CalculationError)?;
287
288 let h = Secp256k1::hash_to_scalar::<ExpandMsgXmd<sha3::Keccak256>>(
289 &[
290 chain_addr.as_ref(),
291 &V.serialize_uncompressed()[1..],
292 &R_v.serialize_uncompressed()[1..],
293 msg.as_ref(),
294 ],
295 &[dst],
296 )?;
297 let s = r + h * Scalar::from(chain_keypair);
298
299 let V = affine_point_from_bytes(&V.serialize_uncompressed()).map_err(|_| CalculationError)?;
300
301 Ok(VrfParameters { V, h, s })
302}
303
304#[cfg(test)]
305mod tests {
306 use hex_literal::hex;
307 use k256::elliptic_curve::ScalarPrimitive;
308 use sha3::Keccak256;
309
310 use super::*;
311 use crate::types::Hash;
312
313 lazy_static::lazy_static! {
314 static ref ALICE: ChainKeypair = ChainKeypair::from_secret(&hex!("e17fe86ce6e99f4806715b0c9412f8dad89334bf07f72d5834207a9d8f19d7f8")).expect("lazy static keypair should be valid");
315 static ref ALICE_ADDR: Address = ALICE.public().to_address();
316
317 static ref TEST_MSG: [u8; 32] = hex!("8248a966b9215e154c8f673cb154da030916be3fb31af3b1220419a1c98eeaed");
318 static ref ALICE_VRF_OUTPUT: [u8; 97] = hex!("02a4e1fa28e8a40348baf79b576a6e040b370b74893d355cd48fc382d5235ff0652ee2b835e7c475fde5adfedeb7cc31ecdd690f13ac6bb59ed046ca4c189c9996fe60abaad8c93e771c19acfe697e15c1e5ed6a182b2960bf8c7bd687e77a9975");
319
320 static ref WRONG_V_POINT_PREFIX: [u8; 97] = hex!("01a4e1fa28e8a40348baf79b576a6e040b370b74893d355cd48fc382d5235ff0652ee2b835e7c475fde5adfedeb7cc31ecdd690f13ac6bb59ed046ca4c189c9996fe60abaad8c93e771c19acfe697e15c1e5ed6a182b2960bf8c7bd687e77a9975");
321 static ref H_NOT_IN_FIELD: [u8; 97] = hex!("02a4e1fa28e8a40348baf79b576a6e040b370b74893d355cd48fc382d5235ff065fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe60abaad8c93e771c19acfe697e15c1e5ed6a182b2960bf8c7bd687e77a9975");
322 static ref S_NOT_IN_FIELD: [u8; 97] = hex!("02a4e1fa28e8a40348baf79b576a6e040b370b74893d355cd48fc382d5235ff0652ee2b835e7c475fde5adfedeb7cc31ecdd690f13ac6bb59ed046ca4c189c9996ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
323 }
324
325 #[test]
326 fn vrf_values_serialize_deserialize() -> anyhow::Result<()> {
327 let vrf_values = derive_vrf_parameters(*TEST_MSG, &ALICE, Hash::default().as_ref())?;
328
329 let deserialized = VrfParameters::try_from(ALICE_VRF_OUTPUT.as_ref())?;
330
331 assert_eq!(vrf_values.V, deserialized.V);
333 assert!(
334 deserialized
335 .verify(&ALICE_ADDR, &TEST_MSG, Hash::default().as_ref())
336 .is_ok()
337 );
338
339 let vrf: [u8; VrfParameters::SIZE] = vrf_values.clone().into();
341 let other = VrfParameters::try_from(vrf.as_ref())?;
342 assert!(vrf_values.s == other.s && vrf_values.V == other.V && vrf_values.h == other.h);
343
344 Ok(())
345 }
346
347 #[test]
348 fn vrf_values_serialize_deserialize_bad_examples() {
349 assert!(VrfParameters::try_from(WRONG_V_POINT_PREFIX.as_ref()).is_err());
350
351 assert!(VrfParameters::try_from(H_NOT_IN_FIELD.as_ref()).is_err());
352
353 assert!(VrfParameters::try_from(S_NOT_IN_FIELD.as_ref()).is_err());
354 }
355
356 #[test]
357 fn vrf_values_crypto() -> anyhow::Result<()> {
358 let vrf_values = derive_vrf_parameters(*TEST_MSG, &ALICE, Hash::default().as_ref())?;
359
360 assert!(
361 vrf_values
362 .verify(&ALICE_ADDR, &TEST_MSG, Hash::default().as_ref())
363 .is_ok()
364 );
365
366 Ok(())
367 }
368
369 #[test]
370 fn test_vrf_parameter_generation() -> anyhow::Result<()> {
371 let dst = b"some DST tag";
372 let priv_key: [u8; 32] = hex!("f13233ff60e1f618525dac5f7d117bef0bad0eb0b0afb2459f9cbc57a3a987ba"); let message = hex!("f13233ff60e1f618525dac5f7d117bef0bad0eb0b0afb2459f9cbc57a3a987ba"); let keypair = ChainKeypair::from_secret(&priv_key)?;
376 let pub_key = PublicKey::from_privkey(&priv_key)?;
378
379 let params = derive_vrf_parameters(message, &keypair, dst)?;
380
381 let cap_b =
382 Secp256k1::hash_from_bytes::<ExpandMsgXmd<Keccak256>>(&[pub_key.to_address().as_ref(), &message], &[dst])?;
383
384 assert_eq!(
385 params.get_s_b_witness(&keypair.public().to_address(), &message, dst)?,
386 (cap_b * params.s).to_encoded_point(false)
387 );
388
389 let a: Scalar = ScalarPrimitive::<Secp256k1>::from_slice(&priv_key)?.into();
390 assert_eq!(params.get_h_v_witness(), (cap_b * a * params.h).to_encoded_point(false));
391
392 let r_v: ProjectivePoint<Secp256k1> =
393 cap_b * params.s - ProjectivePoint::<Secp256k1>::from(params.V) * params.h;
394
395 let h_check = Secp256k1::hash_to_scalar::<ExpandMsgXmd<Keccak256>>(
396 &[
397 pub_key.to_address().as_ref(),
398 ¶ms.V.to_encoded_point(false).as_bytes()[1..],
399 &r_v.to_affine().to_encoded_point(false).as_bytes()[1..],
400 &message,
401 ],
402 &[dst],
403 )?;
404
405 assert_eq!(h_check, params.h);
406
407 Ok(())
408 }
409}