Skip to main content

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