devolutions_crypto/key/
mod.rs1mod 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#[derive(Clone)]
68pub struct KeyPair {
69 pub private_key: PrivateKey,
71 pub public_key: PublicKey,
73}
74
75#[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#[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
131pub 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
167pub 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 } })
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 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 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 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 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 }
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 }
335 }
336}
337
338#[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 pub fn as_bytes(&self) -> &[u8] {
369 match &self.payload {
370 SecretKeyPayload::V1(k) => k.as_bytes(),
371 }
372 }
373}
374
375pub(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
384pub 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 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 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}