use std::cmp;
use std::convert::TryInto;
use std::env;
use std::ffi::{
CStr,
};
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,
};
#[allow(unused_imports)]
use anyhow::Context;
use libc::{
c_char,
c_uint,
size_t,
time_t,
};
use chrono::LocalResult;
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,
};
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();
ffi!(fn pgp_config_cipher_suite(session: *mut Session, suite: PepCipherSuite)
-> Result<()>
{
let session = Session::as_mut(session)?;
session.set_cipher_suite(suite)
});
ffi!(fn pgp_cipher_suite_is_supported(session: *mut Session,
suite: PepCipherSuite)
-> Result<()>
{
let _session = Session::as_mut(session)?;
let suite: Result<openpgp::cert::CipherSuite> = suite.try_into();
if let Err(_err) = suite {
Err(Error::CannotConfig("cipher suite".into()))
} else {
Ok(())
}
});
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)
}
}
}
}
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>>
{
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()))
}
}
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");
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::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
OsStr::from_bytes(per_user_directory.to_bytes())
};
#[cfg(windows)]
let per_user_directory = {
match per_user_directory.to_str() {
Ok(s) => s,
Err(err) =>
return Err(Error::IllegalValue(
format!("\
API violation: per_user_directory not UTF-8 encoded ({:?}: {})",
per_user_directory, err))),
}
};
let ks = keystore::Keystore::init(Path::new(per_user_directory))?;
session.init(MM { malloc, free }, ks);
Ok(())
});
ffi!(fn pgp_release(session: *mut Session, _out_last: bool) -> Result<()> {
if ! session.is_null() {
Session::as_mut(session)?.deinit();
}
Ok(())
});
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,
decrypted: bool,
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 }) => {
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 }) => {
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 }) => {
t!("Can't check signature ({:02X}{:02X}): \
key {} is bad: {}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
ka.cert().fingerprint(),
error);
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;
}
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() {
t!("reason: key is expired: {}", err);
self.expired_key += 1;
}
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(())
}
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();
let mut have_wildcards = false;
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() {
have_wildcards = true;
continue;
}
t!("Considering PKESK for {}", keyid);
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 {
continue;
}
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() {
continue;
}
if tsks.is_none() {
tsks = Some(keystore.cert_all(true)?);
if tsks.as_ref().unwrap().len() == 0 {
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;
}
};
match pkesk.decrypt(&mut keypair, sym_algo) {
Some((sym_algo, sk)) => {
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())
}
}
}
}
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) };
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")));
}
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 _;
*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();
}
};
}
if h.good_checksums > 0 {
return Err(Error::DecryptedAndVerified);
} else if h.revoked_key > 0 {
return Err(Error::VerifySignerKeyRevoked);
} else if h.expired_key > 0 {
return Err(Error::Decrypted);
} else if h.bad_key > 0 {
return Err(Error::Decrypted);
} else if h.bad_checksums > 0 {
return Err(Error::DecryptSignatureDoesNotMatch);
} else {
return Err(Error::Decrypted);
}
});
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) };
if *crate::TRACE {
let mut cr = 0;
let mut crlf = 0;
let mut lf = 0;
for i in 0..text.len() {
if text[i] == b'\r' {
cr += 1;
}
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();
});
if h.good_checksums > 0 {
return Err(Error::Verified);
} else if h.revoked_key > 0 {
return Err(Error::VerifySignerKeyRevoked);
} else if h.expired_key > 0 {
return Err(Error::Decrypted);
} else if h.bad_key > 0 {
return Err(Error::Decrypted);
} else if h.bad_checksums > 0 {
return Err(Error::DecryptSignatureDoesNotMatch);
} else {
return Err(Error::Unencrypted);
}
});
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");
}
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()))?;
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());
}
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(())
}
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)
});
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)
});
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);
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 {
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 {
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 {
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")?;
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(())
});
#[no_mangle] pub extern "C"
fn pgp_generate_keypair(session: *mut Session,
identity: *mut PepIdentity)
-> crate::ErrorCode
{
_pgp_generate_keypair(session, identity, 0)
}
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)
});
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();
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) => {
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);
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);
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)
}
}
}
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) };
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);
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);
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
});
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) };
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(())
});
stub!(pgp_list_keyinfo);
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) };
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(_)) => {
}
Err(err) => {
return Err(err);
}
Ok(listing) => {
for (fpr, _, _) in listing {
keylist.add(fpr.to_hex());
}
}
}
t!("Found {} certificates matching '{}'", keylist.len(), pattern);
*keylistp = keylist.to_c();
Ok(())
}
ffi!(fn pgp_find_keys(session: *mut Session,
pattern: *const c_char,
keylistp: *mut *mut StringListItem)
-> Result<()>
{
list_keys(session, pattern, keylistp, false)
});
ffi!(fn pgp_find_private_keys(session: *mut Session,
pattern: *const c_char,
keylistp: *mut *mut StringListItem)
-> Result<()>
{
list_keys(session, pattern, keylistp, true)
});
stub!(pgp_send_key);
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
.with_ymd_and_hms(1900 + expiration.tm_year,
1 + expiration.tm_mon as u32,
expiration.tm_mday as u32,
expiration.tm_hour as u32,
expiration.tm_min as u32,
expiration.tm_sec as u32);
let expiration = if let LocalResult::Single(t) = expiration {
SystemTime::from(t)
} else {
return Err(Error::UnknownError(
anyhow::anyhow!("invalid expiration time ({:?})",
expiration),
"invalid expiration time".into()));
};
let (cert, _private) = keystore.cert_find(fpr, true)?;
let creation_time = cert.primary_key().creation_time();
if creation_time >= expiration {
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")?;
let mut self_sigs = Vec::new();
for (i, ka) in vc.keys().revoked(false).enumerate() {
let mut self_sig = if i > 0 && (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(())
});
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(())
});
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;
}
if _pgp_key_broken(vc) {
return false; }
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;
}
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 {
*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;
}
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 { if has_revoked_enc_key {
return true;
}
}
} else if has_revoked_sig_key {
return true;
}
false
}
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(())
});
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(());
}
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)
};
}
}
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(())
});
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(())
});
ffi!(fn pgp_binary(path: *mut *mut c_char) -> Result<()> {
let path = unsafe { check_mut!(path) };
*path = ptr::null_mut();
Ok(())
});
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(())
});
ffi!(fn pgp_random(buffer: *mut c_char, len: size_t) -> Result<()> {
let buffer = unsafe { check_slice_mut!(buffer, len) };
openpgp::crypto::random(buffer);
Ok(())
});
#[test]
fn test_random() {
fn rand(i: usize) -> Vec<u8> {
let mut buffer = vec![0u8; i];
let result = pgp_random(buffer.as_mut_ptr() as *mut c_char, i);
assert_eq!(result, 0, "pgp_random does not fail");
buffer
}
for _ in 0..32 {
let mut total = 0u64;
let mut ones_count = 0u64;
let mut samples = 0;
for i in 0..128 {
let buffer = rand(i);
for e in buffer.into_iter() {
total += e as u64;
ones_count += (e as u8).count_ones() as u64;
samples += 1;
}
}
assert!(samples > 0);
let average = total / samples;
eprintln!("{} / {} = {}", total, samples, average);
assert!(128 - 8 < average && average < 128 + 8,
"{} is extremely unlikely, your random number generator \
is broken",
average);
let average_ones = ones_count / samples;
eprintln!("ones count: {} / {} = {}",
ones_count, samples, average_ones);
assert!(3 * samples <= ones_count && ones_count <= 5 * samples,
"Average number of ones ({}) is extremely unlikely, \
your random number generator is broken",
average_ones);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pep::Session;
const ALICE_PGP: &'static str = "\
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: 64E7 981D 4220 C6D2 6638 EA7C B72F C47E 011B C764
Comment: <alice@example.org>
xVgEZDcXhRYJKwYBBAHaRw8BAQdAu8SZs5zqYLLBaMpfbIuRg9CDuQNnkxGqCEiv
MnD0czYAAQD2c2gL/jPZzZHbBQ2OHycdNOep79BLfs6ZBYiTodrukBDvwsALBB8W
CgB9BYJkNxeFAwsJBwkQty/EfgEbx2RHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu
c2VxdW9pYS1wZ3Aub3Jn37FPCmBUoG7ZfDm0Q7haM6zbXc/00GediXbjruMVCU8D
FQoIApsBAh4BFiEEZOeYHUIgxtJmOOp8ty/EfgEbx2QAAKL8AQD3bT5GYlfah/jx
agUUPAU9awR17PJiF6qWZjhk0wX5GgEAyn0OjvpkCh8IUVXpgtmuN8NoBHXvLrPH
FPpmBRhuEQfNEzxhbGljZUBleGFtcGxlLm9yZz7CwA4EExYKAIAFgmQ3F4UDCwkH
CRC3L8R+ARvHZEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5v
cmfHWWYTVAdC9hBB3QvxZv+gKmr8p8yP48nf/4pMJHCbeQMVCggCmQECmwECHgEW
IQRk55gdQiDG0mY46ny3L8R+ARvHZAAAkPcA/06QXtr0odpnQTQAzbgnDHCTisPv
TeWhbP8Bu6dPSIjPAP9aZPn+s+Pdc52SEHBOQyM5dyWpbzvgLgwzNCetez6vDcdY
BGQ3F4UWCSsGAQQB2kcPAQEHQFda1LIxdqccxiIgIbHXral59VRIPqrBnCSUckTe
lZsSAAD/f7zaMPxIrEVkSkevdsdg5Om0Cgyz+SF8f9/763kPWF4SHMLAvwQYFgoB
MQWCZDcXhQkQty/EfgEbx2RHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9p
YS1wZ3Aub3Jn7pAplEnQhQvvgDihCw52kKa/JT50MTuZPkDY5bpsVhMCmwK+oAQZ
FgoAbwWCZDcXhQkQ2W9Jvvf+shBHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2Vx
dW9pYS1wZ3Aub3JnOmJvUfEy2ymUu3MyS+J92P2nuaBYjpJ2L6hcuf8Bm+gWIQQV
Suent38vsP649abZb0m+9/6yEAAApsYA/0Pdfn61X/u1GEVhCc0bKkBVYHwlXEJa
nQ+9KBVaJFWnAQCh5bNyeV+8MU9odXZWxa8sg1VeDkFCTppWTsZTH9UHBRYhBGTn
mB1CIMbSZjjqfLcvxH4BG8dkAABsNAEA9SQeJIPp9SMFJY2nCcclv0u/KxfZTBOJ
1jfMU8LrJZAA/34fNxcn99iv23yIv1QnZtBogP5cfcxxhnaHlVPnaDAMx10EZDcX
hRIKKwYBBAGXVQEFAQEHQLgFZFFGd+IWWAehV4i7b23SLLsxlaWsacPSkFswfl59
AwEIBwAA/1Wj3K1G1S95h+0jhugeYnj1LZLWO4PiaiiSBCN3IxNoD8rCwAAEGBYK
AHIFgmQ3F4UJELcvxH4BG8dkRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVv
aWEtcGdwLm9yZxtd7Kn2Jmj5NYfdhBVEL2qLvRZAvnfp5oveXeradLAhApsMFiEE
ZOeYHUIgxtJmOOp8ty/EfgEbx2QAAMDtAQC5fuVHyLfl1SqncTZaZkdaoSEqHdZA
4RICLYzXmmut2gD/U5k61iRwqBWiepJ1IBNQKJvq4j0CWr0LaUk4FtGi6go=
=qYY6
-----END PGP PRIVATE KEY BLOCK-----
";
const CTEXT: &'static str = "\
-----BEGIN PGP MESSAGE-----
wV4DAD9UK+FUDNQSAQdAjozAEPDG+bvHV3YjHuBcJMhPfltHaK83R6kSyvIYSHUw
4k+spf7kJJ73EMrhgzlf92Ems0RZXTeCDOKKDFAP1yfXP1ZIX3WVHraMsOg3zQSc
0sBsAVbTDJEvN8Y94sDQtZkj7gObqTyyQZAe+oj2ZXxY+b+uGJ+9sAP26mdubfZ+
HyduxJC2nlImUwC/TMInblDyJNUiOJz5i9Wa73I8A0EkGFsdWbXQ5RkK82oQx8Bi
UYr+5DzpYhuKgqgEA46KdQv9L92sUzv/tyRaLH3i6sSmzioWDuSGPxBf6sGrywWc
tCgdoM9Lnwuhbv60Qobm1MfLMCwwkbXsy0rH6Pel5kWPnBrNWEi3hNhzMGeDKD9S
izKTrCcA1NbpFL7nndShmIlRFZ5q+XNdyWB0STFaPV5uzfCnxXpNrz7m4EXOFSZP
k4tPxebef5BpUqRxdEhHQab24bTKrI1cWa9pJdpWQrssEPE7y7pNB83p/I+fKqYv
7ykni6KBcL+Bcyf6d3xx
=PALz
-----END PGP MESSAGE-----
";
const MSG: &'static str = "hi, pep\n";
#[test]
fn decrypt() -> Result<()> {
let session = Session::new();
let rc = pgp_import_keydata(
session,
ALICE_PGP.as_ptr() as *const c_char, ALICE_PGP.len(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut());
assert_eq!(rc, Error::KeyImported.into());
let mut plaintext: *mut c_char = std::ptr::null_mut();
let mut plaintext_len: size_t = 0;
let mut keylist: *mut StringListItem = std::ptr::null_mut();
let rc = pgp_decrypt_and_verify(
session,
CTEXT.as_ptr() as *const c_char, CTEXT.len(),
std::ptr::null(), 0, &mut plaintext, &mut plaintext_len as *mut _,
&mut keylist as *mut *mut _,
std::ptr::null_mut(), );
assert_eq!(rc, Error::DecryptedAndVerified.into());
let ptext = unsafe { check_slice!(plaintext, plaintext_len) };
assert_eq!(ptext, MSG.as_bytes());
unsafe { Box::from_raw(session) };
Ok(())
}
}