1 //! AEAD encrypted data packets.
2 //!
3 //! An encryption container using [Authenticated Encryption with
4 //! Additional Data].
5 //!
6 //! The AED packet is a new packet specified in [Section 5.16 of RFC
7 //! 4880bis].  Its aim is to replace the [SEIP packet], whose security
8 //! has been partially compromised.  SEIP's weaknesses includes its
9 //! use of CFB mode (e.g., EFAIL-style CFB gadgets, see Section 5.3 of
10 //! the [EFAIL paper]), its use of [SHA-1] for integrity protection, and
11 //! the ability to [downgrade SEIP packets] to much weaker SED
12 //! packets.
13 //!
14 //! Although the decision to use AEAD is uncontroversial, the design
15 //! specified in RFC 4880bis is.  According to [RFC 5116], decrypted
16 //! AEAD data can only be released for processing after its
17 //! authenticity has been checked:
18 //!
19 //! > [The authenticated decryption operation] has only a single
20 //! > output, either a plaintext value P or a special symbol FAIL that
21 //! > indicates that the inputs are not authentic.
22 //!
23 //! The controversy has to do with streaming, which OpenPGP has
24 //! traditionally supported.  Streaming a message means that the
25 //! amount of data that needs to be buffered when processing a message
26 //! is independent of the message's length.
27 //!
28 //! At first glance, the AEAD mechanism in RFC 4880bis appears to
29 //! support this mode of operation: instead of encrypting the whole
30 //! message using AEAD, which would require buffering all of the
31 //! plaintext when decrypting the message, the message is chunked, the
32 //! individual chunks are linked together, and AEAD is used to encrypt
33 //! and protect each individual chunk.  Because the plaintext from an
34 //! individual chunk can be integrity checked, an implementation only
35 //! needs to buffer a chunk worth of data.
36 //!
37 //! Unfortunately, RFC 4880bis allows chunk sizes that are, in
38 //! practice, unbounded.  Specifically, a chunk can be up to 4
39 //! exbibytes in size.  Thus when encountering messages that can't be
40 //! buffered, an OpenPGP implementation has a choice: it can either
41 //! release data that has not been integrity checked and violate RFC
42 //! 5116, or it can fail to process the message.  As of 2020, [GnuPG]
43 //! and [RNP] process unauthenticated plaintext.  From a user
44 //! perspective, it then appears that implementations that choose to
45 //! follow RFC 5116 are impaired: "GnuPG can decrypt it," they think,
46 //! "why can't Sequoia?"  This creates pressure on other
47 //! implementations to also behave insecurely.
48 //!
49 //! [Werner argues] that AEAD is not about authenticating the data.
50 //! That is the purpose of the signature.  The reason to introduce
51 //! AEAD is to get the benefits of more modern cryptography, and to be
52 //! able to more quickly detect rare transmission errors.  Our
53 //! position is that an integrity check provides real protection: it
54 //! can detect modified ciphertext.  And, if we are going to stream,
55 //! then this protection is essential as it protects the user from
56 //! real, demonstrated attacks like [EFAIL].
57 //!
58 //! RFC 4880bis has not been finalized.  So, it is still possible that
59 //! the AEAD mechanism will change (which is why the AED packet is
60 //! marked as experimental).  Despite our concerns, because other
61 //! OpenPGP implementations already emit the AEAD packet, we provide
62 //! *experimental* support for it in Sequoia.
63 //!
64 //! [Authenticated Encryption with Additional Data]: https://en.wikipedia.org/wiki/Authenticated_encryption
65 //! [Section 5.16 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-5.16
66 //! [EFAIL paper]: https://www.usenix.org/conference/usenixsecurity18/presentation/poddebniak
67 //! [SHA-1]: https://sha-mbles.github.io/
68 //! [SEIP packet]: https://tools.ietf.org/html/rfc4880#section-5.13
69 //! [RFC 5116]: https://tools.ietf.org/html/rfc5116#section-2.2
70 //! [downgrade SEIP packets]: https://mailarchive.ietf.org/arch/msg/openpgp/JLn7sL6TqikUf-cD34lN7kof7_A/
71 //! [GnuPG]: https://mailarchive.ietf.org/arch/msg/openpgp/fmQgRm94jhvPLEOi0J-o7A8LpkY/
72 //! [RNP]: https://github.com/rnpgp/rnp/issues/807
73 //! [Werner argues]: https://mailarchive.ietf.org/arch/msg/openpgp/J428Mqq3-pHTU4C76EgP5sPkvtA
74 //! [EFAIL]: https://efail.de/
75 
76 use crate::types::{
77     AEADAlgorithm,
78     SymmetricAlgorithm,
79 };
80 use crate::packet;
81 use crate::Packet;
82 use crate::Error;
83 use crate::Result;
84 
85 /// Holds an AEAD encrypted data packet.
86 ///
87 /// An AEAD encrypted data packet holds encrypted data.  The data
88 /// contains additional OpenPGP packets.  See [Section 5.16 of RFC
89 /// 4880bis] for details.
90 ///
91 /// An AED packet is not normally instantiated directly.  In most
92 /// cases, you'll create one as a side-effect of encrypting a message
93 /// using the [streaming serializer], or parsing an encrypted message
94 /// using the [`PacketParser`].
95 ///
96 /// This feature is
97 /// [experimental](../../index.html#experimental-features).  It has
98 /// not been standardized and we advise users to not emit AED packets.
99 ///
100 /// [Section 5.16 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-05#section-5.16
101 /// [streaming serializer]: ../serialize/stream/index.html
102 /// [`PacketParser`]: ../parse/index.html
103 ///
104 /// # A note on equality
105 ///
106 /// An unprocessed (encrypted) `AED` packet is never considered equal
107 /// to a processed (decrypted) one.  Likewise, a processed (decrypted)
108 /// packet is never considered equal to a structured (parsed) one.
109 // IMPORTANT: If you add fields to this struct, you need to explicitly
110 // IMPORTANT: implement PartialEq, Eq, and Hash.
111 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
112 pub struct AED1 {
113     /// CTB packet header fields.
114     pub(crate) common: packet::Common,
115     /// Symmetric algorithm.
116     sym_algo: SymmetricAlgorithm,
117     /// AEAD algorithm.
118     aead: AEADAlgorithm,
119     /// Chunk size.
120     chunk_size: u64,
121     /// Initialization vector for the AEAD algorithm.
122     iv: Box<[u8]>,
123 
124     /// This is a container packet.
125     container: packet::Container,
126 }
127 
128 impl std::ops::Deref for AED1 {
129     type Target = packet::Container;
deref(&self) -> &Self::Target130     fn deref(&self) -> &Self::Target {
131         &self.container
132     }
133 }
134 
135 impl std::ops::DerefMut for AED1 {
deref_mut(&mut self) -> &mut Self::Target136     fn deref_mut(&mut self) -> &mut Self::Target {
137         &mut self.container
138     }
139 }
140 
141 impl AED1 {
142     /// Creates a new AED1 object.
new(sym_algo: SymmetricAlgorithm, aead: AEADAlgorithm, chunk_size: u64, iv: Box<[u8]>) -> Result<Self>143     pub fn new(sym_algo: SymmetricAlgorithm,
144                aead: AEADAlgorithm,
145                chunk_size: u64,
146                iv: Box<[u8]>) -> Result<Self> {
147         if chunk_size.count_ones() != 1 {
148             return Err(Error::InvalidArgument(
149                 format!("chunk size is not a power of two: {}", chunk_size))
150                 .into());
151         }
152 
153         if chunk_size < 64 {
154             return Err(Error::InvalidArgument(
155                 format!("chunk size is too small: {}", chunk_size))
156                 .into());
157         }
158 
159         Ok(AED1 {
160             common: Default::default(),
161             sym_algo,
162             aead,
163             chunk_size,
164             iv,
165             container: Default::default(),
166         })
167     }
168 
169     /// Gets the symmetric algorithm.
symmetric_algo(&self) -> SymmetricAlgorithm170     pub fn symmetric_algo(&self) -> SymmetricAlgorithm {
171         self.sym_algo
172     }
173 
174     /// Sets the symmetric algorithm.
set_symmetric_algo(&mut self, sym_algo: SymmetricAlgorithm) -> SymmetricAlgorithm175     pub fn set_symmetric_algo(&mut self, sym_algo: SymmetricAlgorithm)
176                               -> SymmetricAlgorithm {
177         ::std::mem::replace(&mut self.sym_algo, sym_algo)
178     }
179 
180     /// Gets the AEAD algorithm.
aead(&self) -> AEADAlgorithm181     pub fn aead(&self) -> AEADAlgorithm {
182         self.aead
183     }
184 
185     /// Sets the AEAD algorithm.
set_aead(&mut self, aead: AEADAlgorithm) -> AEADAlgorithm186     pub fn set_aead(&mut self, aead: AEADAlgorithm) -> AEADAlgorithm {
187         ::std::mem::replace(&mut self.aead, aead)
188     }
189 
190     /// Gets the chunk size.
chunk_size(&self) -> u64191     pub fn chunk_size(&self) -> u64 {
192         self.chunk_size
193     }
194 
195     /// Sets the chunk size.
set_chunk_size(&mut self, chunk_size: u64) -> Result<()>196     pub fn set_chunk_size(&mut self, chunk_size: u64) -> Result<()> {
197         if chunk_size.count_ones() != 1 {
198             return Err(Error::InvalidArgument(
199                 format!("chunk size is not a power of two: {}", chunk_size))
200                 .into());
201         }
202 
203         if chunk_size < 64 {
204             return Err(Error::InvalidArgument(
205                 format!("chunk size is too small: {}", chunk_size))
206                 .into());
207         }
208 
209         self.chunk_size = chunk_size;
210         Ok(())
211     }
212 
213     /// Gets the size of a chunk with a digest.
chunk_digest_size(&self) -> Result<u64>214     pub fn chunk_digest_size(&self) -> Result<u64> {
215         Ok(self.chunk_size + self.aead.digest_size()? as u64)
216     }
217 
218     /// Gets the initialization vector for the AEAD algorithm.
iv(&self) -> &[u8]219     pub fn iv(&self) -> &[u8] {
220         &self.iv
221     }
222 
223     /// Sets the initialization vector for the AEAD algorithm.
set_iv(&mut self, iv: Box<[u8]>) -> Box<[u8]>224     pub fn set_iv(&mut self, iv: Box<[u8]>) -> Box<[u8]> {
225         ::std::mem::replace(&mut self.iv, iv)
226     }
227 }
228 
229 impl From<AED1> for Packet {
from(p: AED1) -> Self230     fn from(p: AED1) -> Self {
231         super::AED::from(p).into()
232     }
233 }
234 
235 impl From<AED1> for super::AED {
from(p: AED1) -> Self236     fn from(p: AED1) -> Self {
237         super::AED::V1(p)
238     }
239 }
240