diem_crypto/
test_utils.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8//! Internal module containing convenience utility functions mainly for testing
9
10use crate::traits::Uniform;
11use serde::{Deserialize, Serialize};
12
13/// A deterministic seed for PRNGs related to keys
14pub const TEST_SEED: [u8; 32] = [0u8; 32];
15
16/// A keypair consisting of a private and public key
17#[cfg_attr(feature = "cloneable-private-keys", derive(Clone))]
18#[derive(Serialize, Deserialize, PartialEq, Eq)]
19pub struct KeyPair<S, P>
20where for<'a> P: From<&'a S>
21{
22    /// the private key component
23    pub private_key: S,
24    /// the public key component
25    pub public_key: P,
26}
27
28impl<S, P> From<S> for KeyPair<S, P>
29where for<'a> P: From<&'a S>
30{
31    fn from(private_key: S) -> Self {
32        KeyPair {
33            public_key: (&private_key).into(),
34            private_key,
35        }
36    }
37}
38
39impl<S, P> Uniform for KeyPair<S, P>
40where
41    S: Uniform,
42    for<'a> P: From<&'a S>,
43{
44    fn generate<R>(rng: &mut R) -> Self
45    where R: ::rand::RngCore + ::rand::CryptoRng {
46        let private_key = S::generate(rng);
47        private_key.into()
48    }
49}
50
51/// A pair consisting of a private and public key
52impl<S, P> Uniform for (S, P)
53where
54    S: Uniform,
55    for<'a> P: From<&'a S>,
56{
57    fn generate<R>(rng: &mut R) -> Self
58    where R: ::rand::RngCore + ::rand::CryptoRng {
59        let private_key = S::generate(rng);
60        let public_key = (&private_key).into();
61        (private_key, public_key)
62    }
63}
64
65impl<Priv, Pub> std::fmt::Debug for KeyPair<Priv, Pub>
66where
67    Priv: Serialize,
68    Pub: Serialize + for<'a> From<&'a Priv>,
69{
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        let mut v = bcs::to_bytes(&self.private_key).unwrap();
72        v.extend(&bcs::to_bytes(&self.public_key).unwrap());
73        write!(f, "{}", hex::encode(&v[..]))
74    }
75}
76
77#[cfg(any(test, feature = "fuzzing"))]
78use proptest::prelude::*;
79#[cfg(any(test, feature = "fuzzing"))]
80use rand::{rngs::StdRng, SeedableRng};
81
82/// Produces a uniformly random keypair from a seed
83#[cfg(any(test, feature = "fuzzing"))]
84pub fn uniform_keypair_strategy<Priv, Pub>(
85) -> impl Strategy<Value = KeyPair<Priv, Pub>>
86where
87    Pub: Serialize + for<'a> From<&'a Priv>,
88    Priv: Serialize + Uniform,
89{
90    // The no_shrink is because keypairs should be fixed -- shrinking would
91    // cause a different keypair to be generated, which appears to not be
92    // very useful.
93    any::<[u8; 32]>()
94        .prop_map(|seed| {
95            let mut rng = StdRng::from_seed(seed);
96            KeyPair::<Priv, Pub>::generate(&mut rng)
97        })
98        .no_shrink()
99}
100
101/// This struct provides a means of testing signing and verification through
102/// BCS serialization and domain separation
103#[cfg(any(test, feature = "fuzzing"))]
104#[derive(Debug, Serialize, Deserialize)]
105pub struct TestDiemCrypto(pub String);
106
107// the following block is macro expanded from derive(CryptoHasher,
108// BCSCryptoHash)
109
110/// Cryptographic hasher for an BCS-serializable #item
111#[cfg(any(test, feature = "fuzzing"))]
112pub struct TestDiemCryptoHasher(crate::hash::DefaultHasher);
113#[cfg(any(test, feature = "fuzzing"))]
114impl ::core::clone::Clone for TestDiemCryptoHasher {
115    #[inline]
116    fn clone(&self) -> TestDiemCryptoHasher {
117        match *self {
118            TestDiemCryptoHasher(ref __self_0_0) => TestDiemCryptoHasher(
119                ::core::clone::Clone::clone(&(*__self_0_0)),
120            ),
121        }
122    }
123}
124#[cfg(any(test, feature = "fuzzing"))]
125static TEST_DIEM_CRYPTO_SEED: crate::_once_cell::sync::OnceCell<[u8; 32]> =
126    crate::_once_cell::sync::OnceCell::new();
127#[cfg(any(test, feature = "fuzzing"))]
128impl TestDiemCryptoHasher {
129    fn new() -> Self {
130        let name = crate::_serde_name::trace_name::<TestDiemCrypto>().expect(
131            "The `CryptoHasher` macro only applies to structs and enums",
132        );
133        TestDiemCryptoHasher(crate::hash::DefaultHasher::new(&name.as_bytes()))
134    }
135}
136#[cfg(any(test, feature = "fuzzing"))]
137static TEST_DIEM_CRYPTO_HASHER: crate::_once_cell::sync::Lazy<
138    TestDiemCryptoHasher,
139> = crate::_once_cell::sync::Lazy::new(TestDiemCryptoHasher::new);
140#[cfg(any(test, feature = "fuzzing"))]
141impl std::default::Default for TestDiemCryptoHasher {
142    fn default() -> Self { TEST_DIEM_CRYPTO_HASHER.clone() }
143}
144#[cfg(any(test, feature = "fuzzing"))]
145impl crate::hash::CryptoHasher for TestDiemCryptoHasher {
146    fn seed() -> &'static [u8; 32] {
147        TEST_DIEM_CRYPTO_SEED.get_or_init(|| {
148            let name = crate::_serde_name::trace_name::<TestDiemCrypto>()
149                .expect("The `CryptoHasher` macro only applies to structs and enums.")
150                .as_bytes();
151            crate::hash::DefaultHasher::prefixed_hash(&name)
152        })
153    }
154
155    fn update(&mut self, bytes: &[u8]) { self.0.update(bytes); }
156
157    fn finish(self) -> crate::hash::HashValue { self.0.finish() }
158}
159#[cfg(any(test, feature = "fuzzing"))]
160impl std::io::Write for TestDiemCryptoHasher {
161    fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
162        self.0.update(bytes);
163        Ok(bytes.len())
164    }
165
166    fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
167}
168#[cfg(any(test, feature = "fuzzing"))]
169impl crate::hash::CryptoHash for TestDiemCrypto {
170    type Hasher = TestDiemCryptoHasher;
171
172    fn hash(&self) -> crate::hash::HashValue {
173        use crate::hash::CryptoHasher;
174        let mut state = Self::Hasher::default();
175        bcs::serialize_into(&mut state, &self)
176            .expect("BCS serialization of TestDiemCrypto should not fail");
177        state.finish()
178    }
179}
180
181/// Produces a random TestDiemCrypto signable / verifiable struct.
182#[cfg(any(test, feature = "fuzzing"))]
183pub fn random_serializable_struct() -> impl Strategy<Value = TestDiemCrypto> {
184    (String::arbitrary()).prop_map(TestDiemCrypto).no_shrink()
185}