zeus_wallet/
secure_key.rs

1use alloy_network::EthereumWallet;
2use alloy_primitives::{Address, hex};
3use alloy_signer_local::PrivateKeySigner;
4use k256::ecdsa::{SigningKey, VerifyingKey};
5use secure_types::{SecureArray, SecureString, Zeroize};
6
7/// Wrapper around a [SecureArray<u8, 32>] that holds the private key
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[derive(Clone)]
10pub struct SecureKey {
11   address: Address,
12   data: SecureArray<u8, 32>,
13}
14
15impl SecureKey {
16   /// Generate a new random private key
17   pub fn random() -> Self {
18      let signer = PrivateKeySigner::random();
19      Self::from(signer)
20   }
21
22   /// Return the private key by cloning it
23   pub fn key(&self) -> SecureArray<u8, 32> {
24      self.data.clone()
25   }
26
27   /// Return the key in a SecureString
28   pub fn key_string(&self) -> SecureString {
29      let signer = self.to_signer();
30      let mut key = signer.to_bytes();
31      let string = hex::encode(&key);
32      key.zeroize();
33      SecureString::from(string)
34   }
35
36   /// Securely erase the key from memory
37   pub fn erase(&mut self) {
38      self.data.erase();
39   }
40
41   pub fn is_erased(&self) -> bool {
42      self.data.unlock(|slice| slice.iter().all(|byte| byte == &0))
43   }
44
45   pub fn address(&self) -> Address {
46      self.address
47   }
48
49   /// Converts the private key to a [PrivateKeySigner] by cloning the inner key
50   ///
51   /// # Panics
52   /// Panics if the key is not a valid form of a private key
53   /// or if it has been erased
54   pub fn to_signer(&self) -> PrivateKeySigner {
55      self.data.unlock(|bytes| PrivateKeySigner::from_slice(bytes).unwrap())
56   }
57
58   /// Converts the private key to a [SigningKey] by cloning the inner key
59   ///
60   /// # Panics
61   /// Panics if the key is not a valid form of a private key
62   /// or if it has been erased
63   pub fn to_signing_key(&self) -> SigningKey {
64      self.data.unlock(|bytes| SigningKey::from_slice(bytes).unwrap())
65   }
66
67   /// Converts the private key to an [EthereumWallet] by cloning the inner key
68   ///
69   /// # Panics
70   /// Panics if the key is not a valid form of a private key
71   /// or if it has been erased
72   pub fn to_wallet(&self) -> EthereumWallet {
73      EthereumWallet::from(self.to_signer())
74   }
75
76   /// Converts the private key to a [VerifyingKey] by cloning the inner key
77   ///
78   /// # Panics
79   /// Panics if the key is not a valid form of a private key
80   /// or if it has been erased
81   pub fn verifying_key(&self) -> VerifyingKey {
82      let key = self.to_signing_key();
83      *key.verifying_key()
84   }
85}
86
87impl From<SecureArray<u8, 32>> for SecureKey {
88   /// Converts a [SecureArray<u8, 32>] to a [SecureKey]
89   ///
90   /// # Panics
91   /// Panics if the key is not a valid form of a private key
92   fn from(value: SecureArray<u8, 32>) -> Self {
93      let signer = value.unlock(|slice| PrivateKeySigner::from_slice(slice).unwrap());
94      let address = signer.address();
95      SecureKey {
96         address,
97         data: value,
98      }
99   }
100}
101
102impl From<PrivateKeySigner> for SecureKey {
103   fn from(value: PrivateKeySigner) -> Self {
104      let address = value.address();
105      let mut key_bytes = value.to_bytes();
106      let data = SecureArray::from_slice_mut(key_bytes.as_mut()).unwrap();
107
108      SecureKey { address, data }
109   }
110}
111
112impl From<SigningKey> for SecureKey {
113   fn from(value: SigningKey) -> Self {
114      let mut bytes = value.to_bytes();
115      let signer = PrivateKeySigner::from_slice(&bytes).unwrap();
116      let address = signer.address();
117      let data = SecureArray::from_slice_mut(bytes.as_mut()).unwrap();
118
119      SecureKey { address, data }
120   }
121}
122
123#[cfg(test)]
124mod tests {
125   use std::str::FromStr;
126
127   use super::*;
128
129   #[test]
130   fn test_create() {
131      let signer = PrivateKeySigner::random();
132      let secure_signer = SecureKey::from(signer.clone());
133      let signer2 = secure_signer.to_signer();
134      assert_eq!(signer.address(), signer2.address());
135   }
136
137   #[test]
138   fn sanity_check() {
139      let signer = PrivateKeySigner::random();
140      let secure_signer = SecureKey::from(signer.clone());
141      let signing_key = secure_signer.to_signing_key();
142      let secure_signer2 = SecureKey::from(signing_key);
143      assert_eq!(secure_signer.address(), secure_signer2.address());
144   }
145
146   #[test]
147   fn test_key_string() {
148      let signer = PrivateKeySigner::random();
149      let secure_signer = SecureKey::from(signer.clone());
150      let key_secure_string = secure_signer.key_string();
151
152      key_secure_string.unlock_str(|key_string| {
153         let new_signer = PrivateKeySigner::from_str(key_string).unwrap();
154         assert_eq!(signer.address(), new_signer.address());
155      });
156   }
157
158   #[test]
159   #[should_panic]
160   fn test_erase() {
161      let signer = PrivateKeySigner::random();
162      let mut secure_signer = SecureKey::from(signer.clone());
163      secure_signer.erase();
164      let _address = secure_signer.to_signer().address();
165   }
166
167   #[test]
168   fn test_is_erased() {
169      let signer = PrivateKeySigner::random();
170      let mut secure_signer = SecureKey::from(signer.clone());
171      assert!(!secure_signer.is_erased());
172      secure_signer.erase();
173      assert!(secure_signer.is_erased());
174   }
175
176   #[cfg(feature = "serde")]
177   #[test]
178   fn test_serde() {
179      let signer = PrivateKeySigner::random();
180      let secure_signer = SecureKey::from(signer.clone());
181
182      let json_string = serde_json::to_string(&secure_signer).unwrap();
183      let json_bytes = serde_json::to_vec(&secure_signer).unwrap();
184
185      let deserialized_string: SecureKey = serde_json::from_str(&json_string).unwrap();
186      let deserialized_bytes: SecureKey = serde_json::from_slice(&json_bytes).unwrap();
187
188      let signer2 = deserialized_string.to_signer();
189      let signer3 = deserialized_bytes.to_signer();
190
191      assert_eq!(signer.address(), signer2.address());
192      assert_eq!(signer.address(), signer3.address());
193   }
194}