sop/
lib.rs

1//! A Rust implementation of the Stateless OpenPGP Interface.
2//!
3//! This crate defines an interface that is the Rust equivalent of the
4//! [draft 08] of the Stateless OpenPGP Command Line Interface.  Note
5//! that you need an concrete implementation of this interface (such
6//! as [`sequoia-sop`]) in order to use it.
7//!
8//!   [draft 08]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html
9//!   [`sequoia-sop`]: https://docs.rs/sequoia-sop
10//!
11//! # Examples
12//!
13//! Given a reference to a [`SOP`] implementation, which is the main
14//! entry point for every SOP operation, generate keys, extract certs,
15//! sign, verify, encrypt, and decrypt:
16//!
17//! ```rust
18//! # use sop::*; use std::io::Cursor;
19//! # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
20//! let alice_sec = sop.generate_key()?
21//!     .userid("Alice Lovelace <alice@openpgp.example>")
22//!     .generate()?;
23//! let alice_pgp = sop.extract_cert()?
24//!     .keys(&alice_sec)?;
25//!
26//! let bob_sec = sop.generate_key()?
27//!     .userid("Bob Babbage <bob@openpgp.example>")
28//!     .generate()?;
29//! let bob_pgp = sop.extract_cert()?
30//!     .keys(&bob_sec)?;
31//!
32//! let statement = b"Hello World :)";
33//! let mut data = Cursor::new(&statement);
34//! let (_micalg, signature) = sop.sign()?
35//!     .mode(ops::SignAs::Text)
36//!     .keys(&alice_sec)?
37//!     .data(&mut data)?;
38//!
39//! let verifications = sop.verify()?
40//!     .certs(&alice_pgp)?
41//!     .signatures(&signature)?
42//!     .data(&mut Cursor::new(&statement))?;
43//! assert_eq!(verifications.len(), 1);
44//!
45//! let mut statement_cur = Cursor::new(&statement);
46//! let (_session_key, ciphertext) = sop.encrypt()?
47//!     .sign_with_keys(&alice_sec)?
48//!     .with_certs(&bob_pgp)?
49//!     .plaintext(&mut statement_cur)?
50//!     .to_vec()?;
51//!
52//! let mut ciphertext_cur = Cursor::new(&ciphertext);
53//! let (_, plaintext) = sop.decrypt()?
54//!     .with_keys(&bob_sec)?
55//!     .ciphertext(&mut ciphertext_cur)?
56//!     .to_vec()?;
57//! assert_eq!(&plaintext, statement);
58//! # Ok(()) }
59//! ```
60//!
61//! The above snippet is the equivalent of the following SOP command
62//! line example from the SOP spec:
63//!
64//! ```sh
65//! $ sop generate-key "Alice Lovelace <alice@openpgp.example>" > alice.sec
66//! $ sop extract-cert < alice.sec > alice.pgp
67//!
68//! $ sop sign --as=text alice.sec < statement.txt > statement.txt.asc
69//! $ sop verify announcement.txt.asc alice.pgp < announcement.txt
70//!
71//! $ sop encrypt --sign-with=alice.sec bob.pgp < msg.eml > encrypted.asc
72//! $ sop decrypt alice.sec < ciphertext.asc > cleartext.out
73//! ```
74
75use std::{
76    fmt,
77    io,
78};
79
80pub mod ops;
81use ops::*;
82pub mod errors;
83use errors::*;
84pub mod plumbing;
85
86#[cfg(any(feature = "cli", feature = "cliv"))]
87pub mod cli;
88
89/// Loads objects like certs and keys.
90pub trait Load<'s, S: SOP<'s>> {
91    /// Loads objects like certs and keys from stdin.
92    fn from_stdin(sop: &'s S) -> Result<Self>
93    where
94        Self: Sized,
95    {
96        Self::from_reader(sop, &mut io::stdin(), Some("/dev/stdin".into()))
97    }
98
99    /// Loads objects like certs and keys from the given file.
100    fn from_file<P>(sop: &'s S, path: P) -> Result<Self>
101    where
102        Self: Sized,
103        P: AsRef<std::path::Path>,
104    {
105        let path = path.as_ref();
106        Self::from_reader(sop, &mut std::fs::File::open(path)?,
107                          Some(path.display().to_string()))
108    }
109
110    /// Loads objects like certs and keys from the given reader.
111    fn from_reader(sop: &'s S, source: &mut (dyn io::Read + Send + Sync),
112                   source_name: Option<String>)
113                   -> Result<Self>
114    where Self: Sized;
115
116    /// Loads objects like certs and keys from the given byte slice.
117    fn from_bytes(sop: &'s S, source: &[u8]) -> Result<Self>
118    where
119        Self: Sized,
120    {
121        Self::from_reader(sop, &mut io::Cursor::new(source), None)
122    }
123
124    /// Returns the source name, if any.
125    fn source_name(&self) -> Option<&str>;
126}
127
128/// Saves objects like certs and keys.
129pub trait Save {
130    /// Writes objects like certs and keys to stdout.
131    fn to_stdout(&self, armored: bool) -> Result<()> {
132        self.to_writer(armored, &mut io::stdout())
133    }
134
135    /// Saves objects like certs and keys, writing them to the given writer.
136    fn to_writer(&self, armored: bool, sink: &mut (dyn io::Write + Send + Sync))
137                 -> Result<()>;
138
139    /// Saves objects like certs and keys, writing them to a vector.
140    fn to_vec(&self, armored: bool) -> Result<Vec<u8>> {
141        let mut sink = vec![];
142        self.to_writer(armored, &mut sink)?;
143        Ok(sink)
144    }
145}
146
147/// Main entry point to the Stateless OpenPGP Interface.
148pub trait SOP<'s>: Sized {
149    /// Secret keys.
150    type Keys: Load<'s, Self> + Save + plumbing::SopRef<'s, Self>;
151
152    /// Public keys.
153    type Certs: Load<'s, Self> + Save + plumbing::SopRef<'s, Self>;
154
155    /// Signatures.
156    type Sigs: Load<'s, Self> + Save + plumbing::SopRef<'s, Self>;
157
158    /// Controls debugging.
159    ///
160    /// If enabled, implementations may emit debugging information in
161    /// any way they see fit, most commonly by printing to stderr.
162    fn debug(&mut self, enable: bool) {
163        let _ = enable;
164    }
165
166    /// Gets SOP version information.
167    ///
168    /// The default implementation returns the version of the spec
169    /// that this framework supports.  This should be fine for most
170    /// implementations.  However, implementations may chose to
171    /// override this function to return a more nuanced response.
172    fn spec_version(&'s self) -> &'static str {
173        "~draft-dkg-openpgp-stateless-cli-11"
174    }
175
176    /// Completeness of the sopv subset
177    ///
178    /// If all the features of the sopv subset are implemented, an
179    /// implementation should return Ok("1.0")
180    fn sopv_version(&'s self) -> Result<&'static str> {
181        Err(Error::UnsupportedOption)
182    }
183
184    /// Gets version information.
185    ///
186    /// # Examples
187    ///
188    /// ```rust
189    /// # use sop::*; use std::io::Cursor;
190    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
191    /// // Prints the name of the SOP implementation.
192    /// println!("{}", sop.version()?.frontend()?);
193    ///
194    /// // Prints the name of the underlying OpenPGP implementation.
195    /// println!("{}", sop.version()?.backend()?);
196    ///
197    /// // Prints extended version information.
198    /// println!("{}", sop.version()?.extended()?);
199    /// # Ok(()) }
200    /// ```
201    fn version(&self) -> Result<Box<dyn Version>>;
202
203    /// Generates a Secret Key.
204    ///
205    /// Customize the operation using the builder [`GenerateKey`].
206    ///
207    /// # Examples
208    ///
209    /// ```rust
210    /// # use sop::*; use std::io::Cursor;
211    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
212    /// let alice_sec = sop.generate_key()?
213    ///     .userid("Alice Lovelace <alice@openpgp.example>")
214    ///     .generate()?;
215    /// # Ok(()) }
216    /// ```
217    fn generate_key(&'s self)
218                    -> Result<Box<dyn GenerateKey<Self, Self::Keys> + 's>>;
219
220    /// Updates a key's password.
221    ///
222    /// Customize the operation using the builder [`ChangeKeyPassword`].
223    ///
224    /// # Examples
225    ///
226    /// ```rust
227    /// # use sop::*; use std::io::Cursor; use std::fs::File;
228    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
229    /// # where
230    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
231    /// #     Certs: Load<'s, S> + Save,
232    /// #     Keys: Load<'s, S> + Save,
233    /// # {
234    /// let alice_secret =
235    ///     Keys::from_file(sop, "alice.secret")?;
236    ///
237    /// let alice_updated_secret = sop.change_key_password()?
238    ///     .old_key_password(Password::new_unchecked(b"hunter2".to_vec()))?
239    ///     .new_key_password(Password::new(b"jaeger2".to_vec())?)?
240    ///     .keys(&alice_secret)?;
241    /// # Ok(()) }
242    /// ```
243    fn change_key_password(&'s self)
244                           -> Result<Box<dyn ChangeKeyPassword<Self, Self::Keys> + 's>>;
245
246    /// Creates a Revocation Certificate.
247    ///
248    /// Customize the operation using the builder [`RevokeKey`].
249    ///
250    /// # Examples
251    ///
252    /// ```rust
253    /// # use sop::*; use std::io::Cursor; use std::fs::File;
254    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
255    /// # where
256    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
257    /// #     Certs: Load<'s, S> + Save,
258    /// #     Keys: Load<'s, S> + Save,
259    /// # {
260    /// let alice_secret =
261    ///     Keys::from_file(sop, "alice.secret")?;
262    ///
263    /// let alice_revoked = sop.revoke_key()?
264    ///     .with_key_password(Password::new_unchecked(b"hunter2".to_vec()))?
265    ///     .keys(&alice_secret)?;
266    /// # Ok(()) }
267    /// ```
268    fn revoke_key(&'s self)
269                  -> Result<Box<dyn RevokeKey<Self, Self::Certs, Self::Keys> + 's>>;
270
271    /// Extracts a Certificate from a Secret Key.
272    ///
273    /// Customize the operation using the builder [`ExtractCert`].
274    ///
275    /// # Examples
276    ///
277    /// ```rust
278    /// # use sop::*; use std::io::Cursor; use std::fs::File;
279    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
280    /// # where
281    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
282    /// #     Certs: Load<'s, S> + Save,
283    /// #     Keys: Load<'s, S> + Save,
284    /// # {
285    /// let alice_secret =
286    ///     Keys::from_file(sop, "alice.secret")?;
287    ///
288    /// let alice_public = sop.extract_cert()?
289    ///     .keys(&alice_secret)?;
290    /// # Ok(()) }
291    /// ```
292    fn extract_cert(&'s self) -> Result<Box<
293            dyn ExtractCert<Self, Self::Certs, Self::Keys> + 's>>;
294
295    /// Keeps a Secret Key Up-To-Date.
296    ///
297    /// This update will "fix" everything that the implementation
298    /// knows how to fix to bring each Transferable Secret Key up to
299    /// reasonable modern practice. Each Transferable Secret Key
300    /// output must be capable of signing, and (unless --signing-only
301    /// is provided) capable of decryption. The primary key of each
302    /// Transferable Secret Key will not be changed in any way that
303    /// affects its fingerprint.
304    ///
305    /// One important aspect of sop update-key is how it handles
306    /// advertisement of support for various OpenPGP capabilities
307    /// (algorithms, mechanisms, etc). All capabilities that the
308    /// implementation knows it does not support, or knows to be weak
309    /// and/or deprecated MUST be removed from the output Transferable
310    /// Secret Keys. This includes unknown/deprecated flags in the
311    /// Features subpacket, and any unknown/deprecated algorithm IDs
312    /// in algorithm preferences subpackets. For example, an
313    /// implementation compliant with [RFC9580] will never emit a
314    /// Transferable Secret Key with a Preferred Hash Preferences
315    /// subpacket that explicitly indicates support for MD5,
316    /// RIPEMD160, or SHA1.
317    fn update_key(&'s self) -> Result<Box<
318            dyn UpdateKey<Self, Self::Certs, Self::Keys> + 's>>;
319
320    /// Merge OpenPGP Certificates.
321    ///
322    /// This can be used, for example, to absorb a third-party
323    /// certification into a certificate, or to update a certificate's
324    /// feature advertisements without losing local annotations.
325    fn merge_certs(&'s self)
326                   -> Result<Box<dyn MergeCerts<Self, Self::Certs> + 's>>;
327
328    /// Certify OpenPGP Certificate User IDs.
329    ///
330    /// With each Transferable Secret Key provided, add a third-party
331    /// certification to the provided certificates, and
332    /// emit the updated OpenPGP certificates.
333    fn certify_userid(&'s self) -> Result<Box<
334            dyn CertifyUserID<Self, Self::Certs, Self::Keys> + 's>>;
335
336    /// Validate a User ID in an OpenPGP Certificate.
337    ///
338    /// Given a set of authority OpenPGP certificates, succeed if and
339    /// only if all provided OpenPGP certificates are correctly bound
340    /// by at least one valid signature from one authority to the User
341    /// ID or email address in question.
342    fn validate_userid(&'s self) -> Result<Box<
343            dyn ValidateUserID<Self, Self::Certs> + 's>>;
344
345    /// Creates Detached Signatures.
346    ///
347    /// Customize the operation using the builder [`Sign`].
348    ///
349    /// # Examples
350    ///
351    /// ```rust
352    /// # use sop::*; use std::io::Cursor; use std::fs::File;
353    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
354    /// # where
355    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
356    /// #     Certs: Load<'s, S> + Save,
357    /// #     Keys: Load<'s, S> + Save,
358    /// # {
359    /// let alice_secret =
360    ///     Keys::from_file(sop, "alice.secret")?;
361    ///
362    /// let (_micalg, sig) = sop.sign()?
363    ///     .keys(&alice_secret)?
364    ///     .data(&mut Cursor::new(&b"Hello World :)"))?;
365    /// # Ok(()) }
366    /// ```
367    fn sign(&'s self)
368            -> Result<Box<dyn Sign<Self, Self::Keys, Self::Sigs> + 's>>;
369
370    /// Verifies Detached Signatures.
371    ///
372    /// Customize the operation using the builder [`Verify`].
373    ///
374    /// # Examples
375    ///
376    /// ```rust
377    /// # use sop::*; use std::io::Cursor; use std::fs::File;
378    /// # fn sop_examples<'s, S, Certs, Keys, Sigs>(sop: &'s S) -> Result<()>
379    /// # where
380    /// #     S: SOP<'s, Certs = Certs, Keys = Keys, Sigs = Sigs>,
381    /// #     Certs: Load<'s, S> + Save,
382    /// #     Keys: Load<'s, S> + Save,
383    /// #     Sigs: Load<'s, S> + Save,
384    /// # {
385    /// let alice_public =
386    ///     Certs::from_file(sop, "alice.public")?;
387    /// let sig =
388    ///     Sigs::from_file(sop, "data.asc")?;
389    ///
390    /// let verifications = sop.verify()?
391    ///     .certs(&alice_public)?
392    ///     .signatures(&sig)?
393    ///     .data(&mut Cursor::new(&b"Hello World :)"))?;
394    /// let valid_signatures = ! verifications.is_empty();
395    /// # Ok(()) }
396    /// ```
397    fn verify(&'s self)
398              -> Result<Box<dyn Verify<Self, Self::Certs, Self::Sigs> + 's>>;
399
400    /// Encrypts a Message.
401    ///
402    /// Customize the operation using the builder [`Encrypt`].
403    ///
404    /// # Examples
405    ///
406    /// Encrypts a message for Bob, and signs it using Alice's key.
407    ///
408    /// ```rust
409    /// # use sop::*; use std::io::Cursor; use std::fs::File;
410    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
411    /// # where
412    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
413    /// #     Certs: Load<'s, S> + Save,
414    /// #     Keys: Load<'s, S> + Save,
415    /// # {
416    /// let alice_secret =
417    ///     Keys::from_file(sop, "alice.secret")?;
418    /// let bob_public =
419    ///     Certs::from_file(sop, "bob.public")?;
420    ///
421    /// let (_session_key, ciphertext) = sop.encrypt()?
422    ///     .sign_with_keys(&alice_secret)?
423    ///     .with_certs(&bob_public)?
424    ///     .plaintext(&mut Cursor::new(&b"Hello World :)"))?
425    ///     .to_vec()?;
426    /// # Ok(()) }
427    /// ```
428    fn encrypt(&'s self)
429               -> Result<Box<dyn Encrypt<Self, Self::Certs, Self::Keys> + 's>>;
430
431    /// Decrypts a Message.
432    ///
433    /// Customize the operation using the builder [`Decrypt`].
434    ///
435    /// # Examples
436    ///
437    /// Decrypts a message encrypted for Bob, and verifies Alice's
438    /// signature on it.
439    ///
440    /// ```rust
441    /// # use sop::*; use std::io::Cursor; use std::fs::File;
442    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
443    /// # where
444    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
445    /// #     Certs: Load<'s, S> + Save,
446    /// #     Keys: Load<'s, S> + Save,
447    /// # {
448    /// let alice_public =
449    ///     Certs::from_file(sop, "alice.public")?;
450    /// let bob_secret =
451    ///     Keys::from_file(sop, "bob.secret")?;
452    ///
453    /// let ((_session_key, verifications), plaintext) = sop.decrypt()?
454    ///     .verify_with_certs(&alice_public)?
455    ///     .with_keys(&bob_secret)?
456    ///     .ciphertext(&mut File::open("ciphertext.pgp")?)?
457    ///     .to_vec()?;
458    /// let valid_signatures = ! verifications.is_empty();
459    /// # Ok(()) }
460    /// ```
461    fn decrypt(&'s self)
462               -> Result<Box<dyn Decrypt<Self, Self::Certs, Self::Keys> + 's>>;
463
464    /// Converts binary OpenPGP data to ASCII.
465    ///
466    /// By default, SOP operations emit ASCII-Armored data.  But,
467    /// occasionally it can be useful to explicitly armor data.
468    ///
469    /// Customize the operation using the builder [`Armor`].
470    ///
471    /// # Examples
472    ///
473    /// ```rust
474    /// # use sop::*; use std::io::Cursor; use std::fs::File;
475    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
476    /// let (_, alice_secret_asc) = sop.armor()?
477    ///     .data(&mut File::open("alice.secret.bin")?)?
478    ///     .to_vec()?;
479    /// assert!(alice_secret_asc.starts_with(b"-----BEGIN PGP PRIVATE KEY BLOCK-----"));
480    /// # Ok(()) }
481    /// ```
482    fn armor(&'s self) -> Result<Box<dyn Armor + 's>>;
483
484    /// Converts ASCII OpenPGP data to binary.
485    ///
486    /// By default, SOP operations emit ASCII-Armored data, but this
487    /// behavior can be changed at export time.  Nevertheless,
488    /// occasionally it can be useful to explicitly dearmor data.
489    ///
490    /// Customize the operation using the builder [`Dearmor`].
491    ///
492    /// # Examples
493    ///
494    /// ```rust
495    /// # use sop::*; use std::io::Cursor; use std::fs::File;
496    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
497    /// let (_, alice_secret_bin) = sop.dearmor()?
498    ///     .data(&mut File::open("alice.secret.asc")?)?
499    ///     .to_vec()?;
500    /// assert!(! alice_secret_bin.starts_with(b"-----BEGIN PGP PRIVATE KEY BLOCK-----"));
501    /// # Ok(()) }
502    /// ```
503    fn dearmor(&'s self) -> Result<Box<dyn Dearmor + 's>>;
504
505    /// Splits Signatures from an Inline-Signed Message.
506    ///
507    /// Note: The signatures are not verified, this merely transforms
508    /// an inline-signed message into a detached signature, which in
509    /// turn can be verified using [`SOP::verify`].
510    ///
511    /// Customize the operation using the builder [`InlineDetach`].
512    ///
513    /// # Examples
514    ///
515    /// ```rust
516    /// # use sop::*; use std::io::Cursor; use std::fs::File;
517    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
518    /// let (signatures, data) = sop.inline_detach()?
519    ///     .message(&mut File::open("inline-signed.pgp")?)?
520    ///     .to_vec()?;
521    /// # Ok(()) }
522    /// ```
523    fn inline_detach(&'s self)
524                     -> Result<Box<dyn InlineDetach<Self::Sigs> + 's>>;
525
526    /// Verifies an Inline-Signed Message.
527    ///
528    /// Customize the operation using the builder [`InlineVerify`].
529    ///
530    /// # Examples
531    ///
532    /// ```rust
533    /// # use sop::*; use std::io::Cursor; use std::fs::File;
534    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
535    /// # where
536    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
537    /// #     Certs: Load<'s, S> + Save,
538    /// #     Keys: Load<'s, S> + Save,
539    /// # {
540    /// let alice_public =
541    ///     Certs::from_file(sop, "alice.public")?;
542    ///
543    /// let (verifications, data) = sop.inline_verify()?
544    ///     .certs(&alice_public)?
545    ///     .message(&mut File::open("inline-signed.pgp")?)?
546    ///     .to_vec()?;
547    /// let valid_signatures = ! verifications.is_empty();
548    /// # Ok(()) }
549    /// ```
550    fn inline_verify(&'s self)
551                     -> Result<Box<dyn InlineVerify<Self, Self::Certs> + 's>>;
552
553    /// Creates an Inline-Signed Message.
554    ///
555    /// Customize the operation using the builder [`InlineSign`].
556    ///
557    /// # Examples
558    ///
559    /// ```rust
560    /// # use sop::*; use std::io::Cursor; use std::fs::File;
561    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
562    /// # where
563    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
564    /// #     Certs: Load<'s, S> + Save,
565    /// #     Keys: Load<'s, S> + Save,
566    /// # {
567    /// let alice_secret =
568    ///     Keys::from_file(sop, "alice.secret")?;
569    ///
570    /// let (inline_signed_asc) = sop.inline_sign()?
571    ///     .keys(&alice_secret)?
572    ///     .data(&mut Cursor::new(&b"Hello World :)"))?
573    ///     .to_vec()?;
574    /// # Ok(()) }
575    /// ```
576    fn inline_sign(&'s self)
577                   -> Result<Box<dyn InlineSign<Self, Self::Keys> + 's>>;
578}
579
580/// A password.
581///
582/// See [Passwords are Human-Readable] in the SOP spec.
583///
584///   [Passwords are Human-Readable]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-passwords-are-human-readabl
585pub struct Password(Box<[u8]>);
586
587impl Password {
588    /// Returns a `Password` that is guaranteed to be human-readable.
589    ///
590    /// Use this function when you get a password from the user to
591    /// generate an artifact with (see [Generating Material with
592    /// Human-Readable Passwords]).
593    ///
594    /// [Generating Material with Human-Readable Passwords]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-generating-material-with-hu
595    pub fn new(password: Vec<u8>) -> Result<Password> {
596        // Securely erase the password.
597        fn securely_erase(mut p: Vec<u8>) {
598            unsafe {
599                memsec::memzero(p.as_mut_ptr(), p.len());
600            }
601        }
602
603        let mut s = String::from_utf8(password)
604            .map_err(|e| {
605                securely_erase(e.into_bytes());
606                Error::PasswordNotHumanReadable
607            })?;
608
609        // Check for leading whitespace.
610        if s.trim_start().len() != s.len() {
611            securely_erase(s.into_bytes());
612            return Err(Error::PasswordNotHumanReadable);
613        }
614
615        // Trim trailing whitespace.
616        s.truncate(s.trim_end().len());
617
618        // Check for odd whitespace.
619        if s.chars().any(|c| c.is_whitespace() && c != ' ') {
620            securely_erase(s.into_bytes());
621            return Err(Error::PasswordNotHumanReadable);
622        }
623
624        // XXX: Check that the password is in Unicode Normal Form C,
625        // but I don't think that is possible with Rust's stdlib.
626
627        Ok(Password(s.into_bytes().into()))
628    }
629
630    /// Returns a `Password` without further checking.
631    ///
632    /// Use this function when you get a password from the user that
633    /// is used when consuming an artifact (see [Consuming
634    /// Password-protected Material]).
635    ///
636    /// [Consuming Password-protected Material]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-consuming-password-protecte
637    pub fn new_unchecked(password: Vec<u8>) -> Password {
638        Password(password.into())
639    }
640}
641
642impl plumbing::PasswordsAreHumanReadable for Password {
643    fn normalized(&self) -> &[u8] {
644        // First, let's hope it is UTF-8.
645        if let Ok(p) = std::str::from_utf8(&self.0) {
646            p.trim_end().as_bytes()
647        } else {
648            // As a best effort for now, drop ASCII-whitespace from
649            // the end.
650            let mut p = &self.0[..];
651            while ! p.is_empty() && p[p.len() - 1].is_ascii_whitespace() {
652                p = &p[..p.len() - 1];
653            }
654            p
655        }
656    }
657
658    fn variants(&self) -> Box<dyn Iterator<Item = &[u8]> + '_> {
659        Box::new(std::iter::once(self.normalized())
660            .filter_map(move |normalized| {
661                if normalized.len() < self.0.len() {
662                    Some(normalized)
663                } else {
664                    None
665                }
666            })
667            .chain(std::iter::once(&self.0[..])))
668    }
669}
670
671impl Drop for Password {
672    fn drop(&mut self) {
673        unsafe {
674            memsec::memzero(self.0.as_mut_ptr(), self.0.len());
675        }
676    }
677}
678
679/// A session key.
680pub struct SessionKey {
681    algorithm: u8,
682    key: Box<[u8]>,
683}
684
685impl SessionKey {
686    /// Creates a new session key object.
687    pub fn new<A, K>(algorithm: A, key: K) -> Result<Self>
688    where A: Into<u8>,
689          K: AsRef<[u8]>,
690    {
691        // XXX: Maybe sanity check key lengths.
692        Ok(SessionKey {
693            algorithm: algorithm.into(),
694            key: key.as_ref().to_vec().into(),
695        })
696    }
697
698    /// Returns the symmetric algorithm octet.
699    pub fn algorithm(&self) -> u8 {
700        self.algorithm
701    }
702
703    /// Returns the session key.
704    pub fn key(&self) -> &[u8] {
705        &self.key
706    }
707}
708
709impl fmt::Display for SessionKey {
710    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
711        write!(f, "{}:", self.algorithm)?;
712        for b in &self.key[..] {
713            write!(f, "{:02X}", b)?
714        }
715        Ok(())
716    }
717}
718
719impl Drop for SessionKey {
720    fn drop(&mut self) {
721        unsafe {
722            memsec::memzero(self.key.as_mut_ptr(), self.key.len());
723        }
724    }
725}
726
727impl std::str::FromStr for SessionKey {
728    type Err = ParseError;
729    fn from_str(sk: &str) -> ParseResult<Self> {
730        // The SOP format is:
731        //
732        //   <decimal-cipher-octet> ":" <hex-session-key>
733        //
734        // We most likely will change the first field, so we split
735        // from the end of the string using `rsplit`, which puts the
736        // last segment first.  This is rather unexpected.  Reverse
737        // it.
738        let fields = sk.rsplit(':').rev().collect::<Vec<_>>();
739
740        if fields.len() != 2 {
741            return Err(ParseError(format!(
742                "Expected two colon-separated fields, got {:?}",
743                fields)));
744        }
745
746        let algo: u8 = fields[0].parse().map_err(
747            |e| ParseError(format!("Failed to parse algorithm: {}", e)))?;
748        let sk = from_hex(&fields[1], true)?;
749        Self::new(algo, sk).map_err(
750            |e| ParseError(format!("Bad session key: {}", e)))
751    }
752}
753
754/// A helpful function for converting a hexadecimal string to binary.
755/// This function skips whitespace if `pretty` is set.
756fn from_hex(hex: &str, pretty: bool) -> ParseResult<Vec<u8>> {
757    const BAD: u8 = 255u8;
758    const X: u8 = 'x' as u8;
759
760    let mut nibbles = hex.chars().filter_map(|x| {
761        match x {
762            '0' => Some(0u8),
763            '1' => Some(1u8),
764            '2' => Some(2u8),
765            '3' => Some(3u8),
766            '4' => Some(4u8),
767            '5' => Some(5u8),
768            '6' => Some(6u8),
769            '7' => Some(7u8),
770            '8' => Some(8u8),
771            '9' => Some(9u8),
772            'a' | 'A' => Some(10u8),
773            'b' | 'B' => Some(11u8),
774            'c' | 'C' => Some(12u8),
775            'd' | 'D' => Some(13u8),
776            'e' | 'E' => Some(14u8),
777            'f' | 'F' => Some(15u8),
778            'x' | 'X' if pretty => Some(X),
779            _ if pretty && x.is_whitespace() => None,
780            _ => Some(BAD),
781        }
782    }).collect::<Vec<u8>>();
783
784    if pretty && nibbles.len() >= 2 && nibbles[0] == 0 && nibbles[1] == X {
785        // Drop '0x' prefix.
786        nibbles.remove(0);
787        nibbles.remove(0);
788    }
789
790    if nibbles.iter().any(|&b| b == BAD || b == X) {
791        // Not a hex character.
792        return Err(ParseError("Invalid characters".into()));
793    }
794
795    // We need an even number of nibbles.
796    if nibbles.len() % 2 != 0 {
797        return Err(ParseError("Odd number of nibbles".into()));
798    }
799
800    let bytes = nibbles.chunks(2).map(|nibbles| {
801        (nibbles[0] << 4) | nibbles[1]
802    }).collect::<Vec<u8>>();
803
804    Ok(bytes)
805}
806
807/// Result specialization.
808pub type Result<T> = std::result::Result<T, Error>;
809
810/// Convenience alias.
811type ParseResult<T> = std::result::Result<T, ParseError>;
812
813#[cfg(test)]
814mod tests {
815    use super::*;
816
817    #[test]
818    fn session_key_roundtrip() -> Result<()> {
819        for algo in &[9, 13] {
820            let sk = SessionKey::new(
821                *algo,
822                &[0xE1, 0x48, 0x97, 0x81, 0xAA, 0x22, 0xE1, 0xBF,
823                  0x6E, 0x3E, 0x61, 0x74, 0x8C, 0x8D, 0x3F, 0x35,
824                  0x50, 0x7C, 0x80, 0x9E, 0x95, 0x64, 0x86, 0x87,
825                  0xC7, 0xE4, 0xB9, 0xAF, 0x86, 0x17, 0xD3, 0xAE])?;
826            let sk_s = sk.to_string();
827            let sk_p: SessionKey = sk_s.parse().unwrap();
828            assert_eq!(sk.algorithm(), sk_p.algorithm());
829            assert_eq!(sk.key(), sk_p.key());
830        }
831        Ok(())
832    }
833
834    #[test]
835    fn sign_as_roundtrip() -> Result<()> {
836        use SignAs::*;
837        for a in &[Text, Binary] {
838            let s = a.to_string();
839            let b: SignAs = s.parse().unwrap();
840            assert_eq!(a, &b);
841        }
842        Ok(())
843    }
844
845    #[test]
846    fn encrypt_as_roundtrip() -> Result<()> {
847        use EncryptAs::*;
848        for a in &[Text, Binary] {
849            let s = a.to_string();
850            let b: EncryptAs = s.parse().unwrap();
851            assert_eq!(a, &b);
852        }
853        Ok(())
854    }
855
856    #[test]
857    fn armor_label_roundtrip() -> Result<()> {
858        use ArmorLabel::*;
859        for a in &[Auto, Sig, Key, Cert, Message] {
860            let s = a.to_string();
861            let b: ArmorLabel = s.parse().unwrap();
862            assert_eq!(a, &b);
863        }
864        Ok(())
865    }
866}