A backend for the p≡p Engine built on Sequoia.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

2235 lines
72 KiB

use std::cmp;
use std::convert::TryInto;
use std::env;
use std::ffi::{
CStr,
OsStr,
};
use std::io::{
Read,
Write,
};
use std::mem;
use std::path::Path;
use std::ptr;
use std::slice;
use std::time::{
Duration,
SystemTime,
UNIX_EPOCH,
};
use libc::{
c_char,
c_uint,
size_t,
time_t,
};
use chrono::Utc;
use chrono::TimeZone;
use memmem::{Searcher, TwoWaySearcher};
use sequoia_openpgp as openpgp;
use openpgp::Cert;
use openpgp::cert::{
amalgamation::ValidAmalgamation,
CertBuilder,
CertParser,
ValidCert,
};
use openpgp::crypto::{
Password,
SessionKey,
};
use openpgp::Fingerprint;
use openpgp::Packet;
use openpgp::packet::{
key,
Key,
PKESK,
SKESK,
UserID,
};
use openpgp::parse::{
Parse,
PacketParser,
stream::{
DecryptionHelper,
DecryptorBuilder,
DetachedVerifierBuilder,
GoodChecksum,
MessageLayer,
MessageStructure,
VerificationHelper,
VerificationError,
}
};
use openpgp::policy::NullPolicy;
use openpgp::serialize::{
stream::{
Armorer,
Encryptor,
LiteralWriter,
Message,
Recipient,
Signer,
},
Serialize,
};
use openpgp::types::{
ReasonForRevocation,
RevocationStatus,
SignatureType,
SymmetricAlgorithm,
};
#[macro_use] mod log;
mod constants;
#[macro_use] mod pep;
use pep::{
Error,
ErrorCode,
PepCipherSuite,
PepCommType,
PepEncFormat,
PepIdentity,
PepIdentityFlags,
PepIdentityList,
PepIdentityListItem,
Result,
Session,
StringList,
StringListItem,
Timestamp,
};
#[macro_use] mod ffi;
use ffi::MM;
mod keystore;
use keystore::Keystore;
mod buffer;
use buffer::{
rust_bytes_to_c_str_lossy,
rust_bytes_to_ptr_and_len,
};
// If the PEP_TRACE environment variable is set or we are built in
// debug mode, then enable tracing.
lazy_static::lazy_static! {
static ref TRACE: bool = {
if cfg!(debug_assertions) {
true
} else if let Ok(_) = env::var("PEP_TRACE") {
true
} else {
false
}
};
}
pub const P: &NullPolicy = &NullPolicy::new();
// Given the pEp cipher suite indicator enum, return the equivalent
// sequoia cipher suite enum value
//
// PEP_STATUS pgp_config_cipher_suite(PEP_SESSION session,
// PEP_CIPHER_SUITE suite)
ffi!(fn pgp_config_cipher_suite(session: *mut Session, suite: PepCipherSuite)
-> Result<()>
{
let session = Session::as_mut(session)?;
session.set_cipher_suite(suite)
});
// Decrypts the key.
//
// On success, returns the decrypted key.
fn _pgp_get_decrypted_key(key: Key<key::SecretParts, key::UnspecifiedRole>,
pass: Option<&Password>)
-> Result<Key<key::SecretParts, key::UnspecifiedRole>>
{
tracer!(*crate::TRACE, "_pgp_get_decrypted_key");
match key.secret() {
key::SecretKeyMaterial::Unencrypted { .. } => Ok(key),
key::SecretKeyMaterial::Encrypted { .. } => {
let fpr = key.fingerprint();
if let Some(pass) = pass {
wrap_err!(
key.decrypt_secret(pass),
WrongPassphrase,
format!("Decrypting secret key material for {}", fpr))
} else {
t!("Can't decrypt {}: no password configured", fpr);
Err(Error::PassphraseRequired)
}
}
}
}
// Returns the first key in iter that is already decrypted or can be
// decrypted using `pass`.
fn _pgp_get_decrypted_key_iter<'a, I>(iter: I, pass: Option<&Password>)
-> Result<Key<key::SecretParts, key::UnspecifiedRole>>
where I: Iterator<Item=&'a Key<key::SecretParts, key::UnspecifiedRole>>
{
// Return the "best" (most helpful to the user) error.
let mut bad_pass = None;
let mut missing_pass = false;
let mut other_error = None;
for key in iter {
match _pgp_get_decrypted_key(key.clone(), pass) {
Ok(key) => return Ok(key),
Err(err @ Error::WrongPassphrase(_, _)) => bad_pass = Some(err),
Err(Error::PassphraseRequired) => missing_pass = true,
Err(err) => other_error = Some(err),
}
}
if let Some(err) = bad_pass {
Err(err)
} else if missing_pass {
Err(Error::PassphraseRequired)
} else if let Some(err) = other_error {
Err(err)
} else {
Err(Error::UnknownError(
anyhow::anyhow!("decrypting secret key material"),
"empty iterator".into()))
}
}
// PEP_STATUS pgp_init(PEP_SESSION session, bool in_first)
ffi!(fn pgp_init_(session: *mut Session, _in_first: bool,
per_user_directory: *const c_char,
malloc: ffi::Malloc,
free: ffi::Free,
session_size: c_uint,
session_cookie_offset: c_uint,
session_curr_passphrase_offset: c_uint,
session_new_key_pass_enable: c_uint,
session_generation_passphrase_offset: c_uint,
session_cipher_suite_offset: c_uint,
pep_status_size: c_uint,
pep_comm_type_size: c_uint,
pep_enc_format_size: c_uint,
pep_identity_flags_size: c_uint,
pep_cipher_suite_size: c_uint,
string_list_item_size: c_uint,
pep_identity_size: c_uint,
pep_identity_list_item_size: c_uint,
timestamp_size: c_uint,
_stringpair_size: c_uint,
_stringpair_list_size: c_uint,
magic: c_uint)
-> Result<()>
{
use std::mem::size_of;
use memoffset::offset_of;
assert_eq!(magic, 0xDEADBEEF, "magic");
assert!(session_size as usize >= size_of::<Session>());
assert_eq!(session_cookie_offset as usize,
offset_of!(Session, state),
"session_cookie_offset");
assert_eq!(session_curr_passphrase_offset as usize,
offset_of!(Session, curr_passphrase),
"session_curr_passphrase_offset");
assert_eq!(session_new_key_pass_enable as usize,
offset_of!(Session, new_key_pass_enabled),
"session_new_key_pass_enable");
assert_eq!(session_generation_passphrase_offset as usize,
offset_of!(Session, generation_passphrase),
"session_generation_passphrase_offset");
assert_eq!(session_cipher_suite_offset as usize,
offset_of!(Session, cipher_suite),
"session_cipher_suite_offset");
assert_eq!(pep_status_size as usize, size_of::<ErrorCode>(),
"pep_status_size");
assert_eq!(pep_comm_type_size as usize, size_of::<PepCommType>(),
"pep_comm_type_size");
assert_eq!(pep_enc_format_size as usize, size_of::<PepEncFormat>(),
"pep_enc_format_size");
assert_eq!(pep_identity_flags_size as usize, size_of::<PepIdentityFlags>(),
"pep_identity_flags_size");
assert_eq!(pep_cipher_suite_size as usize, size_of::<PepCipherSuite>(),
"pep_cipher_suite_size");
assert_eq!(string_list_item_size as usize, size_of::<StringListItem>(),
"string_list_item_size");
assert_eq!(pep_identity_size as usize, size_of::<PepIdentity>(),
"pep_identity_size");
assert_eq!(pep_identity_list_item_size as usize, size_of::<PepIdentityListItem>(),
"pep_identity_list_item_size");
assert_eq!(timestamp_size as usize, size_of::<Timestamp>(),
"timestamp_size");
// assert_eq!(stringpair_size as usize, size_of::<StringPair>(),
// "stringpair_size");
// assert_eq!(stringpair_list_size as usize, size_of::<StringPairList>(),
// "stringpair_list_size");
let session = Session::as_mut(session)?;
if per_user_directory.is_null() {
return Err(Error::IllegalValue(
"per_user_directory may not be NULL".into()));
}
let per_user_directory = unsafe { CStr::from_ptr(per_user_directory) };
#[cfg(not(windows))]
let per_user_directory = {
use std::os::unix::ffi::OsStrExt;
OsStr::from_bytes(per_user_directory.to_bytes())
};
#[cfg(windows)]
let per_user_directory = {
use std::ffi::OsString;
use std::os::windows::prelude::*;
let os_string = OsString::from_wide(per_user_directory.as_bytes());
os_string.as_os_str()
};
let ks = keystore::Keystore::init(Path::new(per_user_directory))?;
session.init(MM { malloc, free }, ks);
Ok(())
});
// void pgp_release(PEP_SESSION session, bool out_last)
ffi!(fn pgp_release(session: *mut Session, _out_last: bool) -> Result<()> {
Session::as_mut(session)?.deinit();
Ok(())
});
// Cookie used by the decryption and verification logic.
struct Helper<'a> {
session: &'a mut Session,
secret_keys_called: bool,
recipient_keylist: StringList,
signer_keylist: StringList,
good_checksums: usize,
malformed_signature: usize,
missing_keys: usize,
unbound_key: usize,
revoked_key: usize,
expired_key: usize,
bad_key: usize,
bad_checksums: usize,
// Whether we decrypted anything.
decrypted: bool,
// The filename stored in the literal data packet. Note: this is
// *not* protected by the signature and should not be trusted!!!
filename: Option<Vec<u8>>,
}
impl<'a> Helper<'a> {
fn new(session: &'a mut Session) -> Self {
let mm = session.mm();
Helper {
session: session,
secret_keys_called: false,
recipient_keylist: StringList::empty(mm),
signer_keylist: StringList::empty(mm),
good_checksums: 0,
malformed_signature: 0,
missing_keys: 0,
unbound_key: 0,
revoked_key: 0,
expired_key: 0,
bad_key: 0,
bad_checksums: 0,
decrypted: false,
filename: None,
}
}
}
impl<'a> VerificationHelper for &mut Helper<'a> {
fn get_certs(&mut self, ids: &[openpgp::KeyHandle])
-> openpgp::Result<Vec<Cert>>
{
let mut certs = Vec::new();
for id in ids {
if let Ok((cert, _private))
= self.session.keystore().cert_find_with_key(id.clone(), false)
{
certs.push(cert);
}
}
Ok(certs)
}
fn check(&mut self, structure: MessageStructure)
-> openpgp::Result<()>
{
tracer!(*crate::TRACE, "Helper::check");
for layer in structure.into_iter() {
if let MessageLayer::SignatureGroup { results } = layer {
for result in results {
match result {
Ok(GoodChecksum { sig, ka }) => {
// We need to add the fingerprint of
// the primary key to signer_keylist.
let primary_fpr = ka.cert().fingerprint();
self.signer_keylist.add_unique(
primary_fpr.to_hex());
t!("Good signature ({:02X}{:02X}) from {}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
primary_fpr);
self.good_checksums += 1;
}
Err(VerificationError::MalformedSignature { sig, error }) => {
t!("Malformed signature ({:02X}{:02X}) \
allegedly from {:?}: {}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
sig.issuers().next(),
error);
self.malformed_signature += 1;
}
Err(VerificationError::MissingKey { sig }) => {
t!("No key to check signature ({:02X}{:02X}) \
allegedly from {:?}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
sig.issuers().next());
self.missing_keys += 1;
}
Err(VerificationError::UnboundKey { sig, cert, error }) => {
// This happens if the key doesn't have a binding
// signature.
t!("Certificate {} has no valid self-signature; \
can't check signature ({:02X}{:02X}): {}",
cert.fingerprint(),
sig.digest_prefix()[0],
sig.digest_prefix()[1],
error);
self.unbound_key += 1;
}
Err(VerificationError::BadKey { sig, ka, error }) => {
// This happens if the certificate is not
// alive or revoked, if the key is not
// alive or revoked, of if the key is not
// signing capable.
t!("Can't check signature ({:02X}{:02X}): \
key {} is bad: {}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
ka.cert().fingerprint(),
error);
// Check if the key or certificate is revoked.
if let RevocationStatus::Revoked(_)
= ka.revocation_status()
{
t!("reason: key is revoked");
self.revoked_key += 1;
} else if let RevocationStatus::Revoked(_)
= ka.cert().revocation_status()
{
t!("reason: cert is revoked");
self.revoked_key += 1;
}
// Check if the key or certificate is expired.
else if let Err(err) = ka.cert().alive() {
t!("reason: cert is expired: {}", err);
self.expired_key += 1;
}
else if let Err(err) = ka.alive() {
// Key is expired.
t!("reason: key is expired: {}", err);
self.expired_key += 1;
}
// Wrong key flags or something similar.
else {
t!("reason: other");
self.bad_key += 1;
}
}
Err(VerificationError::BadSignature { sig, ka, error }) => {
t!("Bad signature ({:02X}{:02X}) from {}: {}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
ka.cert().fingerprint(),
error);
self.bad_checksums += 1;
}
}
}
}
}
Ok(())
}
// Save the filename in the literal data packet.
fn inspect(&mut self, pp: &PacketParser<'_>) -> openpgp::Result<()> {
if let Packet::Literal(ref lit) = pp.packet {
if let Some(filename) = lit.filename() {
self.filename = Some(filename.to_vec());
}
}
Ok(())
}
}
impl<'a> DecryptionHelper for &mut Helper<'a> {
fn decrypt<D>(&mut self, pkesks: &[PKESK], _: &[SKESK],
sym_algo: Option<SymmetricAlgorithm>,
mut decrypt: D)
-> openpgp::Result<Option<openpgp::Fingerprint>>
where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
{
tracer!(*crate::TRACE, "Helper::decrypt");
let password = self.session.curr_passphrase();
let keystore = self.session.keystore();
// Whether there are any wildcard recipients.
let mut have_wildcards = false;
// The certificate that decrypted the message.
let mut decryption_identity = None;
let mut missing_passphrase = false;
let mut bad_passphrase = None;
if self.secret_keys_called {
return Err(anyhow::anyhow!(
"Nested encryption containers not supported"));
}
self.secret_keys_called = true;
t!("{} PKESKs", pkesks.len());
for pkesk in pkesks.iter() {
let keyid = pkesk.recipient();
if keyid.is_wildcard() {
// Initially ignore wildcards.
have_wildcards = true;
continue;
}
t!("Considering PKESK for {}", keyid);
// Collect the recipients. Note: we must return the
// primary key's fingerprint.
let (cert, private)
= match keystore.cert_find_with_key(keyid.clone(), false)
{
Err(Error::KeyNotFound(_)) => continue,
Err(err) => {
t!("Error looking up {}: {}", keyid, err);
continue;
}
Ok((cert, private)) => (cert, private)
};
self.recipient_keylist.add_unique(cert.fingerprint().to_hex());
if self.decrypted {
// We already have the session key. We are just
// trying to collect the alleged recipients now.
continue;
}
// Try to decrypt this PKESK.
if ! private {
continue;
}
let ka = match cert.keys().filter(|ka| *keyid == ka.keyid()).next() {
Some(ka) => ka,
None => {
t!("Inconsistent DB: cert {} doesn't contain a subkey with \
keyid {}, but DB says it does!",
cert.fingerprint(), keyid);
continue;
}
};
if let Ok(key) = ka.key().clone().parts_into_secret() {
let fpr = key.fingerprint();
let key = match _pgp_get_decrypted_key(key, password.as_ref()) {
Ok(key) => key,
Err(err @ Error::WrongPassphrase(_, _)) => {
bad_passphrase = Some(err);
continue;
}
Err(Error::PassphraseRequired) => {
missing_passphrase = true;
continue;
}
Err(err) => {
t!("While decrypting {}: {}", fpr, err);
continue;
}
};
let mut keypair = match key.into_keypair() {
Ok(keypair) => keypair,
Err(err) => {
t!("Creating keypair for {}: {}", fpr, err);
continue;
}
};
match pkesk.decrypt(&mut keypair, sym_algo) {
Some((sym_algo, sk)) => {
if decrypt(sym_algo, &sk) {
decryption_identity = Some(cert.fingerprint());
self.decrypted = true;
}
}
None => {
t!("Failed to decrypt PKESK for {}", fpr);
}
}
}
}
let mut tsks = None;
if have_wildcards && ! self.decrypted {
for pkesk in pkesks.iter() {
let keyid = pkesk.recipient();
if ! keyid.is_wildcard() {
// We're only considering the wildcard PKESKs.
continue;
}
if tsks.is_none() {
// Load all certificates with secret key material.
tsks = Some(keystore.cert_all(true)?);
if tsks.as_ref().unwrap().len() == 0 {
// We don't have any keys with secret key
// material. We're done.
break;
}
}
for (tsk, _private) in tsks.as_ref().unwrap().iter() {
for ka in tsk.keys().secret() {
let key = match _pgp_get_decrypted_key(
ka.key().clone(), password.as_ref())
{
Ok(key) => key,
Err(err @ Error::WrongPassphrase(_, _)) => {
bad_passphrase = Some(err);
continue;
}
Err(Error::PassphraseRequired) => {
missing_passphrase = true;
continue;
}
Err(err) => {
t!("decrypting {}: {}",
ka.fingerprint(), err);
continue;
}
};
let mut keypair = match key.into_keypair() {
Ok(keypair) => keypair,
Err(err) => {
t!("Creating keypair for {}: {}",
ka.fingerprint(), err);
continue;
}
};
// Note: for decryption to appear to succeed,
// we must get a valid algorithm (8 of 256
// values) and a 16-bit checksum must match.
// Thus, we have about a 1 in 2**21 chance of
// having a false positive here.
match pkesk.decrypt(&mut keypair, sym_algo) {
Some((sym_algo, sk)) => {
// Add it to the recipient list.
t!("wildcard recipient appears to be {}",
ka.fingerprint());
if decrypt (sym_algo, &sk) {
decryption_identity
= Some(tsk.fingerprint());
self.recipient_keylist.add_unique(
tsk.fingerprint().to_hex());
self.decrypted = true;
break;
} else {
t!("Failed to decrypt message \
using ESK decrypted by {}",
ka.fingerprint());
continue;
}
}
None => {
t!("Failed to decrypt PKESK for {}",
ka.fingerprint());
continue;
}
};
}
}
}
}
if self.decrypted {
Ok(decryption_identity)
} else {
if let Some(err) = bad_passphrase.take() {
Err(err.into())
} else if missing_passphrase {
Err(Error::PassphraseRequired.into())
} else {
Err(Error::DecryptNoKey(
anyhow::anyhow!("No key")).into())
}
}
}
}
// PEP_STATUS pgp_decrypt_and_verify(
// PEP_SESSION session, const char *ctext, size_t csize,
// const char *dsigtext, size_t dsigsize,
// char **ptext, size_t *psize, stringlist_t **keylist,
// char** filename_ptr)
ffi!(fn pgp_decrypt_and_verify(session: *mut Session,
ctext: *const c_char, csize: size_t,
dsigtext: *const c_char, _dsigsize: size_t,
ptextp: *mut *mut c_char, psizep: *mut size_t,
keylistp: *mut *mut StringListItem,
filename_ptr: *mut *mut c_char)
-> Result<()>
{
let session = Session::as_mut(session)?;
let mm = session.mm();
let malloc = mm.malloc;
let ctext = unsafe { check_slice!(ctext, csize) };
// XXX: We don't handle detached signatures over encrypted
// messages (and never have).
if ! dsigtext.is_null() {
return Err(Error::IllegalValue(
"detached signatures over encrypted data are not supported".into()));
}
let ptextp = unsafe { check_mut!(ptextp) };
*ptextp = ptr::null_mut();
let psizep = unsafe { check_mut!(psizep) };
*psizep = 0;
let keylistp = unsafe { check_mut!(keylistp) };
let mut h = Helper::new(session);
let decryptor = wrap_err!(
DecryptorBuilder::from_bytes(ctext),
UnknownError,
"DecryptorBuilder")?;
let mut decryptor = match decryptor.with_policy(crate::P, None, &mut h) {
Ok(decryptor) => decryptor,
Err(err) => {
match err.downcast::<Error>() {
Ok(err) => return Err(err),
Err(err) => return Err(Error::DecryptNoKey(err)),
}
}
};
let mut content = Vec::new();
wrap_err!(decryptor.read_to_end(&mut content),
UnknownError,
"read_to_end")?;
let h = decryptor.helper_mut();
if ! h.decrypted {
return Err(Error::DecryptNoKey(anyhow::anyhow!("decryption failed")));
}
// Add a terminating NUL for naive users.
content.push(0);
unsafe {
let buffer = malloc(content.len()) as *mut u8;
if buffer.is_null() {
return Err(Error::OutOfMemory(
"content".into(), content.len()));
}
slice::from_raw_parts_mut(buffer, content.len())
.copy_from_slice(&content);
*ptextp = buffer as *mut _;
// Don't count the trailing NUL.
*psizep = content.len() - 1;
}
if h.signer_keylist.len() == 0 {
h.signer_keylist.add("");
}
h.signer_keylist.append(&mut h.recipient_keylist);
*keylistp = mem::replace(&mut h.signer_keylist, StringList::empty(mm)).to_c();
if ! filename_ptr.is_null() {
if let Some(p) = unsafe { filename_ptr.as_mut() } {
if let Some(filename) = h.filename.as_ref() {
*p = rust_bytes_to_c_str_lossy(mm, filename)?;
} else {
*p = ptr::null_mut();
}
};
}
// **********************************
// Sync changes with pgp_verify_text.
// **********************************
if h.good_checksums > 0 {
// If there is at least one signature that we can verify,
// succeed.
return Err(Error::DecryptedAndVerified);
} else if h.revoked_key > 0 {
// If there are any signatures from revoked keys, fail.
return Err(Error::VerifySignerKeyRevoked);
} else if h.expired_key > 0 {
// If there are any signatures from expired keys, fail.
return Err(Error::Decrypted);
} else if h.bad_key > 0 {
// If there are any signatures from invalid keys (keys
// that are not signing capable), fail.
return Err(Error::Decrypted);
} else if h.bad_checksums > 0 {
// If there are any bad signatures, fail.
return Err(Error::DecryptSignatureDoesNotMatch);
} else {
// We couldn't verify any signatures (possibly because we
// don't have the keys).
return Err(Error::Decrypted);
}
});
// PEP_STATUS pgp_verify_text(
// PEP_SESSION session, const char *text, size_t size,
// const char *signature, size_t sig_size, stringlist_t **keylist)
ffi!(fn pgp_verify_text(session: *mut Session,
text: *const c_char, size: size_t,
signature: *const c_char, sig_size: size_t,
keylistp: *mut *mut StringListItem)
-> Result<()>
{
let session = Session::as_mut(session)?;
let mm = session.mm();
if size == 0 || sig_size == 0 {
return Err(Error::DecryptWrongFormat);
}
let text = unsafe { check_slice!(text, size) };
let signature = unsafe { check_slice!(signature, sig_size) };
// ASCII text is sometimes mangled in transport. Show some stats
// to make detecting this easier.
if *crate::TRACE {
let mut cr = 0;
let mut crlf = 0;
let mut lf = 0;
for i in 0..text.len() {
// CR
if text[i] == b'\r' {
cr += 1;
}
// LF
if text[i] == b'\n' {
if i > 0 && text[i - 1] == b'\r' {
cr -= 1;
crlf += 1;
} else {
lf += 1;
}
}
}
t!("Text to verify: {} bytes with {} crlfs, {} bare crs and {} bare lfs",
size, crlf, cr, lf);
}
let mut h = Helper::new(session);
let verifier = wrap_err!(
DetachedVerifierBuilder::from_bytes(&signature[..]),
UnknownError,
"Creating DetachedVerifierBuilder")?;
let mut verifier = match verifier.with_policy(crate::P, None, &mut h) {
Ok(verifier) => verifier,
Err(err) => {
match err.downcast::<Error>() {
Ok(err) => return Err(err),
Err(err) => return Err(Error::VerifyNoKey(err)),
}
}
};
wrap_err!(
verifier.verify_bytes(text),
UnknownError,
"Verifying text")?;
if h.signer_keylist.len() == 0 {
h.signer_keylist.add("");
}
h.signer_keylist.append(&mut h.recipient_keylist);
unsafe { keylistp.as_mut() }.map(|p| {
*p = mem::replace(&mut h.signer_keylist, StringList::empty(mm)).to_c();
});
// *****************************************
// Sync changes with pgp_decrypt_and_verify.
// *****************************************
if h.good_checksums > 0 {
// If there is at least one signature that we can verify,
// succeed.
return Err(Error::Verified);
} else if h.revoked_key > 0 {
// If there are any signatures from revoked keys, fail.
return Err(Error::VerifySignerKeyRevoked);
} else if h.expired_key > 0 {
// If there are any signatures from expired keys, fail.
return Err(Error::Decrypted);
} else if h.bad_key > 0 {
// If there are any signatures from invalid keys (keys
// that are not signing capable), fail.
return Err(Error::Decrypted);
} else if h.bad_checksums > 0 {
// If there are any bad signatures, fail.
return Err(Error::DecryptSignatureDoesNotMatch);
} else {
// We couldn't verify any signatures (possibly because we
// don't have the keys).
return Err(Error::Unencrypted);
}
});
// PEP_STATUS pgp_sign_only(
// PEP_SESSION session, const char* fpr, const char *ptext,
// size_t psize, char **stext, size_t *ssize)
ffi!(fn pgp_sign_only(
session: *mut Session,
fpr: *const c_char,
ptext: *const c_char, psize: size_t,
stextp: *mut *mut c_char, ssizep: *mut size_t)
-> Result<()>
{
let session = Session::as_mut(session)?;
let mm = session.mm();
let fpr = unsafe { check_fpr!(fpr) };
let ptext = unsafe { check_slice!(ptext, psize) };
let stextp = unsafe { check_mut!(stextp) };
*stextp = ptr::null_mut();
let ssizep = unsafe { check_mut!(ssizep) };
*ssizep = 0;
let password = session.curr_passphrase();
let keystore = session.keystore();
let (cert, _private) = keystore.cert_find(fpr, true)?;
let vc = wrap_err!(
cert.with_policy(crate::P, None),
KeyUnsuitable,
format!("{} rejected by policy", cert.fingerprint()))?;
let key =
_pgp_get_decrypted_key_iter(
vc.keys().alive().revoked(false).for_signing().secret()
.map(|ka| ka.key()),
password.as_ref())?;
let signer_keypair = wrap_err!(
key.into_keypair(),
UnknownError,
"Creating key pair from signing key")?;
let mut stext = Vec::new();
let message = Message::new(&mut stext);
let message = wrap_err!(
Armorer::new(message).build(),
UnknownError,
"Setting up armorer")?;
let mut message = wrap_err!(
Signer::new(message, signer_keypair).detached().build(),
UnknownError,
"Setting up signer")?;
wrap_err!(
message.write_all(ptext),
UnknownError,
"Signing message")?;
wrap_err!(
message.finalize(),
UnknownError,
"Finalizing message")?;
rust_bytes_to_ptr_and_len(mm, stext, stextp, ssizep)?;
Ok(())
});
fn pgp_encrypt_sign_optional(
session: *mut Session,
keylist: *mut StringListItem,
ptext: *const c_char, psize: size_t,
ctextp: *mut *mut c_char, csizep: *mut size_t,
sign: bool)
-> Result<()>
{
tracer!(*crate::TRACE, "pgp_encrypt_sign_optional");
let session = Session::as_mut(session)?;
let mm = session.mm();
let ptext = unsafe { check_slice!(ptext, psize) };
let ctextp = unsafe { check_mut!(ctextp) };
*ctextp = ptr::null_mut();
let csizep = unsafe { check_mut!(csizep) };
*csizep = 0;
let password = session.curr_passphrase();
let keystore = session.keystore();
let keylist = StringList::to_rust(mm, keylist, false);
t!("{} recipients.", keylist.len());
for (i, v) in keylist.iter().enumerate() {
t!(" {}. {}", i, String::from_utf8_lossy(v.to_bytes()));
}
if sign {
t!("First recipient will sign the message");
}
// Get the keys for the recipients.
let mut recipient_keys = Vec::new();
let mut signer_keypair = None;
for (i, item) in keylist.iter().enumerate() {
let fpr = wrap_err!(
Fingerprint::from_hex(&String::from_utf8_lossy(item.to_bytes())),
UnknownError,
"Not a fingerprint")?;
let (cert, _private) = keystore.cert_find(fpr, false)?;
let vc = wrap_err!(
cert.with_policy(crate::P, None),
KeyUnsuitable,
format!("{} rejected by policy", cert.fingerprint()))?;
// Collect all of the keys that have the encryption for
// transport capability.
// Note: there might not be any valid encryption-capable
// subkeys. Normally this isn't a problem as we consider such
// certificates to be "broken" (cf. _pgp_key_broken) and won't
// use them. But there is a time of check, time of use race,
// which we ignore.
let mut have_one = false;
for ka in vc.keys().alive().revoked(false).for_transport_encryption() {
recipient_keys.push(ka.key().clone());
have_one = true;
}
if ! have_one {
t!("warning: {} doesn't have any valid encryption-capable subkeys",
vc.fingerprint());
}
// The the first recipient is the signer.
if sign && i == 0 {
let key =
_pgp_get_decrypted_key_iter(
vc.keys().alive().revoked(false).for_signing().secret()
.map(|ka| ka.key()),
password.as_ref())?;
let keypair = wrap_err!(
key.into_keypair(),
UnknownError,
"Creating key pair from signing key")?;
signer_keypair = Some(keypair);
}
}
let recipients: Vec<Recipient> = recipient_keys
.iter()
.map(|key| Recipient::new(key.keyid(), key))
.collect();
let mut ctext = Vec::new();
let message = Message::new(&mut ctext);
let message = wrap_err!(
Armorer::new(message).build(),
UnknownError,
"Setting up armorer")?;
let mut message = wrap_err!(
Encryptor::for_recipients(message, recipients).build(),
UnknownError,
"Setting up encryptor")?;
if let Some(keypair) = signer_keypair {
message = wrap_err!(
Signer::new(message, keypair).build(),
UnknownError,
"Setting up signer")?;
}
let mut message = wrap_err!(
LiteralWriter::new(message).build(),
UnknownError,
"Setting up literal writer")?;
wrap_err!(
message.write_all(ptext),
UnknownError,
"Encrypting message")?;
wrap_err!(
message.finalize(),
UnknownError,
"Finalizing message")?;
rust_bytes_to_ptr_and_len(mm, ctext, ctextp, csizep)?;
Ok(())
}
// PEP_STATUS pgp_encrypt_only(
// PEP_SESSION session, const stringlist_t *keylist, const char *ptext,
// size_t psize, char **ctext, size_t *csize)
ffi!(fn pgp_encrypt_only(session: *mut Session,
keylist: *mut StringListItem,
ptext: *const c_char, psize: size_t,
ctextp: *mut *mut c_char, csizep: *mut size_t)
-> Result<()>
{
pgp_encrypt_sign_optional(
session, keylist, ptext, psize, ctextp, csizep, false)
});
// PEP_STATUS pgp_encrypt_and_sign(
// PEP_SESSION session, const stringlist_t *keylist, const char *ptext,
// size_t psize, char **ctext, size_t *csize)
ffi!(fn pgp_encrypt_and_sign(session: *mut Session,
keylist: *mut StringListItem,
ptext: *const c_char, psize: size_t,
ctextp: *mut *mut c_char, csizep: *mut size_t)
-> Result<()>
{
pgp_encrypt_sign_optional(
session, keylist, ptext, psize, ctextp, csizep, true)
});
// PEP_STATUS _pgp_generate_keypair(PEP_SESSION session, pEp_identity *identity, time_t when)
ffi!(fn _pgp_generate_keypair(session: *mut Session,
identity: *mut PepIdentity,
when: time_t)
-> Result<()>
{
let session = Session::as_mut(session)?;
let mm = session.mm();
let identity = PepIdentity::as_mut(identity)?;
t!("identity: {:?}", identity);
let is_group_identity
= identity.identity_flag(PepIdentityFlags::GroupIdent);
// NOTE: FOR NOW, NO PASSPHRASE-BASED KEYS WILL BE GENERATED FOR
// GROUP ENCRYPTION. VOLKER HAS A PLAN TO FIX THIS.
let password = if is_group_identity {
None
} else if session.new_key_pass_enabled() {
if let Some(password) = session.generation_passphrase() {
Some(password)
} else {
return Err(Error::PassphraseForNewKeysRequired);
}
} else {
None
};
t!("password protected: {}",
if password.is_some() { "yes" } else { "no" });
let address = identity.address()
.ok_or_else(|| {
Error::IllegalValue(
"identity->address must be non-NULL".into())
})?
.to_str()
.map_err(|err| {
Error::IllegalValue(
format!("identity->address must be UTF-8 encoded: {}",
err))
})?;
t!("identity.address: {}", address);
let username = identity.username();
let username = if let Some(username) = username {
let username = username.to_str()
.map_err(|err| {
Error::IllegalValue(
format!("identity->username must be UTF-8 encoded: {}",
err))
})?;
if username == address {
// Ignore the username if it is the same as the address.
None
} else {
Some(username)
}
} else {
None
};
t!("identity.username: {:?}", username);
let userid = wrap_err!(
UserID::from_unchecked_address(username, None, address)
.or_else(|err| {
if let Some(username) = username {
// Replace parentheses in input string with
// brackets.
let username = &username
.replace("(", "[")
.replace(")", "]")[..];
t!("Invalid username, trying '{}'", username);
UserID::from_unchecked_address(
Some(username),
None,
address)
} else {
Err(err)
}
})
.or_else(|err| {
if let Some(username) = username {
// Replace everything but letters and numbers
// with _.
let username = &username.chars()
.map(|c| {
match c {
c @ '0'..='9' => c,
c @ 'a'..='z' => c,
c @ 'A'..='Z' => c,
_ => '_'
}
})
.collect::<String>()[..];
t!("Invalid username, trying '{}'", username);
UserID::from_unchecked_address(
Some(username),
None,
address)
} else {
Err(err)
}
}),
UnknownError,
"UserID::from_unchecked_address")?;
// Generate a key.
let mut certb = CertBuilder::general_purpose(
Some(session.cipher_suite().try_into().unwrap_or_default()),
Some(userid));
certb = certb.set_password(password);
if when > 0 {
certb = certb.set_creation_time(
Some(UNIX_EPOCH + Duration::new(when as u64, 0)));
}
let (cert, _) = wrap_err!(
certb.generate(),
CannotCreateKey,
"Generating a key pair")?;
let fpr = cert.fingerprint();
wrap_err!(
session.keystore().cert_save(cert),
CannotCreateKey,
"Saving new key")?;
identity.set_fingerprint(mm, fpr);
Ok(())
});
// PEP_STATUS pgp_generate_keypair(PEP_SESSION session, pEp_identity *identity)
#[no_mangle] pub extern "C"
fn pgp_generate_keypair(session: *mut Session,
identity: *mut PepIdentity)
-> crate::ErrorCode
{
_pgp_generate_keypair(session, identity, 0)
}
// PEP_STATUS pgp_delete_keypair(PEP_SESSION session, const char *fpr_raw)
ffi!(fn pgp_delete_keypair(session: *mut Session,
fpr: *const c_char)
-> Result<()>
{
let session = Session::as_mut(session)?;
let keystore = session.keystore();
let fpr = unsafe { check_fpr!(fpr) };
t!("Deleting {}", fpr);
keystore.cert_delete(fpr)
});
// Imports the keyring. If keydata contains more than one
// ascii-armored keyring, this only imports the first ascii-armored
// keyring.
fn import_keydata(session: &mut Session,
keydata: &[u8],
private_idents: &mut PepIdentityList,
imported_keys: &mut StringList,
changed_bitvec: &mut u64)
-> Result<()>
{
tracer!(*crate::TRACE, "import_keydata");
let keystore = session.keystore();
// We need to look at the first packet to figure out what we
// should do.
let ppr = match PacketParser::from_bytes(keydata) {
Ok(ppr) => ppr,
Err(err) =>
return Err(Error::UnknownError(
err, "Creating packet parser".into())),
};
let packet = match ppr.as_ref() {
Ok(pp) => &pp.packet,
Err(_eof) => {
return Err(Error::UnknownError(
anyhow::anyhow!("Unexpected EOF").into(),
"No data".into()));
}
};
match packet {
Packet::Signature(sig) => {
// Check that we have a certificate revocation
// certification. If so, try to import it.
if sig.typ() != SignatureType::KeyRevocation {
t!("Can't import a {} signature", sig.typ());
return Err(Error::NoKeyImported);
}
for issuer in sig.get_issuers().into_iter() {
match keystore.cert_find_with_key(issuer.clone(), false) {
Err(err) => {
t!("Can't merge signature: \
no certificate for {} available: {}",
issuer, err);
}
Ok((cert, _)) => {
let fpr = cert.fingerprint();
if let Err(err)
= sig.clone().verify_primary_key_revocation(
&cert.primary_key(),
&cert.primary_key())
{
t!("Revocation certificate not issued by {}: {}",
fpr, err);
continue;
}
match cert.insert_packets(sig.clone()) {
Err(err) => {
t!("Merging signature with {} failed: {}",
fpr, err);
// This trumps any other error.
return wrap_err!(
Err(err),
UnknownError,
"inserting packets");
}
Ok(cert) => {
match keystore.cert_save(cert) {
Ok((_, changed)) => {
let count = imported_keys.len();
if changed && count < 64 {
*changed_bitvec |= 1 << count;
}
imported_keys.add(fpr.to_hex());
return Err(Error::KeyImported);
}
Err(err) => {
t!("Saving updated certificate {} \
failed: {}",
fpr, err);
// This trumps any other error.
return Err(err);
}
}
}
}
}
}
}
t!("Failed to import revocation certificate allegedly issued by {:?}.",
sig
.issuers().next()
.map(|kh| kh.to_hex())
.unwrap_or("<no issuer subpacket>".into()));
return Err(Error::NoKeyImported);
}
Packet::PublicKey(_) | Packet::SecretKey(_) => {
let mut got_one = false;
for certo in CertParser::from(ppr) {
match certo {
Ok(cert) => {
let fpr = cert.fingerprint();
t!("Importing certificate {}", fpr);
for ua in cert.userids() {
t!(" User ID: {}", ua.userid());
}
let is_tsk = cert.is_tsk();
let (ident, changed)
= session.keystore().cert_save(cert)?;
imported_keys.add(fpr.to_hex());
t!("Adding {} to imported_keys", fpr);
if let Some(ident) = ident {
if is_tsk {
t!("Adding {:?} to private_idents", ident);
private_idents.add(&ident);
}
}
if changed {
let i = imported_keys.len() - 1;
if i < 64 {
(*changed_bitvec) |= 1 << i;
}
}
got_one = true;
}
e @ Err(_) => {
wrap_err!(e,
UnknownError,
"Error reading keyring")?;
}
}
}
if !got_one {
Err(Error::NoKeyImported)
} else {
Err(Error::KeyImported)
}
}
packet => {
t!("Can't import a {} packet", packet.tag());
Err(Error::NoKeyImported)
}
}
}
// Imports the keydata and returns a PepIdentity and whether
// the certificate is changed relative to the copy on disk.
//
// Whether the certificate is changed is a heuristic. It may
// indicate that the certificate has changed when it hasn't (false
// positive), but it will never say that the certificate has not
// changed when it has (false negative).
//
// PEP_STATUS pgp_import_keydata(PEP_SESSION session, const char *keydata,
// size_t size, identity_list **private_idents,
// stringlist_t** imported_keys,
// uint64_t* changed_key_index)
ffi!(fn pgp_import_keydata(session: *mut Session,
keydata: *const c_char,
keydata_len: size_t,
identity_listp: *mut *mut PepIdentityListItem,
imported_keysp: *mut *mut StringListItem,
changed_key_indexp: *mut u64)
-> Result<()>
{
let session = Session::as_mut(session)?;
let mm = session.mm();
if imported_keysp.is_null() && ! changed_key_indexp.is_null() {
return Err(Error::IllegalValue(
"When changed_key_index is provided, \
import_keys must also be provided."
.into()));
}
let keydata = unsafe { check_slice!(keydata, keydata_len) };
// We add(!) to the existing lists.
let mut identity_list = unsafe { identity_listp.as_mut() }
.map(|p| PepIdentityList::to_rust(mm, *p, false))
.unwrap_or_else(|| PepIdentityList::empty(mm));
let mut imported_keys = unsafe { imported_keysp.as_mut() }
.map(|p| StringList::to_rust(mm, *p, false))
.unwrap_or_else(|| StringList::empty(mm));
let mut changed_key_index: u64 = unsafe { changed_key_indexp.as_mut() }
.map(|p| *p)
.unwrap_or(0);
// Get the start of each ascii armor block.
let mut offsets = Vec::new();
let searcher = TwoWaySearcher::new(b"-----BEGIN PGP");
loop {
let start = offsets.iter().last().map(|&i| i + 1).unwrap_or(0);
if let Some(i) = searcher.search_in(&keydata[start..]) {
offsets.push(start + i);
} else {
break;
}
}
t!("armor block offsets: {:?}", offsets);
let retval = if offsets.len() == 0 {
import_keydata(session,
keydata,
&mut identity_list,
&mut imported_keys,
&mut changed_key_index)
} else if offsets.len() == 1 {
import_keydata(session,
&keydata[offsets[0]..],
&mut identity_list,
&mut imported_keys,
&mut changed_key_index)
} else {
let mut retval = Error::KeyImported;
offsets.push(keydata.len());
for offsets in offsets.windows(2) {
let keydata = &keydata[offsets[0]..offsets[1]];
let curr_status = import_keydata(session,
keydata,
&mut identity_list,
&mut imported_keys,
&mut changed_key_index);
// import_keydata should not return Ok; on success, it
// should return KeyImported.
let curr_status = match curr_status {
Err(err) => err,
Ok(()) => panic!("import_keydata returned Ok"),
};
if ErrorCode::from(&curr_status) != ErrorCode::from(&retval) {
match curr_status {
Error::NoKeyImported
| Error::KeyNotFound(_)
| Error::UnknownError(_, _) => {
match retval {
Error::KeyImported => retval = Error::SomeKeysImported,
Error::UnknownError(_, _) => retval = curr_status,
_ => (),
}
}
Error::KeyImported => retval = Error::SomeKeysImported,
_ => (),
}
}
}
Err(retval)
};
unsafe { identity_listp.as_mut() }.map(|p| {
*p = identity_list.to_c();
});
unsafe { imported_keysp.as_mut() }.map(|p| {
*p = imported_keys.to_c();
});
unsafe { changed_key_indexp.as_mut() }.map(|p| {
*p = changed_key_index;
});
retval
});
// PEP_STATUS pgp_export_keydata(
// PEP_SESSION session, const char *fpr, char **keydata, size_t *size,
// bool secret)
ffi!(fn pgp_export_keydata(session: *mut Session,
fpr: *const c_char,
keydatap: *mut *mut c_char,
keydata_lenp: *mut size_t,
secret: bool)
-> Result<()>
{
let session = Session::as_mut(session)?;
let mm = session.mm();
let fpr = unsafe { check_fpr!(fpr) };
t!("({}, {})", fpr, if secret { "secret" } else { "public" });
let keydatap = unsafe { check_mut!(keydatap) };
let keydata_lenp = unsafe { check_mut!(keydata_lenp) };
// If the caller asks for a secret key and we only have a
// public key, then we return an error.
let (cert, _private) = session.keystore().cert_find(fpr, secret)?;
let mut keydata = Vec::new();
if secret {
wrap_err!(
cert.as_tsk().armored().serialize(&mut keydata),
UnknownError,
format!("Serializing key: {}", cert.fingerprint()))?;
} else {
wrap_err!(
cert.armored().serialize(&mut keydata),
UnknownError,
format!("Serializing certificate: {}", cert.fingerprint()))?;
}
rust_bytes_to_ptr_and_len(mm, keydata, keydatap, keydata_lenp)?;
Ok(())
});
// XXX: The engine does not use this function directly
// (OpenPGP_list_keyinfo is a thin wrapper) and there are no unit
// tests that exercise it. Once there are unit tests, we can add an
// implementation.
//
// PEP_STATUS pgp_list_keyinfo(PEP_SESSION session,
// const char* pattern,
// stringpair_list_t** keyinfo_list)
stub!(pgp_list_keyinfo);
// The sequoia backend has never implemented this function, and the
// engine does not currently use it.
//
// PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern)
stub!(pgp_recv_key);
fn list_keys(session: *mut Session,
pattern: *const c_char,
keylistp: *mut *mut StringListItem,
private_only: bool) -> Result<()>
{
tracer!(*crate::TRACE, "list_keys");
let session = Session::as_mut(session)?;
let mm = session.mm();
let pattern = unsafe { check_cstr!(pattern) };
// XXX: What should we do if pattern is not valid UTF-8?
let pattern = pattern.to_string_lossy();
let keylistp = unsafe { check_mut!(keylistp) };
let mut keylist = StringList::empty(mm);
match session.keystore().list_keys(&pattern, private_only) {
Err(Error::KeyNotFound(_)) => {
// If no keys are found, don't return an error, return the
// empty set.
}
Err(err) => {
return Err(err);
}
Ok(listing) => {
// We return revoked keys.
for (fpr, _, _) in listing {
keylist.add(fpr.to_hex());
}
}
}
t!("Found {} certificates matching '{}'", keylist.len(), pattern);
*keylistp = keylist.to_c();
Ok(())
}
// PEP_STATUS pgp_find_keys(
// PEP_SESSION session, const char *pattern, stringlist_t **keylist)
ffi!(fn pgp_find_keys(session: *mut Session,
pattern: *const c_char,
keylistp: *mut *mut StringListItem)
-> Result<()>
{
list_keys(session, pattern, keylistp, false)
});
// PEP_STATUS pgp_find_private_keys(
// PEP_SESSION session, const char *pattern, stringlist_t **keylist)
ffi!(fn pgp_find_private_keys(session: *mut Session,
pattern: *const c_char,
keylistp: *mut *mut StringListItem)
-> Result<()>
{
list_keys(session, pattern, keylistp, true)
});
// The sequoia backend has never implemented this function, and the
// engine does not currently use it.
//
// PEP_STATUS pgp_send_key(PEP_SESSION session, const char *pattern)
stub!(pgp_send_key);
// PEP_STATUS pgp_renew_key(
// PEP_SESSION session, const char *fpr, const timestamp *ts)
ffi!(fn pgp_renew_key(session: *mut Session,
fpr: *const c_char,
expiration: *const Timestamp)
-> Result<()>
{
let session = Session::as_mut(session)?;
let fpr = unsafe { check_fpr!(fpr) };
let expiration = unsafe { check_ptr!(expiration) };
let password = session.curr_passphrase();
let keystore = session.keystore();
let expiration = Utc.ymd(1900 + expiration.tm_year,
1 + expiration.tm_mon as u32,
expiration.tm_mday as u32)
.and_hms(expiration.tm_hour as u32,
expiration.tm_min as u32,
expiration.tm_sec as u32);
let expiration: SystemTime = expiration.into();
let (cert, _private) = keystore.cert_find(fpr, true)?;
let creation_time = cert.primary_key().creation_time();
if creation_time >= expiration {
// The creation time is after the expiration time!
return Err(Error::UnknownError(
anyhow::anyhow!("creation time ({:?}) \
can't be after expiration time ({:?})",
creation_time, expiration),
"invalid expiration time".into()));
}
let vc = wrap_err!(
cert.with_policy(crate::P, None),
KeyUnsuitable,
format!("{} rejected by policy", cert.fingerprint()))?;
let key =
_pgp_get_decrypted_key_iter(
vc.keys().revoked(false).for_certification().secret()
.map(|ka| ka.key()),
password.as_ref())?;
let mut signer_keypair = wrap_err!(
key.into_keypair(),
UnknownError,
"Creating key pair from certification key")?;
// Set the expiration for all non-revoked keys.
let mut self_sigs = Vec::new();
for (i, ka) in vc.keys().revoked(false).enumerate() {
// Arrange for a backsig, if needed.
let mut self_sig = if i > 0 // subkey
&& (ka.for_certification()
|| ka.for_signing()
|| ka.for_authentication())
{
let subkey = wrap_err!(
ka.key().clone().parts_into_secret(),
UnknownError,
"Can't extend signing-capable subkey's expiration: \
secret key material is not available")?;
let subkey = _pgp_get_decrypted_key(subkey, password.as_ref())?;
let mut subkey_keypair = wrap_err!(
subkey.into_keypair(),
UnknownError,
"Creating key pair from subkey")?;
wrap_err!(
ka.set_expiration_time(
&mut signer_keypair,
Some(&mut subkey_keypair),
Some(expiration)),
UnknownError,
"setting expiration (generating self signature and backsig)")?
} else {
wrap_err!(
ka.set_expiration_time(
&mut signer_keypair,
None,
Some(expiration)),
UnknownError,
"setting expiration (generating self signature)")?
};
self_sigs.append(&mut self_sig);
}
let cert = wrap_err!(
cert.insert_packets(self_sigs),
UnknownError,
"inserting new self signatures")?;
keystore.cert_save(cert)?;
Ok(())
});
// PEP_STATUS pgp_revoke_key(
// PEP_SESSION session, const char *fpr, const char *reason)
ffi!(fn pgp_revoke_key(session: *mut Session,
fpr: *const c_char,
reason: *const c_char)
-> Result<()>
{
let session = Session::as_mut(session)?;
let fpr = unsafe { check_fpr!(fpr) };
let reason = unsafe {
reason.as_ref()
.map(|reason| CStr::from_ptr(reason).to_bytes())
.unwrap_or(b"")
};
let password = session.curr_passphrase();
let keystore = session.keystore();
let (cert, _private) = keystore.cert_find(fpr, true)?;
let vc = wrap_err!(
cert.with_policy(crate::P, None),
KeyUnsuitable,
format!("{} rejected by policy", cert.fingerprint()))?;
let key =
_pgp_get_decrypted_key_iter(
vc.keys().alive().revoked(false).for_certification().secret()
.map(|ka| ka.key()),
password.as_ref())?;
let mut signer_keypair = wrap_err!(
key.into_keypair(),
UnknownError,
"Creating key pair from certification key")?;
let sig = wrap_err!(
cert.revoke(&mut signer_keypair,
ReasonForRevocation::Unspecified,
reason),
UnknownError,
"generating revocation certificate")?;
let cert = wrap_err!(
cert.insert_packets(sig),
UnknownError,
"merging revocation certificate")?;
assert!(matches!(
cert.revocation_status(crate::P, None),
RevocationStatus::Revoked(_)));
keystore.cert_save(cert)?;
Ok(())
});
// Check to see that key, at a minimum, even contains encryption and
// signing subkeys.
fn _pgp_key_broken(vc: &ValidCert) -> bool {
let mut has_enc = false;
let mut has_signing = false;
for ka in vc.keys() {
if ka.for_signing() {
has_signing = true;
}
if ka.for_transport_encryption() || ka.for_storage_encryption() {
has_enc = true;
}
if has_signing && has_enc {
return false;
}
}
return true;
}
fn _pgp_key_expired(vc: &ValidCert) -> bool
{
tracer!(*crate::TRACE, "_pgp_key_expired");
if ! vc.alive().is_ok() {
return true;
}
// Check to see if the key is broken. Ideally, we'd do this in one
// pass below, but givem the choice for how to check for expiry,
// this is the simplest solutiom.
if _pgp_key_broken(vc) {
return false; // still isn't expired. is broken. there's a difference and a different check.
}
// Why is this an indicator of just an expired key and not a
// broken one? This will also reject keys that are not expired,
// but rather missing subkeys.
// Are there at least one certification subkey, one signing subkey
// and one encryption subkey that are live?
let mut can_encrypt = false;
let mut can_sign = false;
for ka in vc.keys().alive().revoked(false) {
if ka.for_transport_encryption() || ka.for_storage_encryption() {
can_encrypt = true;
}
if ka.for_signing() {
can_sign = true;
}
if can_encrypt && can_sign {
break;
}
}
let expired = !(can_encrypt && can_sign);
t!("Key can{} encrypt, can{} sign => {} expired",
if can_encrypt { "" } else { "not" },
if can_sign { "" } else { "not" },
if expired { "" } else { "not" });
return expired;
}
// PEP_STATUS pgp_key_expired(PEP_SESSION session, const char *fpr,
// const time_t when, bool *expired)
ffi!(fn pgp_key_expired(session: *mut Session,
fpr: *const c_char,
when: time_t,
expiredp: *mut bool)
-> Result<()>
{
let session = Session::as_mut(session)?;
let fpr = unsafe { check_fpr!(fpr) };
let expiredp = unsafe { check_mut!(expiredp) };
if when < 0 {
// when is before UNIX EPOCH. The key was not alive at
// this time (the first keys were create around 1990).
*expiredp = true;
return Ok(());
}
let when = SystemTime::UNIX_EPOCH + Duration::new(when as u64, 0);
let (cert, _private) = session.keystore().cert_find(fpr.clone(), false)?;
let vc = wrap_err!(
cert.with_policy(crate::P, when),
UnknownError,
"Invalid certificate")?;
let expired = _pgp_key_expired(&vc);
*expiredp = expired;
t!("{} is {}expired as of {:?}",
fpr,
if expired { "" } else { "not " },
when);
Ok(())
});
fn _pgp_key_revoked(vc: &ValidCert) -> bool
{
if let RevocationStatus::Revoked(_) = vc.revocation_status() {
return true;
}
// Ok, at this point, we need to know if for signing or encryption
// there is ONLY a revoked key available. If so, this key is also
// considered revoked
let mut has_non_revoked_sig_key = false;
let mut has_revoked_sig_key = false;
for ka in vc.keys().for_signing() {
if let RevocationStatus::Revoked(_) = ka.revocation_status() {
has_revoked_sig_key = true;
} else {
has_non_revoked_sig_key = true;
break;
}
}
if has_non_revoked_sig_key {
let mut has_non_revoked_enc_key = false;
let mut has_revoked_enc_key = false;
for ka in vc.keys().for_storage_encryption().for_transport_encryption() {
if let RevocationStatus::Revoked(_) = ka.revocation_status() {
has_revoked_enc_key = true;
} else {
has_non_revoked_enc_key = true;
break;
}
}
if !has_non_revoked_enc_key { // this does NOT mean revoked. it MAY mean broken.
if has_revoked_enc_key {
return true;
}
}
} else if has_revoked_sig_key {
return true;
}
false
}
// PEP_STATUS pgp_key_revoked(PEP_SESSION session, const char *fpr, bool *revoked)
ffi!(fn pgp_key_revoked(session: *mut Session,
fpr: *const c_char,
revokedp: *mut bool)
-> Result<()>
{
let session = Session::as_mut(session)?;
let fpr = unsafe { check_fpr!(fpr) };
let revokedp = unsafe { check_mut!(revokedp) };
let (cert, _private) = session.keystore().cert_find(fpr.clone(), false)?;
let vc = wrap_err!(
cert.with_policy(crate::P, None),
UnknownError,
"Invalid certificate")?;
let revoked = _pgp_key_revoked(&vc);
*revokedp = revoked;
t!("{} is {}revoked",
fpr,
if revoked { "" } else { "not " });
Ok(())
});
// PEP_STATUS pgp_get_key_rating(
// PEP_SESSION session, const char *fpr, PEP_comm_type *comm_type)
// PEP_STATUS pgp_contains_priv_key(PEP_SESSION session, const char *fpr,
// bool *has_private)
ffi!(fn pgp_get_key_rating(session: *mut Session, fpr: *const c_char,
comm_typep: *mut PepCommType)
-> Result<()>
{
let session = Session::as_mut(session)?;
let fpr = unsafe { check_fpr!(fpr) };
let comm_typep = unsafe { check_mut!(comm_typep) };
let mut comm_type = |ct| *comm_typep = ct;
comm_type(PepCommType::Unknown);
let (cert, _private) = session.keystore().cert_find(fpr.clone(), false)?;
let vc = wrap_err!(
cert.with_policy(crate::P, None),
UnknownError,
"Invalid certificate")?;
comm_type(PepCommType::OpenPgpUnconfirmed);
if let RevocationStatus::Revoked(_) = vc.revocation_status() {
comm_type(PepCommType::KeyRevoked);
return Ok(());
}
if _pgp_key_revoked(&vc) {
comm_type(PepCommType::KeyRevoked);
return Ok(());
}
if _pgp_key_broken(&vc) {
comm_type(PepCommType::KeyB0rken);
return Ok(());
}
// MUST guarantee the same behaviour.
if _pgp_key_expired(&vc) {
comm_type(PepCommType::KeyExpired);
return Ok(());
}
let mut worst_enc = PepCommType::NoEncryption;
let mut worst_sign = PepCommType::NoEncryption;
for ka in vc.keys().alive().revoked(false) {
let curr;
use openpgp::types::PublicKeyAlgorithm::*;
match ka.pk_algo() {
#[allow(deprecated)]
RSAEncryptSign | RSAEncrypt | RSASign
| DSA | ElGamalEncrypt | ElGamalEncryptSign =>
{
let bits = ka.mpis().bits().unwrap_or(0);
if bits < 1024 {
curr = PepCommType::KeyTooShort;
} else if bits == 1024 {
curr = PepCommType::OpenPgpWeakUnconfirmed;
} else {
curr = PepCommType::OpenPgpUnconfirmed;
}
}
_ => {
curr = PepCommType::OpenPgpUnconfirmed;
}
}
if ka.for_transport_encryption() || ka.for_storage_encryption() {
worst_enc = if worst_enc == PepCommType::NoEncryption {
curr
} else {
cmp::min(worst_enc, curr)
};
}
if ka.for_signing() {
worst_sign = if worst_sign == PepCommType::NoEncryption {
curr
} else {
cmp::min(worst_sign, curr)
};
}
}
// This may be redundant because of the broken check above; we
// should revisit later. But because this case was falling under
// expired because of how that is written, this was probably never
// hiit here
t!("worse enc: {:?}, worst sig: {:?}", worst_enc, worst_sign);
if worst_enc == PepCommType::NoEncryption
|| worst_sign == PepCommType::NoEncryption
{
comm_type(PepCommType::KeyB0rken);
} else {
comm_type(cmp::min(worst_enc, worst_sign));
}
t!("{}'s rating is {:?}", fpr, *comm_typep);
Ok(())
});
// PEP_STATUS pgp_key_created(PEP_SESSION session, const char *fpr, time_t *created)
ffi!(fn pgp_key_created(session: *mut Session,
fpr: *const c_char,
createdp: *mut time_t)
-> Result<()>
{
let session = Session::as_mut(session)?;
let fpr = unsafe { check_fpr!(fpr) };
let createdp = unsafe { check_mut!(createdp) };
let (cert, _private) = session.keystore().cert_find(fpr, false)?;
let t = wrap_err!(
cert.primary_key().creation_time().duration_since(UNIX_EPOCH),
UnknownError,
"Creation time out of range")?.as_secs();
*createdp = t as time_t;
Ok(())
});
// PEP_STATUS pgp_binary(const char **path)
ffi!(fn pgp_binary(path: *mut *mut c_char) -> Result<()> {
let path = unsafe { check_mut!(path) };
*path = ptr::null_mut();
Ok(())
});
// PEP_STATUS pgp_contains_priv_key(PEP_SESSION session, const char *fpr,
// bool *has_private)
ffi!(fn pgp_contains_priv_key(session: *mut Session, fpr: *const c_char,
has_privatep: *mut bool)
-> Result<()>
{
let session = Session::as_mut(session)?;
let fpr = unsafe { check_fpr!(fpr) };
let has_privatep = unsafe { check_mut!(has_privatep) };
let has_private = match session.keystore().cert_find(fpr, true) {
Ok(_) => true,
Err(Error::KeyNotFound(_)) => false,
Err(err) => return Err(err),
};
*has_privatep = has_private;
Ok(())
});