sequoia_openpgp/
lib.rs

1//! OpenPGP data types and associated machinery.
2//!
3//! This crate aims to provide a complete implementation of OpenPGP as
4//! defined by [RFC 9580] as well as the deprecated OpenPGP as defined
5//! by [RFC 4880].  OpenPGP is a standard by the IETF.  It was derived
6//! from the PGP software, which was created by Phil Zimmermann in
7//! 1991.
8//!
9//! This crate also includes support for unbuffered message
10//! processing.
11//!
12//! A few features that the OpenPGP community considers to be
13//! deprecated (e.g., version 3 compatibility) have been left out.  We
14//! have also updated some OpenPGP defaults to avoid foot guns (e.g.,
15//! we selected modern algorithm defaults).  If some functionality is
16//! missing, please file a bug report.
17//!
18//! A non-goal of this crate is support for any sort of high-level,
19//! bolted-on functionality.  For instance, [RFC 9580] does not define
20//! trust models, such as the web of trust, direct trust, or TOFU.
21//! Neither does this crate.  [RFC 9580] does provide some mechanisms
22//! for creating trust models (specifically, UserID certifications),
23//! and this crate does expose those mechanisms.
24//!
25//! We also try hard to avoid dictating how OpenPGP should be used.
26//! This doesn't mean that we don't have opinions about how OpenPGP
27//! should be used in a number of common scenarios (for instance,
28//! message validation).  But, in this crate, we refrain from
29//! expressing those opinions; we will expose an opinionated,
30//! high-level interface in the future.  In order to figure out the
31//! most appropriate high-level interfaces, we look at existing users.
32//! If you are using Sequoia, please get in contact so that we can
33//! learn from your use cases, discuss your opinions, and develop a
34//! high-level interface based on these experiences in the future.
35//!
36//! Despite —or maybe because of— its unopinionated nature we found
37//! it easy to develop opinionated OpenPGP software based on Sequoia.
38//!
39//! [RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html
40//! [RFC 4880]: https://tools.ietf.org/html/rfc4880
41//!
42//! # Experimental Features
43//!
44//! This crate may implement extensions where the standardization
45//! effort is still ongoing.  These experimental features are marked
46//! as such in the documentation.  We invite you to experiment with
47//! them, but please do expect the semantics and possibly even the
48//! wire format to evolve.
49
50#![doc(html_favicon_url = "https://docs.sequoia-pgp.org/favicon.png")]
51#![doc(html_logo_url = "https://docs.sequoia-pgp.org/logo.svg")]
52#![warn(missing_docs)]
53
54// Public re-exports.
55//
56// We should provide public re-exports for any crate defining types
57// that we use in our public API.  This allows downstream consumers to
58// name the types without explicitly depending on the third-party
59// crates, and provides the correct version of the crates.
60pub use anyhow;
61
62#[cfg(test)]
63#[macro_use]
64extern crate quickcheck;
65
66#[macro_use]
67mod macros;
68
69// On debug builds, Vec<u8>::truncate is very, very slow.  For
70// instance, running the decrypt_test_stream test takes 51 seconds on
71// my (Neal's) computer using Vec<u8>::truncate and <0.1 seconds using
72// `unsafe { v.set_len(len); }`.
73//
74// The issue is that the compiler calls drop on every element that is
75// dropped, even though a u8 doesn't have a drop implementation.  The
76// compiler optimizes this away at high optimization levels, but those
77// levels make debugging harder.
78fn vec_truncate(v: &mut Vec<u8>, len: usize) {
79    if cfg!(debug_assertions) {
80        if len < v.len() {
81            unsafe { v.set_len(len); }
82        }
83    } else {
84        v.truncate(len);
85    }
86}
87
88/// Like `Vec<u8>::resize`, but fast in debug builds.
89fn vec_resize(v: &mut Vec<u8>, new_size: usize) {
90    if v.len() < new_size {
91        v.resize(new_size, 0);
92    } else {
93        vec_truncate(v, new_size);
94    }
95}
96
97/// Like `drop(Vec<u8>::drain(..prefix_len))`, but fast in debug
98/// builds.
99fn vec_drain_prefix(v: &mut Vec<u8>, prefix_len: usize) {
100    if cfg!(debug_assertions) {
101        // Panic like v.drain(..prefix_len).
102        assert!(prefix_len <= v.len(), "prefix len {} > vector len {}",
103                prefix_len, v.len());
104        let new_len = v.len() - prefix_len;
105        unsafe {
106            std::ptr::copy(v[prefix_len..].as_ptr(),
107                           v[..].as_mut_ptr(),
108                           new_len);
109        }
110        vec_truncate(v, new_len);
111    } else {
112        v.drain(..prefix_len);
113    }
114}
115
116/// Like std::time::SystemTime::now, but works on WASM.
117fn now() -> std::time::SystemTime {
118    #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
119        chrono::Utc::now().into()
120    }
121    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
122        std::time::SystemTime::now()
123    }
124}
125
126// Like assert!, but checks a pattern.
127//
128//   assert_match!(Some(_) = x);
129//
130// Note: For modules to see this macro, we need to define it before we
131// declare the modules.
132#[allow(unused_macros)]
133macro_rules! assert_match {
134    ( $error: pat = $expr:expr, $fmt:expr, $($pargs:expr),* ) => {{
135        let x = $expr;
136        if let $error = x {
137            /* Pass.  */
138        } else {
139            let extra = format!($fmt, $($pargs),*);
140            panic!("Expected {}, got {:?}{}{}",
141                   stringify!($error), x,
142                   if $fmt.len() > 0 { ": " } else { "." }, extra);
143        }
144    }};
145    ( $error: pat = $expr: expr, $fmt:expr ) => {
146        assert_match!($error = $expr, $fmt, )
147    };
148    ( $error: pat = $expr: expr ) => {
149        assert_match!($error = $expr, "")
150    };
151}
152
153#[macro_use]
154pub mod armor;
155pub mod fmt;
156pub mod crypto;
157
158pub mod packet;
159#[doc(inline)]
160pub use packet::Packet;
161use crate::packet::key;
162
163pub mod parse;
164
165pub mod cert;
166#[doc(inline)]
167pub use cert::Cert;
168pub mod serialize;
169
170mod packet_pile;
171pub use packet_pile::PacketPile;
172pub mod message;
173#[doc(inline)]
174pub use message::Message;
175
176pub mod types;
177use crate::types::{
178    PublicKeyAlgorithm,
179    SymmetricAlgorithm,
180    HashAlgorithm,
181    SignatureType,
182};
183
184mod fingerprint;
185pub use fingerprint::Fingerprint;
186mod keyid;
187pub use keyid::KeyID;
188mod keyhandle;
189pub use keyhandle::KeyHandle;
190pub mod regex;
191pub mod policy;
192
193pub(crate) mod seal;
194pub(crate) mod utils;
195
196#[cfg(test)]
197mod tests;
198
199/// Returns a timestamp for the tests.
200///
201/// The time is chosen to that the subkeys in
202/// openpgp/tests/data/keys/neal.pgp are not expired.
203#[cfg(test)]
204fn frozen_time() -> std::time::SystemTime {
205    crate::types::Timestamp::from(1554542220 - 1).into()
206}
207
208/// The version of this crate.
209pub const VERSION: &str = env!("CARGO_PKG_VERSION");
210
211/// Profiles select versions of the OpenPGP standard.
212///
213/// While this type implements [`Default`], please consider what a
214/// good default for your use case is.
215///
216/// If you are doing a greenfield implementation where you know that
217/// every client understands RFC9580, you can just explicitly pick
218/// that.
219///
220/// Otherwise, you have to consider the state of the ecosystem your
221/// client will interact with.  Maybe it is better to stick to
222/// generating RFC4880 certificates for the time being, while rolling
223/// out RFC9580 support.  Consider adding a configuration option or
224/// command line switch like `--profile`, but pick a sensible default,
225/// and remember to don't overwhelm your users.
226///
227/// For now, our default is RFC4880.  This is a safe default for every
228/// downstream consumer that has existing legacy deployments
229/// (including their own previous versions using a legacy version of
230/// Sequoia).  We will update this default once RFC9580 is more widely
231/// deployed.
232#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
233#[non_exhaustive]
234pub enum Profile {
235    /// RFC9580, published in 2024, defines "v6" OpenPGP.
236    RFC9580,
237
238    /// RFC4880, published in 2007, defines "v4" OpenPGP.
239    #[default]
240    RFC4880,
241}
242
243/// Crate result specialization.
244pub type Result<T> = ::std::result::Result<T, anyhow::Error>;
245
246/// Errors used in this crate.
247#[non_exhaustive]
248#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
249pub enum Error {
250    /// Invalid argument.
251    #[error("Invalid argument: {0}")]
252    InvalidArgument(String),
253
254    /// Invalid operation.
255    #[error("Invalid operation: {0}")]
256    InvalidOperation(String),
257
258    /// A malformed packet.
259    #[error("Malformed packet: {0}")]
260    MalformedPacket(String),
261
262    /// Packet size exceeds the configured limit.
263    #[error("{} Packet ({} bytes) exceeds limit of {} bytes",
264           _0, _1, _2)]
265    PacketTooLarge(packet::Tag, u32, u32),
266
267    /// Unsupported packet type.
268    #[error("Unsupported packet type.  Tag: {0}")]
269    UnsupportedPacketType(packet::Tag),
270
271    /// Unsupported hash algorithm identifier.
272    #[error("Unsupported hash algorithm: {0}")]
273    UnsupportedHashAlgorithm(HashAlgorithm),
274
275    /// Unsupported public key algorithm identifier.
276    #[error("Unsupported public key algorithm: {0}")]
277    UnsupportedPublicKeyAlgorithm(PublicKeyAlgorithm),
278
279    /// Unsupported elliptic curve ASN.1 OID.
280    #[error("Unsupported elliptic curve: {0}")]
281    UnsupportedEllipticCurve(types::Curve),
282
283    /// Unsupported symmetric key algorithm.
284    #[error("Unsupported symmetric algorithm: {0}")]
285    UnsupportedSymmetricAlgorithm(SymmetricAlgorithm),
286
287    /// Unsupported AEAD algorithm.
288    #[error("Unsupported AEAD algorithm: {0}")]
289    UnsupportedAEADAlgorithm(types::AEADAlgorithm),
290
291    /// Unsupported Compression algorithm.
292    #[error("Unsupported Compression algorithm: {0}")]
293    UnsupportedCompressionAlgorithm(types::CompressionAlgorithm),
294
295    /// Unsupported signature type.
296    #[error("Unsupported signature type: {0}")]
297    UnsupportedSignatureType(SignatureType),
298
299    /// Invalid password.
300    #[error("Invalid password")]
301    InvalidPassword,
302
303    /// Invalid session key.
304    #[error("Invalid session key: {0}")]
305    InvalidSessionKey(String),
306
307    /// Missing session key.
308    #[error("Missing session key: {0}")]
309    MissingSessionKey(String),
310
311    /// Malformed MPI.
312    #[error("Malformed MPI: {0}")]
313    MalformedMPI(String),
314
315    /// Bad signature.
316    #[error("Bad signature: {0}")]
317    BadSignature(String),
318
319    /// Message has been manipulated.
320    #[error("Message has been manipulated")]
321    ManipulatedMessage,
322
323    /// Malformed message.
324    #[error("Malformed Message: {0}")]
325    MalformedMessage(String),
326
327    /// Malformed certificate.
328    #[error("Malformed Cert: {0}")]
329    MalformedCert(String),
330
331    /// Unsupported Cert.
332    ///
333    /// This usually occurs, because the primary key is in an
334    /// unsupported format.  In particular, Sequoia does not support
335    /// version 3 keys.
336    #[error("Unsupported Cert: {0}")]
337    UnsupportedCert(String, Vec<Packet>),
338
339    /// Index out of range.
340    #[error("Index out of range")]
341    IndexOutOfRange,
342
343    /// Expired.
344    #[error("Expired on {}", crate::fmt::time(.0))]
345    Expired(std::time::SystemTime),
346
347    /// Not yet live.
348    #[error("Not live until {}", crate::fmt::time(.0))]
349    NotYetLive(std::time::SystemTime),
350
351    /// No binding signature.
352    #[error("No binding signature at time {}", crate::fmt::time(.0))]
353    NoBindingSignature(std::time::SystemTime),
354
355    /// Invalid key.
356    #[error("Invalid key: {0:?}")]
357    InvalidKey(String),
358
359    /// No hash algorithm found that would be accepted by all signers.
360    #[error("No acceptable hash")]
361    NoAcceptableHash,
362
363    /// The operation is not allowed, because it violates the policy.
364    ///
365    /// The optional time is the time at which the operation was
366    /// determined to no longer be secure.
367    #[error("{} is not considered secure{}",
368            .0,
369            .1.as_ref().map(|t| {
370                if *t == std::time::UNIX_EPOCH {
371                    "".to_string()
372                } else {
373                    format!(" since {}", crate::fmt::time(t))
374                }
375            })
376            .unwrap_or_else(|| "".into()))]
377    PolicyViolation(String, Option<std::time::SystemTime>),
378
379    /// Short key IDs are insecure, and not supported.
380    #[error("Short key IDs are insecure, and not supported: {0}")]
381    ShortKeyID(String),
382}
383
384assert_send_and_sync!(Error);
385
386/// Provide a helper function that generates an arbitrary value from a given
387/// range.  Quickcheck > 1 does not re-export rand so we need to implement this
388/// ourselves.
389#[cfg(test)]
390mod arbitrary_helper {
391    use quickcheck::{Arbitrary, Gen};
392
393    pub(crate) fn gen_arbitrary_from_range<T>(
394        range: std::ops::Range<T>,
395        g: &mut Gen,
396    ) -> T
397    where
398        T: Arbitrary
399            + std::cmp::PartialOrd
400            + std::ops::Sub<Output = T>
401            + std::ops::Rem<Output = T>
402            + std::ops::Add<Output = T>
403            + Copy,
404    {
405        if !range.is_empty() {
406            let m = range.end - range.start;
407            // The % operator calculates the remainder, which is negative for
408            // negative inputs, not the modulus.  This actually calculates the
409            // modulus by making sure the result is positive.  The primitive
410            // integer types implement .rem_euclid for that, but there is no way
411            // to constrain this function to primitive types.
412            range.start + (T::arbitrary(g) % m + m) % m
413        } else {
414            panic!()
415        }
416    }
417
418    pub(crate) fn arbitrary_slice<T>(g: &mut Gen, s: &mut [T])
419    where
420        T: Arbitrary,
421    {
422        s.iter_mut().for_each(|p| *p = Arbitrary::arbitrary(g));
423    }
424
425    pub(crate) fn arbitrary_bounded_vec<T>(g: &mut Gen, limit: usize) -> Vec<T>
426    where
427        T: Arbitrary + Default,
428    {
429        let mut v = vec![Default::default();
430                         gen_arbitrary_from_range(0..limit, g)];
431        arbitrary_slice(g, &mut v[..]);
432        v
433    }
434}