//! This file is no longer used, but serves as reference for //! high-level operations that can be done on TPKs. use std::borrow::Cow; use std::io; use std::ops::{Deref, DerefMut}; use std::path::Path; use time; use { Result, TPK, Error, Packet, packet, conversions::Time, }; use crypto::{KeyPair, Password}; use packet::{ Signature, Tag, UserID, Key, UserAttribute, KeyFlags, }; use serialize::{ Serialize, SerializeKey, }; use parse::{Parse, PacketParserResult}; /// A transferable secret key (TSK). /// /// A TSK (see [RFC 4880, section 11.2]) can be used to create /// signatures and decrypt data. /// /// [RFC 4880, section 11.2]: https://tools.ietf.org/html/rfc4880#section-11.2 #[derive(Debug, PartialEq)] pub struct TSK { key: TPK, } impl Deref for TSK { type Target = TPK; fn deref(&self) -> &Self::Target { &self.key } } impl DerefMut for TSK { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.key } } impl<'a> Parse<'a, TSK> for TSK { /// Initializes a `TSK` from a `Read`er. fn from_reader(reader: R) -> Result { TPK::from_reader(reader).map(|tpk| Self::from_tpk(tpk)) } /// Initializes a `TSK` from a `File`. fn from_file>(path: P) -> Result { TPK::from_file(path).map(|tpk| Self::from_tpk(tpk)) } /// Initializes a `TSK` from a byte string. fn from_bytes(data: &'a [u8]) -> Result { TPK::from_bytes(data).map(|tpk| Self::from_tpk(tpk)) } } impl TSK { /// Initializes a `TSK` from a `PacketParser`. pub fn from_packet_parser<'a>(ppr: PacketParserResult<'a>) -> Result { TPK::from_packet_parser(ppr).map(|tpk| Self::from_tpk(tpk)) } pub(crate) fn from_tpk(tpk: TPK) -> TSK { TSK{ key: tpk } } /// Generates a new key OpenPGP key. The key will be capable of encryption /// and signing. If no user id is given the primary self signature will be /// a direct key signature. pub fn new<'a, O>(primary_uid: O) -> Result<(TSK, Signature)> where O: Into>> { use tpk::TPKBuilder; let key = TPKBuilder::autocrypt( None, primary_uid.into().map(|s| packet::UserID::from(s.deref()))); let (tpk, revocation) = key.generate()?; Ok((TSK::from_tpk(tpk), revocation)) } /// Returns a reference to the corresponding TPK. pub fn tpk<'a>(&'a self) -> &'a TPK { &self.key } /// Converts to a TPK. pub fn into_tpk(self) -> TPK { self.key } /// Signs `key` and `userid` with a 3rd party certification. /// /// Note: This is a convenience function around /// [`signature::Builder::sign_userid_binding()`]. If your TSK /// contains encrypted or remote keys, or you want to customize /// the signature, use this function instead. /// /// [`signature::Builder::sign_userid_binding()`]: packet/signature/struct.Builder.html#method.sign_userid_binding pub fn certify_userid(&self, key: &Key, userid: &UserID) -> Result { use packet::signature; use constants::{HashAlgorithm, SignatureType}; // We're willing to use an expired certification key here, // because otherwise it is impossible to extend the expiration // of an expired TPK. // // XXX: If there are multiple certification keys, then we // should prefer a non-expired one. let certification_key = self.key.keys_all() .certification_capable() .unencrypted_secret(true) .nth(0) .map(|x| x.2) .ok_or(failure::Error::from(Error::InvalidOperation( "this key cannot certify keys".into())))?; let mut signer = certification_key.clone().into_keypair() .expect("filtered for unencrypted secret keys above"); signature::Builder::new(SignatureType::GenericCertificate) .sign_userid_binding(&mut signer, key, userid, HashAlgorithm::SHA512) } /// Signs the primary key's self signatures of `key`. pub fn certify_key(&self, key: &TPK) -> Result { match key.primary_key_signature_full() { None | Some((None, _)) => Err(Error::InvalidOperation( "this key has nothing to certify".into()).into()), Some((Some(uid), _)) => self.certify_userid(key.primary(), uid.userid()), } } /// Signs `userid` with this TSK. pub fn sign_userid(&self, userid: &UserID) -> Result { use packet::{signature, key::SecretKey}; use constants::{HashAlgorithm, SignatureType}; let builder = if let Some(sig) = self.primary_key_signature() { signature::Builder::from(sig.clone()) .set_sigtype(SignatureType::PositiveCertificate) } else { signature::Builder::new(SignatureType::PositiveCertificate) } .set_signature_creation_time(time::now())?; let certification_key = self.key.keys_valid() .certification_capable() .unencrypted_secret(true) .nth(0) .map(|x| x.2); match certification_key { Some(my_key) => { match my_key.secret() { Some(&SecretKey::Unencrypted{ ref mpis }) => { builder .set_issuer_fingerprint(my_key.fingerprint())? .set_issuer(my_key.keyid())? .sign_userid_binding( &mut KeyPair::new((*my_key).clone(), mpis.clone())?, my_key, userid, HashAlgorithm::SHA512) } // XXX _ => Err(Error::InvalidOperation( "secret key missing or encrypted".into()).into()), } } None => Err(Error::InvalidOperation( "this key cannot certify keys".into()).into()), } } /// Signs `userattr` with a the primary key. pub fn sign_user_attribute(&self, userattr: &UserAttribute) -> Result { use packet::{signature, key::SecretKey}; use constants::{HashAlgorithm, SignatureType}; let certification_key = self.key.keys_valid() .certification_capable() .unencrypted_secret(true) .nth(0) .map(|x| x.2); match certification_key { Some(my_key) => { match my_key.secret() { Some(&SecretKey::Unencrypted{ ref mpis }) => { let mut pair = KeyPair::new((*my_key).clone(), mpis.clone())?; signature::Builder::new(SignatureType::GenericCertificate) .set_signature_creation_time(time::now())? .set_issuer_fingerprint(my_key.fingerprint())? .set_issuer(my_key.keyid())? .sign_user_attribute_binding( &mut pair, &my_key, userattr, HashAlgorithm::SHA512) } // XXX _ => Err(Error::InvalidOperation( "secret key missing or encrypted".into()).into()), } } None => Err(Error::InvalidOperation( "this key cannot certify user attributes".into()).into()), } } /// Create a binding signature between this TSK and `subkey`. Uses the TSKs /// primary key to sign the binding. The binding signature will advertise /// `flags` key capabilities. If `subkey` is encrypted that caller must /// supply the password in `passwd`. pub fn sign_subkey(&self, subkey: &Key, flags: &KeyFlags, passwd: Option<&Password>) -> Result { use packet::{signature, Features, key::SecretKey}; use constants::{HashAlgorithm, SignatureType, SymmetricAlgorithm}; let prim = self.key.primary(); let mut sig = signature::Builder::new(SignatureType::SubkeyBinding) .set_features(&Features::sequoia())? .set_key_flags(flags)? .set_signature_creation_time(time::now().canonicalize())? .set_key_expiration_time(Some(time::Duration::weeks(3 * 52)))? .set_issuer_fingerprint(prim.fingerprint())? .set_issuer(prim.keyid())?; if flags.can_encrypt_for_transport() || flags.can_encrypt_at_rest() { sig = sig.set_preferred_symmetric_algorithms( vec![SymmetricAlgorithm::AES256])?; } if flags.can_certify() || flags.can_sign() { sig = sig.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512])?; let sk = match passwd { Some(pwd) => subkey.secret().and_then(|sk| { sk .decrypt(subkey.pk_algo(), pwd) .ok() .map(|mpi| SecretKey::Unencrypted{ mpis: mpi }) .or(subkey.secret().cloned()) }), None => subkey.secret().cloned(), }; let backsig = match sk { Some(SecretKey::Unencrypted{ ref mpis }) => { signature::Builder::new(SignatureType::PrimaryKeyBinding) .set_signature_creation_time(time::now().canonicalize())? .set_issuer_fingerprint(subkey.fingerprint())? .set_issuer(subkey.keyid())? .sign_subkey_binding( &mut KeyPair::new(subkey.clone(), mpis.clone())?, prim, &subkey, HashAlgorithm::SHA512)? } // XXX Some(SecretKey::Encrypted{ .. }) => { return Err(Error::InvalidOperation( "Secret key is encrypted".into()).into()); } None => { return Err(Error::InvalidOperation( "No secret key".into()).into()); } }; sig = sig.set_embedded_signature(backsig)?; } let sig = match prim.secret() { Some(SecretKey::Unencrypted{ ref mpis }) => { sig.sign_subkey_binding(&mut KeyPair::new(prim.clone(), mpis.clone())?, prim, &subkey, HashAlgorithm::SHA512)? } // XXX Some(SecretKey::Encrypted{ .. }) => { return Err(Error::InvalidOperation( "Secret key is encrypted".into()).into()); } None => { return Err(Error::InvalidOperation( "No secret key".into()).into()); } }; Ok(sig) } /// Adds a 3rd party certification by `certifier` of the user ID `userid`. /// It's not checked whether `userid` is bound to this TSK. pub fn with_userid_certification(self, certifier: &TSK, userid: &UserID) -> Result { let sig = certifier.certify_userid(self.key.primary(), userid)?; Ok(TSK{ key: self.key.merge_packets(vec![ Packet::Signature(sig) ])? }) } /// Adds UserID `userid` to this TSK and bind it to the primary key. /// There is no check whether the user ID is already bound to this key. pub fn with_userid(self, userid: UserID) -> Result { let sig = self.sign_userid(&userid)?; Ok(TSK{ key: self.key.merge_packets(vec![ Packet::UserID(userid), Packet::Signature(sig) ])? }) } /// Adds UserID `userid` to this TSK and bind it to the primary key. /// There is no check whether the user attribute is already bound to this key. pub fn with_user_attribute(self, userattr: UserAttribute) -> Result { let sig = self.sign_user_attribute(&userattr)?; Ok(TSK{ key: self.key.merge_packets(vec![ Packet::UserAttribute(userattr), Packet::Signature(sig) ])? }) } /// Adds sub key `subkey` to this TSK and bind it. There is no check whether /// the subkey is already part of the TSK. The binding signature will advertise /// `flags` key capabilities. If `subkey` is encrypted that caller must /// supply the password in `passwd`. pub fn with_subkey(self, subkey: Key, flags: &KeyFlags, passwd: Option<&Password>) -> Result { let sig = self.sign_subkey(&subkey, flags, passwd)?; let pkt = if subkey.secret().is_some() { Packet::SecretSubkey(subkey) } else { Packet::PublicSubkey(subkey) }; Ok(TSK{ key: self.key.merge_packets(vec![ pkt, Packet::Signature(sig) ])? }) } } impl Serialize for TSK { fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { self.key.primary.serialize(o, Tag::SecretKey)?; for s in self.key.primary_selfsigs.iter() { s.serialize(o)?; } for s in self.key.primary_self_revocations.iter() { s.serialize(o)?; } for s in self.key.primary_certifications.iter() { s.serialize(o)?; } for s in self.key.primary_other_revocations.iter() { s.serialize(o)?; } for u in self.key.userids() { u.userid().serialize(o)?; for s in u.self_revocations() { s.serialize(o)?; } for s in u.selfsigs() { s.serialize(o)?; } for s in u.other_revocations() { s.serialize(o)?; } for s in u.certifications() { s.serialize(o)?; } } for u in self.key.user_attributes() { u.user_attribute().serialize(o)?; for s in u.self_revocations() { s.serialize(o)?; } for s in u.selfsigs() { s.serialize(o)?; } for s in u.other_revocations() { s.serialize(o)?; } for s in u.certifications() { s.serialize(o)?; } } for k in self.key.subkeys() { k.subkey().serialize(o, Tag::SecretSubkey)?; for s in k.self_revocations() { s.serialize(o)?; } for s in k.selfsigs() { s.serialize(o)?; } for s in k.other_revocations() { s.serialize(o)?; } for s in k.certifications() { s.serialize(o)?; } } for u in self.unknowns.iter() { u.unknown.serialize(o)?; for s in u.sigs.iter() { s.serialize(o)?; } } for s in self.bad.iter() { s.serialize(o)?; } Ok(()) } } #[cfg(test)] mod tests { use super::*; use tpk::TPKBuilder; #[test] fn certification_direct_key() { let (tpk1, _) = TPKBuilder::default() .add_certification_subkey() .generate().unwrap(); let tsk = tpk1.into_tsk(); let (tpk2, _) = TPKBuilder::default() .generate().unwrap(); assert!(tsk.certify_key(&tpk2).is_err()); } #[test] fn certification_user_id() { let (tpk1, _) = TPKBuilder::default() .add_certification_subkey() .generate().unwrap(); let tsk = tpk1.into_tsk(); let (tpk2, _) = TPKBuilder::default() .add_userid("test1@example.com") .add_userid("test2@example.com") .generate().unwrap(); let sig = tsk.certify_key(&tpk2).unwrap(); let key = tsk.tpk().keys_all() .certification_capable().nth(0).unwrap().2; assert_eq!( sig.verify_userid_binding( key, tpk2.primary(), tpk2.userids().next().unwrap().userid()).unwrap(), true); } #[test] fn add_userid() { use std::str; let ui1 = b"test1@example.com"; let ui2 = b"test2@example.com"; let (tpk, _) = TPKBuilder::default() .add_userid(str::from_utf8(ui1).unwrap()) .generate().unwrap(); let tsk = TSK::from_tpk(tpk) .with_userid(UserID::from(str::from_utf8(ui2).unwrap())) .unwrap(); let userids = tsk .userids() .map(|binding| binding.userid().value()) .collect::>(); assert_eq!(userids.len(), 2); assert!((userids[0] == ui1 && userids[1] == ui2) ^ (userids[0] == ui2 && userids[1] == ui1)); } #[test] fn add_user_attr() { let (tpk, _) = TPKBuilder::default() .add_userid("test1@example.com") .generate().unwrap(); let tsk = TSK::from_tpk(tpk) .with_user_attribute(UserAttribute::from(Vec::from(&b"Hello, World"[..]))) .unwrap(); let userattrs = tsk .user_attributes() .map(|binding| binding.user_attribute().value()) .collect::>(); assert_eq!(userattrs.len(), 1); } #[test] fn add_subkey() { let (tpk, _) = TPKBuilder::default() .add_userid("test1@example.com") .generate().unwrap(); let key: Key = ::packet::key::Key4::generate_ecc(true, ::constants::Curve::Ed25519) .unwrap().into(); let flags = KeyFlags::default().set_sign(true); let tsk = TSK::from_tpk(tpk) .with_subkey(key, &flags, None) .unwrap(); let subkeys = tsk .subkeys() .map(|binding| binding.subkey()) .collect::>(); assert_eq!(subkeys.len(), 1); } #[test] fn user_ids() { let (tpk, _) = TPKBuilder::default() .add_userid("test1@example.com") .add_userid("test2@example.com") .generate().unwrap(); let userids = tpk .userids() .map(|binding| binding.userid().value()) .collect::>(); assert_eq!(userids.len(), 2); assert!((userids[0] == b"test1@example.com" && userids[1] == b"test2@example.com") || (userids[0] == b"test2@example.com" && userids[1] == b"test1@example.com"), "User ids: {:?}", userids); let (tpk, _) = TPKBuilder::autocrypt(None, Some("Foo")) .generate() .unwrap(); let userids = tpk .userids() .map(|binding| binding.userid().value()) .collect::>(); assert_eq!(userids.len(), 1); assert_eq!(userids[0], b"Foo"); let (tsk, _) = TSK::new(Some("test@example.com".into())).unwrap(); let tpk = tsk.into_tpk(); let userids = tpk .userids() .map(|binding| binding.userid().value()) .collect::>(); assert_eq!(userids.len(), 1); assert_eq!(userids[0], b"test@example.com", "User ids: {:?}", userids); } }