Skip to main content

corevpn_crypto/
keys.rs

1//! Cryptographic key types with secure memory handling
2//!
3//! All secret key material implements `Zeroize` to ensure keys are
4//! securely cleared from memory when dropped.
5
6use ed25519_dalek::{
7    SigningKey as Ed25519SigningKey,
8    VerifyingKey as Ed25519VerifyingKey,
9    Signature as Ed25519Signature,
10    Signer, Verifier,
11};
12use x25519_dalek::{
13    StaticSecret as X25519StaticSecret,
14    PublicKey as X25519PublicKey,
15    SharedSecret as X25519SharedSecret,
16};
17use zeroize::ZeroizeOnDrop;
18use serde::{Serialize, Deserialize};
19
20use crate::{CryptoError, Result};
21
22/// X25519 static secret key for key exchange
23///
24/// This key should be generated once and stored securely.
25/// For Perfect Forward Secrecy, use ephemeral keys for each session.
26#[derive(ZeroizeOnDrop)]
27pub struct StaticSecret {
28    inner: X25519StaticSecret,
29}
30
31impl StaticSecret {
32    /// Generate a new random static secret
33    pub fn generate() -> Self {
34        Self {
35            inner: X25519StaticSecret::random_from_rng(rand::rngs::OsRng),
36        }
37    }
38
39    /// Create from raw bytes
40    pub fn from_bytes(bytes: [u8; 32]) -> Self {
41        Self {
42            inner: X25519StaticSecret::from(bytes),
43        }
44    }
45
46    /// Get the corresponding public key
47    pub fn public_key(&self) -> PublicKey {
48        PublicKey {
49            inner: X25519PublicKey::from(&self.inner),
50        }
51    }
52
53    /// Perform Diffie-Hellman key exchange
54    pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
55        SharedSecret {
56            inner: self.inner.diffie_hellman(&their_public.inner),
57        }
58    }
59
60    /// Export as bytes (use with caution - prefer keeping in memory)
61    pub fn to_bytes(&self) -> [u8; 32] {
62        self.inner.to_bytes()
63    }
64}
65
66/// X25519 public key for key exchange
67#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
68pub struct PublicKey {
69    #[serde(with = "serde_bytes_array")]
70    inner: X25519PublicKey,
71}
72
73mod serde_bytes_array {
74    use serde::{Deserialize, Deserializer, Serialize, Serializer};
75    use x25519_dalek::PublicKey;
76
77    pub fn serialize<S>(key: &PublicKey, serializer: S) -> Result<S::Ok, S::Error>
78    where
79        S: Serializer,
80    {
81        key.as_bytes().serialize(serializer)
82    }
83
84    pub fn deserialize<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
85    where
86        D: Deserializer<'de>,
87    {
88        let bytes: [u8; 32] = Deserialize::deserialize(deserializer)?;
89        Ok(PublicKey::from(bytes))
90    }
91}
92
93impl PublicKey {
94    /// Create from raw bytes
95    pub fn from_bytes(bytes: [u8; 32]) -> Self {
96        Self {
97            inner: X25519PublicKey::from(bytes),
98        }
99    }
100
101    /// Export as bytes
102    pub fn as_bytes(&self) -> &[u8; 32] {
103        self.inner.as_bytes()
104    }
105
106    /// Export as bytes (owned)
107    pub fn to_bytes(&self) -> [u8; 32] {
108        *self.inner.as_bytes()
109    }
110}
111
112/// Shared secret from Diffie-Hellman exchange
113#[derive(ZeroizeOnDrop)]
114pub struct SharedSecret {
115    inner: X25519SharedSecret,
116}
117
118impl SharedSecret {
119    /// Get the raw shared secret bytes
120    ///
121    /// Note: You should typically derive keys from this using HKDF,
122    /// not use it directly.
123    pub fn as_bytes(&self) -> &[u8; 32] {
124        self.inner.as_bytes()
125    }
126}
127
128/// Ed25519 signing key
129#[derive(ZeroizeOnDrop)]
130pub struct SigningKey {
131    inner: Ed25519SigningKey,
132}
133
134impl SigningKey {
135    /// Generate a new random signing key
136    pub fn generate() -> Self {
137        Self {
138            inner: Ed25519SigningKey::generate(&mut rand::rngs::OsRng),
139        }
140    }
141
142    /// Create from raw bytes
143    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
144        Self {
145            inner: Ed25519SigningKey::from_bytes(bytes),
146        }
147    }
148
149    /// Get the corresponding verifying key
150    pub fn verifying_key(&self) -> VerifyingKey {
151        VerifyingKey {
152            inner: self.inner.verifying_key(),
153        }
154    }
155
156    /// Sign a message
157    pub fn sign(&self, message: &[u8]) -> Signature {
158        Signature {
159            inner: self.inner.sign(message),
160        }
161    }
162
163    /// Export as bytes (use with caution)
164    pub fn to_bytes(&self) -> [u8; 32] {
165        self.inner.to_bytes()
166    }
167
168    /// Get the inner key for certificate signing
169    #[allow(dead_code)]
170    pub(crate) fn inner(&self) -> &Ed25519SigningKey {
171        &self.inner
172    }
173}
174
175/// Ed25519 verifying (public) key
176#[derive(Clone, Debug, PartialEq, Eq)]
177pub struct VerifyingKey {
178    inner: Ed25519VerifyingKey,
179}
180
181impl VerifyingKey {
182    /// Create from raw bytes
183    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self> {
184        let inner = Ed25519VerifyingKey::from_bytes(bytes)
185            .map_err(|_| CryptoError::InvalidKeyLength { expected: 32, got: bytes.len() })?;
186        Ok(Self { inner })
187    }
188
189    /// Verify a signature
190    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<()> {
191        self.inner
192            .verify(message, &signature.inner)
193            .map_err(|_| CryptoError::InvalidSignature)
194    }
195
196    /// Export as bytes
197    pub fn as_bytes(&self) -> &[u8; 32] {
198        self.inner.as_bytes()
199    }
200
201    /// Export as bytes (owned)
202    pub fn to_bytes(&self) -> [u8; 32] {
203        self.inner.to_bytes()
204    }
205}
206
207/// Ed25519 signature
208#[derive(Clone, Debug)]
209pub struct Signature {
210    inner: Ed25519Signature,
211}
212
213impl Signature {
214    /// Create from raw bytes
215    pub fn from_bytes(bytes: &[u8; 64]) -> Self {
216        Self {
217            inner: Ed25519Signature::from_bytes(bytes),
218        }
219    }
220
221    /// Export as bytes
222    pub fn to_bytes(&self) -> [u8; 64] {
223        self.inner.to_bytes()
224    }
225}
226
227/// Combined key pair for both encryption and signing
228#[derive(ZeroizeOnDrop)]
229pub struct KeyPair {
230    /// Key exchange secret
231    pub exchange: StaticSecret,
232    /// Signing key
233    pub signing: SigningKey,
234}
235
236impl KeyPair {
237    /// Generate a new key pair
238    pub fn generate() -> Self {
239        Self {
240            exchange: StaticSecret::generate(),
241            signing: SigningKey::generate(),
242        }
243    }
244}
245
246/// Ephemeral key pair for Perfect Forward Secrecy
247///
248/// This should be generated fresh for each session and discarded after use.
249pub struct EphemeralKeyPair {
250    secret: StaticSecret,
251    public: PublicKey,
252}
253
254impl Drop for EphemeralKeyPair {
255    fn drop(&mut self) {
256        // StaticSecret already implements ZeroizeOnDrop, so it will be zeroized automatically.
257        // PublicKey doesn't need zeroization as it's public information.
258        // This explicit Drop ensures the secret is cleared when the struct is dropped.
259    }
260}
261
262impl EphemeralKeyPair {
263    /// Generate a new ephemeral key pair
264    pub fn generate() -> Self {
265        let secret = StaticSecret::generate();
266        let public = secret.public_key();
267        Self { secret, public }
268    }
269
270    /// Get the public key to send to the peer
271    pub fn public_key(&self) -> &PublicKey {
272        &self.public
273    }
274
275    /// Perform key exchange and consume this ephemeral key
276    pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
277        self.secret.diffie_hellman(their_public)
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn test_key_exchange() {
287        let alice = StaticSecret::generate();
288        let bob = StaticSecret::generate();
289
290        let alice_public = alice.public_key();
291        let bob_public = bob.public_key();
292
293        let alice_shared = alice.diffie_hellman(&bob_public);
294        let bob_shared = bob.diffie_hellman(&alice_public);
295
296        assert_eq!(alice_shared.as_bytes(), bob_shared.as_bytes());
297    }
298
299    #[test]
300    fn test_signing() {
301        let signing_key = SigningKey::generate();
302        let verifying_key = signing_key.verifying_key();
303
304        let message = b"test message";
305        let signature = signing_key.sign(message);
306
307        assert!(verifying_key.verify(message, &signature).is_ok());
308        assert!(verifying_key.verify(b"wrong message", &signature).is_err());
309    }
310
311    #[test]
312    fn test_ephemeral_pfs() {
313        let server_static = StaticSecret::generate();
314        let client_ephemeral = EphemeralKeyPair::generate();
315
316        let client_public = client_ephemeral.public_key().clone();
317        let shared1 = client_ephemeral.diffie_hellman(&server_static.public_key());
318        let shared2 = server_static.diffie_hellman(&client_public);
319
320        assert_eq!(shared1.as_bytes(), shared2.as_bytes());
321    }
322}