devolutions_crypto/key/mod.rs
1//! Module for dealing with wrapped keys and key exchange.
2//!
3//! For now, this module only deal with keypairs, as the symmetric keys are not wrapped yet.
4//!
5//! ### Generation/Derivation
6//!
7//! Using `generate_keypair` will generate a random keypair.
8//!
9//! Asymmetric keys have two uses. They can be used to [encrypt and decrypt data](##asymmetric) and to perform a [key exchange](#key-exchange).
10//!
11//! #### `generate_keypair`
12//! ```rust
13//! use devolutions_crypto::key::{generate_keypair, KeyVersion, KeyPair};
14//!
15//! let keypair: KeyPair = generate_keypair(KeyVersion::Latest);
16//! ```
17//!
18//! ### Key Exchange
19//!
20//! The goal of using a key exchange is to get a shared secret key between
21//! two parties without making it possible for users listening on the conversation
22//! to guess that shared key.
23//! 1. Alice and Bob generates a `KeyPair` each.
24//! 2. Alice and Bob exchanges their `PublicKey`.
25//! 3. Alice mix her `PrivateKey` with Bob's `PublicKey`. This gives her the shared key.
26//! 4. Bob mixes his `PrivateKey` with Alice's `PublicKey`. This gives him the shared key.
27//! 5. Both Bob and Alice has the same shared key, which they can use for symmetric encryption for further communications.
28//!
29//! ```rust
30//! use devolutions_crypto::key::{generate_keypair, mix_key_exchange, KeyVersion, KeyPair};
31//!
32//! let bob_keypair: KeyPair = generate_keypair(KeyVersion::Latest);
33//! let alice_keypair: KeyPair = generate_keypair(KeyVersion::Latest);
34//!
35//! let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_keypair.public_key).expect("key exchange should not fail");
36//!
37//! let alice_shared = mix_key_exchange(&alice_keypair.private_key, &bob_keypair.public_key).expect("key exchange should not fail");
38//!
39//! // They now have a shared secret!
40//! assert_eq!(bob_shared, alice_shared);
41//! ```
42
43mod key_v1;
44
45use super::DataType;
46use super::Error;
47use super::Header;
48use super::HeaderType;
49use super::KeySubtype;
50pub use super::KeyVersion;
51use super::Result;
52
53use key_v1::{KeyV1Private, KeyV1Public};
54
55use std::borrow::Borrow;
56use std::convert::TryFrom;
57
58#[cfg(feature = "fuzz")]
59use arbitrary::Arbitrary;
60
61#[cfg(feature = "wbindgen")]
62use wasm_bindgen::prelude::*;
63
64/// An asymmetric keypair.
65#[derive(Clone)]
66pub struct KeyPair {
67 /// The private key of this pair.
68 pub private_key: PrivateKey,
69 /// The public key of this pair.
70 pub public_key: PublicKey,
71}
72
73/// A public key. This key can be sent in clear on unsecured channels and stored publicly.
74#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
75#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
76#[derive(Clone, Debug)]
77pub struct PublicKey {
78 pub(crate) header: Header<PublicKey>,
79 payload: PublicKeyPayload,
80}
81
82/// A private key. This key should never be sent over an insecure channel or stored unsecurely.
83#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
84#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
85#[derive(Clone, Debug)]
86pub struct PrivateKey {
87 pub(crate) header: Header<PrivateKey>,
88 payload: PrivateKeyPayload,
89}
90
91impl HeaderType for PublicKey {
92 type Version = KeyVersion;
93 type Subtype = KeySubtype;
94
95 fn data_type() -> DataType {
96 DataType::Key
97 }
98
99 fn subtype() -> Self::Subtype {
100 KeySubtype::Public
101 }
102}
103
104impl HeaderType for PrivateKey {
105 type Version = KeyVersion;
106 type Subtype = KeySubtype;
107
108 fn data_type() -> DataType {
109 DataType::Key
110 }
111
112 fn subtype() -> Self::Subtype {
113 KeySubtype::Private
114 }
115}
116
117#[derive(Clone, Debug)]
118#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
119enum PrivateKeyPayload {
120 V1(KeyV1Private),
121}
122
123#[derive(Clone, Debug)]
124#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
125enum PublicKeyPayload {
126 V1(KeyV1Public),
127}
128
129/// Generates a `KeyPair` to use in a key exchange or to encrypt data.
130/// # Arguments
131/// * `version` - Version of the key scheme to use. Use `KeyVersion::Latest` if you're not dealing with shared data.
132/// # Returns
133/// Returns a `KeyPair` containing the private key and the public key.
134/// # Example
135/// ```rust
136/// use devolutions_crypto::key::{generate_keypair, KeyVersion};
137///
138/// let keypair = generate_keypair(KeyVersion::Latest);
139/// ```
140pub fn generate_keypair(version: KeyVersion) -> KeyPair {
141 let (private_header, public_header) = keypair_headers(version);
142
143 let (private_key, public_key) = match version {
144 KeyVersion::V1 | KeyVersion::Latest => {
145 let keypair = key_v1::generate_keypair();
146 (
147 PrivateKeyPayload::V1(keypair.private_key),
148 PublicKeyPayload::V1(keypair.public_key),
149 )
150 }
151 };
152
153 KeyPair {
154 private_key: PrivateKey {
155 header: private_header,
156 payload: private_key,
157 },
158 public_key: PublicKey {
159 header: public_header,
160 payload: public_key,
161 },
162 }
163}
164
165/// Mix a `PrivateKey` with another client `PublicKey` to get a secret shared between the two parties.
166/// # Arguments
167/// * `private_key` - The user's `PrivateKey` obtained through `generate_keypair()`.
168/// * `public_key` - The peer's `PublicKey`.
169/// # Returns
170/// Returns a shared secret in the form of a `Vec<u8>`, which can then be used
171/// as an encryption key between the two parties.
172/// # Example
173/// ```rust
174/// use std::convert::TryFrom as _;
175/// use devolutions_crypto::key::{PublicKey, PrivateKey, generate_keypair, mix_key_exchange, KeyVersion};
176/// # fn send_key_to_alice(_: &[u8]) {}
177/// # fn send_key_to_bob(_: &[u8]) {}
178/// # fn receive_key_from_alice() {}
179/// # fn receive_key_from_bob() {}
180///
181/// // This happens on Bob's side.
182/// let bob_keypair = generate_keypair(KeyVersion::Latest);
183/// let bob_serialized_pub: Vec<u8> = bob_keypair.public_key.into();
184///
185/// send_key_to_alice(&bob_serialized_pub);
186///
187/// // This happens on Alice's side.
188/// let alice_keypair = generate_keypair(KeyVersion::Latest);
189/// let alice_serialized_pub: Vec<u8> = alice_keypair.public_key.into();
190///
191/// send_key_to_bob(&alice_serialized_pub);
192///
193/// // Bob can now generate the shared secret.
194/// let alice_received_serialized_pub = receive_key_from_alice();
195/// # let alice_received_serialized_pub = alice_serialized_pub;
196/// let alice_received_pub = PublicKey::try_from(alice_received_serialized_pub.as_slice()).unwrap();
197///
198/// let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_received_pub).unwrap();
199///
200/// // Alice can now generate the shared secret
201/// let bob_received_serialized_pub = receive_key_from_bob();
202/// # let bob_received_serialized_pub = bob_serialized_pub;
203/// let bob_received_pub = PublicKey::try_from(bob_received_serialized_pub.as_slice()).unwrap();
204///
205/// let alice_shared = mix_key_exchange(&alice_keypair.private_key, &bob_received_pub).unwrap();
206///
207/// // They now have a shared secret!
208/// assert_eq!(bob_shared, alice_shared);
209/// ```
210pub fn mix_key_exchange(private_key: &PrivateKey, public_key: &PublicKey) -> Result<Vec<u8>> {
211 Ok(match (&private_key.payload, &public_key.payload) {
212 (PrivateKeyPayload::V1(private_key), PublicKeyPayload::V1(public_key)) => {
213 key_v1::mix_key_exchange(private_key, public_key)
214 } //_ => Err(DevoCryptoError::InvalidDataType),
215 })
216}
217
218fn keypair_headers(version: KeyVersion) -> (Header<PrivateKey>, Header<PublicKey>) {
219 let mut private_header = Header::default();
220 let mut public_header = Header::default();
221
222 match version {
223 KeyVersion::V1 | KeyVersion::Latest => {
224 private_header.version = KeyVersion::V1;
225 public_header.version = KeyVersion::V1;
226 }
227 }
228
229 (private_header, public_header)
230}
231
232impl From<PublicKey> for Vec<u8> {
233 /// Serialize the structure into a `Vec<u8>`, for storage, transmission or use in another language.
234 fn from(data: PublicKey) -> Self {
235 let mut header: Self = data.header.borrow().into();
236 let mut payload: Self = data.payload.into();
237 header.append(&mut payload);
238 header
239 }
240}
241
242impl TryFrom<&[u8]> for PublicKey {
243 type Error = Error;
244
245 /// Parses the data. Can return an Error of the data is invalid or unrecognized.
246 fn try_from(data: &[u8]) -> Result<Self> {
247 if data.len() < Header::len() {
248 return Err(Error::InvalidLength);
249 };
250
251 let header = Header::try_from(&data[0..Header::len()])?;
252
253 if header.data_subtype != KeySubtype::Public {
254 return Err(Error::InvalidDataType);
255 }
256
257 let payload = match header.version {
258 KeyVersion::V1 => PublicKeyPayload::V1(KeyV1Public::try_from(&data[Header::len()..])?),
259 _ => return Err(Error::UnknownVersion),
260 };
261
262 Ok(Self { header, payload })
263 }
264}
265
266impl From<PrivateKey> for Vec<u8> {
267 /// Serialize the structure into a `Vec<u8>`, for storage, transmission or use in another language.
268 fn from(data: PrivateKey) -> Self {
269 let mut header: Self = data.header.borrow().into();
270 let mut payload: Self = data.payload.into();
271 header.append(&mut payload);
272 header
273 }
274}
275
276impl TryFrom<&[u8]> for PrivateKey {
277 type Error = Error;
278
279 /// Parses the data. Can return an Error of the data is invalid or unrecognized.
280 fn try_from(data: &[u8]) -> Result<Self> {
281 if data.len() < Header::len() {
282 return Err(Error::InvalidLength);
283 };
284
285 let header = Header::try_from(&data[0..Header::len()])?;
286
287 if header.data_subtype != KeySubtype::Private {
288 return Err(Error::InvalidDataType);
289 }
290
291 let payload = match header.version {
292 KeyVersion::V1 => {
293 PrivateKeyPayload::V1(KeyV1Private::try_from(&data[Header::len()..])?)
294 }
295 _ => return Err(Error::UnknownVersion),
296 };
297
298 Ok(Self { header, payload })
299 }
300}
301
302impl From<PrivateKeyPayload> for Vec<u8> {
303 fn from(data: PrivateKeyPayload) -> Self {
304 match data {
305 PrivateKeyPayload::V1(x) => x.into(),
306 }
307 }
308}
309
310impl From<PublicKeyPayload> for Vec<u8> {
311 fn from(data: PublicKeyPayload) -> Self {
312 match data {
313 PublicKeyPayload::V1(x) => x.into(),
314 }
315 }
316}
317
318impl From<&PublicKey> for x25519_dalek::PublicKey {
319 fn from(data: &PublicKey) -> Self {
320 match &data.payload {
321 PublicKeyPayload::V1(x) => Self::from(x),
322 //_ => Err(DevoCryptoError::InvalidDataType),
323 }
324 }
325}
326
327impl From<&PrivateKey> for x25519_dalek::StaticSecret {
328 fn from(data: &PrivateKey) -> Self {
329 match &data.payload {
330 PrivateKeyPayload::V1(x) => Self::from(x),
331 //_ => Err(DevoCryptoError::InvalidDataType),
332 }
333 }
334}
335
336#[test]
337fn ecdh_test() {
338 let bob_keypair = generate_keypair(KeyVersion::Latest);
339 let alice_keypair = generate_keypair(KeyVersion::Latest);
340
341 let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_keypair.public_key).unwrap();
342 let alice_shared =
343 mix_key_exchange(&alice_keypair.private_key, &bob_keypair.public_key).unwrap();
344
345 assert_eq!(bob_shared, alice_shared);
346}