concordium_base/ecvrf/
mod.rs

1//! Implementation of the verifiable random function as specified in <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-09>.
2
3mod constants;
4mod errors;
5mod proof;
6mod public;
7mod secret;
8
9pub use constants::*;
10
11pub use self::{errors::*, proof::*, public::*, secret::*};
12use crate::common::*;
13use rand::{CryptoRng, Rng};
14
15/// An ed25519 keypair.
16#[derive(Debug, Serialize)]
17pub struct Keypair {
18    /// The secret half of this keypair.
19    pub secret: SecretKey,
20    /// The public half of this keypair.
21    pub public: PublicKey,
22}
23
24impl Keypair {
25    /// Generate an ed25519 keypair.
26    pub fn generate<R>(csprng: &mut R) -> Keypair
27    where
28        R: CryptoRng + Rng,
29    {
30        let sk = SecretKey::generate(csprng);
31        let pk = PublicKey::from(&sk);
32
33        Keypair {
34            public: pk,
35            secret: sk,
36        }
37    }
38
39    /// Construct a VRF proof with this keypair's secret key.
40    pub fn prove(&self, message: &[u8]) -> Proof {
41        let expanded: ExpandedSecretKey = (&self.secret).into();
42
43        expanded.prove(&self.public, message)
44    }
45}
46
47#[cfg(feature = "ffi")]
48mod expose_ffi {
49    use super::*;
50    use crate::{common::size_t, ffi_helpers::*};
51    use rand::thread_rng;
52    use std::{cmp::Ordering, sync::Arc};
53    use subtle::ConstantTimeEq;
54
55    // foreign interface
56
57    // Boilerplate serialization functions.
58    macro_derive_from_bytes!(Arc ecvrf_proof_from_bytes, Proof);
59    macro_derive_from_bytes!(
60        Box ecvrf_public_key_from_bytes,
61        PublicKey
62    );
63    macro_derive_from_bytes_no_cursor!(
64        Box ecvrf_secret_key_from_bytes,
65        SecretKey,
66        SecretKey::from_bytes
67    );
68    macro_derive_to_bytes!(Arc ecvrf_proof_to_bytes, Proof);
69    macro_derive_to_bytes!(Box ecvrf_public_key_to_bytes, PublicKey);
70    macro_derive_to_bytes!(Box ecvrf_secret_key_to_bytes, SecretKey);
71    // Cleanup of allocated structs.
72    macro_free_ffi!(Arc ecvrf_proof_free, Proof);
73    macro_free_ffi!(Box ecvrf_public_key_free, PublicKey);
74    macro_free_ffi!(Box ecvrf_secret_key_free, SecretKey);
75
76    // equality testing
77    macro_derive_binary!(Arc ecvrf_proof_eq, Proof, Proof::eq);
78    macro_derive_binary!(Box ecvrf_public_key_eq, PublicKey, PublicKey::eq);
79    // NB: Using constant time comparison.
80    macro_derive_binary!(Box ecvrf_secret_key_eq, SecretKey, |x, y| bool::from(
81        SecretKey::ct_eq(x, y)
82    ));
83
84    // ord instance for proof
85
86    #[no_mangle]
87    #[allow(clippy::not_unsafe_ptr_arg_deref)]
88    /// Generate a VRF proof. This function assumes the arguments are not
89    /// null-pointers and it always returns a non-null pointer.
90    extern "C" fn ecvrf_prove(
91        public: *mut PublicKey,
92        secret: *mut SecretKey,
93        message: *const u8,
94        len: size_t,
95    ) -> *const Proof {
96        let sk = from_ptr!(secret);
97        let pk = from_ptr!(public);
98        let data: &[u8] = slice_from_c_bytes!(message, len);
99        let proof = sk.prove(pk, data);
100        Arc::into_raw(Arc::new(proof))
101    }
102
103    #[no_mangle]
104    /// Generate a new secret key using the system random number generator.
105    /// The result is always a non-null pointer.
106    extern "C" fn ecvrf_priv_key() -> *mut SecretKey {
107        let mut csprng = thread_rng();
108        let sk = SecretKey::generate(&mut csprng);
109        Box::into_raw(Box::new(sk))
110    }
111
112    #[no_mangle]
113    /// Derive a public key from a secret key.
114    /// We assume the secret key pointer is non-null.
115    /// The result is always a non-null pointer.
116    #[allow(clippy::not_unsafe_ptr_arg_deref)]
117    extern "C" fn ecvrf_pub_key(secret_key: *mut SecretKey) -> *mut PublicKey {
118        let sk = from_ptr!(secret_key);
119        let pk = PublicKey::from(sk);
120        Box::into_raw(Box::new(pk))
121    }
122
123    #[no_mangle]
124    /// Compute hash of a proof.
125    /// We assume the proof pointer is non-null.
126    #[allow(clippy::not_unsafe_ptr_arg_deref)]
127    extern "C" fn ecvrf_proof_to_hash(hash_ptr: *mut u8, proof_ptr: *const Proof) {
128        let hash = mut_slice_from_c_bytes!(hash_ptr, 64);
129        let proof = from_ptr!(proof_ptr);
130        hash.copy_from_slice(&proof.to_hash())
131    }
132
133    #[no_mangle]
134    #[allow(clippy::not_unsafe_ptr_arg_deref)]
135    extern "C" fn ecvrf_verify_key(key_ptr: *mut PublicKey) -> i32 {
136        let key = from_ptr!(key_ptr);
137        if key.verify_key() {
138            1
139        } else {
140            0
141        }
142    }
143
144    #[no_mangle]
145    #[allow(clippy::not_unsafe_ptr_arg_deref)]
146    /// Verify. Returns 1 if verification successful and 0 otherwise.
147    /// We assume all pointers are non-null.
148    extern "C" fn ecvrf_verify(
149        public_key_ptr: *mut PublicKey,
150        proof_ptr: *const Proof,
151        message_ptr: *const u8,
152        len: size_t,
153    ) -> i32 {
154        let pk = from_ptr!(public_key_ptr);
155        let proof = from_ptr!(proof_ptr);
156        let message: &[u8] = slice_from_c_bytes!(message_ptr, len);
157
158        if pk.verify(proof, message) {
159            1
160        } else {
161            0
162        }
163    }
164
165    #[no_mangle]
166    #[allow(clippy::not_unsafe_ptr_arg_deref)]
167    // support ord instance needed in Haskell
168    extern "C" fn ecvrf_proof_cmp(proof_ptr_1: *const Proof, proof_ptr_2: *const Proof) -> i32 {
169        // optimistic check first.
170        if proof_ptr_1 == proof_ptr_2 {
171            return 0;
172        }
173
174        let p1 = from_ptr!(proof_ptr_1);
175        let p2 = from_ptr!(proof_ptr_2);
176        match p1.2.as_bytes().cmp(p2.2.as_bytes()) {
177            Ordering::Less => return -1,
178            Ordering::Greater => return 1,
179            Ordering::Equal => (),
180        }
181
182        // we now have that the last component is equal
183        // check the middle scalar
184        match p1.1.as_bytes().cmp(p2.1.as_bytes()) {
185            Ordering::Less => return -1,
186            Ordering::Greater => return 1,
187            Ordering::Equal => (),
188        }
189
190        // the scalars are equal, need to check the edwards point
191        match p1.0.compress().as_bytes().cmp(p2.0.compress().as_bytes()) {
192            Ordering::Less => -1,
193            Ordering::Equal => 0,
194            Ordering::Greater => 1,
195        }
196    }
197
198    #[no_mangle]
199    #[allow(clippy::not_unsafe_ptr_arg_deref)]
200    // ord instance for public keys
201    extern "C" fn ecvrf_public_key_cmp(
202        public_key_ptr_1: *mut PublicKey,
203        public_key_ptr_2: *mut PublicKey,
204    ) -> i32 {
205        // optimistic check first.
206        if public_key_ptr_1 == public_key_ptr_2 {
207            return 0;
208        }
209
210        let p1 = from_ptr!(public_key_ptr_1);
211        let p2 = from_ptr!(public_key_ptr_2);
212
213        // only compare the compressed point since the
214        // decompressed one is derived.
215        match p1.0.as_bytes().cmp(p2.0.as_bytes()) {
216            Ordering::Less => -1,
217            Ordering::Equal => 0,
218            Ordering::Greater => 1,
219        }
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    use curve25519_dalek::scalar::Scalar;
228
229    /// Test against test vectors specified in
230    /// https://tools.ietf.org/id/draft-irtf-cfrg-vrf-07.html#rfc.appendix.A.3
231    #[test]
232    fn test_vrf_proof_and_hash() {
233        /// The test vectors specify a secret key, 'SK', and input, 'alpha',
234        /// alongside the serialized values of the public key, 'PK',
235        /// secret scalar, 'x', proof, 'pi', and hash of proof 'beta'.
236        ///
237        /// All other values of the test vectors are determined by 'SK' and
238        /// 'alpha'.
239        ///
240        /// All values that could potentially be stored or sent are tested to
241        /// ensure that the serialization agrees with the reference
242        /// implementation.
243        ///
244        /// The values of 'H', 'k', 'U', 'V', and 'ctr' on the succesfull
245        /// iteration (all of which are specified in the test vectors)
246        /// are omitted from this test as they are internal to the
247        /// 'ECVRF_prove' implementation and never serialized. The correctness
248        /// of these values is implied by the correctness of 'pi' and
249        /// 'beta'.
250        fn test_example(
251            sk_bytes: [u8; 32],   // Secret key - 32 random bytes
252            alpha_bytes: Vec<u8>, // The input to the VRF
253            pk_bytes: [u8; 32],   // Public key - derived from the secret key
254            x_bytes: [u8; 32],    // Secret scalar - derived from the secret key
255            pi_bytes: [u8; 80],   // Result of 'ECVRF_prove(SK, alpha)', encoded as bytestring
256            beta_bytes: [u8; 64], // Hash of pi
257        ) {
258            // Test serialization of public key
259            let sk = SecretKey(sk_bytes);
260            let expanded_sk = ExpandedSecretKey::from(&sk);
261            let pk: PublicKey = PublicKey::from(&expanded_sk);
262            assert_eq!(pk.as_bytes(), &pk_bytes);
263
264            // Test serialization of generated secret scalar
265            let x = expanded_sk.key;
266            assert_eq!(x, Scalar::from_bytes_mod_order(x_bytes));
267
268            // Test serialization of proof
269            let proof = expanded_sk.prove(&pk, &alpha_bytes);
270            let mut proof_bytes: Vec<u8> = Vec::new();
271            proof.serial(&mut proof_bytes);
272            assert!(proof_bytes.iter().eq(pi_bytes.iter()));
273
274            // Test hash of proof
275            let p2h = proof.to_hash();
276            assert!(p2h.iter().eq(beta_bytes.iter()));
277        }
278
279        {
280            // First example from https://tools.ietf.org/id/draft-irtf-cfrg-vrf-07.html#rfc.appendix.A.3:
281            // SK = 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
282            // PK = d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
283            // alpha = (the empty string)
284            // x = 307c83864f2833cb427a2ef1c00a013cfdff2768d980c0a3a520f006904de94f
285            // ...
286            // pi = 8657106690b5526245a92b003bb079ccd1a92130477671f6fc01ad16f26f723f5e8bd1839b414219e8626d393787a192241fc442e6569e96c462f62b8079b9ed83ff2ee21c90c7c398802fdeebea4001
287            // beta = 90cf1df3b703cce59e2a35b925d411164068269d7b2d29f3301c03dd757876ff66b71dda49d2de59d03450451af026798e8f81cd2e333de5cdf4f3e140fdd8ae
288
289            let sk_bytes: [u8; 32] = [
290                0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60, 0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec,
291                0x2c, 0xc4, 0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19, 0x70, 0x3b, 0xac, 0x03,
292                0x1c, 0xae, 0x7f, 0x60,
293            ];
294
295            let pk_bytes: [u8; 32] = [
296                0xd7, 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64,
297                0x07, 0x3a, 0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68,
298                0xf7, 0x07, 0x51, 0x1a,
299            ];
300
301            let alpha_bytes: Vec<u8> = Vec::new();
302
303            let x_bytes: [u8; 32] = [
304                0x30, 0x7c, 0x83, 0x86, 0x4f, 0x28, 0x33, 0xcb, 0x42, 0x7a, 0x2e, 0xf1, 0xc0, 0x0a,
305                0x01, 0x3c, 0xfd, 0xff, 0x27, 0x68, 0xd9, 0x80, 0xc0, 0xa3, 0xa5, 0x20, 0xf0, 0x06,
306                0x90, 0x4d, 0xe9, 0x4f,
307            ];
308
309            // pi
310            let pi_bytes: [u8; 80] = [
311                0x86, 0x57, 0x10, 0x66, 0x90, 0xb5, 0x52, 0x62, 0x45, 0xa9, 0x2b, 0x00, 0x3b, 0xb0,
312                0x79, 0xcc, 0xd1, 0xa9, 0x21, 0x30, 0x47, 0x76, 0x71, 0xf6, 0xfc, 0x01, 0xad, 0x16,
313                0xf2, 0x6f, 0x72, 0x3f, 0x5e, 0x8b, 0xd1, 0x83, 0x9b, 0x41, 0x42, 0x19, 0xe8, 0x62,
314                0x6d, 0x39, 0x37, 0x87, 0xa1, 0x92, 0x24, 0x1f, 0xc4, 0x42, 0xe6, 0x56, 0x9e, 0x96,
315                0xc4, 0x62, 0xf6, 0x2b, 0x80, 0x79, 0xb9, 0xed, 0x83, 0xff, 0x2e, 0xe2, 0x1c, 0x90,
316                0xc7, 0xc3, 0x98, 0x80, 0x2f, 0xde, 0xeb, 0xea, 0x40, 0x01,
317            ];
318
319            // beta
320            let beta_bytes: [u8; 64] = [
321                0x90, 0xcf, 0x1d, 0xf3, 0xb7, 0x03, 0xcc, 0xe5, 0x9e, 0x2a, 0x35, 0xb9, 0x25, 0xd4,
322                0x11, 0x16, 0x40, 0x68, 0x26, 0x9d, 0x7b, 0x2d, 0x29, 0xf3, 0x30, 0x1c, 0x03, 0xdd,
323                0x75, 0x78, 0x76, 0xff, 0x66, 0xb7, 0x1d, 0xda, 0x49, 0xd2, 0xde, 0x59, 0xd0, 0x34,
324                0x50, 0x45, 0x1a, 0xf0, 0x26, 0x79, 0x8e, 0x8f, 0x81, 0xcd, 0x2e, 0x33, 0x3d, 0xe5,
325                0xcd, 0xf4, 0xf3, 0xe1, 0x40, 0xfd, 0xd8, 0xae,
326            ];
327
328            test_example(
329                sk_bytes,
330                alpha_bytes,
331                pk_bytes,
332                x_bytes,
333                pi_bytes,
334                beta_bytes,
335            )
336        }
337
338        {
339            // Second example from https://tools.ietf.org/id/draft-irtf-cfrg-vrf-07.html#rfc.appendix.A.3:
340            // SK = 4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb
341            // PK = 3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
342            // alpha = 72 (1 byte)
343            // x = 68bd9ed75882d52815a97585caf4790a7f6c6b3b7f821c5e259a24b02e502e51
344            // ...
345            // pi = f3141cd382dc42909d19ec5110469e4feae18300e94f304590abdced48aed593f7eaf3eb2f1a968cba3f6e23b386aeeaab7b1ea44a256e811892e13eeae7c9f6ea8992557453eac11c4d5476b1f35a08
346            // beta = eb4440665d3891d668e7e0fcaf587f1b4bd7fbfe99d0eb2211ccec90496310eb5e33821bc613efb94db5e5b54c70a848a0bef4553a41befc57663b56373a5031
347
348            let sk_bytes: [u8; 32] = [
349                0x4c, 0xcd, 0x08, 0x9b, 0x28, 0xff, 0x96, 0xda, 0x9d, 0xb6, 0xc3, 0x46, 0xec, 0x11,
350                0x4e, 0x0f, 0x5b, 0x8a, 0x31, 0x9f, 0x35, 0xab, 0xa6, 0x24, 0xda, 0x8c, 0xf6, 0xed,
351                0x4f, 0xb8, 0xa6, 0xfb,
352            ];
353
354            let pk_bytes: [u8; 32] = [
355                0x3d, 0x40, 0x17, 0xc3, 0xe8, 0x43, 0x89, 0x5a, 0x92, 0xb7, 0x0a, 0xa7, 0x4d, 0x1b,
356                0x7e, 0xbc, 0x9c, 0x98, 0x2c, 0xcf, 0x2e, 0xc4, 0x96, 0x8c, 0xc0, 0xcd, 0x55, 0xf1,
357                0x2a, 0xf4, 0x66, 0x0c,
358            ];
359
360            let alpha_bytes: Vec<u8> = vec![0x72];
361
362            let x_bytes: [u8; 32] = [
363                0x68, 0xbd, 0x9e, 0xd7, 0x58, 0x82, 0xd5, 0x28, 0x15, 0xa9, 0x75, 0x85, 0xca, 0xf4,
364                0x79, 0x0a, 0x7f, 0x6c, 0x6b, 0x3b, 0x7f, 0x82, 0x1c, 0x5e, 0x25, 0x9a, 0x24, 0xb0,
365                0x2e, 0x50, 0x2e, 0x51,
366            ];
367
368            // pi
369            let pi_bytes: [u8; 80] = [
370                0xf3, 0x14, 0x1c, 0xd3, 0x82, 0xdc, 0x42, 0x90, 0x9d, 0x19, 0xec, 0x51, 0x10, 0x46,
371                0x9e, 0x4f, 0xea, 0xe1, 0x83, 0x00, 0xe9, 0x4f, 0x30, 0x45, 0x90, 0xab, 0xdc, 0xed,
372                0x48, 0xae, 0xd5, 0x93, 0xf7, 0xea, 0xf3, 0xeb, 0x2f, 0x1a, 0x96, 0x8c, 0xba, 0x3f,
373                0x6e, 0x23, 0xb3, 0x86, 0xae, 0xea, 0xab, 0x7b, 0x1e, 0xa4, 0x4a, 0x25, 0x6e, 0x81,
374                0x18, 0x92, 0xe1, 0x3e, 0xea, 0xe7, 0xc9, 0xf6, 0xea, 0x89, 0x92, 0x55, 0x74, 0x53,
375                0xea, 0xc1, 0x1c, 0x4d, 0x54, 0x76, 0xb1, 0xf3, 0x5a, 0x08,
376            ];
377
378            // beta
379            let beta_bytes: [u8; 64] = [
380                0xeb, 0x44, 0x40, 0x66, 0x5d, 0x38, 0x91, 0xd6, 0x68, 0xe7, 0xe0, 0xfc, 0xaf, 0x58,
381                0x7f, 0x1b, 0x4b, 0xd7, 0xfb, 0xfe, 0x99, 0xd0, 0xeb, 0x22, 0x11, 0xcc, 0xec, 0x90,
382                0x49, 0x63, 0x10, 0xeb, 0x5e, 0x33, 0x82, 0x1b, 0xc6, 0x13, 0xef, 0xb9, 0x4d, 0xb5,
383                0xe5, 0xb5, 0x4c, 0x70, 0xa8, 0x48, 0xa0, 0xbe, 0xf4, 0x55, 0x3a, 0x41, 0xbe, 0xfc,
384                0x57, 0x66, 0x3b, 0x56, 0x37, 0x3a, 0x50, 0x31,
385            ];
386
387            test_example(
388                sk_bytes,
389                alpha_bytes,
390                pk_bytes,
391                x_bytes,
392                pi_bytes,
393                beta_bytes,
394            )
395        }
396
397        {
398            // Third example from https://tools.ietf.org/id/draft-irtf-cfrg-vrf-07.html#rfc.appendix.A.3:
399            // SK = c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7
400            // PK = fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025
401            // alpha = af82 (2 bytes)
402            // x = 909a8b755ed902849023a55b15c23d11ba4d7f4ec5c2f51b1325a181991ea95c
403            // ...
404            // pi = 9bc0f79119cc5604bf02d23b4caede71393cedfbb191434dd016d30177ccbf80e29dc513c01c3a980e0e545bcd848222d08a6c3e3665ff5a4cab13a643bef812e284c6b2ee063a2cb4f456794723ad0a
405            // beta = 645427e5d00c62a23fb703732fa5d892940935942101e456ecca7bb217c61c452118fec1219202a0edcf038bb6373241578be7217ba85a2687f7a0310b2df19f
406
407            let sk_bytes: [u8; 32] = [
408                0xc5, 0xaa, 0x8d, 0xf4, 0x3f, 0x9f, 0x83, 0x7b, 0xed, 0xb7, 0x44, 0x2f, 0x31, 0xdc,
409                0xb7, 0xb1, 0x66, 0xd3, 0x85, 0x35, 0x07, 0x6f, 0x09, 0x4b, 0x85, 0xce, 0x3a, 0x2e,
410                0x0b, 0x44, 0x58, 0xf7,
411            ];
412
413            let pk_bytes: [u8; 32] = [
414                0xfc, 0x51, 0xcd, 0x8e, 0x62, 0x18, 0xa1, 0xa3, 0x8d, 0xa4, 0x7e, 0xd0, 0x02, 0x30,
415                0xf0, 0x58, 0x08, 0x16, 0xed, 0x13, 0xba, 0x33, 0x03, 0xac, 0x5d, 0xeb, 0x91, 0x15,
416                0x48, 0x90, 0x80, 0x25,
417            ];
418
419            let alpha_bytes: Vec<u8> = vec![0xaf, 0x82];
420
421            let x_bytes: [u8; 32] = [
422                0x90, 0x9a, 0x8b, 0x75, 0x5e, 0xd9, 0x02, 0x84, 0x90, 0x23, 0xa5, 0x5b, 0x15, 0xc2,
423                0x3d, 0x11, 0xba, 0x4d, 0x7f, 0x4e, 0xc5, 0xc2, 0xf5, 0x1b, 0x13, 0x25, 0xa1, 0x81,
424                0x99, 0x1e, 0xa9, 0x5c,
425            ];
426
427            // pi
428            let pi_bytes: [u8; 80] = [
429                0x9b, 0xc0, 0xf7, 0x91, 0x19, 0xcc, 0x56, 0x04, 0xbf, 0x02, 0xd2, 0x3b, 0x4c, 0xae,
430                0xde, 0x71, 0x39, 0x3c, 0xed, 0xfb, 0xb1, 0x91, 0x43, 0x4d, 0xd0, 0x16, 0xd3, 0x01,
431                0x77, 0xcc, 0xbf, 0x80, 0xe2, 0x9d, 0xc5, 0x13, 0xc0, 0x1c, 0x3a, 0x98, 0x0e, 0x0e,
432                0x54, 0x5b, 0xcd, 0x84, 0x82, 0x22, 0xd0, 0x8a, 0x6c, 0x3e, 0x36, 0x65, 0xff, 0x5a,
433                0x4c, 0xab, 0x13, 0xa6, 0x43, 0xbe, 0xf8, 0x12, 0xe2, 0x84, 0xc6, 0xb2, 0xee, 0x06,
434                0x3a, 0x2c, 0xb4, 0xf4, 0x56, 0x79, 0x47, 0x23, 0xad, 0x0a,
435            ];
436
437            // beta
438            let beta_bytes: [u8; 64] = [
439                0x64, 0x54, 0x27, 0xe5, 0xd0, 0x0c, 0x62, 0xa2, 0x3f, 0xb7, 0x03, 0x73, 0x2f, 0xa5,
440                0xd8, 0x92, 0x94, 0x09, 0x35, 0x94, 0x21, 0x01, 0xe4, 0x56, 0xec, 0xca, 0x7b, 0xb2,
441                0x17, 0xc6, 0x1c, 0x45, 0x21, 0x18, 0xfe, 0xc1, 0x21, 0x92, 0x02, 0xa0, 0xed, 0xcf,
442                0x03, 0x8b, 0xb6, 0x37, 0x32, 0x41, 0x57, 0x8b, 0xe7, 0x21, 0x7b, 0xa8, 0x5a, 0x26,
443                0x87, 0xf7, 0xa0, 0x31, 0x0b, 0x2d, 0xf1, 0x9f,
444            ];
445
446            test_example(
447                sk_bytes,
448                alpha_bytes,
449                pk_bytes,
450                x_bytes,
451                pi_bytes,
452                beta_bytes,
453            )
454        }
455    }
456}