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
228pub struct KeyPair {
229    /// Key exchange secret
230    pub exchange: StaticSecret,
231    /// Signing key
232    pub signing: SigningKey,
233}
234
235impl KeyPair {
236    /// Generate a new key pair
237    pub fn generate() -> Self {
238        Self {
239            exchange: StaticSecret::generate(),
240            signing: SigningKey::generate(),
241        }
242    }
243}
244
245/// Ephemeral key pair for Perfect Forward Secrecy
246///
247/// This should be generated fresh for each session and discarded after use.
248pub struct EphemeralKeyPair {
249    secret: StaticSecret,
250    public: PublicKey,
251}
252
253impl EphemeralKeyPair {
254    /// Generate a new ephemeral key pair
255    pub fn generate() -> Self {
256        let secret = StaticSecret::generate();
257        let public = secret.public_key();
258        Self { secret, public }
259    }
260
261    /// Get the public key to send to the peer
262    pub fn public_key(&self) -> &PublicKey {
263        &self.public
264    }
265
266    /// Perform key exchange and consume this ephemeral key
267    pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
268        self.secret.diffie_hellman(their_public)
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[test]
277    fn test_key_exchange() {
278        let alice = StaticSecret::generate();
279        let bob = StaticSecret::generate();
280
281        let alice_public = alice.public_key();
282        let bob_public = bob.public_key();
283
284        let alice_shared = alice.diffie_hellman(&bob_public);
285        let bob_shared = bob.diffie_hellman(&alice_public);
286
287        assert_eq!(alice_shared.as_bytes(), bob_shared.as_bytes());
288    }
289
290    #[test]
291    fn test_signing() {
292        let signing_key = SigningKey::generate();
293        let verifying_key = signing_key.verifying_key();
294
295        let message = b"test message";
296        let signature = signing_key.sign(message);
297
298        assert!(verifying_key.verify(message, &signature).is_ok());
299        assert!(verifying_key.verify(b"wrong message", &signature).is_err());
300    }
301
302    #[test]
303    fn test_ephemeral_pfs() {
304        let server_static = StaticSecret::generate();
305        let client_ephemeral = EphemeralKeyPair::generate();
306
307        let client_public = client_ephemeral.public_key().clone();
308        let shared1 = client_ephemeral.diffie_hellman(&server_static.public_key());
309        let shared2 = server_static.diffie_hellman(&client_public);
310
311        assert_eq!(shared1.as_bytes(), shared2.as_bytes());
312    }
313}