devolutions_crypto/ciphertext/
mod.rs1mod ciphertext_v1;
44mod ciphertext_v2;
45
46use super::CiphertextSubtype;
47pub use super::CiphertextVersion;
48use super::DataType;
49use super::Error;
50use super::Header;
51use super::HeaderType;
52use super::Result;
53
54use super::key::{PrivateKey, PublicKey};
55
56use ciphertext_v1::CiphertextV1;
57use ciphertext_v2::{CiphertextV2Asymmetric, CiphertextV2Symmetric};
58
59use std::borrow::Borrow;
60use std::convert::TryFrom;
61
62#[cfg(feature = "fuzz")]
63use arbitrary::Arbitrary;
64
65#[derive(Clone, Debug)]
67#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
68pub struct Ciphertext {
69 pub(crate) header: Header<Ciphertext>,
70 payload: CiphertextPayload,
71}
72
73impl HeaderType for Ciphertext {
74 type Version = CiphertextVersion;
75 type Subtype = CiphertextSubtype;
76
77 fn data_type() -> DataType {
78 DataType::Ciphertext
79 }
80}
81
82#[derive(Clone, Debug)]
83#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
84enum CiphertextPayload {
85 V1(CiphertextV1),
86 V2Symmetric(CiphertextV2Symmetric),
87 V2Asymmetric(CiphertextV2Asymmetric),
88}
89
90pub fn encrypt(data: &[u8], key: &[u8], version: CiphertextVersion) -> Result<Ciphertext> {
107 encrypt_with_aad(data, key, [].as_slice(), version)
108}
109
110pub fn encrypt_with_aad(
129 data: &[u8],
130 key: &[u8],
131 aad: &[u8],
132 version: CiphertextVersion,
133) -> Result<Ciphertext> {
134 let mut header = Header::default();
135
136 header.data_subtype = CiphertextSubtype::Symmetric;
137
138 let payload = match version {
139 CiphertextVersion::V1 => {
140 header.version = CiphertextVersion::V1;
141 CiphertextPayload::V1(CiphertextV1::encrypt(data, key, aad, &header)?)
142 }
143 CiphertextVersion::V2 | CiphertextVersion::Latest => {
144 header.version = CiphertextVersion::V2;
145 CiphertextPayload::V2Symmetric(CiphertextV2Symmetric::encrypt(data, key, aad, &header)?)
146 } };
148
149 Ok(Ciphertext { header, payload })
150}
151
152pub fn encrypt_asymmetric(
171 data: &[u8],
172 public_key: &PublicKey,
173 version: CiphertextVersion,
174) -> Result<Ciphertext> {
175 encrypt_asymmetric_with_aad(data, public_key, [].as_slice(), version)
176}
177
178pub fn encrypt_asymmetric_with_aad(
199 data: &[u8],
200 public_key: &PublicKey,
201 aad: &[u8],
202 version: CiphertextVersion,
203) -> Result<Ciphertext> {
204 let mut header = Header::default();
205
206 header.data_subtype = CiphertextSubtype::Asymmetric;
207
208 let payload = match version {
209 CiphertextVersion::V2 | CiphertextVersion::Latest => {
210 header.version = CiphertextVersion::V2;
211 CiphertextPayload::V2Asymmetric(CiphertextV2Asymmetric::encrypt(
212 data, public_key, aad, &header,
213 )?)
214 }
215 _ => return Err(Error::UnknownVersion),
216 };
217
218 Ok(Ciphertext { header, payload })
219}
220
221impl Ciphertext {
222 pub fn decrypt(&self, key: &[u8]) -> Result<Vec<u8>> {
240 self.decrypt_with_aad(key, [].as_slice())
241 }
242
243 pub fn decrypt_with_aad(&self, key: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
263 match &self.payload {
264 CiphertextPayload::V1(x) => x.decrypt(key, aad, &self.header),
265 CiphertextPayload::V2Symmetric(x) => x.decrypt(key, aad, &self.header),
266 _ => Err(Error::InvalidDataType),
267 }
268 }
269
270 pub fn decrypt_asymmetric(&self, private_key: &PrivateKey) -> Result<Vec<u8>> {
289 self.decrypt_asymmetric_with_aad(private_key, [].as_slice())
290 }
291
292 pub fn decrypt_asymmetric_with_aad(
313 &self,
314 private_key: &PrivateKey,
315 aad: &[u8],
316 ) -> Result<Vec<u8>> {
317 match &self.payload {
318 CiphertextPayload::V2Asymmetric(x) => x.decrypt(private_key, aad, &self.header),
319 CiphertextPayload::V1(_) => Err(Error::UnknownVersion),
320 _ => Err(Error::InvalidDataType),
321 }
322 }
323}
324
325impl From<Ciphertext> for Vec<u8> {
326 fn from(data: Ciphertext) -> Self {
328 let mut header: Self = data.header.borrow().into();
329 let mut payload: Self = data.payload.into();
330 header.append(&mut payload);
331 header
332 }
333}
334
335impl TryFrom<&[u8]> for Ciphertext {
336 type Error = Error;
337
338 fn try_from(data: &[u8]) -> Result<Self> {
340 if data.len() < Header::len() {
341 return Err(Error::InvalidLength);
342 };
343
344 let header = Header::try_from(&data[0..Header::len()])?;
345
346 let payload = match header.version {
347 CiphertextVersion::V1 => {
348 CiphertextPayload::V1(CiphertextV1::try_from(&data[Header::len()..])?)
349 }
350 CiphertextVersion::V2 => match header.data_subtype {
351 CiphertextSubtype::Symmetric | CiphertextSubtype::None => {
352 CiphertextPayload::V2Symmetric(CiphertextV2Symmetric::try_from(
353 &data[Header::len()..],
354 )?)
355 }
356 CiphertextSubtype::Asymmetric => CiphertextPayload::V2Asymmetric(
357 CiphertextV2Asymmetric::try_from(&data[Header::len()..])?,
358 ),
359 },
360 _ => return Err(Error::UnknownVersion),
361 };
362
363 Ok(Self { header, payload })
364 }
365}
366
367impl From<CiphertextPayload> for Vec<u8> {
368 fn from(data: CiphertextPayload) -> Self {
369 match data {
370 CiphertextPayload::V1(x) => x.into(),
371 CiphertextPayload::V2Symmetric(x) => x.into(),
372 CiphertextPayload::V2Asymmetric(x) => x.into(),
373 }
374 }
375}
376
377#[test]
378fn encrypt_decrypt_test() {
379 let key = "0123456789abcdefghijkl".as_bytes();
380 let data = "This is a very complex string of character that we need to encrypt".as_bytes();
381
382 let encrypted = encrypt(data, key, CiphertextVersion::Latest).unwrap();
383
384 let encrypted: Vec<u8> = encrypted.into();
385
386 let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
387 let decrypted = encrypted.decrypt(key).unwrap();
388
389 assert_eq!(decrypted, data);
390}
391
392#[test]
393fn encrypt_decrypt_aad_test() {
394 let key = b"0123456789abcdefghijkl";
395 let data = b"This is a very complex string of character that we need to encrypt";
396 let aad = b"This is some public data that we want to authenticate";
397 let wrong_aad = b"this is some public data that we want to authenticate";
398
399 let encrypted = encrypt_with_aad(data, key, aad, CiphertextVersion::Latest).unwrap();
400
401 let encrypted: Vec<u8> = encrypted.into();
402
403 let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
404 let decrypted = encrypted.decrypt_with_aad(key, aad).unwrap();
405
406 assert_eq!(decrypted, data);
407
408 let decrypted = encrypted.decrypt_with_aad(key, wrong_aad);
409
410 assert!(decrypted.is_err());
411
412 let decrypted = encrypted.decrypt(key);
413
414 assert!(decrypted.is_err());
415}
416
417#[test]
418fn encrypt_decrypt_v1_test() {
419 let key = "0123456789abcdefghijkl".as_bytes();
420 let data = "This is a very complex string of character that we need to encrypt".as_bytes();
421
422 let encrypted = encrypt(data, key, CiphertextVersion::V1).unwrap();
423
424 assert_eq!(encrypted.header.version, CiphertextVersion::V1);
425 let encrypted: Vec<u8> = encrypted.into();
426
427 let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
428 let decrypted = encrypted.decrypt(key).unwrap();
429
430 assert_eq!(decrypted, data);
431}
432
433#[test]
434fn encrypt_decrypt_aad_v1_test() {
435 let key = b"0123456789abcdefghijkl";
436 let data = b"This is a very complex string of character that we need to encrypt";
437 let aad = b"This is some public data that we want to authenticate";
438 let wrong_aad = b"this is some public data that we want to authenticate";
439
440 let encrypted = encrypt_with_aad(data, key, aad, CiphertextVersion::V1).unwrap();
441
442 let encrypted: Vec<u8> = encrypted.into();
443
444 let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
445 let decrypted = encrypted.decrypt_with_aad(key, aad).unwrap();
446
447 assert_eq!(decrypted, data);
448
449 let decrypted = encrypted.decrypt_with_aad(key, wrong_aad);
450
451 assert!(decrypted.is_err());
452
453 let decrypted = encrypted.decrypt(key);
454
455 assert!(decrypted.is_err());
456}
457
458#[test]
459fn encrypt_decrypt_v2_test() {
460 let key = "0123456789abcdefghijkl".as_bytes();
461 let data = "This is a very complex string of character that we need to encrypt".as_bytes();
462
463 let encrypted = encrypt(data, key, CiphertextVersion::V2).unwrap();
464
465 assert_eq!(encrypted.header.version, CiphertextVersion::V2);
466 let encrypted: Vec<u8> = encrypted.into();
467
468 let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
469 let decrypted = encrypted.decrypt(key).unwrap();
470
471 assert_eq!(decrypted, data);
472}
473
474#[test]
475fn encrypt_decrypt_aad_v2_test() {
476 let key = b"0123456789abcdefghijkl";
477 let data = b"This is a very complex string of character that we need to encrypt";
478 let aad = b"This is some public data that we want to authenticate";
479 let wrong_aad = b"this is some public data that we want to authenticate";
480
481 let encrypted = encrypt_with_aad(data, key, aad, CiphertextVersion::V2).unwrap();
482
483 let encrypted: Vec<u8> = encrypted.into();
484
485 let encrypted = Ciphertext::try_from(encrypted.as_slice()).unwrap();
486 let decrypted = encrypted.decrypt_with_aad(key, aad).unwrap();
487
488 assert_eq!(decrypted, data);
489
490 let decrypted = encrypted.decrypt_with_aad(key, wrong_aad);
491
492 assert!(decrypted.is_err());
493
494 let decrypted = encrypted.decrypt(key);
495
496 assert!(decrypted.is_err());
497}
498
499#[test]
500fn asymmetric_test() {
501 use super::key::{generate_keypair, KeyVersion};
502
503 let test_plaintext = b"this is a test data";
504
505 let keypair = generate_keypair(KeyVersion::Latest);
506
507 let encrypted_data = encrypt_asymmetric(
508 test_plaintext,
509 &keypair.public_key,
510 CiphertextVersion::Latest,
511 )
512 .unwrap();
513
514 let encrypted_data_vec: Vec<u8> = encrypted_data.into();
515
516 assert_ne!(encrypted_data_vec.len(), 0);
517
518 let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
519
520 let decrypted_data = encrypted_data
521 .decrypt_asymmetric(&keypair.private_key)
522 .unwrap();
523
524 assert_eq!(decrypted_data, test_plaintext);
525}
526
527#[test]
528fn asymmetric_aad_test() {
529 use super::key::{generate_keypair, KeyVersion};
530
531 let test_plaintext = b"this is a test data";
532 let aad = b"This is some public data that we want to authenticate";
533 let wrong_aad = b"this is some public data that we want to authenticate";
534
535 let keypair = generate_keypair(KeyVersion::Latest);
536
537 let encrypted_data = encrypt_asymmetric_with_aad(
538 test_plaintext,
539 &keypair.public_key,
540 aad,
541 CiphertextVersion::Latest,
542 )
543 .unwrap();
544
545 let encrypted_data_vec: Vec<u8> = encrypted_data.into();
546
547 assert_ne!(encrypted_data_vec.len(), 0);
548
549 let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
550
551 let decrypted_data = encrypted_data
552 .decrypt_asymmetric_with_aad(&keypair.private_key, aad)
553 .unwrap();
554
555 assert_eq!(decrypted_data, test_plaintext);
556
557 let decrypted = encrypted_data.decrypt_asymmetric_with_aad(&keypair.private_key, wrong_aad);
558
559 assert!(decrypted.is_err());
560
561 let decrypted = encrypted_data.decrypt_asymmetric(&keypair.private_key);
562
563 assert!(decrypted.is_err());
564}
565
566#[test]
567fn asymmetric_test_v2() {
568 use super::key::{generate_keypair, KeyVersion};
569
570 let test_plaintext = b"this is a test data";
571
572 let keypair = generate_keypair(KeyVersion::V1);
573
574 let encrypted_data =
575 encrypt_asymmetric(test_plaintext, &keypair.public_key, CiphertextVersion::V2).unwrap();
576
577 assert_eq!(encrypted_data.header.version, CiphertextVersion::V2);
578 let encrypted_data_vec: Vec<u8> = encrypted_data.into();
579
580 assert_ne!(encrypted_data_vec.len(), 0);
581
582 let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
583
584 let decrypted_data = encrypted_data
585 .decrypt_asymmetric(&keypair.private_key)
586 .unwrap();
587
588 assert_eq!(decrypted_data, test_plaintext);
589}
590
591#[test]
592fn asymmetric_aad_test_v2() {
593 use super::key::{generate_keypair, KeyVersion};
594
595 let test_plaintext = b"this is a test data";
596 let aad = b"This is some public data that we want to authenticate";
597 let wrong_aad = b"this is some public data that we want to authenticate";
598
599 let keypair = generate_keypair(KeyVersion::V1);
600
601 let encrypted_data = encrypt_asymmetric_with_aad(
602 test_plaintext,
603 &keypair.public_key,
604 aad,
605 CiphertextVersion::V2,
606 )
607 .unwrap();
608
609 let encrypted_data_vec: Vec<u8> = encrypted_data.into();
610
611 assert_ne!(encrypted_data_vec.len(), 0);
612
613 let encrypted_data = Ciphertext::try_from(encrypted_data_vec.as_slice()).unwrap();
614
615 let decrypted_data = encrypted_data
616 .decrypt_asymmetric_with_aad(&keypair.private_key, aad)
617 .unwrap();
618
619 assert_eq!(decrypted_data, test_plaintext);
620
621 let decrypted = encrypted_data.decrypt_asymmetric_with_aad(&keypair.private_key, wrong_aad);
622
623 assert!(decrypted.is_err());
624
625 let decrypted = encrypted_data.decrypt_asymmetric(&keypair.private_key);
626
627 assert!(decrypted.is_err());
628}