1use crate::traits::Uniform;
7use rand::distributions;
8use serde::{Deserialize, Serialize};
9
10pub const TEST_SEED: [u8; 32] = [0u8; 32];
12
13#[cfg_attr(feature = "cloneable-private-keys", derive(Clone))]
15#[derive(Serialize, Deserialize, PartialEq, Eq)]
16pub struct KeyPair<S, P>
17where
18 for<'a> P: From<&'a S>,
19{
20 pub private_key: S,
22 pub public_key: P,
24}
25
26impl<S, P> From<S> for KeyPair<S, P>
27where
28 for<'a> P: From<&'a S>,
29{
30 fn from(private_key: S) -> Self {
31 KeyPair {
32 public_key: (&private_key).into(),
33 private_key,
34 }
35 }
36}
37
38impl<S, P> Uniform for KeyPair<S, P>
39where
40 S: Uniform,
41 for<'a> P: From<&'a S>,
42{
43 fn generate<R>(rng: &mut R) -> Self
44 where
45 R: ::rand::RngCore + ::rand::CryptoRng,
46 {
47 let private_key = S::generate(rng);
48 private_key.into()
49 }
50}
51
52impl<S, P> Uniform for (S, P)
54where
55 S: Uniform,
56 for<'a> P: From<&'a S>,
57{
58 fn generate<R>(rng: &mut R) -> Self
59 where
60 R: ::rand::RngCore + ::rand::CryptoRng,
61 {
62 let private_key = S::generate(rng);
63 let public_key = (&private_key).into();
64 (private_key, public_key)
65 }
66}
67
68impl<Priv, Pub> std::fmt::Debug for KeyPair<Priv, Pub>
69where
70 Priv: Serialize,
71 Pub: Serialize + for<'a> From<&'a Priv>,
72{
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 let mut v = bcs::to_bytes(&self.private_key).unwrap();
75 v.extend(&bcs::to_bytes(&self.public_key).unwrap());
76 write!(f, "{}", hex::encode(&v[..]))
77 }
78}
79
80#[cfg(any(test, feature = "fuzzing"))]
81use crate::signing_message;
82#[cfg(any(test, feature = "fuzzing"))]
83use curve25519_dalek::constants::EIGHT_TORSION;
84#[cfg(any(test, feature = "fuzzing"))]
85use curve25519_dalek::edwards::EdwardsPoint;
86#[cfg(any(test, feature = "fuzzing"))]
87use curve25519_dalek::scalar::Scalar;
88#[cfg(any(test, feature = "fuzzing"))]
89use curve25519_dalek::traits::Identity;
90#[cfg(any(test, feature = "fuzzing"))]
91use digest::Digest;
92#[cfg(any(test, feature = "fuzzing"))]
93use proptest::prelude::*;
94use rand::prelude::IteratorRandom;
95#[cfg(any(test, feature = "fuzzing"))]
96use rand::{rngs::StdRng, SeedableRng};
97#[cfg(any(test, feature = "fuzzing"))]
98use sha2::Sha512;
99
100#[cfg(any(test, feature = "fuzzing"))]
102pub fn uniform_keypair_strategy<Priv, Pub>() -> impl Strategy<Value = KeyPair<Priv, Pub>>
103where
104 Pub: Serialize + for<'a> From<&'a Priv>,
105 Priv: Serialize + Uniform,
106{
107 any::<[u8; 32]>()
110 .prop_map(|seed| {
111 let mut rng = StdRng::from_seed(seed);
112 KeyPair::<Priv, Pub>::generate(&mut rng)
113 })
114 .no_shrink()
115}
116
117#[cfg(any(test, feature = "fuzzing"))]
119pub fn small_order_strategy() -> impl Strategy<Value = EdwardsPoint> {
120 (0..EIGHT_TORSION.len())
121 .prop_map(|exp| {
122 let generator = EIGHT_TORSION[1]; Scalar::from(exp as u64) * generator
124 })
125 .no_shrink()
126}
127
128#[allow(non_snake_case)]
131#[cfg(any(test, feature = "fuzzing"))]
132pub fn small_order_pk_with_adversarial_message(
133) -> impl Strategy<Value = (EdwardsPoint, EdwardsPoint, TestAptosCrypto)> {
134 (
135 small_order_strategy(),
136 small_order_strategy(),
137 random_serializable_struct(),
138 )
139 .prop_filter(
140 "Filtering messages by hash * pk == R",
141 |(R, pk_point, msg)| {
142 let pk_bytes = pk_point.compress().to_bytes();
143
144 let msg_bytes = signing_message(msg).unwrap();
145
146 let mut h: Sha512 = Sha512::new();
147 h.update(R.compress().as_bytes());
148 h.update(pk_bytes);
149 h.update(&msg_bytes);
150
151 let k = Scalar::from_hash(h);
152
153 k * pk_point + (*R) == EdwardsPoint::identity()
154 },
155 )
156}
157
158#[cfg(any(test, feature = "fuzzing"))]
162pub fn uniform_keypair_strategy_with_perturbation<Priv, Pub>(
163 perturbation: u8,
164) -> impl Strategy<Value = KeyPair<Priv, Pub>>
165where
166 Pub: Serialize + for<'a> From<&'a Priv>,
167 Priv: Serialize + Uniform,
168{
169 any::<[u8; 32]>()
172 .prop_map(move |mut seed| {
173 for elem in seed.iter_mut() {
174 *elem = elem.saturating_add(perturbation);
175 }
176 let mut rng = StdRng::from_seed(seed);
177 KeyPair::<Priv, Pub>::generate(&mut rng)
178 })
179 .no_shrink()
180}
181
182pub fn random_subset<R>(mut rng: &mut R, max_set_size: usize, subset_size: usize) -> Vec<usize>
184where
185 R: ::rand::Rng + ?Sized,
186{
187 let mut vec = (0..max_set_size)
188 .choose_multiple(&mut rng, subset_size)
189 .into_iter()
190 .collect::<Vec<usize>>();
191
192 vec.sort_unstable();
193
194 vec
195}
196
197pub fn random_bytes<R>(rng: &mut R, n: usize) -> Vec<u8>
199where
200 R: ::rand::Rng + Copy,
201{
202 let range = distributions::Uniform::from(0u8..u8::MAX);
203 rng.sample_iter(&range).take(n).collect()
204}
205
206pub fn random_keypairs<R, PrivKey, PubKey>(
208 mut rng: &mut R,
209 num_signers: usize,
210) -> Vec<KeyPair<PrivKey, PubKey>>
211where
212 R: ::rand::RngCore + ::rand::CryptoRng,
213 PubKey: for<'a> std::convert::From<&'a PrivKey>,
214 PrivKey: Uniform,
215{
216 let mut key_pairs = vec![];
217 for _ in 0..num_signers {
218 key_pairs.push(KeyPair::<PrivKey, PubKey>::generate(&mut rng));
219 }
220 key_pairs
221}
222
223#[derive(Debug, Serialize, Deserialize)]
227pub struct TestAptosCrypto(pub String);
228
229pub struct TestAptosCryptoHasher(crate::hash::DefaultHasher);
234impl ::core::clone::Clone for TestAptosCryptoHasher {
236 #[inline]
237 fn clone(&self) -> TestAptosCryptoHasher {
238 match *self {
239 TestAptosCryptoHasher(ref __self_0_0) => {
240 TestAptosCryptoHasher(::core::clone::Clone::clone(__self_0_0))
241 }
242 }
243 }
244}
245static TEST_CRYPTO_SEED: crate::_once_cell::sync::OnceCell<[u8; 32]> =
247 crate::_once_cell::sync::OnceCell::new();
248impl TestAptosCryptoHasher {
250 fn new() -> Self {
251 let name = crate::_serde_name::trace_name::<TestAptosCrypto>()
252 .expect("The `CryptoHasher` macro only applies to structs and enums");
253 TestAptosCryptoHasher(crate::hash::DefaultHasher::new(name.as_bytes()))
254 }
255}
256static TEST_CRYPTO_HASHER: crate::_once_cell::sync::Lazy<TestAptosCryptoHasher> =
258 crate::_once_cell::sync::Lazy::new(TestAptosCryptoHasher::new);
259impl std::default::Default for TestAptosCryptoHasher {
261 fn default() -> Self {
262 TEST_CRYPTO_HASHER.clone()
263 }
264}
265impl crate::hash::CryptoHasher for TestAptosCryptoHasher {
267 fn seed() -> &'static [u8; 32] {
268 TEST_CRYPTO_SEED.get_or_init(|| {
269 let name = crate::_serde_name::trace_name::<TestAptosCrypto>()
270 .expect("The `CryptoHasher` macro only applies to structs and enums.")
271 .as_bytes();
272 crate::hash::DefaultHasher::prefixed_hash(name)
273 })
274 }
275 fn update(&mut self, bytes: &[u8]) {
276 self.0.update(bytes);
277 }
278 fn finish(self) -> crate::hash::HashValue {
279 self.0.finish()
280 }
281}
282impl std::io::Write for TestAptosCryptoHasher {
284 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
285 self.0.update(bytes);
286 Ok(bytes.len())
287 }
288 fn flush(&mut self) -> std::io::Result<()> {
289 Ok(())
290 }
291}
292impl crate::hash::CryptoHash for TestAptosCrypto {
294 type Hasher = TestAptosCryptoHasher;
295 fn hash(&self) -> crate::hash::HashValue {
296 use crate::hash::CryptoHasher;
297 let mut state = Self::Hasher::default();
298 bcs::serialize_into(&mut state, &self)
299 .expect("BCS serialization of TestAptosCrypto should not fail");
300 state.finish()
301 }
302}
303
304#[cfg(any(test, feature = "fuzzing"))]
306pub fn random_serializable_struct() -> impl Strategy<Value = TestAptosCrypto> {
307 (String::arbitrary()).prop_map(TestAptosCrypto).no_shrink()
308}