Skip to main content

blueprint_crypto_sr25519/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub mod error;
4use error::{Result, Sr25519Error};
5
6#[cfg(test)]
7mod tests;
8
9use blueprint_crypto_core::BytesEncoding;
10use blueprint_crypto_core::{KeyType, KeyTypeId};
11use blueprint_std::{
12    hash::Hash,
13    string::{String, ToString},
14    vec::Vec,
15};
16use schnorrkel::MiniSecretKey;
17
18/// Schnorrkel key type
19#[derive(
20    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
21)]
22pub struct SchnorrkelSr25519;
23
24macro_rules! impl_schnorrkel_serde {
25    ($name:ident, $inner:ty) => {
26        #[derive(Clone, PartialEq, Eq, Debug)]
27        pub struct $name(pub $inner);
28
29        impl PartialOrd for $name {
30            fn partial_cmp(&self, other: &Self) -> Option<blueprint_std::cmp::Ordering> {
31                Some(self.cmp(other))
32            }
33        }
34
35        impl Ord for $name {
36            fn cmp(&self, other: &Self) -> blueprint_std::cmp::Ordering {
37                self.0.to_bytes().cmp(&other.0.to_bytes())
38            }
39        }
40
41        impl Hash for $name {
42            fn hash<H: blueprint_std::hash::Hasher>(&self, state: &mut H) {
43                self.0.to_bytes().hash(state);
44            }
45        }
46
47        impl BytesEncoding for $name {
48            fn to_bytes(&self) -> Vec<u8> {
49                self.0.to_bytes().to_vec()
50            }
51
52            fn from_bytes(bytes: &[u8]) -> core::result::Result<Self, serde::de::value::Error> {
53                <$inner>::from_bytes(bytes)
54                    .map(Self)
55                    .map_err(|e| serde::de::Error::custom(e.to_string()))
56            }
57        }
58
59        impl serde::Serialize for $name {
60            fn serialize<S: serde::Serializer>(
61                &self,
62                serializer: S,
63            ) -> core::result::Result<S::Ok, S::Error> {
64                <Vec<u8>>::serialize(&self.to_bytes(), serializer)
65            }
66        }
67
68        impl<'de> serde::Deserialize<'de> for $name {
69            fn deserialize<D: serde::Deserializer<'de>>(
70                deserializer: D,
71            ) -> core::result::Result<Self, D::Error> {
72                let bytes = <Vec<u8>>::deserialize(deserializer)?;
73                let inner = <$inner>::from_bytes(&bytes)
74                    .map_err(|e| serde::de::Error::custom(e.to_string()))?;
75                Ok($name(inner))
76            }
77        }
78    };
79}
80
81impl_schnorrkel_serde!(SchnorrkelPublic, schnorrkel::PublicKey);
82impl_schnorrkel_serde!(SchnorrkelSecret, schnorrkel::SecretKey);
83impl_schnorrkel_serde!(SchnorrkelSignature, schnorrkel::Signature);
84
85impl zeroize::Zeroize for SchnorrkelSecret {
86    fn zeroize(&mut self) {
87        let ptr = (&raw mut self.0).cast::<u8>();
88        let len = core::mem::size_of::<schnorrkel::SecretKey>();
89        unsafe { core::ptr::write_bytes(ptr, 0, len) };
90        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
91    }
92}
93
94impl Drop for SchnorrkelSecret {
95    fn drop(&mut self) {
96        use zeroize::Zeroize;
97        self.zeroize();
98    }
99}
100
101impl KeyType for SchnorrkelSr25519 {
102    type Secret = SchnorrkelSecret;
103    type Public = SchnorrkelPublic;
104    type Signature = SchnorrkelSignature;
105    type Error = Sr25519Error;
106
107    fn key_type_id() -> KeyTypeId {
108        KeyTypeId::Sr25519
109    }
110
111    fn generate_with_seed(seed: Option<&[u8]>) -> Result<Self::Secret> {
112        let secret_key = if let Some(seed) = seed {
113            // Pad seed to 64 bytes or error if too large
114            if seed.len() > 64 {
115                return Err(Sr25519Error::InvalidSeed(
116                    "Seed must not exceed 64 bytes".into(),
117                ));
118            }
119            let mut padded_seed = [0u8; 64];
120            padded_seed[..seed.len()].copy_from_slice(seed);
121            schnorrkel::SecretKey::from_bytes(&padded_seed)
122                .map_err(|e| Sr25519Error::InvalidSeed(e.to_string()))
123        } else {
124            #[cfg(feature = "std")]
125            {
126                let mut rng = Self::get_rng();
127                let mini_secret_key = MiniSecretKey::generate_with(&mut rng);
128                Ok(mini_secret_key.expand(MiniSecretKey::UNIFORM_MODE))
129            }
130            #[cfg(not(feature = "std"))]
131            return Err(Sr25519Error::InvalidSeed(
132                "Random key generation requires the std feature".into(),
133            ));
134        };
135
136        secret_key.map(SchnorrkelSecret)
137    }
138
139    fn generate_with_string(secret: String) -> Result<Self::Secret> {
140        let hex_encoded = hex::decode(secret)?;
141        let secret_key = schnorrkel::SecretKey::from_bytes(&hex_encoded)
142            .map_err(|e| Sr25519Error::InvalidSeed(e.to_string()))?;
143        Ok(SchnorrkelSecret(secret_key))
144    }
145
146    fn public_from_secret(secret: &Self::Secret) -> Self::Public {
147        SchnorrkelPublic(secret.0.to_public())
148    }
149
150    fn sign_with_secret(secret: &mut Self::Secret, msg: &[u8]) -> Result<Self::Signature> {
151        let ctx = schnorrkel::signing_context(b"tangle").bytes(msg);
152        Ok(SchnorrkelSignature(
153            secret.0.sign(ctx, &secret.0.to_public()),
154        ))
155    }
156
157    fn sign_with_secret_pre_hashed(
158        secret: &mut Self::Secret,
159        msg: &[u8; 32],
160    ) -> Result<Self::Signature> {
161        let ctx = schnorrkel::signing_context(b"tangle").bytes(msg);
162        Ok(SchnorrkelSignature(
163            secret.0.sign(ctx, &secret.0.to_public()),
164        ))
165    }
166
167    fn verify(public: &Self::Public, msg: &[u8], signature: &Self::Signature) -> bool {
168        let ctx = schnorrkel::signing_context(b"tangle").bytes(msg);
169        public.0.verify(ctx, &signature.0).is_ok()
170    }
171}