blueprint_crypto_core/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3use blueprint_std::fmt::Debug;
4use blueprint_std::hash::Hash;
5use blueprint_std::string::String;
6use blueprint_std::vec::Vec;
7use serde::{Deserialize, Serialize, de::DeserializeOwned};
8
9pub mod aggregation;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
12pub enum KeyTypeId {
13    #[cfg(feature = "bn254")]
14    Bn254,
15    #[cfg(any(feature = "k256", feature = "tangle"))]
16    Ecdsa,
17    #[cfg(any(feature = "sr25519-schnorrkel", feature = "tangle"))]
18    Sr25519,
19    #[cfg(any(feature = "bls", feature = "tangle"))]
20    Bls381,
21    #[cfg(any(feature = "bls", feature = "tangle"))]
22    Bls377,
23    #[cfg(any(feature = "zebra", feature = "tangle"))]
24    Ed25519,
25}
26
27impl KeyTypeId {
28    pub const ENABLED: &'static [Self] = &[
29        #[cfg(feature = "bn254")]
30        Self::Bn254,
31        #[cfg(any(feature = "k256", feature = "tangle"))]
32        Self::Ecdsa,
33        #[cfg(any(feature = "sr25519-schnorrkel", feature = "tangle"))]
34        Self::Sr25519,
35        #[cfg(any(feature = "bls", feature = "tangle"))]
36        Self::Bls381,
37        #[cfg(any(feature = "bls", feature = "tangle"))]
38        Self::Bls377,
39        #[cfg(any(feature = "zebra", feature = "tangle"))]
40        Self::Ed25519,
41    ];
42
43    pub fn name(&self) -> &'static str {
44        match *self {
45            #[cfg(feature = "bn254")]
46            Self::Bn254 => "bn254",
47            #[cfg(any(feature = "k256", feature = "tangle"))]
48            Self::Ecdsa => "ecdsa",
49            #[cfg(any(feature = "sr25519-schnorrkel", feature = "tangle"))]
50            Self::Sr25519 => "sr25519",
51            #[cfg(any(feature = "bls", feature = "tangle"))]
52            Self::Bls381 => "bls381",
53            #[cfg(any(feature = "bls", feature = "tangle"))]
54            Self::Bls377 => "bls377",
55            #[cfg(any(feature = "zebra", feature = "tangle"))]
56            Self::Ed25519 => "ed25519",
57            #[cfg(all(
58                not(feature = "bn254"),
59                not(feature = "k256"),
60                not(feature = "sr25519-schnorrkel"),
61                not(feature = "bls"),
62                not(feature = "zebra"),
63                not(feature = "tangle")
64            ))]
65            _ => unreachable!("All possible variants are feature-gated"),
66        }
67    }
68}
69
70pub trait BytesEncoding: Sized {
71    fn to_bytes(&self) -> Vec<u8>;
72    fn from_bytes(bytes: &[u8]) -> Result<Self, serde::de::value::Error>;
73}
74
75/// Trait for key types that can be stored in the keystore
76pub trait KeyType:
77    Clone
78    + Eq
79    + Debug
80    + PartialEq
81    + Ord
82    + PartialOrd
83    + Hash
84    + Sized
85    + Send
86    + Sync
87    + Serialize
88    + DeserializeOwned
89    + 'static
90{
91    type Secret: Clone
92        + Serialize
93        + DeserializeOwned
94        + Ord
95        + Send
96        + Sync
97        + Eq
98        + PartialEq
99        + BytesEncoding
100        + Debug;
101    type Public: Clone
102        + Serialize
103        + DeserializeOwned
104        + Ord
105        + Send
106        + Sync
107        + Hash
108        + Eq
109        + PartialEq
110        + BytesEncoding
111        + Debug;
112
113    type Signature: Clone
114        + Serialize
115        + DeserializeOwned
116        + Ord
117        + Send
118        + Sync
119        + Eq
120        + PartialEq
121        + BytesEncoding
122        + Debug;
123    type Error: Clone + Send + Sync + Debug;
124
125    fn key_type_id() -> KeyTypeId;
126
127    /// Get a cryptographically secure random number generator
128    #[cfg(feature = "std")]
129    fn get_rng() -> impl blueprint_std::CryptoRng + blueprint_std::Rng {
130        blueprint_std::rand::thread_rng()
131    }
132
133    #[cfg(not(feature = "std"))]
134    fn get_rng() -> impl blueprint_std::CryptoRng + blueprint_std::Rng {
135        blueprint_std::test_rng()
136    }
137
138    /// Get a deterministic random number generator for testing
139    fn get_test_rng() -> impl blueprint_std::CryptoRng + blueprint_std::Rng {
140        blueprint_std::test_rng()
141    }
142
143    fn generate_with_seed(seed: Option<&[u8]>) -> Result<Self::Secret, Self::Error>;
144    fn generate_with_string(secret: String) -> Result<Self::Secret, Self::Error>;
145    fn public_from_secret(secret: &Self::Secret) -> Self::Public;
146    fn sign_with_secret(
147        secret: &mut Self::Secret,
148        msg: &[u8],
149    ) -> Result<Self::Signature, Self::Error>;
150    fn sign_with_secret_pre_hashed(
151        secret: &mut Self::Secret,
152        msg: &[u8; 32],
153    ) -> Result<Self::Signature, Self::Error>;
154    fn verify(public: &Self::Public, msg: &[u8], signature: &Self::Signature) -> bool;
155}
156
157#[cfg(feature = "clap")]
158impl clap::ValueEnum for KeyTypeId {
159    fn value_variants<'a>() -> &'a [Self] {
160        &[
161            Self::Sr25519,
162            Self::Ed25519,
163            Self::Ecdsa,
164            Self::Bls381,
165            Self::Bn254,
166        ]
167    }
168
169    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
170        Some(match self {
171            Self::Sr25519 => {
172                clap::builder::PossibleValue::new("sr25519").help("Schnorrkel/Ristretto x25519")
173            }
174            Self::Ed25519 => {
175                clap::builder::PossibleValue::new("ed25519").help("Edwards Curve 25519")
176            }
177            Self::Ecdsa => clap::builder::PossibleValue::new("ecdsa")
178                .help("Elliptic Curve Digital Signature Algorithm"),
179            Self::Bls381 => {
180                clap::builder::PossibleValue::new("bls381").help("Boneh-Lynn-Shacham on BLS12-381")
181            }
182            Self::Bn254 => {
183                clap::builder::PossibleValue::new("blsbn254").help("Boneh-Lynn-Shacham on BN254")
184            }
185            _ => return None,
186        })
187    }
188}
189
190#[macro_export]
191macro_rules! impl_crypto_tests {
192    ($crypto_type:ty, $signing_key:ty, $signature:ty) => {
193        use $crate::KeyType;
194        #[test]
195        fn test_key_generation() {
196            // Test random key generation
197            let secret = <$crypto_type>::generate_with_seed(None).unwrap();
198            let _public = <$crypto_type>::public_from_secret(&secret);
199        }
200
201        #[test]
202        fn test_signing_and_verification() {
203            let mut secret = <$crypto_type>::generate_with_seed(None).unwrap();
204            let public = <$crypto_type>::public_from_secret(&secret);
205
206            // Test normal signing
207            let message = b"Hello, world!";
208            let signature = <$crypto_type>::sign_with_secret(&mut secret, message).unwrap();
209            assert!(
210                <$crypto_type>::verify(&public, message, &signature),
211                "Signature verification failed"
212            );
213
214            // Test pre-hashed signing
215            let hashed_msg = [42u8; 32];
216            let signature =
217                <$crypto_type>::sign_with_secret_pre_hashed(&mut secret, &hashed_msg).unwrap();
218
219            // Verify with wrong message should fail
220            let wrong_message = b"Wrong message";
221            assert!(
222                !<$crypto_type>::verify(&public, wrong_message, &signature),
223                "Verification should fail with wrong message"
224            );
225        }
226
227        #[test]
228        fn test_key_serialization() {
229            let secret = <$crypto_type>::generate_with_seed(None).unwrap();
230            let public = <$crypto_type>::public_from_secret(&secret);
231
232            // Test signing key serialization
233            let serialized = serde_json::to_string(&secret).unwrap();
234            let deserialized: $signing_key = serde_json::from_str(&serialized).unwrap();
235            assert_eq!(
236                secret, deserialized,
237                "SigningKey serialization roundtrip failed"
238            );
239
240            // Test verifying key serialization
241            let serialized = serde_json::to_string(&public).unwrap();
242            let deserialized = serde_json::from_str(&serialized).unwrap();
243            assert_eq!(
244                public, deserialized,
245                "VerifyingKey serialization roundtrip failed"
246            );
247        }
248
249        #[test]
250        fn test_signature_serialization() {
251            let mut secret = <$crypto_type>::generate_with_seed(None).unwrap();
252            let message = b"Test message";
253            let signature = <$crypto_type>::sign_with_secret(&mut secret, message).unwrap();
254
255            // Test signature serialization
256            let serialized = serde_json::to_string(&signature).unwrap();
257            let deserialized: $signature = serde_json::from_str(&serialized).unwrap();
258            assert_eq!(
259                signature, deserialized,
260                "Signature serialization roundtrip failed"
261            );
262        }
263
264        #[test]
265        fn test_key_comparison() {
266            let secret1 = <$crypto_type>::generate_with_seed(None).unwrap();
267            let secret2 = <$crypto_type>::generate_with_seed(None).unwrap();
268            let public1 = <$crypto_type>::public_from_secret(&secret1);
269            let public2 = <$crypto_type>::public_from_secret(&secret2);
270
271            // Test Ord implementation
272            assert!(public1 != public2, "Different keys should not be equal");
273            assert_eq!(public1.cmp(&public1), blueprint_std::cmp::Ordering::Equal);
274
275            // Verify consistency between PartialOrd and Ord
276            assert_eq!(public1.partial_cmp(&public2), Some(public1.cmp(&public2)));
277        }
278    };
279}