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}