diff options
Diffstat (limited to 'openpgp/src')
24 files changed, 1252 insertions, 411 deletions
diff --git a/openpgp/src/autocrypt.rs b/openpgp/src/autocrypt.rs index b15fddbf..39d374d8 100644 --- a/openpgp/src/autocrypt.rs +++ b/openpgp/src/autocrypt.rs @@ -101,13 +101,13 @@ impl AutocryptHeader { -> Result<Self> where P: Into<Option<&'a str>> { - use crate::packet::Tag; + use crate::packet::key; // Minimize TPK. let mut acc = Vec::new(); // The primary key and the most recent selfsig. - acc.push(tpk.primary().key().clone().into_packet(Tag::PublicKey)?); + acc.push(tpk.primary().key().clone().into()); tpk.selfsigs().iter().take(1) .for_each(|s| acc.push(s.clone().into())); @@ -120,7 +120,8 @@ impl AutocryptHeader { continue; } - acc.push(skb.key().clone().into_packet(Tag::PublicSubkey)?); + let k : key::PublicSubkey = skb.key().clone(); + acc.push(k.into()); skb.selfsigs().iter().take(1) .for_each(|s| acc.push(s.clone().into())); } diff --git a/openpgp/src/crypto/asymmetric.rs b/openpgp/src/crypto/asymmetric.rs index af8ea7f0..9232bd14 100644 --- a/openpgp/src/crypto/asymmetric.rs +++ b/openpgp/src/crypto/asymmetric.rs @@ -2,7 +2,7 @@ use nettle::{dsa, ecc, ecdsa, ed25519, rsa, Yarrow}; -use crate::packet::{self, Key}; +use crate::packet::{self, key, Key}; use crate::crypto::SessionKey; use crate::crypto::mpis::{self, MPI}; use crate::constants::{Curve, HashAlgorithm}; @@ -16,9 +16,11 @@ use crate::Result; /// signature. Using this trait allows Sequoia to perform all /// operations involving signing to use a variety of secret key /// storage mechanisms (e.g. smart cards). -pub trait Signer { +pub trait Signer<R> + where R: key::KeyRole +{ /// Returns a reference to the public key. - fn public(&self) -> &Key; + fn public(&self) -> &Key<key::PublicParts, R>; /// Creates a signature over the `digest` produced by `hash_algo`. fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) @@ -31,9 +33,11 @@ pub trait Signer { /// ciphertext. Using this trait allows Sequoia to perform all /// operations involving decryption to use a variety of secret key /// storage mechanisms (e.g. smart cards). -pub trait Decryptor { +pub trait Decryptor<R> + where R: key::KeyRole +{ /// Returns a reference to the public key. - fn public(&self) -> &Key; + fn public(&self) -> &Key<key::PublicParts, R>; /// Decrypts `ciphertext`, returning the plain session key. fn decrypt(&mut self, ciphertext: &mpis::Ciphertext) @@ -49,14 +53,21 @@ pub trait Decryptor { /// [`Signer`]: trait.Signer.html /// [`Decryptor`]: trait.Decryptor.html #[derive(Clone)] -pub struct KeyPair { - public: Key, +pub struct KeyPair<R> + where R: key::KeyRole +{ + public: Key<key::PublicParts, R>, secret: packet::key::Unencrypted, } -impl KeyPair { +impl<R> KeyPair<R> + where R: key::KeyRole +{ /// Creates a new key pair. - pub fn new(public: Key, secret: packet::key::Unencrypted) -> Result<Self> { + pub fn new(public: Key<key::PublicParts, R>, + secret: packet::key::Unencrypted) + -> Result<Self> + { Ok(Self { public: public, secret: secret, @@ -64,7 +75,7 @@ impl KeyPair { } /// Returns a reference to the public key. - pub fn public(&self) -> &Key { + pub fn public(&self) -> &Key<key::PublicParts, R> { &self.public } @@ -74,8 +85,10 @@ impl KeyPair { } } -impl Signer for KeyPair { - fn public(&self) -> &Key { +impl<R> Signer<R> for KeyPair<R> + where R: key::KeyRole +{ + fn public(&self) -> &Key<key::PublicParts, R> { &self.public } @@ -202,8 +215,10 @@ impl Signer for KeyPair { } } -impl Decryptor for KeyPair { - fn public(&self) -> &Key { +impl<R> Decryptor<R> for KeyPair<R> + where R: key::KeyRole +{ + fn public(&self) -> &Key<key::PublicParts, R> { &self.public } @@ -248,11 +263,12 @@ impl Decryptor for KeyPair { } } -impl From<KeyPair> for packet::Key { - fn from(p: KeyPair) -> Self { +impl<R> From<KeyPair<R>> for Key<key::SecretParts, R> + where R: key::KeyRole +{ + fn from(p: KeyPair<R>) -> Self { let (mut key, secret) = (p.public, p.secret); key.set_secret(Some(secret.into())); - key + key.mark_parts_secret() } - } diff --git a/openpgp/src/crypto/ecdh.rs b/openpgp/src/crypto/ecdh.rs index a51362b6..9b8ad9e0 100644 --- a/openpgp/src/crypto/ecdh.rs +++ b/openpgp/src/crypto/ecdh.rs @@ -1,7 +1,10 @@ //! Elliptic Curve Diffie-Hellman. use crate::Error; -use crate::packet::Key; +use crate::packet::{ + Key, + key, +}; use crate::Result; use crate::constants::{ Curve, @@ -20,7 +23,10 @@ use nettle::{cipher, curve25519, mode, Mode, ecc, ecdh, Yarrow}; /// Wraps a session key using Elliptic Curve Diffie-Hellman. #[allow(non_snake_case)] -pub fn encrypt(recipient: &Key, session_key: &SessionKey) -> Result<Ciphertext> +pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, + session_key: &SessionKey) + -> Result<Ciphertext> + where R: key::KeyRole { let mut rng = Yarrow::default(); @@ -130,9 +136,11 @@ pub fn encrypt(recipient: &Key, session_key: &SessionKey) -> Result<Ciphertext> /// `VB` is the ephemeral public key (with 0x40 prefix), `S` is the /// shared Diffie-Hellman secret. #[allow(non_snake_case)] -pub fn encrypt_shared(recipient: &Key, session_key: &SessionKey, VB: MPI, - S: &Protected) - -> Result<Ciphertext> +pub fn encrypt_shared<R>(recipient: &Key<key::PublicParts, R>, + session_key: &SessionKey, VB: MPI, + S: &Protected) + -> Result<Ciphertext> + where R: key::KeyRole { match recipient.mpis() { &PublicKey::ECDH{ ref curve, ref hash, ref sym,.. } => { @@ -169,9 +177,12 @@ pub fn encrypt_shared(recipient: &Key, session_key: &SessionKey, VB: MPI, /// Unwraps a session key using Elliptic Curve Diffie-Hellman. #[allow(non_snake_case)] -pub fn decrypt(recipient: &Key, recipient_sec: &SecretKeyMaterial, - ciphertext: &Ciphertext) - -> Result<SessionKey> { +pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, + recipient_sec: &SecretKeyMaterial, + ciphertext: &Ciphertext) + -> Result<SessionKey> + where R: key::KeyRole +{ match (recipient.mpis(), recipient_sec, ciphertext) { (PublicKey::ECDH { ref curve, ..}, SecretKeyMaterial::ECDH { ref scalar, }, @@ -280,8 +291,11 @@ pub fn decrypt(recipient: &Key, recipient_sec: &SecretKeyMaterial, /// `recipient` is the message receiver's public key, `S` is the /// shared Diffie-Hellman secret used to encrypt `ciphertext`. #[allow(non_snake_case)] -pub fn decrypt_shared(recipient: &Key, S: &Protected, ciphertext: &Ciphertext) - -> Result<SessionKey> +pub fn decrypt_shared<R>(recipient: &Key<key::PublicParts, R>, + S: &Protected, + ciphertext: &Ciphertext) + -> Result<SessionKey> + where R: key::KeyRole { match (recipient.mpis(), ciphertext) { (PublicKey::ECDH { ref curve, ref hash, ref sym, ..}, @@ -308,8 +322,13 @@ pub fn decrypt_shared(recipient: &Key, S: &Protected, ciphertext: &Ciphertext) } } -fn make_param(recipient: &Key, curve: &Curve, hash: &HashAlgorithm, - sym: &SymmetricAlgorithm) -> Vec<u8> { +fn make_param<P, R>(recipient: &Key<P, R>, + curve: &Curve, hash: &HashAlgorithm, + sym: &SymmetricAlgorithm) + -> Vec<u8> + where P: key::KeyParts, + R: key::KeyRole +{ // Param = curve_OID_len || curve_OID || // public_key_alg_ID || 03 || 01 || KDF_hash_ID || // KEK_alg_ID for AESKeyWrap || "Anonymous Sender " || diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs index 9ace7d38..3ffded0c 100644 --- a/openpgp/src/crypto/hash.rs +++ b/openpgp/src/crypto/hash.rs @@ -3,7 +3,7 @@ use crate::HashAlgorithm; use crate::packet::UserID; use crate::packet::UserAttribute; -use crate::packet::Key; +use crate::packet::key; use crate::packet::key::Key4; use crate::packet::Signature; use crate::packet::signature::{self, Signature4}; @@ -221,7 +221,10 @@ impl Hash for UserAttribute { } } -impl Hash for Key4 { +impl<P, R> Hash for Key4<P, R> + where P: key::KeyParts, + R: key::KeyRole, +{ /// Update the Hash with a hash of the key. fn hash(&self, hash: &mut Context) { // We hash 8 bytes plus the MPIs. But, the len doesn't @@ -335,9 +338,10 @@ impl Hash for signature::Builder { impl Signature { /// Returns the message digest of the primary key binding over the /// specified primary key. - pub fn primary_key_binding_hash<'a, S>(sig: S, key: &Key) + pub fn primary_key_binding_hash<'a, S>(sig: S, key: &key::PublicKey) -> Result<Vec<u8>> - where S: Into<&'a signature::Builder> { + where S: Into<&'a signature::Builder> + { let sig = sig.into(); let mut h = sig.hash_algo().context()?; @@ -352,9 +356,12 @@ impl Signature { /// Returns the message digest of the subkey binding over the /// specified primary key and subkey. - pub fn subkey_binding_hash<'a, S>(sig: S, key: &Key, subkey: &Key) + pub fn subkey_binding_hash<'a, S>(sig: S, + key: &key::PublicKey, + subkey: &key::PublicSubkey) -> Result<Vec<u8>> - where S: Into<&'a signature::Builder> { + where S: Into<&'a signature::Builder> + { let sig = sig.into(); let mut h = sig.hash_algo().context()?; @@ -370,10 +377,12 @@ impl Signature { /// Returns the message digest of the user ID binding over the /// specified primary key, user ID, and signature. - pub fn userid_binding_hash<'a, S>(sig: S, key: &Key, userid: &UserID) + pub fn userid_binding_hash<'a, S>(sig: S, + key: &key::PublicKey, + userid: &UserID) -> Result<Vec<u8>> - where S: Into<&'a signature::Builder> { - + where S: Into<&'a signature::Builder> + { let sig = sig.into(); let mut h = sig.hash_algo().context()?; @@ -388,10 +397,12 @@ impl Signature { /// Returns the message digest of the user attribute binding over /// the specified primary key, user attribute, and signature. - pub fn user_attribute_binding_hash<'a, S>(sig: S, key: &Key, + pub fn user_attribute_binding_hash<'a, S>(sig: S, + key: &key::PublicKey, ua: &UserAttribute) -> Result<Vec<u8>> - where S: Into<&'a signature::Builder> { + where S: Into<&'a signature::Builder> + { let sig = sig.into(); let mut h = sig.hash_algo().context()?; diff --git a/openpgp/src/crypto/sexp.rs b/openpgp/src/crypto/sexp.rs index 454ed5e7..acd970aa 100644 --- a/openpgp/src/crypto/sexp.rs +++ b/openpgp/src/crypto/sexp.rs @@ -90,11 +90,14 @@ impl Sexp { /// Such an expression is returned from gpg-agent's `PKDECRYPT` /// command. `padding` must be set according to the status /// messages sent. - pub fn finish_decryption(&self, - recipient: &crate::packet::Key, - ciphertext: &mpis::Ciphertext, - padding: bool) - -> Result<SessionKey> { + pub fn finish_decryption<R>(&self, + recipient: &crate::packet::Key< + crate::packet::key::PublicParts, R>, + ciphertext: &mpis::Ciphertext, + padding: bool) + -> Result<SessionKey> + where R: crate::packet::key::KeyRole + { use crate::crypto::mpis::PublicKey; let not_a_session_key = || -> failure::Error { Error::MalformedMPI( diff --git a/openpgp/src/lib.rs b/openpgp/src/lib.rs index 1c44137d..54b124c5 100644 --- a/openpgp/src/lib.rs +++ b/openpgp/src/lib.rs @@ -109,7 +109,7 @@ pub mod conversions; pub mod crypto; pub mod packet; -use crate::packet::{BodyLength, Header, Container}; +use crate::packet::{BodyLength, Header, Container, key}; use crate::packet::ctb::{CTB, CTBOld, CTBNew}; pub mod parse; @@ -265,13 +265,13 @@ pub enum Packet { /// One pass signature packet. OnePassSig(packet::OnePassSig), /// Public key packet. - PublicKey(packet::Key), + PublicKey(packet::key::PublicKey), /// Public subkey packet. - PublicSubkey(packet::Key), + PublicSubkey(packet::key::PublicSubkey), /// Public/Secret key pair. - SecretKey(packet::Key), + SecretKey(packet::key::SecretKey), /// Public/Secret subkey pair. - SecretSubkey(packet::Key), + SecretSubkey(packet::key::SecretSubkey), /// Marker packet. Marker(packet::Marker), /// Trust packet. diff --git a/openpgp/src/message/mod.rs b/openpgp/src/message/mod.rs index 877f503b..d36ddb3c 100644 --- a/openpgp/src/message/mod.rs +++ b/openpgp/src/message/mod.rs @@ -798,7 +798,7 @@ mod tests { lit.set_body(b"data".to_vec()); let hash = crate::constants::HashAlgorithm::SHA512; - let key: Key = + let key: key::SecretKey = crate::packet::key::Key4::generate_ecc(true, crate::constants::Curve::Ed25519) .unwrap().into(); let mut pair = key.clone().into_keypair().unwrap(); @@ -910,7 +910,7 @@ mod tests { lit.set_body(b"data".to_vec()); let hash = crate::constants::HashAlgorithm::SHA512; - let key: Key = + let key: key::SecretKey = crate::packet::key::Key4::generate_ecc(true, crate::constants::Curve::Ed25519) .unwrap().into(); let mut pair = key.clone().into_keypair().unwrap(); diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key/mod.rs index 85b1ec86..5d9c9bef 100644 --- a/openpgp/src/packet/key.rs +++ b/openpgp/src/packet/key/mod.rs @@ -1,4 +1,54 @@ //! Public key, public subkey, private key and private subkey packets. +//! Key variants. +//! +//! There are four variants of OpenPGP keys: public keys, public +//! subkeys, secret keys, and secret subkeys. These are based on +//! the cross product of two attributes: whether the key contains +//! any secret key material, and the key's role. +//! +//! The underlying representation of these four variants is +//! identical (even a public key and a secret key are the same: +//! the public key variant just contains 0 bits of secret key +//! material), and many (but not all) operations can be done on +//! all four variants. +//! +//! We separate these variants into two types: parts (public or +//! secret) and roles (primary or secondary). We also add +//! unspecified variants, because sometimes we want a slice of +//! keys, and we don't care about the key's role. For instance, +//! when iterating over all of the keys in a TPK, we want the +//! primary and the subkeys. These can't be put in the same slice +//! without first wrapping them, which is awkward. +//! +//! For the most part, the user doesn't need to worry about the +//! markers. Occasionally, it is necessary to change a key's markers. +//! For these cases, it is possible to just use the `From` trait to +//! get the require markers. But, it is also possible to explicitly +//! set markers. Compare: +//! +//! ```rust +//! # extern crate sequoia_openpgp as openpgp; +//! # use openpgp::Result; +//! # use openpgp::parse::{Parse, PacketParserResult, PacketParser}; +//! # use openpgp::tpk::TPKParser; +//! # use openpgp::tpk::{CipherSuite, TPKBuilder}; +//! use openpgp::packet::{Key, key}; +//! +//! # fn main() { f().unwrap(); } +//! # fn f() -> Result<()> +//! # { +//! # let (tpk, _) = TPKBuilder::new() +//! # .set_cipher_suite(CipherSuite::Cv25519) +//! # .generate()?; +//! // Get a handle to the TPK's primary key that allows using the +//! // secret key material. +//! let sk : &key::SecretKey = tpk.primary().key().into(); +//! +//! // Make the conversion explicit. +//! let sk : &key::SecretKey = tpk.primary().key().mark_parts_secret_ref(); +//! # Ok(()) +//! # } +//! ``` use std::fmt; use std::cmp::Ordering; @@ -6,9 +56,8 @@ use time; use crate::Error; use crate::crypto::{self, mem::{self, Protected}, mpis, hash::Hash}; -use crate::packet::Tag; use crate::packet; -use crate::Packet; +use crate::packet::prelude::*; use crate::PublicKeyAlgorithm; use crate::SymmetricAlgorithm; use crate::HashAlgorithm; @@ -20,13 +69,447 @@ use crate::crypto::Password; use crate::KeyID; use crate::Fingerprint; +/// A marker trait that indicates whether a `Key` only contains +/// public key material or *may* also contains secret key +/// material. +pub trait KeyParts: fmt::Debug {} + +/// A marker trait that indicates whether a `Key` is a primary key or +/// subordinate key (i.e., a subkey). +pub trait KeyRole: fmt::Debug {} + +/// Indicates that a `Key` should be treated like a public key. +/// +/// Note: this doesn't indicate whether the data structure contains +/// secret key material; it indicates whether any secret key material +/// should be ignored. For instance, when exporting a key with the +/// `PublicParts` marker, secret key material will *not* be exported. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct PublicParts; +impl KeyParts for PublicParts {} + +/// Indicates that a `Key` should be treated like a secret key. +/// +/// Note: this doesn't indicate whether the data structure contains +/// secret key material; it indicates whether any secret key material +/// should be used. For instance, when exporting a key with the +/// `SecretParts` marker, secret key material will be exported. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct SecretParts; +impl KeyParts for SecretParts {} + +/// Indicates that a `Key`'s parts are unspecified. +/// +/// Neither public key-specific nor secret key-specific operations are +/// allowed on such keys. +/// +/// For instance, it is not possible to export a key with the +/// `UnspecifiedParts` marker, because it is unclear how to treat any +/// secret key material. To export such a key, you need to use a +/// different `KeyParts` marker. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct UnspecifiedParts; +impl KeyParts for UnspecifiedParts {} + +/// Indicates that a `Key` should treated like a primary key. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct PrimaryRole; +impl KeyRole for PrimaryRole {} + +/// Indicates that a `Key` should treated like a subkey key. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct SubordinateRole; +impl KeyRole for SubordinateRole {} + +/// Indicates that a `Key`'s role is unknown. +/// +/// Neither primary key-specific nor subkey-specific operations +/// are allowed. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct UnspecifiedRole; +impl KeyRole for UnspecifiedRole {} + +/// A Public Key. +pub type PublicKey = Key<PublicParts, PrimaryRole>; +/// A Public Subkey. +pub type PublicSubkey = Key<PublicParts, SubordinateRole>; +/// A Secret Key. +pub type SecretKey = Key<SecretParts, PrimaryRole>; +/// A Secret Subkey. +pub type SecretSubkey = Key<SecretParts, SubordinateRole>; + +/// A key with public parts, and an unspecified role +/// (`UnspecifiedRole`). +pub type UnspecifiedPublic = Key<PublicParts, UnspecifiedRole>; +/// A key with secret parts, and an unspecified role +/// (`UnspecifiedRole`). +pub type UnspecifiedSecret = Key<SecretParts, UnspecifiedRole>; + +/// A primary key with unspecified parts (`UnspecifiedParts`). +pub type UnspecifiedPrimary = Key<UnspecifiedParts, PrimaryRole>; +/// A subkey key with unspecified parts (`UnspecifiedParts`). +pub type UnspecifiedSecondary = Key<UnspecifiedParts, SubordinateRole>; + +/// A key whose parts and role are unspecified +/// (`UnspecifiedParts`, `UnspecifiedRole`). +pub type UnspecifiedKey = Key<UnspecifiedParts, UnspecifiedRole>; + +macro_rules! convert { + ( $x:ident ) => { + // XXX: This is ugly, but how can we do better? + unsafe { std::mem::transmute($x) } + } +} + +macro_rules! convert_ref { + ( $x:id |