1use std::io;
2
3use bstr::BStr;
4use chrono::SubsecRound;
5use flate2::write::{DeflateEncoder, ZlibEncoder};
6use flate2::Compression;
7use log::{debug, warn};
8use rand::{CryptoRng, Rng};
9
10use crate::armor;
11use crate::composed::message::decrypt::*;
12use crate::composed::shared::Deserializable;
13use crate::composed::signed_key::SignedSecretKey;
14use crate::composed::StandaloneSignature;
15use crate::crypto::aead::AeadAlgorithm;
16use crate::crypto::hash::HashAlgorithm;
17use crate::crypto::sym::SymmetricKeyAlgorithm;
18use crate::errors::{Error, Result};
19use crate::packet::{
20 write_packet, CompressedData, LiteralData, OnePassSignature, Packet,
21 PublicKeyEncryptedSessionKey, Signature, SignatureConfig, SignatureType,
22 SignatureVersionSpecific, Subpacket, SubpacketData, SymEncryptedData,
23 SymEncryptedProtectedData, SymKeyEncryptedSessionKey,
24};
25use crate::ser::Serialize;
26use crate::types::{
27 CompressionAlgorithm, EskType, Fingerprint, KeyId, KeyVersion, PkeskVersion, PublicKeyTrait,
28 SecretKeyTrait, StringToKey, Tag,
29};
30
31#[derive(Clone, Debug, PartialEq, Eq)]
33pub enum Message {
34 Literal(LiteralData),
35 Compressed(CompressedData),
36 Signed {
37 message: Option<Box<Message>>,
39 one_pass_signature: Option<OnePassSignature>,
41 signature: Signature,
43 },
44 Encrypted {
45 esk: Vec<Esk>,
46 edata: Edata,
47 },
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum Esk {
56 PublicKeyEncryptedSessionKey(PublicKeyEncryptedSessionKey),
57 SymKeyEncryptedSessionKey(SymKeyEncryptedSessionKey),
58}
59
60impl Serialize for Esk {
61 fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
62 match self {
63 Esk::PublicKeyEncryptedSessionKey(k) => write_packet(writer, k),
64 Esk::SymKeyEncryptedSessionKey(k) => write_packet(writer, k),
65 }
66 }
67}
68
69impl_try_from_into!(
70 Esk,
71 PublicKeyEncryptedSessionKey => PublicKeyEncryptedSessionKey,
72 SymKeyEncryptedSessionKey => SymKeyEncryptedSessionKey
73);
74
75impl Esk {
76 pub fn tag(&self) -> Tag {
77 match self {
78 Esk::PublicKeyEncryptedSessionKey(_) => Tag::PublicKeyEncryptedSessionKey,
79 Esk::SymKeyEncryptedSessionKey(_) => Tag::SymKeyEncryptedSessionKey,
80 }
81 }
82}
83
84impl TryFrom<Packet> for Esk {
85 type Error = Error;
86
87 fn try_from(other: Packet) -> Result<Esk> {
88 match other {
89 Packet::PublicKeyEncryptedSessionKey(k) => Ok(Esk::PublicKeyEncryptedSessionKey(k)),
90 Packet::SymKeyEncryptedSessionKey(k) => Ok(Esk::SymKeyEncryptedSessionKey(k)),
91 _ => Err(format_err!("not a valid edata packet: {:?}", other)),
92 }
93 }
94}
95
96impl From<Esk> for Packet {
97 fn from(other: Esk) -> Packet {
98 match other {
99 Esk::PublicKeyEncryptedSessionKey(k) => Packet::PublicKeyEncryptedSessionKey(k),
100 Esk::SymKeyEncryptedSessionKey(k) => Packet::SymKeyEncryptedSessionKey(k),
101 }
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
109pub enum Edata {
110 SymEncryptedData(SymEncryptedData),
111 SymEncryptedProtectedData(SymEncryptedProtectedData),
112}
113
114impl Serialize for Edata {
115 fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
116 match self {
117 Edata::SymEncryptedData(d) => write_packet(writer, d),
118 Edata::SymEncryptedProtectedData(d) => write_packet(writer, d),
119 }
120 }
121}
122
123impl_try_from_into!(
124 Edata,
125 SymEncryptedData => SymEncryptedData,
126 SymEncryptedProtectedData => SymEncryptedProtectedData
127);
128
129impl TryFrom<Packet> for Edata {
130 type Error = Error;
131
132 fn try_from(other: Packet) -> Result<Edata> {
133 match other {
134 Packet::SymEncryptedData(d) => Ok(Edata::SymEncryptedData(d)),
135 Packet::SymEncryptedProtectedData(d) => Ok(Edata::SymEncryptedProtectedData(d)),
136 _ => Err(format_err!("not a valid edata packet: {:?}", other)),
137 }
138 }
139}
140
141impl From<Edata> for Packet {
142 fn from(other: Edata) -> Packet {
143 match other {
144 Edata::SymEncryptedData(d) => Packet::SymEncryptedData(d),
145 Edata::SymEncryptedProtectedData(d) => Packet::SymEncryptedProtectedData(d),
146 }
147 }
148}
149
150impl Edata {
151 pub fn data(&self) -> &[u8] {
152 match self {
153 Edata::SymEncryptedData(d) => d.data(),
154 Edata::SymEncryptedProtectedData(d) => d.data_as_slice(),
155 }
156 }
157
158 pub fn tag(&self) -> Tag {
159 match self {
160 Edata::SymEncryptedData(_) => Tag::SymEncryptedData,
161 Edata::SymEncryptedProtectedData(_) => Tag::SymEncryptedProtectedData,
162 }
163 }
164
165 fn version(&self) -> Option<usize> {
166 match self {
167 Edata::SymEncryptedData(_) => None,
168 Edata::SymEncryptedProtectedData(d) => Some(d.version()),
169 }
170 }
171
172 fn process_decrypted(packet_data: &[u8]) -> Result<Message> {
175 let mut messages = Message::from_bytes_many(packet_data);
176 let Some(message) = messages.next() else {
178 bail!("no valid message found");
179 };
180 let message = message?;
181
182 if let Some(msg) = messages.next() {
185 bail!("unexpected message: {:?}", msg);
186 }
187
188 Ok(message)
189 }
190
191 pub fn decrypt(&self, key: PlainSessionKey) -> Result<Message> {
192 let protected = self.tag() == Tag::SymEncryptedProtectedData;
193 debug!("decrypting protected = {:?}", protected);
194
195 match key {
196 PlainSessionKey::V3_4 { sym_alg, ref key } => {
197 ensure!(
198 sym_alg != SymmetricKeyAlgorithm::Plaintext,
199 "session key algorithm cannot be plaintext"
200 );
201
202 match self {
203 Self::SymEncryptedProtectedData(p) => {
204 ensure_eq!(
205 self.version(),
206 Some(1),
207 "Version mismatch between key and integrity packet"
208 );
209 let data = p.decrypt(key, Some(sym_alg))?;
210 Self::process_decrypted(&data[..])
211 }
212 Self::SymEncryptedData(p) => {
213 ensure_eq!(
214 self.version(),
215 None,
216 "Version mismatch between key and integrity packet"
217 );
218 let mut data = p.data().to_vec();
219 let res = sym_alg.decrypt(key, &mut data)?;
220 Self::process_decrypted(res)
221 }
222 }
223 }
224 PlainSessionKey::V5 { .. } => match self {
225 Self::SymEncryptedProtectedData(_p) => {
226 ensure_eq!(
227 self.version(),
228 Some(2),
229 "Version mismatch between key and integrity packet"
230 );
231 unimplemented_err!("V5 decryption");
232 }
233 Self::SymEncryptedData(_) => {
234 bail!("invalid packet combination");
235 }
236 },
237 PlainSessionKey::V6 { ref key } => match self {
238 Self::SymEncryptedProtectedData(p) => {
239 ensure_eq!(
240 self.version(),
241 Some(2),
242 "Version mismatch between key and integrity packet"
243 );
244
245 let decrypted_packets = p.decrypt(key, None)?;
246 Self::process_decrypted(&decrypted_packets[..])
247 }
248 Self::SymEncryptedData(_) => {
249 bail!("invalid packet combination");
250 }
251 },
252 }
253 }
254}
255
256impl Serialize for Message {
257 fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
258 match self {
259 Message::Literal(data) => write_packet(writer, data),
260 Message::Compressed(data) => write_packet(writer, data),
261 Message::Signed {
262 message,
263 one_pass_signature,
264 signature,
265 ..
266 } => {
267 if let Some(ops) = one_pass_signature {
268 write_packet(writer, ops)?;
269 }
270 if let Some(message) = message {
271 (**message).to_writer(writer)?;
272 }
273
274 write_packet(writer, signature)?;
275
276 Ok(())
277 }
278 Message::Encrypted { esk, edata, .. } => {
279 for e in esk {
280 e.to_writer(writer)?;
281 }
282 edata.to_writer(writer)?;
283
284 Ok(())
285 }
286 }
287 }
288}
289
290impl Message {
291 pub fn new_literal(file_name: impl AsRef<BStr>, data: &str) -> Self {
292 Message::Literal(LiteralData::from_str(file_name.as_ref(), data))
293 }
294
295 pub fn new_literal_bytes(file_name: impl AsRef<BStr>, data: &[u8]) -> Self {
296 Message::Literal(LiteralData::from_bytes(file_name.as_ref(), data))
297 }
298
299 pub fn compress(&self, alg: CompressionAlgorithm) -> Result<Self> {
301 let data = match alg {
302 CompressionAlgorithm::Uncompressed => {
303 let mut data = Vec::new();
304 self.to_writer(&mut data)?;
305 data
306 }
307 CompressionAlgorithm::ZIP => {
308 let mut enc = DeflateEncoder::new(Vec::new(), Compression::default());
309 self.to_writer(&mut enc)?;
310 enc.finish()?
311 }
312 CompressionAlgorithm::ZLIB => {
313 let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
314 self.to_writer(&mut enc)?;
315 enc.finish()?
316 }
317 CompressionAlgorithm::BZip2 => unimplemented_err!("BZip2"),
318 CompressionAlgorithm::Private10 | CompressionAlgorithm::Other(_) => {
319 unsupported_err!("CompressionAlgorithm {} is unsupported", u8::from(alg))
320 }
321 };
322
323 Ok(Message::Compressed(CompressedData::from_compressed(
324 alg, data,
325 )))
326 }
327
328 pub fn decompress(self) -> Result<Self> {
330 match self {
331 Message::Compressed(data) => Message::from_bytes(data.decompress()?),
332 _ => Ok(self),
333 }
334 }
335
336 pub fn encrypt_to_keys_seipdv1<R: CryptoRng + Rng>(
338 &self,
339 mut rng: R,
340 alg: SymmetricKeyAlgorithm,
341 pkeys: &[&impl PublicKeyTrait],
342 ) -> Result<Self> {
343 let session_key = alg.new_session_key(&mut rng);
345
346 let esk = pkeys
348 .iter()
349 .map(|pkey| {
350 let pkes = PublicKeyEncryptedSessionKey::from_session_key_v3(
351 &mut rng,
352 &session_key,
353 alg,
354 pkey,
355 )?;
356 Ok(Esk::PublicKeyEncryptedSessionKey(pkes))
357 })
358 .collect::<Result<_>>()?;
359
360 self.encrypt_symmetric_seipdv1(&mut rng, esk, alg, &session_key)
362 }
363
364 pub fn encrypt_to_keys_seipdv2<R: CryptoRng + Rng>(
366 &self,
367 mut rng: R,
368 alg: SymmetricKeyAlgorithm,
369 aead: AeadAlgorithm,
370 chunk_size: u8,
371 pkeys: &[&impl PublicKeyTrait],
372 ) -> Result<Self> {
373 let session_key = alg.new_session_key(&mut rng);
375
376 let esk = pkeys
378 .iter()
379 .map(|pkey| {
380 let pkes = PublicKeyEncryptedSessionKey::from_session_key_v6(
381 &mut rng,
382 &session_key,
383 pkey,
384 )?;
385 Ok(Esk::PublicKeyEncryptedSessionKey(pkes))
386 })
387 .collect::<Result<_>>()?;
388
389 self.encrypt_symmetric_seipdv2(&mut rng, esk, alg, aead, chunk_size, &session_key)
391 }
392
393 pub fn encrypt_with_password_seipdv1<R, F>(
395 &self,
396 mut rng: R,
397 s2k: StringToKey,
398 alg: SymmetricKeyAlgorithm,
399 msg_pw: F,
400 ) -> Result<Self>
401 where
402 R: Rng + CryptoRng,
403 F: FnOnce() -> String + Clone,
404 {
405 let session_key = alg.new_session_key(&mut rng);
407
408 let skesk = Esk::SymKeyEncryptedSessionKey(SymKeyEncryptedSessionKey::encrypt_v4(
410 msg_pw,
411 &session_key,
412 s2k,
413 alg,
414 )?);
415
416 self.encrypt_symmetric_seipdv1(rng, vec![skesk], alg, &session_key)
418 }
419
420 pub fn encrypt_with_password_seipdv2<R, F>(
422 &self,
423 mut rng: R,
424 s2k: StringToKey,
425 alg: SymmetricKeyAlgorithm,
426 aead: AeadAlgorithm,
427 chunk_size: u8,
428 msg_pw: F,
429 ) -> Result<Self>
430 where
431 R: Rng + CryptoRng,
432 F: FnOnce() -> String + Clone,
433 {
434 let session_key = alg.new_session_key(&mut rng);
436
437 let skesk = Esk::SymKeyEncryptedSessionKey(SymKeyEncryptedSessionKey::encrypt_v6(
439 &mut rng,
440 msg_pw,
441 &session_key,
442 s2k,
443 alg,
444 aead,
445 )?);
446
447 self.encrypt_symmetric_seipdv2(rng, vec![skesk], alg, aead, chunk_size, &session_key)
449 }
450
451 fn encrypt_symmetric_seipdv1<R: CryptoRng + Rng>(
455 &self,
456 rng: R,
457 esk: Vec<Esk>,
458 alg: SymmetricKeyAlgorithm,
459 session_key: &[u8],
460 ) -> Result<Self> {
461 let data = self.to_bytes()?;
462
463 let edata = Edata::SymEncryptedProtectedData(SymEncryptedProtectedData::encrypt_seipdv1(
464 rng,
465 alg,
466 session_key,
467 &data,
468 )?);
469
470 Ok(Message::Encrypted { esk, edata })
471 }
472
473 fn encrypt_symmetric_seipdv2<R: CryptoRng + Rng>(
477 &self,
478 rng: R,
479 esk: Vec<Esk>,
480 alg: SymmetricKeyAlgorithm,
481 aead: AeadAlgorithm,
482 chunk_size: u8,
483 session_key: &[u8],
484 ) -> Result<Self> {
485 let data = self.to_bytes()?;
486
487 let edata = Edata::SymEncryptedProtectedData(SymEncryptedProtectedData::encrypt_seipdv2(
488 rng,
489 alg,
490 aead,
491 chunk_size,
492 session_key,
493 &data,
494 )?);
495
496 Ok(Message::Encrypted { esk, edata })
497 }
498
499 pub fn sign<R, F>(
501 self,
502 rng: R,
503 key: &impl SecretKeyTrait,
504 key_pw: F,
505 hash_algorithm: HashAlgorithm,
506 ) -> Result<Self>
507 where
508 R: CryptoRng + Rng,
509 F: FnOnce() -> String,
510 {
511 let key_id = key.key_id();
512 let algorithm = key.algorithm();
513
514 let hashed_subpackets = vec![
515 Subpacket::regular(SubpacketData::IssuerFingerprint(key.fingerprint())),
516 Subpacket::regular(SubpacketData::SignatureCreationTime(
517 chrono::Utc::now().trunc_subsecs(0),
518 )),
519 ];
520 let unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(key_id.clone()))];
521
522 let (typ, signature) = match self {
523 Message::Literal(ref l) => {
524 let typ = if l.is_binary() {
525 SignatureType::Binary
526 } else {
527 SignatureType::Text
528 };
529
530 let mut config = match key.version() {
531 KeyVersion::V4 => SignatureConfig::v4(typ, algorithm, hash_algorithm),
532 KeyVersion::V6 => SignatureConfig::v6(rng, typ, algorithm, hash_algorithm)?,
533 v => bail!("unsupported key version {:?}", v),
534 };
535 config.hashed_subpackets = hashed_subpackets;
536 config.unhashed_subpackets = unhashed_subpackets;
537
538 (typ, config.sign(key, key_pw, l.data())?)
539 }
540 _ => {
541 let typ = SignatureType::Binary;
542
543 let mut config = match key.version() {
544 KeyVersion::V4 => SignatureConfig::v4(typ, algorithm, hash_algorithm),
545 KeyVersion::V6 => SignatureConfig::v6(rng, typ, algorithm, hash_algorithm)?,
546 v => bail!("unsupported key version {:?}", v),
547 };
548 config.hashed_subpackets = hashed_subpackets;
549 config.unhashed_subpackets = unhashed_subpackets;
550
551 let data = self.to_bytes()?;
552 let signature = config.sign(key, key_pw, &data[..])?;
553
554 (typ, signature)
555 }
556 };
557
558 let ops = match key.version() {
559 KeyVersion::V4 => OnePassSignature::v3(typ, hash_algorithm, algorithm, key_id),
560 KeyVersion::V6 => {
561 let SignatureVersionSpecific::V6 { ref salt } = signature.config.version_specific
562 else {
563 bail!("Inconsistent Signature and OnePassSignature version")
565 };
566
567 let Fingerprint::V6(fp) = key.fingerprint() else {
568 bail!("Inconsistent Signature and Fingerprint version")
569 };
570
571 OnePassSignature::v6(typ, hash_algorithm, algorithm, salt.clone(), fp)
572 }
573 v => bail!("Unsupported key version {:?}", v),
574 };
575
576 Ok(Message::Signed {
577 message: Some(Box::new(self)),
578 one_pass_signature: Some(ops),
579 signature,
580 })
581 }
582
583 pub fn into_signature(self) -> StandaloneSignature {
585 match self {
586 Message::Signed { signature, .. } => StandaloneSignature::new(signature),
587 _ => panic!("only signed messages can be converted to standalone signature messages"),
588 }
589 }
590
591 pub fn verify(&self, key: &impl PublicKeyTrait) -> Result<()> {
597 self.verify_internal(key, true)
598 }
599
600 fn verify_internal(&self, key: &impl PublicKeyTrait, decompress: bool) -> Result<()> {
606 match self {
607 Message::Signed {
608 signature, message, ..
609 } => {
610 if let Some(message) = message {
611 match **message {
612 Message::Literal(ref data) => signature.verify(key, data.data()),
613 _ => {
614 let data = message.to_bytes()?;
615 signature.verify(key, &data[..])
616 }
617 }
618 } else {
619 unimplemented_err!("no message, what to do?");
620 }
621 }
622 Message::Compressed(data) => {
623 if decompress {
624 let msg = Message::from_bytes(data.decompress()?)?;
625 msg.verify_internal(key, false)
626 } else {
627 bail!("Recursive decompression not allowed");
628 }
629 }
630 _ => Err(Error::Unsupported(format!(
632 "Unexpected message format: {self:?}",
633 ))),
634 }
635 }
636
637 pub fn decrypt<G>(&self, key_pw: G, keys: &[&SignedSecretKey]) -> Result<(Message, Vec<KeyId>)>
640 where
641 G: FnOnce() -> String + Clone,
642 {
643 match self {
644 Message::Compressed { .. } | Message::Literal { .. } => {
645 bail!("not encrypted");
646 }
647 Message::Signed { message, .. } => match message {
648 Some(message) => message.as_ref().decrypt(key_pw, keys),
649 None => bail!("not encrypted"),
650 },
651 Message::Encrypted { esk, edata, .. } => {
652 let valid_keys = keys
653 .iter()
654 .filter_map(|key| {
655 let mut packet = None;
657 let mut encoding_key = None;
658 let mut encoding_subkey = None;
659
660 for esk_packet in esk.iter().filter_map(|k| match k {
661 Esk::PublicKeyEncryptedSessionKey(k) => Some(k),
662 _ => None,
663 }) {
664 debug!("esk packet: {:?}", esk_packet);
665 debug!("{:?}", key.key_id());
666 debug!(
667 "{:?}",
668 key.secret_subkeys
669 .iter()
670 .map(PublicKeyTrait::key_id)
671 .collect::<Vec<_>>()
672 );
673
674 if esk_packet.match_identity(&key.primary_key) {
677 encoding_key = Some(&key.primary_key);
678 }
679
680 if encoding_key.is_none() {
681 encoding_subkey = key
682 .secret_subkeys
683 .iter()
684 .find(|&subkey| esk_packet.match_identity(&subkey));
685 }
686
687 if encoding_key.is_some() || encoding_subkey.is_some() {
688 packet = Some(esk_packet);
689 break;
690 }
691 }
692
693 packet.map(|packet| (packet, encoding_key, encoding_subkey))
694 })
695 .collect::<Vec<_>>();
696
697 if valid_keys.is_empty() {
698 return Err(Error::MissingKey);
699 }
700
701 let session_keys = valid_keys
702 .iter()
703 .map(|(pkesk, encoding_key, encoding_subkey)| {
704 let typ = match pkesk.version() {
705 PkeskVersion::V3 => EskType::V3_4,
706 PkeskVersion::V6 => EskType::V6,
707 PkeskVersion::Other(v) => {
708 unimplemented_err!("Unexpected PKESK version {}", v)
709 }
710 };
711
712 if let Some(ek) = encoding_key {
713 Ok((
714 ek.key_id(),
715 decrypt_session_key(ek, key_pw.clone(), pkesk.values()?, typ)?,
716 ))
717 } else if let Some(ek) = encoding_subkey {
718 Ok((
719 ek.key_id(),
720 decrypt_session_key(ek, key_pw.clone(), pkesk.values()?, typ)?,
721 ))
722 } else {
723 unreachable!("either a key or a subkey were found");
724 }
725 })
726 .filter(|res| match res {
727 Ok(_) => true,
728 Err(err) => {
729 warn!("failed to decrypt session_key for key: {:?}", err);
730 false
731 }
732 })
733 .collect::<Result<Vec<_>>>()?;
734
735 ensure!(!session_keys.is_empty(), "failed to decrypt session key");
736
737 let session_key = {
739 let (_key_id, k0) = &session_keys[0];
740 if !session_keys.iter().skip(1).all(|(_, k)| k0 == k) {
741 bail!("found inconsistent session keys, possible message corruption");
742 }
743
744 k0.clone()
746 };
747
748 let ids = session_keys.into_iter().map(|(k, _)| k).collect();
749 let msg = edata.decrypt(session_key)?;
750
751 Ok((msg, ids))
752 }
753 }
754 }
755
756 pub fn decrypt_with_password<F>(&self, msg_pw: F) -> Result<Message>
759 where
760 F: FnOnce() -> String + Clone,
761 {
762 match self {
763 Message::Compressed { .. } | Message::Literal { .. } => {
764 bail!("not encrypted");
765 }
766 Message::Signed { message, .. } => match message {
767 Some(ref message) => message.decrypt_with_password(msg_pw),
768 None => bail!("not encrypted"),
769 },
770 Message::Encrypted { esk, edata, .. } => {
771 let skesk = esk.iter().find_map(|esk| match esk {
773 Esk::SymKeyEncryptedSessionKey(k) => Some(k),
774 _ => None,
775 });
776
777 ensure!(skesk.is_some(), "message is not password protected");
778
779 let session_key =
780 decrypt_session_key_with_password(skesk.expect("checked above"), msg_pw)?;
781 edata.decrypt(session_key)
782 }
783 }
784 }
785
786 pub fn is_one_pass_signed(&self) -> bool {
788 match self {
789 Message::Signed {
790 one_pass_signature, ..
791 } => one_pass_signature.is_some(),
792 _ => false,
793 }
794 }
795
796 pub fn is_literal(&self) -> bool {
797 match self {
798 Message::Literal { .. } => true,
799 Message::Signed { message, .. } => message
800 .as_ref()
801 .map(|msg| msg.is_literal())
802 .unwrap_or_default(),
803 _ => false,
804 }
805 }
806
807 pub fn get_literal(&self) -> Option<&LiteralData> {
808 match self {
809 Message::Literal(ref data) => Some(data),
810 Message::Signed { message, .. } => message.as_ref().and_then(|msg| msg.get_literal()),
811 _ => None,
812 }
813 }
814
815 pub fn get_content(&self) -> Result<Option<Vec<u8>>> {
819 self.get_content_internal(true)
820 }
821
822 fn get_content_internal(&self, decompress: bool) -> Result<Option<Vec<u8>>> {
826 match self {
827 Message::Literal(ref data) => Ok(Some(data.data().to_vec())),
828 Message::Signed { message, .. } => Ok(message
829 .as_ref()
830 .and_then(|m| m.get_literal())
831 .map(|l| l.data().to_vec())),
832 Message::Compressed(data) => {
833 if decompress {
834 let msg = Message::from_bytes(data.decompress()?)?;
835 msg.get_content_internal(false)
836 } else {
837 bail!("Recursive decompression not allowed");
838 }
839 }
840 Message::Encrypted { .. } => Ok(None),
841 }
842 }
843
844 pub fn to_armored_writer(
845 &self,
846 writer: &mut impl io::Write,
847 opts: ArmorOptions<'_>,
848 ) -> Result<()> {
849 armor::write(
850 self,
851 armor::BlockType::Message,
852 writer,
853 opts.headers,
854 opts.include_checksum,
855 )
856 }
857
858 pub fn to_armored_bytes(&self, opts: ArmorOptions<'_>) -> Result<Vec<u8>> {
859 let mut buf = Vec::new();
860
861 self.to_armored_writer(&mut buf, opts)?;
862
863 Ok(buf)
864 }
865
866 pub fn to_armored_string(&self, opts: ArmorOptions<'_>) -> Result<String> {
867 let res = String::from_utf8(self.to_armored_bytes(opts)?).map_err(|e| e.utf8_error())?;
868 Ok(res)
869 }
870}
871
872#[derive(Debug, Clone)]
874pub struct ArmorOptions<'a> {
875 pub headers: Option<&'a armor::Headers>,
877 pub include_checksum: bool,
879}
880
881impl Default for ArmorOptions<'_> {
882 fn default() -> Self {
883 Self {
884 headers: None,
885 include_checksum: true,
886 }
887 }
888}
889
890impl<'a> From<Option<&'a armor::Headers>> for ArmorOptions<'a> {
891 fn from(headers: Option<&'a armor::Headers>) -> Self {
892 Self {
893 headers,
894 include_checksum: true,
895 }
896 }
897}
898
899#[cfg(test)]
900mod tests {
901 #![allow(clippy::unwrap_used)]
902
903 use std::fs;
904
905 use rand::SeedableRng;
906 use rand_chacha::ChaCha8Rng;
907
908 use super::*;
909 use crate::cleartext::CleartextSignedMessage;
910 use crate::SignedPublicKey;
911
912 #[test]
913 fn test_compression_zlib() {
914 let lit_msg = Message::new_literal("hello-zlib.txt", "hello world");
915
916 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
917 let uncompressed_msg = compressed_msg.decompress().unwrap();
918
919 assert_eq!(&lit_msg, &uncompressed_msg);
920 }
921
922 #[test]
923 fn test_compression_zip() {
924 let lit_msg = Message::new_literal("hello-zip.txt", "hello world");
925
926 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZIP).unwrap();
927 let uncompressed_msg = compressed_msg.decompress().unwrap();
928
929 assert_eq!(&lit_msg, &uncompressed_msg);
930 }
931
932 #[test]
933 fn test_compression_uncompressed() {
934 let lit_msg = Message::new_literal("hello.txt", "hello world");
935
936 let compressed_msg = lit_msg
937 .compress(CompressionAlgorithm::Uncompressed)
938 .unwrap();
939 let uncompressed_msg = compressed_msg.decompress().unwrap();
940
941 assert_eq!(&lit_msg, &uncompressed_msg);
942 }
943
944 #[test]
945 fn test_rsa_encryption_seipdv1() {
946 let (skey, _headers) = SignedSecretKey::from_armor_single(
947 fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
948 .unwrap(),
949 )
950 .unwrap();
951
952 let pkey = skey.secret_subkeys[0].public_key();
954 let mut rng = rand::rngs::StdRng::seed_from_u64(100);
955 let mut rng2 = rand::rngs::StdRng::seed_from_u64(100);
956
957 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
958 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
959
960 let encrypted = compressed_msg
962 .encrypt_to_keys_seipdv1(&mut rng, SymmetricKeyAlgorithm::AES128, &[&pkey][..])
963 .unwrap();
964 let encrypted2 = compressed_msg
965 .encrypt_to_keys_seipdv1(&mut rng2, SymmetricKeyAlgorithm::AES128, &[&pkey][..])
966 .unwrap();
967 assert_eq!(encrypted, encrypted2);
968
969 let armored = encrypted.to_armored_bytes(None.into()).unwrap();
970 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
973
974 let decrypted = parsed.decrypt(|| "test".into(), &[&skey]).unwrap().0;
975
976 assert_eq!(compressed_msg, decrypted);
977 }
978
979 #[test]
980 fn test_rsa_encryption_seipdv2() {
981 let (skey, _headers) = SignedSecretKey::from_armor_single(
982 fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
983 .unwrap(),
984 )
985 .unwrap();
986
987 let pkey = skey.secret_subkeys[0].public_key();
989 let mut rng = rand::rngs::StdRng::seed_from_u64(100);
990 let mut rng2 = rand::rngs::StdRng::seed_from_u64(100);
991
992 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
993 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
994
995 let encrypted = compressed_msg
997 .encrypt_to_keys_seipdv2(
998 &mut rng,
999 SymmetricKeyAlgorithm::AES128,
1000 AeadAlgorithm::Ocb,
1001 0x06,
1002 &[&pkey][..],
1003 )
1004 .unwrap();
1005 let encrypted2 = compressed_msg
1006 .encrypt_to_keys_seipdv2(
1007 &mut rng2,
1008 SymmetricKeyAlgorithm::AES128,
1009 AeadAlgorithm::Ocb,
1010 0x06,
1011 &[&pkey][..],
1012 )
1013 .unwrap();
1014 assert_eq!(encrypted, encrypted2);
1015
1016 let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1017 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1020
1021 let decrypted = parsed.decrypt(|| "test".into(), &[&skey]).unwrap().0;
1022
1023 assert_eq!(compressed_msg, decrypted);
1024 }
1025
1026 #[test]
1027 fn test_x25519_encryption_seipdv1() {
1028 let (skey, _headers) = SignedSecretKey::from_armor_single(
1029 fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1030 )
1031 .unwrap();
1032
1033 let pkey = skey.secret_subkeys[0].public_key();
1035 let mut rng = ChaCha8Rng::seed_from_u64(0);
1036
1037 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1038 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1039 for _ in 0..1000 {
1040 let encrypted = compressed_msg
1041 .encrypt_to_keys_seipdv1(&mut rng, SymmetricKeyAlgorithm::AES128, &[&pkey][..])
1042 .unwrap();
1043
1044 let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1045 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1048
1049 let decrypted = parsed.decrypt(|| "".into(), &[&skey]).unwrap().0;
1050
1051 assert_eq!(compressed_msg, decrypted);
1052 }
1053 }
1054
1055 #[test]
1056 fn test_x25519_encryption_seipdv2() {
1057 let (skey, _headers) = SignedSecretKey::from_armor_single(
1058 fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1059 )
1060 .unwrap();
1061
1062 let pkey = skey.secret_subkeys[0].public_key();
1064 let mut rng = ChaCha8Rng::seed_from_u64(0);
1065
1066 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1067 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1068
1069 for aead in [AeadAlgorithm::Ocb, AeadAlgorithm::Eax, AeadAlgorithm::Gcm] {
1070 for sym in [
1071 SymmetricKeyAlgorithm::MKV128256,
1072 SymmetricKeyAlgorithm::MKV128192,
1073 SymmetricKeyAlgorithm::MKV128128,
1074 SymmetricKeyAlgorithm::AES128,
1075 SymmetricKeyAlgorithm::AES192,
1076 SymmetricKeyAlgorithm::AES256,
1077 ] {
1078 for _ in 0..1000 {
1079 let encrypted = compressed_msg
1080 .encrypt_to_keys_seipdv2(&mut rng, sym, aead, 0x06, &[&pkey][..])
1081 .unwrap();
1082
1083 let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1084 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1087
1088 let decrypted = parsed.decrypt(|| "".into(), &[&skey]).unwrap().0;
1089
1090 assert_eq!(compressed_msg, decrypted);
1091 }
1092 }
1093 }
1094 }
1095
1096 #[test]
1097 fn test_password_encryption_seipdv1() {
1098 let _ = pretty_env_logger::try_init();
1099
1100 let mut rng = ChaCha8Rng::seed_from_u64(0);
1101
1102 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1103 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1104
1105 let s2k = StringToKey::new_default(&mut rng);
1106
1107 let encrypted = compressed_msg
1108 .encrypt_with_password_seipdv1(&mut rng, s2k, SymmetricKeyAlgorithm::AES128, || {
1109 "secret".into()
1110 })
1111 .unwrap();
1112
1113 let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1114 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1117
1118 let decrypted = parsed.decrypt_with_password(|| "secret".into()).unwrap();
1119
1120 assert_eq!(compressed_msg, decrypted);
1121 }
1122
1123 #[test]
1124 fn test_password_encryption_seipdv2() {
1125 let _ = pretty_env_logger::try_init();
1126
1127 let mut rng = ChaCha8Rng::seed_from_u64(0);
1128
1129 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1130 let compressed_msg = lit_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1131
1132 for aead in [AeadAlgorithm::Ocb, AeadAlgorithm::Eax, AeadAlgorithm::Gcm] {
1133 for sym in [
1134 SymmetricKeyAlgorithm::MKV128256,
1135 SymmetricKeyAlgorithm::MKV128192,
1136 SymmetricKeyAlgorithm::MKV128128,
1137 SymmetricKeyAlgorithm::AES128,
1138 SymmetricKeyAlgorithm::AES192,
1139 SymmetricKeyAlgorithm::AES256,
1140 ] {
1141 let s2k = StringToKey::new_default(&mut rng);
1142
1143 let encrypted = compressed_msg
1144 .encrypt_with_password_seipdv2(&mut rng, s2k, sym, aead, 0x06, || {
1145 "secret".into()
1146 })
1147 .unwrap();
1148
1149 let armored = encrypted.to_armored_bytes(None.into()).unwrap();
1150 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1153
1154 let decrypted = parsed.decrypt_with_password(|| "secret".into()).unwrap();
1155
1156 assert_eq!(compressed_msg, decrypted);
1157 }
1158 }
1159 }
1160
1161 #[test]
1162 fn test_no_plaintext_decryption() {
1163 let msg_raw = b"\xc3\x04\x04\x00\x00\x08\xd2-\x01\x00\x00\xcb\x12b\x00\x00\x00\x00\x00Hello world!\xd3\x14\xc3\xadw\x022\x05\x0ek'k\x8d\x12\xaa8\r'\x8d\xc0\x82)";
1166 let msg = Message::from_bytes(&msg_raw[..]).unwrap();
1198
1199 assert!(msg
1203 .decrypt_with_password(|| "foobarbaz".into())
1204 .err()
1205 .unwrap()
1206 .to_string()
1207 .contains("plaintext"));
1208 }
1209
1210 #[test]
1211 fn test_x25519_signing_string() {
1212 let (skey, _headers) = SignedSecretKey::from_armor_single(
1213 fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1214 )
1215 .unwrap();
1216
1217 let pkey = skey.public_key();
1218 let mut rng = ChaCha8Rng::seed_from_u64(0);
1219
1220 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1221 assert!(lit_msg.verify(&pkey).is_err()); let signed_msg = lit_msg
1224 .sign(&mut rng, &skey, || "".into(), HashAlgorithm::SHA2_256)
1225 .unwrap();
1226
1227 let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1228 signed_msg.verify(&pkey).unwrap();
1231
1232 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1233 parsed.verify(&pkey).unwrap();
1234 }
1235
1236 #[test]
1237 fn test_x25519_signing_bytes() {
1238 let (skey, _headers) = SignedSecretKey::from_armor_single(
1239 fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1240 )
1241 .unwrap();
1242
1243 let pkey = skey.public_key();
1244 let mut rng = ChaCha8Rng::seed_from_u64(0);
1245
1246 let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1247 let signed_msg = lit_msg
1248 .sign(&mut rng, &skey, || "".into(), HashAlgorithm::SHA2_256)
1249 .unwrap();
1250
1251 let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1252 signed_msg.verify(&pkey).unwrap();
1255
1256 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1257 parsed.verify(&pkey).unwrap();
1258 }
1259
1260 #[test]
1261 fn test_x25519_signing_bytes_compressed() {
1262 let (skey, _headers) = SignedSecretKey::from_armor_single(
1263 fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1264 )
1265 .unwrap();
1266
1267 let pkey = skey.public_key();
1268 let mut rng = ChaCha8Rng::seed_from_u64(0);
1269
1270 let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1271 let signed_msg = lit_msg
1272 .sign(&mut rng, &skey, || "".into(), HashAlgorithm::SHA2_256)
1273 .unwrap();
1274 let compressed_msg = signed_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1275
1276 let armored = compressed_msg.to_armored_bytes(None.into()).unwrap();
1277 signed_msg.verify(&pkey).unwrap();
1280
1281 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1282 parsed.verify(&pkey).unwrap();
1283 }
1284
1285 #[test]
1286 fn test_rsa_signing_string() {
1287 for _ in 0..100 {
1288 let (skey, _headers) = SignedSecretKey::from_armor_single(
1289 fs::File::open(
1290 "./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc",
1291 )
1292 .unwrap(),
1293 )
1294 .unwrap();
1295
1296 let pkey = skey.public_key();
1297 let mut rng = ChaCha8Rng::seed_from_u64(0);
1298
1299 let lit_msg = Message::new_literal("hello.txt", "hello world\n");
1300 let signed_msg = lit_msg
1301 .sign(&mut rng, &skey, || "test".into(), HashAlgorithm::SHA2_256)
1302 .unwrap();
1303
1304 let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1305 signed_msg.verify(&pkey).unwrap();
1308
1309 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1310 parsed.verify(&pkey).unwrap();
1311 }
1312 }
1313
1314 #[test]
1315 fn test_rsa_signing_bytes() {
1316 let (skey, _headers) = SignedSecretKey::from_armor_single(
1317 fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
1318 .unwrap(),
1319 )
1320 .unwrap();
1321
1322 let pkey = skey.public_key();
1323 let mut rng = ChaCha8Rng::seed_from_u64(0);
1324
1325 let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1326 let signed_msg = lit_msg
1327 .sign(&mut rng, &skey, || "test".into(), HashAlgorithm::SHA2_256)
1328 .unwrap();
1329
1330 let armored = signed_msg.to_armored_bytes(None.into()).unwrap();
1331 signed_msg.verify(&pkey).unwrap();
1334
1335 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1336 parsed.verify(&pkey).unwrap();
1337 }
1338
1339 #[test]
1340 fn test_rsa_signing_bytes_compressed() {
1341 let (skey, _headers) = SignedSecretKey::from_armor_single(
1342 fs::File::open("./tests/openpgp-interop/testcases/messages/gnupg-v1-001-decrypt.asc")
1343 .unwrap(),
1344 )
1345 .unwrap();
1346
1347 let pkey = skey.public_key();
1348 let mut rng = ChaCha8Rng::seed_from_u64(0);
1349
1350 let lit_msg = Message::new_literal_bytes("hello.txt", &b"hello world\n"[..]);
1351 let signed_msg = lit_msg
1352 .sign(&mut rng, &skey, || "test".into(), HashAlgorithm::SHA2_256)
1353 .unwrap();
1354
1355 let compressed_msg = signed_msg.compress(CompressionAlgorithm::ZLIB).unwrap();
1356 let armored = compressed_msg.to_armored_bytes(None.into()).unwrap();
1357 signed_msg.verify(&pkey).unwrap();
1360
1361 let parsed = Message::from_armor_single(&armored[..]).unwrap().0;
1362 parsed.verify(&pkey).unwrap();
1363 }
1364
1365 #[test]
1366 fn test_text_signature_normalization() {
1367 let (signed_msg, _header) = Message::from_armor_single(
1379 fs::File::open("./tests/unit-tests/text_signature_normalization.msg").unwrap(),
1380 )
1381 .unwrap();
1382
1383 let (skey, _headers) = SignedSecretKey::from_armor_single(
1384 fs::File::open("./tests/unit-tests/text_signature_normalization_alice.key").unwrap(),
1385 )
1386 .unwrap();
1387
1388 let signing = skey
1390 .secret_subkeys
1391 .iter()
1392 .find(|key| {
1393 key.key_id()
1394 == KeyId::from_slice(&[0x64, 0x35, 0x7E, 0xB6, 0xBB, 0x55, 0xDE, 0x12]).unwrap()
1395 })
1396 .unwrap();
1397
1398 let verify = signing.public_key();
1400
1401 signed_msg.verify(&verify).expect("signature seems bad");
1403 }
1404
1405 #[test]
1409 fn test_compression_quine() {
1410 let (skey, _headers) = SignedSecretKey::from_armor_single(
1412 fs::File::open("./tests/autocrypt/alice@autocrypt.example.sec.asc").unwrap(),
1413 )
1414 .unwrap();
1415 let pkey = skey.public_key();
1416
1417 let msg = Message::from_bytes(&include_bytes!("../../../tests/quine.out")[..]).unwrap();
1418 assert!(msg.get_content().is_err());
1419 assert!(msg.verify(&pkey).is_err());
1420 }
1421
1422 const ANNEX_A_3: &str = "-----BEGIN PGP PUBLIC KEY BLOCK-----
1425
1426xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
1427GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
1428KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
1429gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
1430QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
1431+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
1432BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
1433j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
1434I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
1435-----END PGP PUBLIC KEY BLOCK-----";
1436
1437 const ANNEX_A_4: &str = "-----BEGIN PGP PRIVATE KEY BLOCK-----
1440
1441xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
1442exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
1443BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
14442azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
1445RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
14467XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
1447LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
1448GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
14492azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
1450M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
1451k0mXubZvyl4GBg==
1452-----END PGP PRIVATE KEY BLOCK-----";
1453
1454 #[test]
1459 fn test_v6_annex_a_6() {
1460 let (ssk, _) = SignedPublicKey::from_string(ANNEX_A_3).expect("SSK from armor");
1461
1462 let msg = "-----BEGIN PGP SIGNED MESSAGE-----
1463
1464What we need from the grocery store:
1465
1466- - tofu
1467- - vegetables
1468- - noodles
1469
1470-----BEGIN PGP SIGNATURE-----
1471
1472wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
14732azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo
1474/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr
1475NK2ay45cX1IVAQ==
1476-----END PGP SIGNATURE-----";
1477
1478 let (msg, _) = CleartextSignedMessage::from_string(msg).unwrap();
1479
1480 msg.verify(&ssk).expect("verify");
1481 }
1482
1483 #[test]
1488 fn test_v6_annex_a_7() {
1489 let (ssk, _) = SignedPublicKey::from_string(ANNEX_A_3).expect("SSK from armor");
1490
1491 let msg = "-----BEGIN PGP MESSAGE-----
1492
1493xEYGAQobIHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usyxhsTwYJppfk
14941S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkBy0p1AAAAAABXaGF0IHdlIG5lZWQgZnJv
1495bSB0aGUgZ3JvY2VyeSBzdG9yZToKCi0gdG9mdQotIHZlZ2V0YWJsZXMKLSBub29k
1496bGVzCsKYBgEbCgAAACkFgmOYo2MiIQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9l
1497JewnutmsyQAAAABpNiB2SV9QIYiQ9/Xi7jwYIlFPcFAPVR2G5ckh5ATjSlP7rCfQ
1498b7gKqPxbyxbhljGygHQPnqau1eBzrQD5QVplPEDnemrnfmkrpx0GmhCfokxYz9jj
1499FtCgazStmsuOXF9SFQE=
1500-----END PGP MESSAGE-----";
1501
1502 let (msg, _) = Message::from_string(msg).unwrap();
1503
1504 msg.verify(&ssk).expect("verify");
1505 }
1506
1507 #[test]
1512 fn test_v6_annex_a_8() {
1513 let (ssk, _) = SignedSecretKey::from_string(ANNEX_A_4).expect("SSK from armor");
1514
1515 let msg = "-----BEGIN PGP MESSAGE-----
1517
1518wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
1519WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
1520aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
1521yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
1522bhF30A+IitsxxA==
1523-----END PGP MESSAGE-----";
1524
1525 let (message, _) = Message::from_string(msg).expect("ok");
1526 let (dec, _) = message.decrypt(String::default, &[&ssk]).expect("decrypt");
1527
1528 let decrypted =
1529 String::from_utf8(dec.get_literal().expect("literal").data().to_vec()).expect("utf8");
1530
1531 assert_eq!(&decrypted, "Hello, world!");
1532 }
1533}