Skip to main content

devolutions_crypto/key/
mod.rs

1//! Module for dealing with wrapped keys and key exchange.
2//!
3//! ### Generation/Derivation
4//!
5//! Using `generate_keypair` will generate a random keypair.
6//!
7//! Asymmetric keys have two uses. They can be used to [encrypt and decrypt data](##asymmetric) and to perform a [key exchange](#key-exchange).
8//!
9//! #### `generate_keypair`
10//! ```rust
11//! use devolutions_crypto::key::{generate_keypair, KeyVersion, KeyPair};
12//!
13//! let keypair: KeyPair = generate_keypair(KeyVersion::Latest);
14//! ```
15//!
16//! ### Key Exchange
17//!
18//! The goal of using a key exchange is to get a shared secret key between
19//! two parties without making it possible for users listening on the conversation
20//! to guess that shared key.
21//! 1. Alice and Bob generates a `KeyPair` each.
22//! 2. Alice and Bob exchanges their `PublicKey`.
23//! 3. Alice mix her `PrivateKey` with Bob's `PublicKey`. This gives her the shared key.
24//! 4. Bob mixes his `PrivateKey` with Alice's `PublicKey`. This gives him the shared key.
25//! 5. Both Bob and Alice has the same shared key, which they can use for symmetric encryption for further communications.
26//!
27//! ```rust
28//! use devolutions_crypto::key::{generate_keypair, mix_key_exchange, KeyVersion, KeyPair};
29//!
30//! let bob_keypair: KeyPair = generate_keypair(KeyVersion::Latest);
31//! let alice_keypair: KeyPair = generate_keypair(KeyVersion::Latest);
32//!
33//! let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_keypair.public_key).expect("key exchange should not fail");
34//!
35//! let alice_shared = mix_key_exchange(&alice_keypair.private_key, &bob_keypair.public_key).expect("key exchange should not fail");
36//!
37//! // They now have a shared secret!
38//! assert_eq!(bob_shared, alice_shared);
39//! ```
40
41mod key_v1;
42mod secret_key_v1;
43
44use super::DataType;
45use super::Error;
46use super::Header;
47use super::HeaderType;
48use super::KeySubtype;
49pub use super::KeyVersion;
50use super::Result;
51
52use key_v1::{KeyV1Private, KeyV1Public};
53use secret_key_v1::SecretKeyV1;
54
55use std::borrow::Borrow;
56use std::convert::TryFrom;
57
58use zeroize::Zeroizing;
59
60#[cfg(feature = "fuzz")]
61use arbitrary::Arbitrary;
62
63#[cfg(feature = "wbindgen")]
64use wasm_bindgen::prelude::*;
65
66/// An asymmetric keypair.
67#[derive(Clone)]
68pub struct KeyPair {
69    /// The private key of this pair.
70    pub private_key: PrivateKey,
71    /// The public key of this pair.
72    pub public_key: PublicKey,
73}
74
75/// A public key. This key can be sent in clear on unsecured channels and stored publicly.
76#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
77#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
78#[derive(Clone, Debug)]
79pub struct PublicKey {
80    pub(crate) header: Header<PublicKey>,
81    payload: PublicKeyPayload,
82}
83
84/// A private key. This key should never be sent over an insecure channel or stored unsecurely.
85#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
86#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
87#[derive(Clone, Debug)]
88pub struct PrivateKey {
89    pub(crate) header: Header<PrivateKey>,
90    payload: PrivateKeyPayload,
91}
92
93impl HeaderType for PublicKey {
94    type Version = KeyVersion;
95    type Subtype = KeySubtype;
96
97    fn data_type() -> DataType {
98        DataType::Key
99    }
100
101    fn subtype() -> Self::Subtype {
102        KeySubtype::Public
103    }
104}
105
106impl HeaderType for PrivateKey {
107    type Version = KeyVersion;
108    type Subtype = KeySubtype;
109
110    fn data_type() -> DataType {
111        DataType::Key
112    }
113
114    fn subtype() -> Self::Subtype {
115        KeySubtype::Private
116    }
117}
118
119#[derive(Clone, Debug)]
120#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
121enum PrivateKeyPayload {
122    V1(KeyV1Private),
123}
124
125#[derive(Clone, Debug)]
126#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
127enum PublicKeyPayload {
128    V1(KeyV1Public),
129}
130
131/// Generates a `KeyPair` to use in a key exchange or to encrypt data.
132/// # Arguments
133///  * `version` - Version of the key scheme to use. Use `KeyVersion::Latest` if you're not dealing with shared data.
134/// # Returns
135/// Returns a `KeyPair` containing the private key and the public key.
136/// # Example
137/// ```rust
138/// use devolutions_crypto::key::{generate_keypair, KeyVersion};
139///
140/// let keypair = generate_keypair(KeyVersion::Latest);
141/// ```
142pub fn generate_keypair(version: KeyVersion) -> KeyPair {
143    let (private_header, public_header) = keypair_headers(version);
144
145    let (private_key, public_key) = match version {
146        KeyVersion::V1 | KeyVersion::Latest => {
147            let keypair = key_v1::generate_keypair();
148            (
149                PrivateKeyPayload::V1(keypair.private_key),
150                PublicKeyPayload::V1(keypair.public_key),
151            )
152        }
153    };
154
155    KeyPair {
156        private_key: PrivateKey {
157            header: private_header,
158            payload: private_key,
159        },
160        public_key: PublicKey {
161            header: public_header,
162            payload: public_key,
163        },
164    }
165}
166
167/// Mix a `PrivateKey` with another client `PublicKey` to get a secret shared between the two parties.
168/// # Arguments
169///  * `private_key` - The user's `PrivateKey` obtained through `generate_keypair()`.
170///  * `public_key` - The peer's `PublicKey`.
171/// # Returns
172/// Returns a shared secret in the form of a `Vec<u8>`, which can then be used
173///     as an encryption key between the two parties.
174/// # Example
175/// ```rust
176/// use std::convert::TryFrom as _;
177/// use devolutions_crypto::key::{PublicKey, PrivateKey, generate_keypair, mix_key_exchange, KeyVersion};
178/// # fn send_key_to_alice(_: &[u8]) {}
179/// # fn send_key_to_bob(_: &[u8]) {}
180/// # fn receive_key_from_alice() {}
181/// # fn receive_key_from_bob() {}
182///
183/// // This happens on Bob's side.
184/// let bob_keypair = generate_keypair(KeyVersion::Latest);
185/// let bob_serialized_pub: Vec<u8> = bob_keypair.public_key.into();
186///
187/// send_key_to_alice(&bob_serialized_pub);
188///
189/// // This happens on Alice's side.
190/// let alice_keypair = generate_keypair(KeyVersion::Latest);
191/// let alice_serialized_pub: Vec<u8> = alice_keypair.public_key.into();
192///
193/// send_key_to_bob(&alice_serialized_pub);
194///
195/// // Bob can now generate the shared secret.
196/// let alice_received_serialized_pub = receive_key_from_alice();
197/// # let alice_received_serialized_pub = alice_serialized_pub;
198/// let alice_received_pub = PublicKey::try_from(alice_received_serialized_pub.as_slice()).unwrap();
199///
200/// let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_received_pub).unwrap();
201///
202/// // Alice can now generate the shared secret
203/// let bob_received_serialized_pub = receive_key_from_bob();
204/// # let bob_received_serialized_pub = bob_serialized_pub;
205/// let bob_received_pub = PublicKey::try_from(bob_received_serialized_pub.as_slice()).unwrap();
206///
207/// let alice_shared = mix_key_exchange(&alice_keypair.private_key, &bob_received_pub).unwrap();
208///
209/// // They now have a shared secret!
210/// assert_eq!(bob_shared, alice_shared);
211/// ```
212pub fn mix_key_exchange(private_key: &PrivateKey, public_key: &PublicKey) -> Result<Vec<u8>> {
213    Ok(match (&private_key.payload, &public_key.payload) {
214        (PrivateKeyPayload::V1(private_key), PublicKeyPayload::V1(public_key)) => {
215            key_v1::mix_key_exchange(private_key, public_key)
216        } //_ => Err(DevoCryptoError::InvalidDataType),
217    })
218}
219
220fn keypair_headers(version: KeyVersion) -> (Header<PrivateKey>, Header<PublicKey>) {
221    let mut private_header = Header::default();
222    let mut public_header = Header::default();
223
224    match version {
225        KeyVersion::V1 | KeyVersion::Latest => {
226            private_header.version = KeyVersion::V1;
227            public_header.version = KeyVersion::V1;
228        }
229    }
230
231    (private_header, public_header)
232}
233
234impl From<PublicKey> for Vec<u8> {
235    /// Serialize the structure into a `Vec<u8>`, for storage, transmission or use in another language.
236    fn from(data: PublicKey) -> Self {
237        let mut header: Self = data.header.borrow().into();
238        let mut payload: Self = data.payload.into();
239        header.append(&mut payload);
240        header
241    }
242}
243
244impl TryFrom<&[u8]> for PublicKey {
245    type Error = Error;
246
247    /// Parses the data. Can return an Error of the data is invalid or unrecognized.
248    fn try_from(data: &[u8]) -> Result<Self> {
249        if data.len() < Header::len() {
250            return Err(Error::InvalidLength);
251        };
252
253        let header = Header::try_from(&data[0..Header::len()])?;
254
255        if header.data_subtype != KeySubtype::Public {
256            return Err(Error::InvalidDataType);
257        }
258
259        let payload = match header.version {
260            KeyVersion::V1 => PublicKeyPayload::V1(KeyV1Public::try_from(&data[Header::len()..])?),
261            _ => return Err(Error::UnknownVersion),
262        };
263
264        Ok(Self { header, payload })
265    }
266}
267
268impl From<PrivateKey> for Vec<u8> {
269    /// Serialize the structure into a `Vec<u8>`, for storage, transmission or use in another language.
270    fn from(data: PrivateKey) -> Self {
271        let mut header: Self = data.header.borrow().into();
272        let mut payload: Self = data.payload.into();
273        header.append(&mut payload);
274        header
275    }
276}
277
278impl TryFrom<&[u8]> for PrivateKey {
279    type Error = Error;
280
281    /// Parses the data. Can return an Error of the data is invalid or unrecognized.
282    fn try_from(data: &[u8]) -> Result<Self> {
283        if data.len() < Header::len() {
284            return Err(Error::InvalidLength);
285        };
286
287        let header = Header::try_from(&data[0..Header::len()])?;
288
289        if header.data_subtype != KeySubtype::Private {
290            return Err(Error::InvalidDataType);
291        }
292
293        let payload = match header.version {
294            KeyVersion::V1 => {
295                PrivateKeyPayload::V1(KeyV1Private::try_from(&data[Header::len()..])?)
296            }
297            _ => return Err(Error::UnknownVersion),
298        };
299
300        Ok(Self { header, payload })
301    }
302}
303
304impl From<PrivateKeyPayload> for Vec<u8> {
305    fn from(data: PrivateKeyPayload) -> Self {
306        match data {
307            PrivateKeyPayload::V1(x) => x.into(),
308        }
309    }
310}
311
312impl From<PublicKeyPayload> for Vec<u8> {
313    fn from(data: PublicKeyPayload) -> Self {
314        match data {
315            PublicKeyPayload::V1(x) => x.into(),
316        }
317    }
318}
319
320impl From<&PublicKey> for x25519_dalek::PublicKey {
321    fn from(data: &PublicKey) -> Self {
322        match &data.payload {
323            PublicKeyPayload::V1(x) => Self::from(x),
324            //_ => Err(DevoCryptoError::InvalidDataType),
325        }
326    }
327}
328
329impl From<&PrivateKey> for x25519_dalek::StaticSecret {
330    fn from(data: &PrivateKey) -> Self {
331        match &data.payload {
332            PrivateKeyPayload::V1(x) => Self::from(x),
333            //_ => Err(DevoCryptoError::InvalidDataType),
334        }
335    }
336}
337
338/// A secret key for symmetric encryption. Should never be sent over an insecure channel or stored unsecurely.
339#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
340#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
341#[derive(Clone, Debug)]
342pub struct SecretKey {
343    pub(crate) header: Header<SecretKey>,
344    payload: SecretKeyPayload,
345}
346
347impl HeaderType for SecretKey {
348    type Version = KeyVersion;
349    type Subtype = KeySubtype;
350
351    fn data_type() -> DataType {
352        DataType::Key
353    }
354
355    fn subtype() -> Self::Subtype {
356        KeySubtype::Secret
357    }
358}
359
360#[derive(Clone, Debug)]
361#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
362enum SecretKeyPayload {
363    V1(SecretKeyV1),
364}
365
366impl SecretKey {
367    /// Returns the raw key bytes for use with symmetric encryption primitives.
368    pub fn as_bytes(&self) -> &[u8] {
369        match &self.payload {
370            SecretKeyPayload::V1(k) => k.as_bytes(),
371        }
372    }
373}
374
375/// Constructs a `SecretKey` from raw derived key bytes (must be exactly 32 bytes).
376/// Takes ownership of the `Zeroizing` wrapper to ensure the raw bytes are cleared on drop.
377pub(crate) fn secret_key_from_raw(bytes: Zeroizing<Vec<u8>>) -> Result<SecretKey> {
378    let mut header: Header<SecretKey> = Header::default();
379    header.version = KeyVersion::V1;
380    let payload = SecretKeyPayload::V1(SecretKeyV1::try_from(bytes.as_slice())?);
381    Ok(SecretKey { header, payload })
382}
383
384/// Generates a `SecretKey` for use with symmetric encryption.
385/// # Arguments
386///  * `version` - Version of the key scheme to use. Use `KeyVersion::Latest` if you're not dealing with shared data.
387/// # Returns
388/// Returns a `SecretKey` containing the raw key material.
389/// # Example
390/// ```rust
391/// use devolutions_crypto::key::{generate_secret_key, KeyVersion};
392///
393/// let key = generate_secret_key(KeyVersion::Latest);
394/// ```
395pub fn generate_secret_key(version: KeyVersion) -> SecretKey {
396    let mut header: Header<SecretKey> = Header::default();
397
398    match version {
399        KeyVersion::V1 | KeyVersion::Latest => {
400            header.version = KeyVersion::V1;
401        }
402    }
403
404    SecretKey {
405        header,
406        payload: SecretKeyPayload::V1(SecretKeyV1::generate()),
407    }
408}
409
410impl From<SecretKey> for Vec<u8> {
411    /// Serialize the structure into a `Vec<u8>`, for storage, transmission or use in another language.
412    fn from(data: SecretKey) -> Self {
413        let mut header: Self = data.header.borrow().into();
414        let mut payload: Self = data.payload.into();
415        header.append(&mut payload);
416        header
417    }
418}
419
420impl TryFrom<&[u8]> for SecretKey {
421    type Error = Error;
422
423    /// Parses the data. Can return an Error if the data is invalid or unrecognized.
424    fn try_from(data: &[u8]) -> Result<Self> {
425        if data.len() < Header::len() {
426            return Err(Error::InvalidLength);
427        };
428
429        let header = Header::try_from(&data[0..Header::len()])?;
430
431        if header.data_subtype != KeySubtype::Secret {
432            return Err(Error::InvalidDataType);
433        }
434
435        let payload = match header.version {
436            KeyVersion::V1 => SecretKeyPayload::V1(SecretKeyV1::try_from(&data[Header::len()..])?),
437            _ => return Err(Error::UnknownVersion),
438        };
439
440        Ok(Self { header, payload })
441    }
442}
443
444impl From<SecretKeyPayload> for Vec<u8> {
445    fn from(data: SecretKeyPayload) -> Self {
446        match data {
447            SecretKeyPayload::V1(x) => x.into(),
448        }
449    }
450}
451
452#[test]
453fn ecdh_test() {
454    let bob_keypair = generate_keypair(KeyVersion::Latest);
455    let alice_keypair = generate_keypair(KeyVersion::Latest);
456
457    let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_keypair.public_key).unwrap();
458    let alice_shared =
459        mix_key_exchange(&alice_keypair.private_key, &bob_keypair.public_key).unwrap();
460
461    assert_eq!(bob_shared, alice_shared);
462}
463
464#[test]
465fn secret_key_generate_roundtrip() {
466    let key = generate_secret_key(KeyVersion::Latest);
467    let original_bytes = key.as_bytes().to_vec();
468
469    let serialized: Vec<u8> = key.into();
470    let deserialized = SecretKey::try_from(serialized.as_slice()).unwrap();
471
472    assert_eq!(deserialized.as_bytes(), original_bytes.as_slice());
473}
474
475#[test]
476fn secret_key_wrong_subtype_rejected() {
477    use std::convert::TryFrom as _;
478    let keypair = generate_keypair(KeyVersion::Latest);
479    let private_bytes: Vec<u8> = keypair.private_key.into();
480
481    let result = SecretKey::try_from(private_bytes.as_slice());
482    assert!(matches!(result, Err(Error::InvalidDataType)));
483}
484
485#[test]
486fn secret_key_wrong_length_rejected() {
487    let short = [0u8; 4];
488    let result = SecretKey::try_from(short.as_slice());
489    assert!(matches!(result, Err(Error::InvalidLength)));
490}