gadget_crypto_core/
lib.rs

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