use std::{
collections::{BTreeMap, BTreeSet, HashMap},
io::{self, Cursor, Write},
time::SystemTime,
};
use sequoia_openpgp as openpgp;
use openpgp::{
armor,
cert::prelude::*,
parse::{
Parse,
PacketParser,
PacketParserResult,
buffered_reader::*,
stream::*,
},
packet::prelude::*,
policy::{
NullPolicy,
StandardPolicy,
},
serialize::{
Serialize,
stream::*,
},
types::*,
};
use openpgp::{
Cert,
Fingerprint,
KeyID,
};
use openpgp::crypto::{self, SessionKey};
use openpgp::packet::{key, Key, PKESK, SKESK};
use openpgp::policy::Policy;
pub use sop::{self, SOP};
use sop::{
*,
errors::*,
ops::{
ArmorLabel,
EncryptAs,
InlineSignAs,
SignAs,
SignatureMode,
Verification,
},
plumbing::PasswordsAreHumanReadable,
};
#[macro_use]
mod macros;
mod version;
pub struct Certs<'s> {
sop: &'s SQOP<'s>,
certs: Vec<openpgp::Cert>,
source_name: Option<String>,
}
impl<'s> sop::plumbing::SopRef<'s, SQOP<'s>> for Certs<'s> {
fn sop(&self) -> &'s SQOP<'s> {
self.sop
}
}
impl<'s> sop::Load<'s, SQOP<'s>> for Certs<'s> {
fn from_reader(sop: &'s SQOP, source: &mut (dyn io::Read + Send + Sync),
source_name: Option<String>)
-> Result<Self>
where
Self: Sized,
{
Ok(Certs {
sop,
certs: CertParser::from_reader(source).map_err(sop_error)?
.collect::<openpgp::Result<Vec<openpgp::Cert>>>()
.map_err(sop_error)?,
source_name,
})
}
fn source_name(&self) -> Option<&str> {
self.source_name.as_ref().map(|s| s.as_str())
}
}
impl sop::Save for Certs<'_> {
fn to_writer(&self, armored: bool, sink: &mut (dyn io::Write + Send + Sync))
-> Result<()>
{
if self.certs.len() == 1 {
let cert = &self.certs[0];
if armored {
cert.armored().serialize(sink).map_err(sop_error)?;
} else {
cert.serialize(sink).map_err(sop_error)?;
}
} else {
if armored {
let mut armorer =
armor::Writer::new(sink, armor::Kind::PublicKey)?;
for cert in &self.certs {
cert.serialize(&mut armorer).map_err(sop_error)?;
}
armorer.finalize()?;
} else {
for cert in &self.certs {
cert.serialize(sink).map_err(sop_error)?;
}
}
}
Ok(())
}
}
pub struct Keys<'s> {
sop: &'s SQOP<'s>,
keys: Vec<openpgp::Cert>,
source_name: Option<String>,
}
impl<'s> sop::plumbing::SopRef<'s, SQOP<'s>> for Keys<'s> {
fn sop(&self) -> &'s SQOP<'s> {
self.sop
}
}
impl<'s> sop::Load<'s, SQOP<'s>> for Keys<'s> {
fn from_reader(sop: &'s SQOP, source: &mut (dyn io::Read + Send + Sync),
source_name: Option<String>)
-> Result<Self>
where
Self: Sized,
{
Ok(Keys {
sop,
keys: CertParser::from_reader(source).map_err(sop_error)?
.collect::<openpgp::Result<Vec<openpgp::Cert>>>()
.map_err(sop_error)?,
source_name,
})
}
fn source_name(&self) -> Option<&str> {
self.source_name.as_ref().map(|s| s.as_str())
}
}
impl sop::Save for Keys<'_> {
fn to_writer(&self, armored: bool, sink: &mut (dyn io::Write + Send + Sync))
-> Result<()>
{
if self.keys.len() == 1 {
let key = &self.keys[0];
if armored {
key.as_tsk().armored().serialize(sink).map_err(sop_error)?;
} else {
key.as_tsk().serialize(sink).map_err(sop_error)?;
}
} else {
if armored {
let mut armorer =
armor::Writer::new(sink, armor::Kind::SecretKey)?;
for key in &self.keys {
key.as_tsk().serialize(&mut armorer).map_err(sop_error)?;
}
armorer.finalize()?;
} else {
for key in &self.keys {
key.as_tsk().serialize(sink).map_err(sop_error)?;
}
}
}
Ok(())
}
}
pub struct Sigs<'s> {
sop: &'s SQOP<'s>,
data: Vec<u8>,
source_name: Option<String>,
}
impl<'s> sop::plumbing::SopRef<'s, SQOP<'s>> for Sigs<'s> {
fn sop(&self) -> &'s SQOP<'s> {
self.sop
}
}
impl<'s> sop::Load<'s, SQOP<'s>> for Sigs<'s> {
fn from_reader(sop: &'s SQOP, source: &mut (dyn io::Read + Send + Sync),
source_name: Option<String>)
-> Result<Self>
where
Self: Sized,
{
let mut data = vec![];
source.read_to_end(&mut data)?;
Ok(Sigs {
sop,
data,
source_name,
})
}
fn source_name(&self) -> Option<&str> {
self.source_name.as_ref().map(|s| s.as_str())
}
}
impl sop::Save for Sigs<'_> {
fn to_writer(&self, armored: bool, sink: &mut (dyn io::Write + Send + Sync))
-> Result<()>
{
if armored && self.data.starts_with(
b"-----BEGIN PGP PUBLIC KEY BLOCK-----")
{
sink.write_all(&self.data)?;
return Ok(());
}
if armored {
self.sop.armor()?
.data(&mut Cursor::new(&self.data))?
.to_writer(sink)?;
} else {
self.sop.dearmor()?
.data(&mut Cursor::new(&self.data))?
.to_writer(sink)?;
}
Ok(())
}
}
pub struct SQOP<'s> {
policy: &'s dyn openpgp::policy::Policy,
debug: bool,
}
impl Default for SQOP<'_> {
fn default() -> Self {
const P: &StandardPolicy = &StandardPolicy::new();
Self::with_policy(P)
}
}
impl<'s> SQOP<'s> {
pub fn with_policy(policy: &'s dyn Policy) -> Self {
SQOP {
policy,
debug: false,
}
}
}
impl<'s> sop::SOP<'s> for SQOP<'s> {
type Keys = Keys<'s>;
type Certs = Certs<'s>;
type Sigs = Sigs<'s>;
fn debug(&mut self, enable: bool) {
self.debug = enable;
}
fn version(&self) -> Result<Box<dyn sop::ops::Version>> {
Ok(Box::new(version::Version {}))
}
fn sopv_version(&'s self) -> Result<&'static str> {
Ok("1.0")
}
fn generate_key(&'s self)
-> Result<Box<dyn sop::ops::GenerateKey<SQOP, Keys> + 's>>
{
GenerateKey::new(self)
}
fn change_key_password(&'s self)
-> Result<Box<dyn sop::ops::ChangeKeyPassword<SQOP, Keys> + 's>>
{
ChangeKeyPassword::new(self)
}
fn revoke_key(&'s self)
-> Result<Box<dyn sop::ops::RevokeKey<SQOP<'s>, Certs, Keys> + 's>>
{
RevokeKey::new(self)
}
fn extract_cert(&'s self)
-> Result<Box<dyn sop::ops::ExtractCert<SQOP, Certs, Keys> + 's>>
{
ExtractCert::new(self)
}
fn update_key(&'s self)
-> Result<Box<dyn sop::ops::UpdateKey<Self, Self::Certs, Self::Keys> + 's>>
{
Err(Error::NotImplemented)
}
fn merge_certs(&'s self)
-> Result<Box<dyn sop::ops::MergeCerts<Self, Self::Certs> + 's>>
{
MergeCerts::new(self)
}
fn certify_userid(&'s self)
-> Result<Box<dyn sop::ops::CertifyUserID<Self, Self::Certs, Self::Keys> + 's>>
{
CertifyUserID::new(self)
}
fn validate_userid(&'s self)
-> Result<Box<dyn sop::ops::ValidateUserID<Self, Self::Certs> + 's>>
{
ValidateUserID::new(self)
}
fn sign(&'s self)
-> Result<Box<dyn sop::ops::Sign<SQOP, Keys, Sigs> + 's>>
{
Sign::new(self)
}
fn verify(&'s self)
-> Result<Box<dyn sop::ops::Verify<SQOP, Certs, Sigs> + 's>>
{
Verify::new(self)
}
fn encrypt(&'s self)
-> Result<Box<dyn sop::ops::Encrypt<SQOP, Certs, Keys> + 's>> {
Encrypt::new(self)
}
fn decrypt(&'s self)
-> Result<Box<dyn sop::ops::Decrypt<SQOP, Certs, Keys> + 's>> {
Decrypt::new(self)
}
fn armor(&'s self) -> Result<Box<dyn sop::ops::Armor + 's>> {
Armor::new(self)
}
fn dearmor(&'s self) -> Result<Box<dyn sop::ops::Dearmor + 's>> {
Dearmor::new(self)
}
fn inline_detach(&'s self)
-> Result<Box<dyn sop::ops::InlineDetach<Sigs> + 's>>
{
InlineDetach::new(self)
}
fn inline_verify(&'s self)
-> Result<Box<dyn sop::ops::InlineVerify<SQOP, Certs> + 's>>
{
InlineVerify::new(self)
}
fn inline_sign(&'s self)
-> Result<Box<dyn sop::ops::InlineSign<SQOP, Keys> + 's>>
{
InlineSign::new(self)
}
}
struct GenerateKey<'s> {
#[allow(dead_code)]
sqop: &'s SQOP<'s>,
profile: &'static str,
signing_only: bool,
with_key_password: Option<Password>,
userids: Vec<String>,
}
impl<'s> GenerateKey<'s> {
const PROFILE_EDDSA: &'static str = "draft-koch-eddsa-for-openpgp-00";
const PROFILE_RFC4880: &'static str = "rfc4880";
const PROFILES: &'static [(&'static str, &'static str)] = &[
(Self::PROFILE_EDDSA, "use EdDSA & ECDH over Cv25519"),
(Self::PROFILE_RFC4880, "use RSA with 3072 bit keys"),
];
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::GenerateKey<'s, SQOP<'s>, Keys<'s>> + 's>> {
Ok(Box::new(GenerateKey {
sqop,
profile: Self::PROFILE_EDDSA,
signing_only: false,
with_key_password: Default::default(),
userids: Default::default(),
}))
}
}
impl<'s> sop::ops::GenerateKey<'s, SQOP<'s>, Keys<'s>> for GenerateKey<'s> {
fn list_profiles(&self) -> Vec<(String, String)> {
Self::PROFILES.iter()
.map(|(p, d)| (p.to_string(), d.to_string()))
.collect()
}
fn profile(mut self: Box<Self>, profile: &str)
-> Result<Box<dyn sop::ops::GenerateKey<'s, SQOP<'s>, Keys<'s>> + 's>> {
self.profile = match profile {
Self::PROFILE_EDDSA | "default" => Self::PROFILE_EDDSA,
Self::PROFILE_RFC4880 => Self::PROFILE_RFC4880,
_ => return Err(Error::UnsupportedProfile),
};
Ok(self)
}
fn signing_only(mut self: Box<Self>) -> Box<dyn sop::ops::GenerateKey<'s, SQOP<'s>, Keys<'s>> + 's> {
self.signing_only = true;
self
}
fn with_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::GenerateKey<'s, SQOP<'s>, Keys<'s>> + 's>> {
self.with_key_password = Some(password);
Ok(self)
}
fn userid(mut self: Box<Self>, userid: &str)
-> Box<dyn sop::ops::GenerateKey<'s, SQOP<'s>, Keys<'s>> + 's> {
self.userids.push(userid.into());
self
}
fn generate(self: Box<Self>) -> Result<Keys<'s>> {
let mut builder = CertBuilder::new();
builder = builder.add_signing_subkey();
if ! self.signing_only {
builder = builder.add_subkey(
KeyFlags::empty()
.set_storage_encryption()
.set_transport_encryption(),
None,
None);
}
builder = builder.set_cipher_suite(match self.profile {
Self::PROFILE_EDDSA => CipherSuite::Cv25519,
Self::PROFILE_RFC4880 => CipherSuite::RSA3k,
_ => return Err(Error::UnsupportedProfile),
});
for u in self.userids {
builder = builder.add_userid(u);
}
if let Some(p) = self.with_key_password {
builder = builder.set_password(Some(p.normalized().into()));
}
let (key, _) = builder.generate().map_err(sop_error)?;
Ok(Keys {
sop: self.sqop,
keys: vec![key],
source_name: None,
})
}
}
struct ChangeKeyPassword<'s> {
#[allow(dead_code)]
sqop: &'s SQOP<'s>,
new_password: Option<openpgp::crypto::Password>,
old_password: Vec<openpgp::crypto::Password>,
}
impl<'s> ChangeKeyPassword<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::ChangeKeyPassword<'s, SQOP<'s>, Keys<'s>> + 's>> {
Ok(Box::new(ChangeKeyPassword {
sqop,
new_password: None,
old_password: vec![],
}))
}
}
impl<'s> sop::ops::ChangeKeyPassword<'s, SQOP<'s>, Keys<'s>> for ChangeKeyPassword<'s> {
fn new_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::ChangeKeyPassword<'s, SQOP<'s>, Keys<'s>> + 's>> {
self.new_password = Some(password.normalized().into());
Ok(self)
}
fn old_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::ChangeKeyPassword<'s, SQOP<'s>, Keys<'s>> + 's>> {
for p in password.variants() {
self.old_password.push(p.into());
}
Ok(self)
}
fn keys(self: Box<Self>, keys: &Keys) -> Result<Keys<'s>> {
let mut result = vec![];
for key in keys.keys.clone() {
let fp = key.fingerprint();
let mut key_packets =
key.keys().unencrypted_secret()
.map(|ka| ka.key().clone())
.collect::<Vec<_>>();
for locked_key in key.keys().secret()
.filter(|ka| ka.secret().is_encrypted())
.map(|ka| ka.key())
{
let mut unlocked = false;
for p in &self.old_password {
if let Ok(k) = locked_key.clone().decrypt_secret(p) {
key_packets.push(k);
unlocked = true;
break;
}
}
if ! unlocked {
return Err(Error::KeyIsProtected);
}
}
if let Some(p) = &self.new_password {
for k in std::mem::take(&mut key_packets) {
key_packets.push(k.encrypt_secret(&p).map_err(sop_error)?);
}
}
let (key, _changed) =
key.insert_packets2(
key_packets.into_iter().map(|k| -> openpgp::Packet {
if k.fingerprint() == fp {
k.role_into_primary().into()
} else {
k.role_into_subordinate().into()
}
}))
.map_err(sop_error)?;
result.push(key);
}
Ok(Keys {
sop: self.sqop,
keys: result,
source_name: None,
})
}
}
struct RevokeKey<'s> {
#[allow(dead_code)]
sqop: &'s SQOP<'s>,
key_password: Vec<openpgp::crypto::Password>,
}
impl<'s> RevokeKey<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::RevokeKey<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
Ok(Box::new(RevokeKey {
sqop,
key_password: vec![],
}))
}
}
impl<'s> sop::ops::RevokeKey<'s, SQOP<'s>, Certs<'s>, Keys<'s>> for RevokeKey<'s> {
fn with_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::RevokeKey<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
for p in password.variants() {
self.key_password.push(p.into());
}
Ok(self)
}
fn keys(self: Box<Self>, keys: &Keys) -> Result<Certs<'s>> {
let mut results = vec![];
for key in &keys.keys {
let mut primary = match key.primary_key().key().parts_as_secret() {
Ok(p) => p.clone(),
Err(_) => return Err(Error::BadData),
};
if primary.secret().is_encrypted() {
let mut unlocked = false;
for p in &self.key_password {
if let Ok(k) = primary.clone().decrypt_secret(p) {
primary = k;
unlocked = true;
break;
}
}
if ! unlocked {
return Err(Error::KeyIsProtected);
}
}
let mut signer = primary.into_keypair().map_err(sop_error)?;
let revocation = key.revoke(&mut signer,
ReasonForRevocation::Unspecified,
b"unspecified").map_err(sop_error)?;
results.push(key.clone().insert_packets2(
std::iter::once(openpgp::Packet::from(revocation)))
.map(|(cert, _changed)| cert)
.map_err(sop_error)?
.strip_secret_key_material());
}
Ok(Certs {
sop: self.sqop,
certs: results,
source_name: None,
})
}
}
struct ExtractCert<'s> {
#[allow(dead_code)]
sqop: &'s SQOP<'s>,
}
impl<'s> ExtractCert<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::ExtractCert<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
Ok(Box::new(ExtractCert {
sqop,
}))
}
}
impl<'s> sop::ops::ExtractCert<'s, SQOP<'s>, Certs<'s>, Keys<'s>> for ExtractCert<'s> {
fn keys(self: Box<Self>, keys: &Keys) -> Result<Certs<'s>> {
Ok(Certs {
sop: self.sqop,
certs: keys.keys.iter()
.map(|c| c.clone().strip_secret_key_material())
.collect::<Vec<_>>(),
source_name: None,
})
}
}
struct MergeCerts<'s> {
#[allow(dead_code)]
sqop: &'s SQOP<'s>,
updates: BTreeMap<openpgp::Fingerprint, openpgp::Cert>,
}
impl<'s> MergeCerts<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::MergeCerts<'s, SQOP<'s>, Certs<'s>> + 's>> {
Ok(Box::new(MergeCerts {
sqop,
updates: Default::default(),
}))
}
fn merge_internal(&mut self, updates: &Certs) -> Result<()> {
use std::collections::btree_map::Entry;
for cert in updates.certs.iter().cloned() {
match self.updates.entry(cert.fingerprint()) {
Entry::Occupied(e) => {
let e = e.into_mut();
*e = e.clone().merge_public(cert).map_err(sop_error)?;
},
Entry::Vacant(e) => {
e.insert(cert);
},
}
}
Ok(())
}
}
impl<'s> sop::ops::MergeCerts<'s, SQOP<'s>, Certs<'s>> for MergeCerts<'s> {
fn merge_updates(mut self: Box<Self>, updates: &Certs)
-> Result<Box<dyn sop::ops::MergeCerts<'s, SQOP<'s>, Certs<'s>> + 's>> {
self.merge_internal(updates)?;
Ok(self)
}
fn merge(mut self: Box<Self>, certs: &Certs) -> Result<Certs<'s>> {
let fps: BTreeSet<_> =
certs.certs.iter().map(openpgp::Cert::fingerprint).collect();
self.merge_internal(certs)?;
Ok(Certs {
sop: self.sqop,
certs: fps.iter()
.map(|fp| self.updates.get(fp).expect("have ingested certs").clone())
.collect(),
source_name: None,
})
}
}
struct CertifyUserID<'s> {
#[allow(dead_code)]
sqop: &'s SQOP<'s>,
userids: Vec<String>,
with_key_password: Vec<Password>,
no_require_self_sig: bool,
certifiers: Vec<openpgp::Cert>,
}
impl<'s> CertifyUserID<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::CertifyUserID<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
Ok(Box::new(CertifyUserID {
sqop,
userids: Default::default(),
with_key_password: Default::default(),
no_require_self_sig: false,
certifiers: Default::default(),
}))
}
}
impl<'s> sop::ops::CertifyUserID<'s, SQOP<'s>, Certs<'s>, Keys<'s>> for CertifyUserID<'s> {
fn userid(mut self: Box<Self>, userid: String)
-> Box<dyn sop::ops::CertifyUserID<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's> {
self.userids.push(userid);
self
}
fn with_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::CertifyUserID<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.with_key_password.push(password);
Ok(self)
}
fn no_require_self_sig(mut self: Box<Self>)
-> Box<dyn sop::ops::CertifyUserID<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's> {
self.no_require_self_sig = true;
self
}
fn keys(mut self: Box<Self>, keys: &Keys)
-> Result<Box<dyn sop::ops::CertifyUserID<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
for key in &keys.keys {
self.certifiers.push(key.clone());
}
Ok(self)
}
fn certify(self: Box<Self>, certs: &Certs)-> Result<Certs<'s>> {
if self.certifiers.is_empty() {
return Err(Error::MissingArg);
}
let mut certifiers = Vec::with_capacity(self.certifiers.len());
for cert in &self.certifiers {
let vcert =
cert.with_policy(self.sqop.policy, None)
.map_err(|_| Error::KeyCannotSign)?;
let mut one = false;
for mut key in vcert.keys()
.supported()
.secret()
.alive()
.revoked(false)
.for_certification()
.map(|ka| ka.key().clone())
{
let algo = key.pk_algo();
if key.secret().is_encrypted() {
for p in self.with_key_password.iter()
.flat_map(|p| p.variants())
{
if key.secret_mut().decrypt_in_place(algo, &p.into())
.is_ok()
{
break;
}
}
}
if ! key.secret().is_encrypted() {
one = true;
certifiers.push(key.into_keypair().map_err(sop_error)?);
break;
}
}
if ! one {
return Err(Error::KeyIsProtected);
}
}
let mut outputs = Vec::new();
for cert in &certs.certs {
let mut acc = Vec::<openpgp::Packet>::new();
for new_userid in &self.userids {
if ! self.no_require_self_sig
&& ! cert.userids().any(|u| u.value() == new_userid.as_bytes())
{
return Err(Error::UnspecifiedFailure);
}
let uid = openpgp::packet::UserID::from(new_userid.as_str());
acc.push(uid.clone().into());
for certifier in &mut certifiers {
let sig = uid.bind(certifier, cert,
SignatureBuilder::new(
SignatureType::PositiveCertification))
.map_err(sop_error)?;
acc.push(sig.into());
}
}
outputs.push(cert.clone().insert_packets(acc)
.map_err(sop_error)?);
}
Ok(Certs {
sop: self.sqop,
certs: outputs,
source_name: None,
})
}
}
struct ValidateUserID<'s> {
#[allow(dead_code)]
sqop: &'s SQOP<'s>,
trust_roots: Vec<openpgp::Cert>,
target_certs: Vec<openpgp::Cert>,
validate_at: Option<SystemTime>,
}
impl<'s> ValidateUserID<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::ValidateUserID<'s, SQOP<'s>, Certs<'s>> + 's>> {
Ok(Box::new(ValidateUserID {
sqop,
trust_roots: Default::default(),
target_certs: Default::default(),
validate_at: Default::default(),
}))
}
fn authenticate(&self, userid: &str, email: bool) -> Result<()> {
tracer!(self.sqop.debug, "ValidateUserID::authenticate");
use sequoia_wot::{FULLY_TRUSTED, Network};
t!("authenticating {} {:?}",
if email { "email" } else { "user ID" }, userid);
let roots: Vec<_> =
self.trust_roots.iter().map(openpgp::Cert::fingerprint).collect();
t!("using trust roots {:?}", roots);
let n = Network::from_cert_refs(
self.trust_roots.iter().chain(self.target_certs.iter()),
self.sqop.policy,
self.validate_at.clone(),
&roots[..])
.map_err(sop_error)?;
if ! email {
let uid = openpgp::packet::UserID::from(userid);
for cert in &self.target_certs {
t!("considering {}", cert.fingerprint());
let paths =
n.authenticate(&uid, cert.fingerprint(), FULLY_TRUSTED);
t!("authenticate({:?}, {}) => {}",
cert.fingerprint(), userid, paths.amount());
if paths.amount() < FULLY_TRUSTED {
return Err(Error::CertUseridNoMatch);
}
}
} else {
for cert in &self.target_certs {
t!("considering {}", cert.fingerprint());
let mut one = false;
for uid in cert.userids()
.filter(|u| u.userid().email2().ok().flatten()
.map(|email| email == userid)
.unwrap_or(false))
{
let paths =
n.authenticate(uid.userid(), cert.fingerprint(),
FULLY_TRUSTED);
t!("authenticate({}, {:?}) => {}",
cert.fingerprint(), userid, paths.amount());
if paths.amount() >= FULLY_TRUSTED {
one = true;
break;
}
}
if ! one {
return Err(Error::CertUseridNoMatch);
}
}
}
Ok(())
}
}
impl<'s> sop::ops::ValidateUserID<'s, SQOP<'s>, Certs<'s>> for ValidateUserID<'s> {
fn trust_roots(mut self: Box<Self>, certs: &Certs)
-> Result<Box<dyn sop::ops::ValidateUserID<'s, SQOP<'s>, Certs<'s>> + 's>> {
for cert in &certs.certs {
self.trust_roots.push(cert.clone());
}
Ok(self)
}
fn target_certs(mut self: Box<Self>, certs: &Certs)
-> Result<Box<dyn sop::ops::ValidateUserID<'s, SQOP<'s>, Certs<'s>> + 's>> {
for cert in &certs.certs {
self.target_certs.push(cert.clone());
}
Ok(self)
}
fn validate_at(mut self: Box<Self>, at: SystemTime)
-> Result<Box<dyn sop::ops::ValidateUserID<'s, SQOP<'s>, Certs<'s>> + 's>> {
self.validate_at = Some(at);
Ok(self)
}
fn userid(self: Box<Self>, userid: &str) -> Result<()> {
self.authenticate(userid, false)
}
fn email(self: Box<Self>, address: &str) -> Result<()> {
self.authenticate(address, true)
}
}
struct Sign<'s> {
sqop: &'s SQOP<'s>,
mode: SignAs,
hash_algos: Vec<HashAlgorithm>,
with_key_password: Vec<Password>,
signers: Vec<openpgp::Cert>,
}
impl<'s> Sign<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::Sign<'s, SQOP<'s>, Keys<'s>, Sigs<'s>> + 's>> {
Ok(Box::new(Self::unboxed(sqop)))
}
fn unboxed(sqop: &'s SQOP) -> Sign<'s> {
Sign {
sqop,
mode: Default::default(),
hash_algos: [
HashAlgorithm::SHA512,
HashAlgorithm::SHA384,
HashAlgorithm::SHA256,
HashAlgorithm::SHA224,
].iter().copied().filter(|h| h.is_supported()).collect(),
with_key_password: Default::default(),
signers: Default::default(),
}
}
}
impl Sign<'_> {
fn add_signing_keys(&mut self, keys: &Keys) -> Result<()> {
for key in &keys.keys {
self.add_signing_cert(key)?;
}
Ok(())
}
fn add_signing_cert(&mut self, cert: &Cert) -> Result<()> {
let vcert =
cert.with_policy(self.sqop.policy, None)
.map_err(|_| Error::KeyCannotSign)?;
if let Some(p) = vcert.preferred_hash_algorithms() {
self.hash_algos.retain(|a| p.contains(a));
}
let mut one = false;
for _key in vcert.keys()
.supported()
.secret()
.alive()
.revoked(false)
.for_signing()
.map(|ka| ka.key())
{
one = true;
break;
}
self.signers.push(cert.clone());
if one {
Ok(())
} else {
Err(Error::KeyCannotSign)
}
}
fn make_signers(&self) -> Result<Vec<openpgp::crypto::KeyPair>> {
let mut signers = Vec::with_capacity(self.signers.len());
for cert in &self.signers {
let vcert =
cert.with_policy(self.sqop.policy, None)
.map_err(|_| Error::KeyCannotSign)?;
let mut one = false;
for mut key in vcert.keys()
.supported()
.secret()
.alive()
.revoked(false)
.for_signing()
.map(|ka| ka.key().clone())
{
let algo = key.pk_algo();
if key.secret().is_encrypted() {
for p in self.with_key_password.iter()
.flat_map(|p| p.variants())
{
if key.secret_mut().decrypt_in_place(algo, &p.into())
.is_ok()
{
break;
}
}
}
if ! key.secret().is_encrypted() {
one = true;
signers.push(key.into_keypair().map_err(sop_error)?);
break;
}
}
if ! one {
return Err(Error::KeyIsProtected);
}
}
Ok(signers)
}
}
impl<'s> sop::ops::Sign<'s, SQOP<'s>, Keys<'s>, Sigs<'s>> for Sign<'s> {
fn mode(mut self: Box<Self>, mode: SignAs)
-> Box<dyn sop::ops::Sign<'s, SQOP<'s>, Keys<'s>, Sigs<'s>> + 's> {
self.mode = mode;
self
}
fn keys(mut self: Box<Self>, keys: &Keys)
-> Result<Box<dyn sop::ops::Sign<'s, SQOP<'s>, Keys<'s>, Sigs<'s>> + 's>> {
self.add_signing_keys(keys)?;
Ok(self)
}
fn with_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::Sign<'s, SQOP<'s>, Keys<'s>, Sigs<'s>> + 's>> {
self.with_key_password.push(password);
Ok(self)
}
fn data(self: Box<Self>, data: &mut (dyn io::Read + Send + Sync))
-> Result<(sop::ops::Micalg, Sigs<'s>)>
{
if self.signers.is_empty() {
return Err(Error::MissingArg);
}
let mut buf = vec![];
let message = Message::new(&mut buf);
let mut signers = self.make_signers()?;
let mut signer = Signer::with_template(
message,
signers.pop().expect("at least one"),
signature::SignatureBuilder::new(into_sig_type(self.mode)))
.hash_algo(
self.hash_algos.get(0).cloned().unwrap_or_default()
).map_err(sop_error)?
.detached();
for s in signers {
signer = signer.add_signer(s);
}
let mut message = signer.build().map_err(sop_error)?;
io::copy(data, &mut message)?;
message.finalize().map_err(sop_error)?;
Ok((u8::from(HashAlgorithm::default()).into(),
Sigs::from_bytes(self.sqop, &buf)?))
}
}
struct Verify<'s> {
sqop: &'s SQOP<'s>,
verbose: bool,
not_before: Option<SystemTime>,
not_after: Option<SystemTime>,
certs: CertBag,
}
impl<'s> Verify<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::Verify<'s, SQOP<'s>, Certs<'s>, Sigs<'s>> + 's>> {
Ok(Box::new(Self::unboxed(sqop)))
}
fn unboxed(sqop: &'s SQOP) -> Verify<'s>
where
's: 's,
{
Verify {
sqop,
verbose: Default::default(),
not_before: Default::default(),
not_after: Default::default(),
certs: Default::default(),
}
}
}
impl<'s> sop::ops::Verify<'s, SQOP<'s>, Certs<'s>, Sigs<'s>> for Verify<'s> {
fn not_before(mut self: Box<Self>, t: SystemTime)
-> Box<dyn sop::ops::Verify<'s, SQOP<'s>, Certs<'s>, Sigs<'s>> + 's> {
self.not_before = Some(t);
self
}
fn not_after(mut self: Box<Self>, t: SystemTime)
-> Box<dyn sop::ops::Verify<'s, SQOP<'s>, Certs<'s>, Sigs<'s>> + 's> {
self.not_after = Some(t);
self
}
fn certs(mut self: Box<Self>, cert: &Certs)
-> Result<Box<dyn sop::ops::Verify<'s, SQOP<'s>, Certs<'s>, Sigs<'s>> + 's>> {
self.certs.extend_from(cert);
Ok(self)
}
fn signatures<'sigs>(self: Box<Self>, signatures: &'sigs Sigs)
-> Result<Box<dyn sop::ops::VerifySignatures<'sigs> + 'sigs>>
where
's: 'sigs,
{
Ok(Box::new(VerifySignatures {
verify: *self,
signatures: signatures,
}))
}
}
struct VerifySignatures<'s, 'sigs> {
verify: Verify<'s>,
signatures: &'sigs Sigs<'s>,
}
impl sop::ops::VerifySignatures<'_> for VerifySignatures<'_, '_> {
fn data(self: Box<Self>, data: &mut (dyn io::Read + Send + Sync))
-> Result<Vec<sop::ops::Verification>> {
if self.verify.certs.is_empty() {
return Err(Error::MissingArg);
}
let helper = VHelper::new(
if self.verify.verbose {
Box::new(io::stderr())
} else {
Box::new(io::sink())
},
1,
self.verify.not_before,
self.verify.not_after,
self.verify.certs);
let mut v =
DetachedVerifierBuilder::from_bytes(&self.signatures.data)
.map_err(sop_error)?
.with_policy(self.verify.sqop.policy, None, helper)
.map_err(sop_error)?;
v.verify_reader(data).map_err(sop_error)?;
Ok(v.into_helper().verifications)
}
}
struct Encrypt<'s> {
no_armor: bool,
sign: Sign<'s>,
profile: &'static str,
mode: EncryptAs,
symmetric_algos: Vec<SymmetricAlgorithm>,
recipients: Vec<Key<key::PublicParts, key::UnspecifiedRole>>,
passwords: Vec<Password>,
}
impl<'s> Encrypt<'s> {
const PROFILE_RFC4880: &'static str = "rfc4880";
const PROFILES: &'static [(&'static str, &'static str)] = &[
(Self::PROFILE_RFC4880, "use SEIPDv1"),
];
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
Ok(Box::new(Encrypt {
no_armor: false,
sign: Sign::unboxed(sqop),
profile: Self::PROFILE_RFC4880,
mode: Default::default(),
symmetric_algos: [
SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES192,
SymmetricAlgorithm::AES128,
SymmetricAlgorithm::Camellia256,
SymmetricAlgorithm::Camellia192,
SymmetricAlgorithm::Camellia128,
SymmetricAlgorithm::Blowfish,
SymmetricAlgorithm::Twofish,
].iter().copied().filter(|&a| {
a.is_supported() && sqop.policy.symmetric_algorithm(a).is_ok()
}).collect(),
recipients: Default::default(),
passwords: Default::default(),
}))
}
fn add_cert(mut self: Box<Self>, cert: &Cert)
-> Result<Box<Self>> {
let vcert = cert.with_policy(self.sign.sqop.policy, None)
.map_err(|_| Error::CertCannotEncrypt)?;
if let Some(p) = vcert.preferred_hash_algorithms() {
self.sign.hash_algos.retain(|a| p.contains(a));
}
if let Some(p) = vcert.preferred_symmetric_algorithms() {
self.symmetric_algos.retain(|a| p.contains(a));
}
let mut one = false;
for key in vcert.keys()
.supported()
.alive()
.revoked(false)
.for_storage_encryption()
.for_transport_encryption()
.map(|ka| ka.key())
{
self.recipients.push(key.clone());
one = true;
}
if one {
Ok(self)
} else {
Err(Error::CertCannotEncrypt)
}
}
}
impl<'s> sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> for Encrypt<'s> {
fn no_armor(mut self: Box<Self>) -> Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's> {
self.no_armor = true;
self
}
fn list_profiles(&self) -> Vec<(String, String)> {
Self::PROFILES.iter()
.map(|(p, d)| (p.to_string(), d.to_string()))
.collect()
}
fn profile(mut self: Box<Self>, profile: &str)
-> Result<Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.profile = match profile {
Self::PROFILE_RFC4880 | "default" => Self::PROFILE_RFC4880,
_ => return Err(Error::UnsupportedProfile),
};
Ok(self)
}
fn mode(mut self: Box<Self>, mode: EncryptAs) -> Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's> {
self.sign.mode = mode.into();
self.mode = mode;
self
}
fn sign_with_keys(mut self: Box<Self>,
keys: &Keys)
-> Result<Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.sign.add_signing_keys(keys)?;
Ok(self)
}
fn with_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.sign.with_key_password.push(password);
Ok(self)
}
fn with_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.passwords.push(password);
Ok(self)
}
fn with_certs(mut self: Box<Self>, certs: &Certs)
-> Result<Box<dyn sop::ops::Encrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
for cert in &certs.certs {
self = self.add_cert(cert)?;
}
Ok(self)
}
fn plaintext<'d>(self: Box<Self>,
plaintext: &'d mut (dyn io::Read + Send + Sync))
-> Result<Box<dyn sop::ops::Ready<Option<sop::SessionKey>> + 'd>>
where
's: 'd
{
Ok(Box::new(EncryptReady {
encrypt: *self,
plaintext,
}))
}
}
struct EncryptReady<'s> {
encrypt: Encrypt<'s>,
plaintext: &'s mut (dyn io::Read + Send + Sync),
}
impl<'s> sop::ops::Ready<Option<sop::SessionKey>> for EncryptReady<'s> {
fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
-> Result<Option<sop::SessionKey>>
{
if self.encrypt.recipients.is_empty()
&& self.encrypt.passwords.is_empty()
{
return Err(Error::MissingArg);
}
let mut message = Message::new(sink);
if ! self.encrypt.no_armor {
message = Armorer::new(message).build().map_err(sop_error)?;
}
let cipher =
self.encrypt.symmetric_algos.get(0).cloned().unwrap_or_default();
let session_key =
SessionKey::new(cipher.key_size().map_err(sop_error)?);
let sop_session_key = sop::SessionKey::new(cipher, &session_key)?;
let mut message =
Encryptor2::with_session_key(message, cipher, session_key)
.map_err(sop_error)?
.add_recipients(self.encrypt.recipients.iter())
.add_passwords(
self.encrypt.passwords.into_iter()
.map(|p| crypto::Password::from(p.normalized())))
.symmetric_algo(cipher)
.build().map_err(sop_error)?;
let mut signers = self.encrypt.sign.make_signers()?;
if let Some(first) = signers.pop() {
let mut signer = Signer::with_template(
message, first,
signature::SignatureBuilder::new(into_sig_type(self.encrypt.sign.mode)))
.hash_algo(
self.encrypt.sign.hash_algos.get(0).cloned().unwrap_or_default())
.map_err(sop_error)?;
for s in signers {
signer = signer.add_signer(s);
}
message = signer.build().map_err(sop_error)?;
}
let mut message = LiteralWriter::new(message)
.format(into_data_format(self.encrypt.mode))
.build().map_err(sop_error)?;
io::copy(self.plaintext, &mut message)?;
message.finalize().map_err(sop_error)?;
Ok(Some(sop_session_key))
}
}
struct Decrypt<'s> {
verify: Verify<'s>,
session_keys: Vec<sop::SessionKey>,
passwords: Vec<Password>,
keys: Vec<Cert>,
with_key_password: Vec<Password>,
}
impl<'s> Decrypt<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
Ok(Box::new(Decrypt {
verify: Verify::unboxed(sqop),
session_keys: Default::default(),
passwords: Default::default(),
keys: Default::default(),
with_key_password: Default::default(),
}))
}
}
impl<'s> sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> for Decrypt<'s> {
fn verify_not_before(mut self: Box<Self>, t: SystemTime)
-> Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's> {
self.verify.not_before = Some(t);
self
}
fn verify_not_after(mut self: Box<Self>, t: SystemTime)
-> Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's> {
self.verify.not_after = Some(t);
self
}
fn verify_with_certs(mut self: Box<Self>,
certs: &Certs)
-> Result<Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.verify.certs.extend_from(certs);
Ok(self)
}
fn with_session_key(mut self: Box<Self>, sk: sop::SessionKey)
-> Result<Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.session_keys.push(sk);
Ok(self)
}
fn with_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.passwords.push(password);
Ok(self)
}
fn with_keys(mut self: Box<Self>, keys: &Keys)
-> Result<Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
for key in &keys.keys {
self.keys.push(key.clone());
}
Ok(self)
}
fn with_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::Decrypt<'s, SQOP<'s>, Certs<'s>, Keys<'s>> + 's>> {
self.with_key_password.push(password);
Ok(self)
}
fn ciphertext<'d>(self: Box<Self>,
ciphertext: &'d mut (dyn io::Read + Send + Sync))
-> Result<Box<dyn sop::ops::Ready<(Option<sop::SessionKey>,
Vec<sop::ops::Verification>)> + 'd>>
where
's: 'd
{
Ok(Box::new(DecryptReady {
decrypt: *self,
ciphertext,
}))
}
}
struct DecryptReady<'s> {
decrypt: Decrypt<'s>,
ciphertext: &'s mut (dyn io::Read + Send + Sync),
}
impl<'s> sop::ops::Ready<(Option<sop::SessionKey>, Vec<sop::ops::Verification>)>
for DecryptReady<'s>
{
fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
-> Result<(Option<sop::SessionKey>, Vec<sop::ops::Verification>)> {
let vhelper = VHelper::new(
if self.decrypt.verify.verbose {
Box::new(io::stderr())
} else {
Box::new(io::sink())
},
0,
self.decrypt.verify.not_before,
self.decrypt.verify.not_after,
self.decrypt.verify.certs);
let helper = Helper::new(self.decrypt.verify.sqop.policy,
vhelper,
self.decrypt.session_keys,
self.decrypt.passwords,
self.decrypt.with_key_password,
self.decrypt.keys);
let mut d = DecryptorBuilder::from_reader(self.ciphertext)
.map_err(sop_error)?
.with_policy(self.decrypt.verify.sqop.policy, None, helper)
.map_err(sop_error)?;
io::copy(&mut d, sink)?;
let helper = d.into_helper();
Ok((helper.session_key, helper.vhelper.verifications))
}
}
struct Armor<'s> {
sqop: &'s SQOP<'s>,
label: ArmorLabel,
}
impl<'s> Armor<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::Armor<'s> + 's>> {
Ok(Box::new(Armor {
sqop,
label: Default::default(),
}))
}
}
impl<'s> sop::ops::Armor<'s> for Armor<'s> {
fn label(mut self: Box<Self>, label: ArmorLabel)
-> Box<dyn sop::ops::Armor<'s> + 's> {
self.label = label;
self
}
fn data<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
-> Result<Box<dyn sop::ops::Ready + 'd>>
where
's: 'd
{
Ok(Box::new(ArmorReady {
armor: *self,
data,
}))
}
}
struct ArmorReady<'s> {
armor: Armor<'s>,
data: &'s mut (dyn io::Read + Send + Sync),
}
impl<'s> sop::ops::Ready for ArmorReady<'s> {
fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
-> Result<()> {
let _ = self.armor.sqop;
let mut source = Generic::new(self.data, None).into_boxed();
let armored =
source.data(1)?.get(0).map(|b| b & 0x80 == 0).unwrap_or(false);
let mut headers: Vec<(String, String)> = vec![];
if armored {
let mut r = armor::Reader::from_reader(
source, armor::ReaderMode::Tolerant(None));
headers = r.headers()?.iter().cloned().collect();
source = Box::new(Adapter::new(r));
}
fn make<'s>(sink: &'s mut (dyn io::Write + Send + Sync),
kind: armor::Kind,
headers: Vec<(String, String)>)
-> Result<Message>
{
let m = Message::new(sink);
let mut a = Armorer::new(m).kind(kind);
for (k, v) in headers {
a = a.add_header(k, v);
}
a.build().map_err(sop_error)
}
let mut sink = match self.armor.label {
ArmorLabel::Auto => {
source = Dup::new(source).into_boxed();
let ppr = PacketParser::from_reader(&mut source)
.map_err(sop_error)?;
let kind =
if let PacketParserResult::Some(pp) = &ppr {
match &pp.packet {
Packet::Signature(_) =>
armor::Kind::Signature,
Packet::SecretKey(_) =>
armor::Kind::SecretKey,
Packet::PublicKey(_) =>
armor::Kind::PublicKey,
Packet::PKESK(_) | Packet::SKESK(_)
| Packet::OnePassSig(_) =>
armor::Kind::Message,
_ => return Err(Error::BadData),
}
} else {
return Err(Error::BadData);
};
drop(ppr);
source = source.into_inner()
.expect("the Dup to be popped off");
make(sink, kind, headers)?
},
ArmorLabel::Sig => make(sink, armor::Kind::Signature, headers)?,
ArmorLabel::Key => make(sink, armor::Kind::SecretKey, headers)?,
ArmorLabel::Cert => make(sink, armor::Kind::PublicKey, headers)?,
ArmorLabel::Message => make(sink, armor::Kind::Message, headers)?,
};
std::io::copy(&mut source, &mut sink)?;
sink.finalize().map_err(sop_error)?;
Ok(())
}
}
struct Dearmor<'s> {
sqop: &'s SQOP<'s>,
}
impl<'s> Dearmor<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::Dearmor<'s> + 's>> {
Ok(Box::new(Dearmor {
sqop,
}))
}
}
impl<'s> sop::ops::Dearmor<'s> for Dearmor<'s> {
fn data<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
-> Result<Box<dyn sop::ops::Ready + 'd>>
where
's: 'd
{
Ok(Box::new(DearmorReady {
dearmor: *self,
data,
}))
}
}
struct DearmorReady<'s> {
dearmor: Dearmor<'s>,
data: &'s mut (dyn io::Read + Send + Sync),
}
impl sop::ops::Ready for DearmorReady<'_> {
fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
-> Result<()> {
let _ = self.dearmor.sqop;
let mut source = Generic::new(self.data, None).into_boxed();
let armored =
source.data(1)?.get(0).map(|b| b & 0x80 == 0).unwrap_or(false);
if armored {
let mut r = openpgp::armor::Reader::from_reader(source, None);
std::io::copy(&mut r, sink)?;
} else {
std::io::copy(&mut source, sink)?;
}
Ok(())
}
}
struct InlineDetach<'s> {
sqop: &'s SQOP<'s>,
no_armor: bool,
}
impl<'s> InlineDetach<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::InlineDetach<'s, Sigs<'s>> + 's>> {
Ok(Box::new(InlineDetach {
sqop,
no_armor: Default::default(),
}))
}
}
impl<'s> sop::ops::InlineDetach<'s, Sigs<'s>> for InlineDetach<'s> {
fn message<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
-> Result<Box<dyn sop::ops::Ready<Sigs<'s>> + 'd>>
where
's: 'd
{
Ok(Box::new(InlineDetachReady {
inline_detach: *self,
data,
}))
}
}
struct InlineDetachReady<'s, 'd> {
inline_detach: InlineDetach<'s>,
data: &'d mut (dyn io::Read + Send + Sync),
}
impl<'s> sop::ops::Ready<Sigs<'s>> for InlineDetachReady<'s, '_> {
fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
-> Result<Sigs<'s>> {
let _ = self.inline_detach.sqop;
let np = NullPolicy::new();
#[derive(Default)]
struct NullHelper {
sigs: Vec<Packet>,
}
impl VerificationHelper for NullHelper {
fn get_certs(&mut self, _: &[openpgp::KeyHandle])
-> openpgp::Result<Vec<Cert>> {
Ok(Default::default())
}
fn check(&mut self, structure: MessageStructure)
-> openpgp::Result<()> {
for layer in structure {
match layer {
MessageLayer::SignatureGroup { results } => {
for result in results {
match result
.expect_err("no certs given")
{
VerificationError::MalformedSignature {
sig, ..
} =>
self.sigs.push(sig.clone().into()),
VerificationError::MissingKey {
sig, ..
} =>
self.sigs.push(sig.clone().into()),
_ => unreachable!("no certs given"),
}
}
},
_ => (),
}
}
Ok(())
}
}
let mut verifier = VerifierBuilder::from_reader(self.data)
.map_err(sop_error)?
.with_policy(&np, None, NullHelper::default())
.map_err(sop_error)?;
io::copy(&mut verifier, sink)?;
let mut signatures = Vec::new();
let mut signature_sink = Message::new(&mut signatures);
if ! self.inline_detach.no_armor {
signature_sink = Armorer::new(signature_sink)
.kind(armor::Kind::Signature)
.build().map_err(sop_error)?;
}
for sig in verifier.into_helper().sigs {
sig.serialize(&mut signature_sink).map_err(sop_error)?;
}
signature_sink.finalize().map_err(sop_error)?;
Ok(Sigs {
sop: self.inline_detach.sqop,
data: signatures,
source_name: None,
})
}
}
struct InlineVerify<'s> {
sqop: &'s SQOP<'s>,
verbose: bool,
not_before: Option<SystemTime>,
not_after: Option<SystemTime>,
certs: CertBag,
}
impl<'s> InlineVerify<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::InlineVerify<'s, SQOP<'s>, Certs<'s>> + 's>> {
Ok(Box::new(InlineVerify {
sqop,
verbose: Default::default(),
not_before: Default::default(),
not_after: Default::default(),
certs: Default::default(),
}))
}
}
impl<'s> sop::ops::InlineVerify<'s, SQOP<'s>, Certs<'s>> for InlineVerify<'s> {
fn not_before(mut self: Box<Self>, t: SystemTime)
-> Box<dyn sop::ops::InlineVerify<'s, SQOP<'s>, Certs<'s>> + 's> {
self.not_before = Some(t);
self
}
fn not_after(mut self: Box<Self>, t: SystemTime)
-> Box<dyn sop::ops::InlineVerify<'s, SQOP<'s>, Certs<'s>> + 's> {
self.not_after = Some(t);
self
}
fn certs(mut self: Box<Self>, certs: &Certs)
-> Result<Box<dyn sop::ops::InlineVerify<'s, SQOP<'s>, Certs<'s>> + 's>> {
self.certs.extend_from(certs);
Ok(self)
}
fn message<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
-> Result<Box<dyn sop::ops::Ready<Vec<Verification>> + 'd>>
where
's: 'd
{
Ok(Box::new(InlineVerifyReady {
inline_verify: *self,
data,
}))
}
}
struct InlineVerifyReady<'s> {
inline_verify: InlineVerify<'s>,
data: &'s mut (dyn io::Read + Send + Sync),
}
impl<'s> sop::ops::Ready<Vec<Verification>> for InlineVerifyReady<'_> {
fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
-> Result<Vec<Verification>> {
let helper = VHelper::new(
if self.inline_verify.verbose {
Box::new(io::stderr())
} else {
Box::new(io::sink())
},
1,
self.inline_verify.not_before,
self.inline_verify.not_after,
self.inline_verify.certs);
let mut verifier = VerifierBuilder::from_reader(self.data)
.map_err(sop_error)?
.with_policy(self.inline_verify.sqop.policy, None, helper)
.map_err(sop_error)?;
io::copy(&mut verifier, sink)?;
Ok(verifier.into_helper().verifications)
}
}
struct InlineSign<'s> {
no_armor: bool,
sign: Sign<'s>,
mode: InlineSignAs,
}
impl<'s> InlineSign<'s> {
fn new(sqop: &'s SQOP) -> Result<Box<dyn sop::ops::InlineSign<'s, SQOP<'s>, Keys<'s>> + 's>> {
Ok(Box::new(InlineSign {
no_armor: false,
sign: Sign::unboxed(sqop),
mode: Default::default(),
}))
}
}
impl<'s> sop::ops::InlineSign<'s, SQOP<'s>, Keys<'s>> for InlineSign<'s> {
fn no_armor(mut self: Box<Self>) -> Box<dyn sop::ops::InlineSign<'s, SQOP<'s>, Keys<'s>> + 's> {
self.no_armor = true;
self
}
fn mode(mut self: Box<Self>, mode: InlineSignAs) -> Box<dyn sop::ops::InlineSign<'s, SQOP<'s>, Keys<'s>> + 's> {
self.mode = mode;
self
}
fn keys(mut self: Box<Self>, keys: &Keys)
-> Result<Box<dyn sop::ops::InlineSign<'s, SQOP<'s>, Keys<'s>> + 's>> {
self.sign.add_signing_keys(keys)?;
Ok(self)
}
fn with_key_password(mut self: Box<Self>, password: Password)
-> Result<Box<dyn sop::ops::InlineSign<'s, SQOP<'s>, Keys<'s>> + 's>> {
self.sign.with_key_password.push(password);
Ok(self)
}
fn data<'d>(self: Box<Self>, data: &'d mut (dyn io::Read + Send + Sync))
-> Result<Box<dyn sop::ops::Ready + 'd>>
where
's: 'd
{
if self.sign.signers.is_empty() {
return Err(Error::MissingArg);
}
if self.no_armor && matches!(self.mode, InlineSignAs::ClearSigned)
{
return Err(Error::IncompatibleOptions);
}
Ok(Box::new(InlineSignReady {
inline_sign: *self,
data,
}))
}
}
struct InlineSignReady<'s> {
inline_sign: InlineSign<'s>,
data: &'s mut (dyn io::Read + Send + Sync),
}
impl<'s> sop::ops::Ready for InlineSignReady<'s> {
fn to_writer(self: Box<Self>, sink: &mut (dyn io::Write + Send + Sync))
-> Result<()>
{
let mut message = Message::new(sink);
if ! (self.inline_sign.no_armor
|| matches!(self.inline_sign.mode, InlineSignAs::ClearSigned))
{
message =
Armorer::new(message).build().map_err(sop_error)?;
}
let mut signers = self.inline_sign.sign.make_signers()?;
let mut signer = Signer::with_template(
message,
signers.pop().expect("at least one"),
signature::SignatureBuilder::new(
into_isig_type(self.inline_sign.mode)))
.hash_algo(
self.inline_sign.sign.hash_algos.get(0).cloned()
.unwrap_or_default()).map_err(sop_error)?;
for s in signers {
signer = signer.add_signer(s);
}
if matches!(self.inline_sign.mode, InlineSignAs::ClearSigned) {
signer = signer.cleartext();
}
let mut message = signer.build().map_err(sop_error)?;
if ! matches!(self.inline_sign.mode, InlineSignAs::ClearSigned) {
message = LiteralWriter::new(message).build().map_err(sop_error)?;
}
io::copy(self.data, &mut message)?;
message.finalize().map_err(sop_error)?;
Ok(())
}
}
#[derive(Default)]
struct CertBag {
certs: BTreeMap<openpgp::Fingerprint, (Cert, Vec<String>)>,
}
impl CertBag {
fn is_empty(&self) -> bool {
self.certs.is_empty()
}
fn insert(&mut self, cert: Cert, source_name: Option<&str>) {
use std::collections::btree_map::Entry;
match self.certs.entry(cert.fingerprint()) {
Entry::Occupied(mut e) => {
let e = e.get_mut();
e.0 = e.0.clone().merge_public(cert)
.expect("merging certs with the same fipr is infallible");
if let Some(n) = source_name {
e.1.push(n.into());
}
},
Entry::Vacant(e) => {
e.insert((cert,
if let Some(n) = source_name {
vec![n.into()]
} else {
vec![]
}));
},
}
}
fn extend_from(&mut self, certs: &Certs) {
for cert in &certs.certs {
self.insert(cert.clone(), certs.source_name());
}
}
}
struct VHelper {
verbose_out: Box<dyn io::Write>,
verifications: Vec<Verification>,
not_before: Option<SystemTime>,
not_after: Option<SystemTime>,
good: usize,
total: usize,
threshold: usize,
keyring: CertBag,
}
impl VHelper {
fn new(verbose_out: Box<dyn io::Write>,
threshold: usize,
not_before: Option<SystemTime>,
not_after: Option<SystemTime>,
keyring: CertBag)
-> Self
{
assert!(threshold <= 1);
VHelper {
verbose_out,
verifications: Default::default(),
not_before,
not_after,
good: 0,
total: 0,
threshold,
keyring,
}
}
}
impl VerificationHelper for VHelper {
fn get_certs(&mut self, _: &[openpgp::KeyHandle])
-> openpgp::Result<Vec<Cert>> {
Ok(self.keyring.certs.values()
.map(|(cert, _)| cert.clone())
.collect())
}
fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
use self::VerificationError::*;
for layer in structure.into_iter() {
match layer {
MessageLayer::SignatureGroup { results } =>
for result in results {
self.total += 1;
match result {
Ok(GoodChecksum { sig, ka, .. }) => {
let t = match sig.signature_creation_time() {
Some(t) => t,
None => {
writeln!(self.verbose_out,
"Malformed signature:")?;
print_error_chain(&mut self.verbose_out, &anyhow::anyhow!(
"no signature creation time"))?;
continue;
},
};
if let Some(not_before) = self.not_before {
if t < not_before {
writeln!(self.verbose_out,
"Signature by {:X} was created before \
the --not-before date.",
ka.key().fingerprint())?;
continue;
}
}
if let Some(not_after) = self.not_after {
if t > not_after {
writeln!(self.verbose_out,
"Signature by {:X} was created after \
the --not-after date.",
ka.key().fingerprint())?;
continue;
}
}
let mut v = Verification::new(
t,
ka.fingerprint(),
ka.cert().fingerprint(),
match sig.typ() {
SignatureType::Text =>
SignatureMode::Text,
_ => SignatureMode::Binary,
},
None)?;
for n in self.keyring.certs.get(&ka.cert().fingerprint())
.map(|(_, source_names)| source_names)
.unwrap_or(&vec![])
{
v.add_signer(n);
}
self.verifications.push(v);
},
Err(MalformedSignature { error, .. }) => {
writeln!(self.verbose_out,
"Signature is malformed:")?;
print_error_chain(&mut self.verbose_out, &error)?;
},
Err(MissingKey { sig, .. }) => {
let issuers = sig.get_issuers();
writeln!(self.verbose_out,
"Missing key {:X}, which is needed to \
verify signature.",
issuers.first().unwrap())?;
},
Err(UnboundKey { cert, error, .. }) => {
writeln!(self.verbose_out,
"Signing key on {:X} is not bound:",
cert.fingerprint())?;
print_error_chain(&mut self.verbose_out, &error)?;
},
Err(BadKey { ka, error, .. }) => {
writeln!(self.verbose_out,
"Signing key on {:X} is bad:",
ka.cert().fingerprint())?;
print_error_chain(&mut self.verbose_out, &error)?;
},
Err(BadSignature { error, .. }) => {
writeln!(self.verbose_out,
"Verifying signature:")?;
print_error_chain(&mut self.verbose_out, &error)?;
},
}
}
MessageLayer::Compression { .. } => (),
MessageLayer::Encryption { .. } => (),
}
}
self.good = self.verifications.len();
if self.good >= self.threshold {
Ok(())
} else {
Err(Error::NoSignature.into())
}
}
}
struct Helper {
vhelper: VHelper,
session_keys: Vec<sop::SessionKey>,
passwords: Vec<crypto::Password>,
secret_keys:
HashMap<KeyID, Key<key::SecretParts, key::UnspecifiedRole>>,
with_key_password: Vec<Password>,
identities: HashMap<KeyID, Fingerprint>,
session_key: Option<sop::SessionKey>,
}
impl Helper {
fn new(policy: &dyn Policy,
vhelper: VHelper,
session_keys: Vec<sop::SessionKey>,
passwords: Vec<Password>,
with_key_password: Vec<Password>,
secrets: Vec<Cert>) -> Self
{
let mut secret_keys = HashMap::new();
let mut identities: HashMap<KeyID, Fingerprint> = HashMap::new();
for tsk in secrets {
for ka in tsk.keys().secret()
.with_policy(policy, None)
.supported()
.for_transport_encryption().for_storage_encryption()
{
let id: KeyID = ka.key().fingerprint().into();
secret_keys.insert(id.clone(), ka.key().clone().into());
identities.insert(id.clone(), tsk.fingerprint());
}
}
Helper {
vhelper,
session_keys: session_keys.into_iter().map(Into::into).collect(),
passwords: passwords.into_iter()
.flat_map(|p| p.variants().map(crypto::Password::from)
.collect::<Vec<_>>()).collect(),
secret_keys,
with_key_password,
identities,
session_key: None,
}
}
fn try_decrypt<D>(&self, pkesk: &PKESK,
algo: Option<SymmetricAlgorithm>,
keypair: &mut dyn crypto::Decryptor,
decrypt: &mut D)
-> Option<(SymmetricAlgorithm,
SessionKey,
Option<Fingerprint>)>
where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
{
let keyid = keypair.public().fingerprint().into();
let (algo, sk) = pkesk.decrypt(keypair, algo)
.and_then(|(algo, sk)| {
if decrypt(algo, &sk) { Some((algo, sk)) } else { None }
})?;
Some((algo, sk, self.identities.get(&keyid).map(|fp| fp.clone())))
}
fn dump_session_key(&mut self, algo: SymmetricAlgorithm, sk: &SessionKey)
-> Result<()> {
self.session_key = Some(sop::SessionKey::new(algo, sk)?);
Ok(())
}
}
impl VerificationHelper for Helper {
fn get_certs(&mut self, ids: &[openpgp::KeyHandle])
-> openpgp::Result<Vec<Cert>> {
self.vhelper.get_certs(ids)
}
fn check(&mut self, structure: MessageStructure)
-> openpgp::Result<()> {
self.vhelper.check(structure)
}
}
impl DecryptionHelper for Helper {
fn decrypt<D>(&mut self, pkesks: &[PKESK], skesks: &[SKESK],
algo: Option<SymmetricAlgorithm>,
mut decrypt: D) -> openpgp::Result<Option<Fingerprint>>
where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
{
let mut error = Error::CannotDecrypt;
while let Some(sk) = self.session_keys.pop() {
let algo = SymmetricAlgorithm::from(sk.algorithm());
let sk = SessionKey::from(sk.key());
if decrypt(algo, &sk) {
self.dump_session_key(algo, &sk)?;
return Ok(None);
}
}
for pkesk in pkesks {
let keyid = pkesk.recipient();
if let Some(key) = self.secret_keys.get_mut(&keyid) {
let pk_algo = key.pk_algo();
if key.secret().is_encrypted() {
for p in self.with_key_password.iter()
.flat_map(|p| p.variants())
{
if key.secret_mut().decrypt_in_place(pk_algo, &p.into())
.is_ok()
{
break;
}
}
}
if ! key.secret().is_encrypted() {
if let Some((algo, sk, fp)) =
key.clone().into_keypair().ok().and_then(|mut k| {
self.try_decrypt(pkesk, algo, &mut k, &mut decrypt)
})
{
self.dump_session_key(algo, &sk)?;
return Ok(fp);
}
} else {
error = Error::KeyIsProtected;
}
}
}
for pkesk in pkesks.iter().filter(|p| p.recipient().is_wildcard()) {
for mut key in std::mem::take(&mut self.secret_keys).into_values() {
let pk_algo = key.pk_algo();
if key.secret().is_encrypted() {
for p in self.with_key_password.iter()
.flat_map(|p| p.variants())
{
if key.secret_mut().decrypt_in_place(pk_algo, &p.into())
.is_ok()
{
break;
}
}
}
if ! key.secret().is_encrypted() {
if let Some((algo, sk, fp)) =
key.clone().into_keypair().ok().and_then(|mut k| {
self.try_decrypt(pkesk, algo, &mut k, &mut decrypt)
})
{
self.dump_session_key(algo, &sk)?;
return Ok(fp);
}
} else {
error = Error::KeyIsProtected;
}
}
}
if skesks.is_empty() {
return Err(error.into());
}
for password in self.passwords.iter() {
for skesk in skesks {
if let Some((algo, sk)) = skesk.decrypt(password).ok()
.and_then(|(algo, sk)| {
if decrypt(algo, &sk) {
Some((algo, sk))
} else {
None
}
})
{
self.dump_session_key(algo, &sk)?;
return Ok(None);
}
}
}
Err(error.into())
}
}
fn into_sig_type(mode: sop::ops::SignAs) -> SignatureType {
match mode {
SignAs::Binary => SignatureType::Binary,
SignAs::Text => SignatureType::Text,
}
}
fn into_isig_type(mode: sop::ops::InlineSignAs) -> SignatureType {
match mode {
InlineSignAs::Binary => SignatureType::Binary,
InlineSignAs::Text => SignatureType::Text,
InlineSignAs::ClearSigned => SignatureType::Text,
}
}
fn into_data_format(mode: EncryptAs) -> DataFormat {
match mode {
EncryptAs::Binary => DataFormat::Binary,
EncryptAs::Text => DataFormat::Text,
}
}
fn print_error_chain(sink: &mut dyn io::Write, err: &anyhow::Error)
-> io::Result<()>
{
writeln!(sink, " {}", err)?;
for cause in err.chain().skip(1) {
writeln!(sink, " because: {}", cause)?;
}
Ok(())
}
fn sop_error(e: anyhow::Error) -> Error {
let e = match e.downcast::<Error>() {
Ok(e) => return e,
Err(e) => e,
};
let e = match e.downcast::<io::Error>() {
Ok(e) => return if e.kind() == io::ErrorKind::UnexpectedEof {
Error::BadData
} else {
e.into()
},
Err(e) => e,
};
if let Some(e) = e.downcast_ref::<openpgp::Error>() {
use openpgp::Error::*;
return match e {
InvalidArgument(_) => Error::BadData,
InvalidOperation(_) => Error::BadData,
MalformedPacket(_) => Error::BadData,
PacketTooLarge(_, _, _) => Error::BadData,
UnsupportedPacketType(_) => Error::BadData,
UnsupportedHashAlgorithm(_) => Error::UnsupportedAsymmetricAlgo,
UnsupportedPublicKeyAlgorithm(_) => Error::UnsupportedAsymmetricAlgo,
UnsupportedEllipticCurve(_) => Error::UnsupportedAsymmetricAlgo,
UnsupportedSymmetricAlgorithm(_) => Error::UnsupportedAsymmetricAlgo,
UnsupportedAEADAlgorithm(_) => Error::UnsupportedAsymmetricAlgo,
UnsupportedCompressionAlgorithm(_) => Error::UnsupportedAsymmetricAlgo,
UnsupportedSignatureType(_) => Error::BadData,
InvalidSessionKey(_) => Error::CannotDecrypt,
MissingSessionKey(_) => Error::CannotDecrypt,
MalformedMPI(_) => Error::BadData,
BadSignature(_) => Error::BadData,
MalformedMessage(_) => Error::BadData,
MalformedCert(_) => Error::BadData,
UnsupportedCert2(_, _) => Error::BadData,
Expired(_) => Error::BadData,
NotYetLive(_) => Error::BadData,
NoBindingSignature(_) => Error::BadData,
InvalidKey(_) => Error::BadData,
PolicyViolation(_, _) => Error::BadData,
e => {
eprintln!("Warning: Unknown Sequoia error: {}", e);
Error::BadData
},
};
}
eprintln!("Warning: Untranslated error: {}", e);
Error::BadData
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[test]
fn sop_examples() -> Result<()> {
let sop = SQOP::default();
let alice_sec = sop.generate_key()?
.userid("Alice Lovelace <alice@openpgp.example>")
.generate()?;
let alice_pgp = sop.extract_cert()?
.keys(&alice_sec)?;
let bob_sec = sop.generate_key()?
.userid("Bob Babbage <bob@openpgp.example>")
.generate()?;
let bob_pgp = sop.extract_cert()?
.keys(&bob_sec)?;
let statement = b"Hello World :)";
let mut data = Cursor::new(&statement);
let (_micalg, signature) = sop.sign()?
.mode(SignAs::Text)
.keys(&alice_sec)?
.data(&mut data)?;
let verifications = sop.verify()?
.certs(&alice_pgp)?
.signatures(&signature)?
.data(&mut Cursor::new(&statement))?;
assert_eq!(verifications.len(), 1);
let mut statement_cur = Cursor::new(&statement);
let (_session_key, ciphertext) = sop.encrypt()?
.sign_with_keys(&alice_sec)?
.with_certs(&bob_pgp)?
.plaintext(&mut statement_cur)?
.to_vec()?;
let mut ciphertext_cur = Cursor::new(&ciphertext);
let (_, plaintext) = sop.decrypt()?
.with_keys(&bob_sec)?
.ciphertext(&mut ciphertext_cur)?
.to_vec()?;
assert_eq!(&plaintext, statement);
Ok(())
}
#[test]
fn issue_29() -> Result<()> {
let sop = SQOP::default();
let alice_sec = sop.generate_key()?
.userid("Alice Lovelace <alice@openpgp.example>")
.generate()?;
let alice_pgp = sop.extract_cert()?
.keys(&alice_sec)?;
let no_signature = b"\n";
assert!(matches!(sop.inline_verify()?
.certs(&alice_pgp)?
.message(&mut Cursor::new(&no_signature))
.and_then(|ready| ready.to_vec()),
Err(sop::errors::Error::BadData)));
Ok(())
}
}