use std::fmt;
use std::io::{self, Write};
use std::time::SystemTime;
use crate::{
armor,
crypto,
Error,
Fingerprint,
HashAlgorithm,
KeyHandle,
Profile,
Result,
crypto::Password,
crypto::SessionKey,
packet::prelude::*,
packet::signature,
packet::key,
cert::prelude::*,
};
use crate::packet::header::CTB;
use crate::packet::header::BodyLength;
use crate::parse::HashingMode;
use super::{
Marshal,
};
use crate::types::{
AEADAlgorithm,
CompressionAlgorithm,
CompressionLevel,
DataFormat,
Features,
SignatureType,
SymmetricAlgorithm,
};
pub(crate) mod writer;
pub mod padding;
mod partial_body;
use partial_body::PartialBodyFilter;
mod dash_escape;
use dash_escape::DashEscapeFilter;
mod trim_whitespace;
use trim_whitespace::TrailingWSFilter;
#[derive(Debug)]
struct Cookie {
level: usize,
private: Private,
}
impl Cookie {
pub fn set_private(mut self, p: Private) -> Self {
self.private = p;
self
}
}
#[derive(Debug)]
enum Private {
Nothing,
Signer,
Armorer {
set_profile: Option<Profile>,
},
Encryptor {
profile: Profile,
},
}
impl Cookie {
fn new(level: usize) -> Self {
Cookie {
level,
private: Private::Nothing,
}
}
}
impl Default for Cookie {
fn default() -> Self {
Cookie::new(0)
}
}
#[derive(Debug)]
pub struct Message<'a>(writer::BoxStack<'a, Cookie>);
assert_send_and_sync!(Message<'_>);
impl<'a> Message<'a> {
pub fn new<W: 'a + io::Write + Send + Sync>(w: W) -> Message<'a> {
writer::Generic::new(w, Cookie::new(0))
}
pub fn finalize_one(self) -> Result<Option<Message<'a>>> {
Ok(self.0.into_inner()?.map(|bs| Self::from(bs)))
}
pub fn finalize(self) -> Result<()> {
let mut stack = self;
while let Some(s) = stack.finalize_one()? {
stack = s;
}
Ok(())
}
}
impl<'a> From<&'a mut (dyn io::Write + Send + Sync)> for Message<'a> {
fn from(w: &'a mut (dyn io::Write + Send + Sync)) -> Self {
writer::Generic::new(w, Cookie::new(0))
}
}
pub struct Armorer<'a> {
kind: armor::Kind,
headers: Vec<(String, String)>,
inner: Message<'a>,
}
assert_send_and_sync!(Armorer<'_>);
impl<'a> Armorer<'a> {
pub fn new(inner: Message<'a>) -> Self {
Self {
kind: armor::Kind::Message,
headers: Vec::with_capacity(0),
inner,
}
}
pub fn kind(mut self, kind: armor::Kind) -> Self {
self.kind = kind;
self
}
pub fn add_header<K, V>(mut self, key: K, value: V) -> Self
where K: AsRef<str>,
V: AsRef<str>,
{
self.headers.push((key.as_ref().to_string(),
value.as_ref().to_string()));
self
}
pub fn build(self) -> Result<Message<'a>> {
let level = self.inner.as_ref().cookie_ref().level;
let mut cookie = Cookie::new(level + 1);
cookie.private = Private::Armorer {
set_profile: None,
};
writer::Armorer::new(
self.inner,
cookie,
self.kind,
self.headers,
)
}
}
impl<'a> fmt::Debug for Armorer<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Armorer")
.field("inner", &self.inner)
.field("kind", &self.kind)
.field("headers", &self.headers)
.finish()
}
}
pub struct ArbitraryWriter<'a> {
inner: writer::BoxStack<'a, Cookie>,
}
assert_send_and_sync!(ArbitraryWriter<'_>);
impl<'a> ArbitraryWriter<'a> {
pub fn new(mut inner: Message<'a>, tag: Tag)
-> Result<Message<'a>> {
let level = inner.as_ref().cookie_ref().level + 1;
CTB::new(tag).serialize(&mut inner)?;
Ok(Message::from(Box::new(ArbitraryWriter {
inner: PartialBodyFilter::new(inner, Cookie::new(level)).into()
})))
}
}
impl<'a> fmt::Debug for ArbitraryWriter<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ArbitraryWriter")
.field("inner", &self.inner)
.finish()
}
}
impl<'a> Write for ArbitraryWriter<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<'a> writer::Stackable<'a, Cookie> for ArbitraryWriter<'a> {
fn into_inner(self: Box<Self>) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
Box::new(self.inner).into_inner()
}
fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
unreachable!("Only implemented by Signer")
}
fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
unreachable!("Only implemented by Signer")
}
fn inner_ref(&self) -> Option<&(dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(self.inner.as_ref())
}
fn inner_mut(&mut self) -> Option<&mut (dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(self.inner.as_mut())
}
fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
self.inner.cookie_set(cookie)
}
fn cookie_ref(&self) -> &Cookie {
self.inner.cookie_ref()
}
fn cookie_mut(&mut self) -> &mut Cookie {
self.inner.cookie_mut()
}
fn position(&self) -> u64 {
self.inner.position()
}
}
pub struct Signer<'a> {
inner: Option<writer::BoxStack<'a, Cookie>>,
signers: Vec<(Box<dyn crypto::Signer + Send + Sync + 'a>,
HashAlgorithm, Vec<u8>)>,
acceptable_hash_algos: Vec<HashAlgorithm>,
hash_algo: Option<HashAlgorithm>,
intended_recipients: Vec<Fingerprint>,
mode: SignatureMode,
template: signature::SignatureBuilder,
creation_time: Option<SystemTime>,
hashes: Vec<HashingMode<crypto::hash::Context>>,
cookie: Cookie,
position: u64,
}
assert_send_and_sync!(Signer<'_>);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum SignatureMode {
Inline,
Detached,
Cleartext,
}
impl<'a> Signer<'a> {
pub fn new<S>(inner: Message<'a>, signer: S) -> Result<Self>
where S: crypto::Signer + Send + Sync + 'a
{
Self::with_template(inner, signer,
signature::SignatureBuilder::new(SignatureType::Binary))
}
pub fn with_template<S, T>(inner: Message<'a>, signer: S, template: T)
-> Result<Self>
where S: crypto::Signer + Send + Sync + 'a,
T: Into<signature::SignatureBuilder>,
{
let inner = writer::BoxStack::from(inner);
let level = inner.cookie_ref().level + 1;
Signer {
inner: Some(inner),
signers: Default::default(),
acceptable_hash_algos:
crate::crypto::hash::default_hashes().to_vec(),
intended_recipients: Vec::new(),
mode: SignatureMode::Inline,
template: template.into(),
creation_time: None,
hash_algo: Default::default(),
hashes: vec![],
cookie: Cookie {
level,
private: Private::Signer,
},
position: 0,
}.add_signer(signer)
}
pub fn detached(mut self) -> Self {
self.mode = SignatureMode::Detached;
self
}
pub fn cleartext(mut self) -> Self {
self.mode = SignatureMode::Cleartext;
self
}
pub fn add_signer<S>(mut self, signer: S) -> Result<Self>
where S: crypto::Signer + Send + Sync + 'a
{
let is_sorted = |data: &[HashAlgorithm]| {
data.windows(2).all(|w| w[0] <= w[1])
};
let mut signer_hashes = signer.acceptable_hashes();
let mut signer_hashes_;
if ! is_sorted(signer_hashes) {
signer_hashes_ = signer_hashes.to_vec();
signer_hashes_.sort();
signer_hashes = &signer_hashes_;
}
self.acceptable_hash_algos.retain(
|hash| signer_hashes.binary_search(hash).is_ok());
if self.acceptable_hash_algos.is_empty() {
return Err(Error::NoAcceptableHash.into());
}
if let Some(a) = self.hash_algo {
if ! self.acceptable_hash_algos.contains(&a) {
return Err(Error::NoAcceptableHash.into());
}
}
self.signers.push((Box::new(signer), Default::default(), Vec::new()));
Ok(self)
}
pub fn add_intended_recipient(mut self, recipient: &Cert) -> Self {
self.intended_recipients.push(recipient.fingerprint());
self
}
pub fn hash_algo(mut self, algo: HashAlgorithm) -> Result<Self> {
if self.acceptable_hash_algos.contains(&algo) {
self.hash_algo = Some(algo);
Ok(self)
} else {
Err(Error::NoAcceptableHash.into())
}
}
pub fn creation_time<T: Into<SystemTime>>(mut self, creation_time: T)
-> Self
{
self.creation_time = Some(creation_time.into());
self
}
pub fn build(mut self) -> Result<Message<'a>>
{
assert!(!self.signers.is_empty(), "The constructor adds a signer.");
assert!(self.inner.is_some(), "The constructor adds an inner writer.");
if self.signers.iter().all(|(kp, _, _)| kp.public().version() > 4) {
writer::Armorer::set_profile(&mut self, Profile::RFC9580);
}
for (keypair, signer_hash, signer_salt) in self.signers.iter_mut() {
let algo = if let Some(a) = self.hash_algo {
a
} else {
self.acceptable_hash_algos.get(0)
.expect("we make sure the set is never empty")
.clone()
};
*signer_hash = algo;
let mut hash = algo.context()?
.for_signature(keypair.public().version());
match keypair.public().version() {
4 => {
self.hashes.push(
if self.template.typ() == SignatureType::Text
|| self.mode == SignatureMode::Cleartext
{
HashingMode::Text(vec![], hash)
} else {
HashingMode::Binary(vec![], hash)
});
},
6 => {
let mut salt = vec![0; algo.salt_size()?];
crate::crypto::random(&mut salt)?;
hash.update(&salt);
self.hashes.push(
if self.template.typ() == SignatureType::Text
|| self.mode == SignatureMode::Cleartext
{
HashingMode::Text(salt.clone(), hash)
} else {
HashingMode::Binary(salt.clone(), hash)
});
*signer_salt = salt;
},
v => return Err(Error::InvalidOperation(
format!("Unsupported Key version {}", v)).into()),
}
}
match self.mode {
SignatureMode::Inline => {
let signers_count = self.signers.len();
for (i, (keypair, hash_algo, salt)) in
self.signers.iter().enumerate()
{
let last = i == signers_count - 1;
let key = keypair.public();
match key.version() {
4 => {
let mut ops = OnePassSig3::new(self.template.typ());
ops.set_pk_algo(key.pk_algo());
ops.set_hash_algo(*hash_algo);
ops.set_issuer(key.keyid());
ops.set_last(last);
Packet::from(ops)
.serialize(self.inner.as_mut().unwrap())?;
},
6 => {
let mut ops = OnePassSig6::new(
self.template.typ(), key.fingerprint());
ops.set_pk_algo(key.pk_algo());
ops.set_hash_algo(*hash_algo);
ops.set_salt(salt.clone());
ops.set_last(last);
Packet::from(ops)
.serialize(self.inner.as_mut().unwrap())?;
},
v => return Err(Error::InvalidOperation(
format!("Unsupported Key version {}", v)).into()),
}
}
},
SignatureMode::Detached => (), SignatureMode::Cleartext => {
self.template = self.template.set_type(SignatureType::Text);
let mut sink = self.inner.take().unwrap();
writeln!(sink, "-----BEGIN PGP SIGNED MESSAGE-----")?;
let mut hashes = self.signers.iter().filter_map(
|(keypair, algo, _)| if keypair.public().version() == 4 {
Some(algo)
} else {
None
})
.collect::<Vec<_>>();
hashes.sort();
hashes.dedup();
for hash in hashes {
writeln!(sink, "Hash: {}", hash.text_name()?)?;
}
writeln!(sink)?;
self.inner =
Some(writer::BoxStack::from(
DashEscapeFilter::new(Message::from(sink),
Default::default())));
return Ok(TrailingWSFilter::new(Message::from(Box::new(self)),
Default::default()));
},
}
Ok(Message::from(Box::new(self)))
}
fn emit_signatures(&mut self) -> Result<()> {
if self.mode == SignatureMode::Cleartext {
let mut inner =
self.inner.take().expect("It's the DashEscapeFilter")
.into_inner()?.expect("It's the DashEscapeFilter");
writeln!(inner)?;
self.inner =
Some(writer::BoxStack::from(
writer::Armorer::new(Message::from(inner),
Default::default(),
armor::Kind::Signature,
Option::<(&str, &str)>::None)?));
}
if let Some(ref mut sink) = self.inner {
for (signer, algo, signer_salt) in self.signers.iter_mut().rev() {
let (mut sig, hash) = match signer.public().version() {
4 => {
let hash = self.hashes.iter()
.find_map(|hash| {
if hash.salt().is_empty()
&& hash.as_ref().algo() == *algo
{
Some(hash.clone())
} else {
None
}
})
.expect("we put it in there");
let sig = self.template.clone();
(sig, hash)
},
6 => {
let hash = self.hashes.iter()
.find_map(|hash| if signer_salt == hash.salt() {
Some(hash.clone())
} else {
None
})
.expect("we put it in there");
let sig = self.template.clone()
.set_prefix_salt(signer_salt.clone()).0;
(sig, hash)
},
v => return Err(Error::InvalidOperation(
format!("Unsupported Key version {}", v)).into()),
};
sig = sig.set_signature_creation_time(
self.creation_time
.unwrap_or_else(crate::now))?;
if ! self.intended_recipients.is_empty() {
sig = sig.set_intended_recipients(
self.intended_recipients.clone())?;
}
let sig = sig.sign_hash(signer.as_mut(),
hash.into_inner())?;
Packet::Signature(sig).serialize(sink)?;
}
}
Ok(())
}
}
impl<'a> fmt::Debug for Signer<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Signer")
.field("inner", &self.inner)
.field("cookie", &self.cookie)
.field("mode", &self.mode)
.finish()
}
}
impl<'a> Write for Signer<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
use SignatureMode::*;
let written = match (self.inner.as_mut(), self.mode) {
(Some(ref mut w), Inline) => w.write(buf),
(Some(_), Detached) => Ok(buf.len()),
(Some(ref mut w), Cleartext) => w.write(buf),
(None, _) => Ok(buf.len()),
};
if let Ok(amount) = written {
let data = &buf[..amount];
self.hashes.iter_mut().for_each(
|hash| hash.update(data));
self.position += amount as u64;
}
written
}
fn flush(&mut self) -> io::Result<()> {
match self.inner.as_mut() {
Some(ref mut w) => w.flush(),
None => Ok(()),
}
}
}
impl<'a> writer::Stackable<'a, Cookie> for Signer<'a> {
fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
Ok(self.inner.take())
}
fn mount(&mut self, new: writer::BoxStack<'a, Cookie>) {
self.inner = Some(new);
}
fn inner_mut(&mut self) -> Option<&mut (dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
if let Some(ref mut i) = self.inner {
Some(i)
} else {
None
}
}
fn inner_ref(&self) -> Option<&(dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
self.inner.as_ref().map(|r| r.as_ref())
}
fn into_inner(mut self: Box<Self>)
-> Result<Option<writer::BoxStack<'a, Cookie>>> {
self.emit_signatures()?;
Ok(self.inner.take())
}
fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
::std::mem::replace(&mut self.cookie, cookie)
}
fn cookie_ref(&self) -> &Cookie {
&self.cookie
}
fn cookie_mut(&mut self) -> &mut Cookie {
&mut self.cookie
}
fn position(&self) -> u64 {
self.position
}
}
pub struct LiteralWriter<'a> {
template: Literal,
inner: writer::BoxStack<'a, Cookie>,
signature_writer: Option<writer::BoxStack<'a, Cookie>>,
}
assert_send_and_sync!(LiteralWriter<'_>);
impl<'a> LiteralWriter<'a> {
pub fn new(inner: Message<'a>) -> Self {
LiteralWriter {
template: Literal::new(DataFormat::default()),
inner: writer::BoxStack::from(inner),
signature_writer: None,
}
}
pub fn format(mut self, format: DataFormat) -> Self {
self.template.set_format(format);
self
}
pub fn filename<B: AsRef<[u8]>>(mut self, filename: B) -> Result<Self> {
self.template.set_filename(filename.as_ref())?;
Ok(self)
}
pub fn date<T: Into<SystemTime>>(mut self, timestamp: T) -> Result<Self>
{
self.template.set_date(Some(timestamp.into()))?;
Ok(self)
}
pub fn build(mut self) -> Result<Message<'a>> {
let level = self.inner.cookie_ref().level + 1;
let signer_above =
matches!(self.inner.cookie_ref(), &Cookie {
private: Private::Signer{..},
..
});
if signer_above {
let stack = self.inner.pop()?;
let stack = stack.unwrap();
self.signature_writer = Some(self.inner);
self.inner = stack;
}
CTB::new(Tag::Literal).serialize(&mut self.inner)?;
self.inner
= PartialBodyFilter::new(Message::from(self.inner),
Cookie::new(level)).into();
self.template.serialize_headers(&mut self.inner, false)?;
Ok(Message::from(Box::new(self)))
}
}
impl<'a> fmt::Debug for LiteralWriter<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("LiteralWriter")
.field("inner", &self.inner)
.finish()
}
}
impl<'a> Write for LiteralWriter<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let written = self.inner.write(buf);
if let (&Ok(ref amount), &mut Some(ref mut sig))
= (&written, &mut self.signature_writer) {
sig.write_all(&buf[..*amount])?;
};
written
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<'a> writer::Stackable<'a, Cookie> for LiteralWriter<'a> {
fn into_inner(mut self: Box<Self>)
-> Result<Option<writer::BoxStack<'a, Cookie>>> {
let signer = self.signature_writer.take();
let stack = self.inner
.into_inner()?.unwrap();
if let Some(mut signer) = signer {
signer.mount(stack);
Ok(Some(signer))
} else {
Ok(Some(stack))
}
}
fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
unreachable!("Only implemented by Signer")
}
fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
unreachable!("Only implemented by Signer")
}
fn inner_ref(&self) -> Option<&(dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(self.inner.as_ref())
}
fn inner_mut(&mut self) -> Option<&mut (dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(self.inner.as_mut())
}
fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
self.inner.cookie_set(cookie)
}
fn cookie_ref(&self) -> &Cookie {
self.inner.cookie_ref()
}
fn cookie_mut(&mut self) -> &mut Cookie {
self.inner.cookie_mut()
}
fn position(&self) -> u64 {
self.inner.position()
}
}
pub struct Compressor<'a> {
algo: CompressionAlgorithm,
level: CompressionLevel,
inner: writer::BoxStack<'a, Cookie>,
}
assert_send_and_sync!(Compressor<'_>);
impl<'a> Compressor<'a> {
pub fn new(inner: Message<'a>) -> Self {
Self {
algo: Default::default(),
level: Default::default(),
inner: inner.into(),
}
}
pub fn algo(mut self, algo: CompressionAlgorithm) -> Self {
self.algo = algo;
self
}
pub fn level(mut self, level: CompressionLevel) -> Self {
self.level = level;
self
}
pub fn build(mut self) -> Result<Message<'a>> {
let level = self.inner.cookie_ref().level + 1;
CTB::new(Tag::CompressedData).serialize(&mut self.inner)?;
let inner: Message<'a>
= PartialBodyFilter::new(Message::from(self.inner),
Cookie::new(level));
Self::new_naked(inner, self.algo, self.level, level)
}
pub(crate) fn new_naked(mut inner: Message<'a>,
algo: CompressionAlgorithm,
compression_level: CompressionLevel,
level: usize)
-> Result<Message<'a>>
{
inner.as_mut().write_u8(algo.into())?;
let inner: Message<'a> = match algo {
CompressionAlgorithm::Uncompressed => {
let _ = compression_level;
writer::Identity::new(inner, Cookie::new(level))
},
#[cfg(feature = "compression-deflate")]
CompressionAlgorithm::Zip =>
writer::ZIP::new(inner, Cookie::new(level), compression_level),
#[cfg(feature = "compression-deflate")]
CompressionAlgorithm::Zlib =>
writer::ZLIB::new(inner, Cookie::new(level), compression_level),
#[cfg(feature = "compression-bzip2")]
CompressionAlgorithm::BZip2 =>
writer::BZ::new(inner, Cookie::new(level), compression_level),
a =>
return Err(Error::UnsupportedCompressionAlgorithm(a).into()),
};
Ok(Message::from(Box::new(Self {
algo,
level: compression_level,
inner: inner.into(),
})))
}
}
impl<'a> fmt::Debug for Compressor<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Compressor")
.field("inner", &self.inner)
.finish()
}
}
impl<'a> io::Write for Compressor<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<'a> writer::Stackable<'a, Cookie> for Compressor<'a> {
fn into_inner(self: Box<Self>) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
Box::new(self.inner).into_inner()?.unwrap().into_inner()
}
fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
unreachable!("Only implemented by Signer")
}
fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
unreachable!("Only implemented by Signer")
}
fn inner_ref(&self) -> Option<&(dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(self.inner.as_ref())
}
fn inner_mut(&mut self) -> Option<&mut (dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(self.inner.as_mut())
}
fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
self.inner.cookie_set(cookie)
}
fn cookie_ref(&self) -> &Cookie {
self.inner.cookie_ref()
}
fn cookie_mut(&mut self) -> &mut Cookie {
self.inner.cookie_mut()
}
fn position(&self) -> u64 {
self.inner.position()
}
}
#[derive(Debug)]
pub struct Recipient<'a> {
handle: Option<KeyHandle>,
features: Features,
key: &'a Key<key::PublicParts, key::UnspecifiedRole>,
}
assert_send_and_sync!(Recipient<'_>);
impl<'a, P> From<ValidSubordinateKeyAmalgamation<'a, P>>
for Recipient<'a>
where
P: key::KeyParts,
{
fn from(ka: ValidSubordinateKeyAmalgamation<'a, P>) -> Self {
let features = ka.valid_cert().features()
.unwrap_or_else(Features::empty);
let handle: KeyHandle = if features.supports_seipdv2() {
ka.key().fingerprint().into()
} else {
ka.key().keyid().into()
};
use crate::cert::Preferences;
use crate::cert::amalgamation::ValidAmalgamation;
Self::new(features, handle,
ka.key().parts_as_public().role_as_unspecified())
}
}
impl<'a, P> From<ValidErasedKeyAmalgamation<'a, P>>
for Recipient<'a>
where
P: key::KeyParts,
{
fn from(ka: ValidErasedKeyAmalgamation<'a, P>) -> Self {
let features = ka.valid_cert().features()
.unwrap_or_else(Features::empty);
let handle: KeyHandle = if features.supports_seipdv2() {
ka.key().fingerprint().into()
} else {
ka.key().keyid().into()
};
use crate::cert::Preferences;
use crate::cert::amalgamation::ValidAmalgamation;
Self::new(features, handle,
ka.key().parts_as_public().role_as_unspecified())
}
}
impl<'a> Recipient<'a> {
pub fn new<F, H, P, R>(features: F, handle: H, key: &'a Key<P, R>)
-> Recipient<'a>
where
F: Into<Option<Features>>,
H: Into<Option<KeyHandle>>,
P: key::KeyParts,
R: key::KeyRole,
{
Recipient {
features: features.into().unwrap_or_else(Features::sequoia),
handle: handle.into(),
key: key.parts_as_public().role_as_unspecified(),
}
}
pub fn key_handle(&self) -> Option<KeyHandle> {
self.handle.clone()
}
pub fn set_key_handle<H>(mut self, handle: H) -> Result<Self>
where
H: Into<Option<KeyHandle>>,
{
let handle = handle.into();
if self.key.version() == 6
&& matches!(handle, Some(KeyHandle::KeyID(_)))
{
return Err(Error::InvalidOperation(
"need a fingerprint for v6 recipient key".into()).into());
}
self.handle = handle;
Ok(self)
}
}
pub struct Encryptor<'a, 'b>
where 'b: 'a
{
inner: writer::BoxStack<'a, Cookie>,
session_key: Option<SessionKey>,
recipients: Vec<Recipient<'b>>,
passwords: Vec<Password>,
sym_algo: SymmetricAlgorithm,
aead_algo: Option<AEADAlgorithm>,
hash: crypto::hash::Context,
cookie: Cookie,
}
assert_send_and_sync!(Encryptor<'_, '_>);
impl<'a, 'b> Encryptor<'a, 'b> {
pub fn for_recipients<R>(inner: Message<'a>, recipients: R) -> Self
where R: IntoIterator,
R::Item: Into<Recipient<'b>>,
{
Self {
inner: inner.into(),
session_key: None,
recipients: recipients.into_iter().map(|r| r.into()).collect(),
passwords: Vec::new(),
sym_algo: Default::default(),
aead_algo: Default::default(),
hash: HashAlgorithm::SHA1.context().unwrap().for_digest(),
cookie: Default::default(), }
}
pub fn with_passwords<P>(inner: Message<'a>, passwords: P) -> Self
where P: IntoIterator,
P::Item: Into<Password>,
{
Self {
inner: inner.into(),
session_key: None,
recipients: Vec::new(),
passwords: passwords.into_iter().map(|p| p.into()).collect(),
sym_algo: Default::default(),
aead_algo: Default::default(),
hash: HashAlgorithm::SHA1.context().unwrap().for_digest(),
cookie: Default::default(), }
}
pub fn with_session_key(inner: Message<'a>,
sym_algo: SymmetricAlgorithm,
session_key: SessionKey)
-> Result<Self>
{
let sym_key_size = sym_algo.key_size()?;
if session_key.len() != sym_key_size {
return Err(Error::InvalidArgument(
format!("{} requires a {} bit key, but session key has {}",
sym_algo, sym_key_size, session_key.len())).into());
}
Ok(Self {
inner: inner.into(),
session_key: Some(session_key),
recipients: Vec::new(),
passwords: Vec::with_capacity(0),
sym_algo,
aead_algo: Default::default(),
hash: HashAlgorithm::SHA1.context().unwrap().for_digest(),
cookie: Default::default(), })
}
pub fn add_recipients<R>(mut self, recipients: R) -> Self
where R: IntoIterator,
R::Item: Into<Recipient<'b>>,
{
for r in recipients {
self.recipients.push(r.into());
}
self
}
pub fn add_passwords<P>(mut self, passwords: P) -> Self
where P: IntoIterator,
P::Item: Into<Password>,
{
for p in passwords {
self.passwords.push(p.into());
}
self
}
pub fn symmetric_algo(mut self, algo: SymmetricAlgorithm) -> Self {
self.sym_algo = algo;
self
}
pub fn aead_algo(mut self, algo: AEADAlgorithm) -> Self {
self.aead_algo = Some(algo);
self
}
const AEAD_CHUNK_SIZE : usize = 4096;
pub fn build(mut self) -> Result<Message<'a>> {
if self.recipients.len() + self.passwords.len() == 0
&& self.session_key.is_none()
{
return Err(Error::InvalidOperation(
"Neither recipients, passwords, nor session key given".into()
).into());
}
if self.aead_algo.is_none() {
if ! self.recipients.is_empty()
&& self.recipients.iter().all(|r| {
r.features.supports_seipdv2()
})
{
self.aead_algo = Some(AEADAlgorithm::default());
}
}
struct AEADParameters {
algo: AEADAlgorithm,
chunk_size: usize,
salt: [u8; 32],
}
let aead = if let Some(algo) = self.aead_algo {
writer::Armorer::set_profile(&mut self, Profile::RFC9580);
let mut salt = [0u8; 32];
crypto::random(&mut salt)?;
Some(AEADParameters {
algo,
chunk_size: Self::AEAD_CHUNK_SIZE,
salt,
})
} else {
None
};
let mut inner = self.inner;
let level = inner.as_ref().cookie_ref().level + 1;
let sym_key_size = self.sym_algo.key_size()?;
let sk = self.session_key.take()
.map(|sk| Ok(sk))
.unwrap_or_else(|| SessionKey::new(sym_key_size))?;
if sk.len() != sym_key_size {
return Err(Error::InvalidOperation(
format!("{} requires a {} bit key, but session key has {}",
self.sym_algo, sym_key_size, sk.len())).into());
}
for recipient in self.recipients.iter() {
if aead.is_some() {
let mut pkesk =
PKESK6::for_recipient(&sk, recipient.key)?;
pkesk.set_recipient(recipient.key_handle()
.map(TryInto::try_into)
.transpose()?);
Packet::from(pkesk).serialize(&mut inner)?;
} else {
let mut pkesk =
PKESK3::for_recipient(self.sym_algo, &sk, recipient.key)?;
pkesk.set_recipient(recipient.key_handle().map(Into::into));
Packet::PKESK(pkesk.into()).serialize(&mut inner)?;
}
}
for password in self.passwords.iter() {
if let Some(aead) = aead.as_ref() {
let skesk = SKESK6::with_password(self.sym_algo,
self.sym_algo,
aead.algo,
Default::default(),
&sk, password).unwrap();
Packet::SKESK(skesk.into()).serialize(&mut inner)?;
} else {
let skesk = SKESK4::with_password(self.sym_algo,
self.sym_algo,
Default::default(),
&sk, password).unwrap();
Packet::SKESK(skesk.into()).serialize(&mut inner)?;
}
}
if let Some(aead) = aead {
CTB::new(Tag::SEIP).serialize(&mut inner)?;
let mut inner = PartialBodyFilter::new(Message::from(inner),
Cookie::new(level));
let seip = SEIP2::new(self.sym_algo, aead.algo,
aead.chunk_size as u64, aead.salt)?;
seip.serialize_headers(&mut inner)?;
use crate::crypto::aead::SEIPv2Schedule;
let schedule = SEIPv2Schedule::new(
&sk,
seip.symmetric_algo(), seip.aead(), aead.chunk_size,
seip.salt())?;
writer::AEADEncryptor::new(
inner,
Cookie::new(level).set_private(Private::Encryptor {
profile: Profile::RFC9580,
}),
seip.symmetric_algo(),
seip.aead(),
aead.chunk_size,
schedule,
)
} else {
CTB::new(Tag::SEIP).serialize(&mut inner)?;
let mut inner = PartialBodyFilter::new(Message::from(inner),
Cookie::new(level));
inner.write_all(&[1])?;
self.inner = writer::Encryptor::new(
inner,
Cookie::new(level),
self.sym_algo,
&sk,
)?.into();
self.cookie = Cookie::new(level)
.set_private(Private::Encryptor {
profile: Profile::RFC4880,
});
let mut iv = vec![0; self.sym_algo.block_size()?];
crypto::random(&mut iv)?;
self.write_all(&iv)?;
self.write_all(&iv[iv.len() - 2..])?;
Ok(Message::from(Box::new(self)))
}
}
fn emit_mdc(mut self) -> Result<writer::BoxStack<'a, Cookie>> {
let mut w = self.inner;
let mut header = Vec::new();
CTB::new(Tag::MDC).serialize(&mut header)?;
BodyLength::Full(20).serialize(&mut header)?;
self.hash.update(&header);
#[allow(deprecated)]
Packet::MDC(MDC::from(self.hash.clone())).serialize(&mut w)?;
let w = w.into_inner()?.unwrap();
let w = w.into_inner()?.unwrap();
Ok(w)
}
}
impl<'a, 'b> fmt::Debug for Encryptor<'a, 'b>
where 'b: 'a
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Encryptor")
.field("inner", &self.inner)
.finish()
}
}
impl<'a, 'b> Write for Encryptor<'a, 'b>
where 'b: 'a
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let written = self.inner.write(buf);
if let Ok(amount) = written {
self.hash.update(&buf[..amount]);
}
written
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<'a, 'b> writer::Stackable<'a, Cookie> for Encryptor<'a, 'b>
where 'b: 'a
{
fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
unreachable!("Only implemented by Signer")
}
fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
unreachable!("Only implemented by Signer")
}
fn inner_ref(&self) -> Option<&(dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(&self.inner)
}
fn inner_mut(&mut self) -> Option<&mut (dyn writer::Stackable<'a, Cookie> + Send + Sync)> {
Some(&mut self.inner)
}
fn into_inner(self: Box<Self>) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
Ok(Some(self.emit_mdc()?))
}
fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
::std::mem::replace(&mut self.cookie, cookie)
}
fn cookie_ref(&self) -> &Cookie {
&self.cookie
}
fn cookie_mut(&mut self) -> &mut Cookie {
&mut self.cookie
}
fn position(&self) -> u64 {
self.inner.position()
}
}
#[cfg(test)]
mod test {
use std::io::Read;
use crate::{Packet, PacketPile, Profile, packet::CompressedData};
use crate::parse::{Parse, PacketParserResult, PacketParser};
use super::*;
use crate::types::DataFormat::Unicode as T;
use crate::policy::Policy;
use crate::policy::StandardPolicy as P;
#[test]
fn arbitrary() {
let mut o = vec![];
{
let m = Message::new(&mut o);
let mut ustr = ArbitraryWriter::new(m, Tag::Literal).unwrap();
ustr.write_all(b"u").unwrap(); ustr.write_all(b"\x00").unwrap(); ustr.write_all(b"\x00\x00\x00\x00").unwrap(); ustr.write_all(b"Hello world.").unwrap(); ustr.finalize().unwrap();
}
let mut pp = PacketParser::from_bytes(&o).unwrap().unwrap();
if let Packet::Literal(ref l) = pp.packet {
assert_eq!(l.format(), DataFormat::Unicode);
assert_eq!(l.filename(), None);
assert_eq!(l.date(), None);
} else {
panic!("Unexpected packet type.");
}
let mut body = vec![];
pp.read_to_end(&mut body).unwrap();
assert_eq!(&body, b"Hello world.");
let (_, ppr) = pp.recurse().unwrap();
assert!(ppr.is_eof());
}
#[test]
fn stream_0() {
let mut one = Literal::new(T);
one.set_body(b"one".to_vec());
let mut two = Literal::new(T);
two.set_body(b"two".to_vec());
let mut three = Literal::new(T);
three.set_body(b"three".to_vec());
let mut reference = Vec::new();
reference.push(
CompressedData::new(CompressionAlgorithm::Uncompressed)
.push(one.into())
.push(two.into())
.into());
reference.push(three.into());
let mut o = vec![];
{
let m = Message::new(&mut o);
let c = Compressor::new(m)
.algo(CompressionAlgorithm::Uncompressed).build().unwrap();
let mut ls = LiteralWriter::new(c).format(T).build().unwrap();
write!(ls, "one").unwrap();
let c = ls.finalize_one().unwrap().unwrap(); let mut ls = LiteralWriter::new(c).format(T).build().unwrap();
write!(ls, "two").unwrap();
let c = ls.finalize_one().unwrap().unwrap(); let c = c.finalize_one().unwrap().unwrap(); let mut ls = LiteralWriter::new(c).format(T).build().unwrap();
write!(ls, "three").unwrap();
ls.finalize().unwrap();
}
let pile = PacketPile::from(reference);
let pile2 = PacketPile::from_bytes(&o).unwrap();
if pile != pile2 {
eprintln!("REFERENCE...");
pile.pretty_print();
eprintln!("REPARSED...");
pile2.pretty_print();
panic!("Reparsed packet does not match reference packet!");
}
}
#[test]
fn stream_1() {
let mut one = Literal::new(T);
one.set_body(b"one".to_vec());
let mut two = Literal::new(T);
two.set_body(b"two".to_vec());
let mut three = Literal::new(T);
three.set_body(b"three".to_vec());
let mut four = Literal::new(T);
four.set_body(b"four".to_vec());
let mut reference = Vec::new();
reference.push(
CompressedData::new(CompressionAlgorithm::Uncompressed)
.push(CompressedData::new(CompressionAlgorithm::Uncompressed)
.push(one.into())
.push(two.into())
.into())
.push(CompressedData::new(CompressionAlgorithm::Uncompressed)
.push(three.into())
.push(four.into())
.into())
.into());
let mut o = vec![];
{
let m = Message::new(&mut o);
let c0 = Compressor::new(m)
.algo(CompressionAlgorithm::Uncompressed).build().unwrap();
let c = Compressor::new(c0)
.algo(CompressionAlgorithm::Uncompressed).build().unwrap();
let mut ls = LiteralWriter::new(c).format(T).build().unwrap();
write!(ls, "one").unwrap();
let c = ls.finalize_one().unwrap().unwrap();
let mut ls = LiteralWriter::new(c).format(T).build().unwrap();
write!(ls, "two").unwrap();
let c = ls.finalize_one().unwrap().unwrap();
let c0 = c.finalize_one().unwrap().unwrap();
let c = Compressor::new(c0)
.algo(CompressionAlgorithm::Uncompressed).build().unwrap();
let mut ls = LiteralWriter::new(c).format(T).build().unwrap();
write!(ls, "three").unwrap();
let c = ls.finalize_one().unwrap().unwrap();
let mut ls = LiteralWriter::new(c).format(T).build().unwrap();
write!(ls, "four").unwrap();
ls.finalize().unwrap();
}
let pile = PacketPile::from(reference);
let pile2 = PacketPile::from_bytes(&o).unwrap();
if pile != pile2 {
eprintln!("REFERENCE...");
pile.pretty_print();
eprintln!("REPARSED...");
pile2.pretty_print();
panic!("Reparsed packet does not match reference packet!");
}
}
#[cfg(feature = "compression-bzip2")]
#[test]
fn stream_big() {
let zeros = vec![0; 1024 * 1024 * 4];
let mut o = vec![];
{
let m = Message::new(&mut o);
let c = Compressor::new(m)
.algo(CompressionAlgorithm::BZip2).build().unwrap();
let mut ls = LiteralWriter::new(c).build().unwrap();
for _ in 0 .. 16 {
ls.write_all(&zeros).unwrap();
}
}
assert!(o.len() < 1024);
}
#[test]
fn signature() {
let p = &P::new();
use crate::crypto::KeyPair;
use std::collections::HashMap;
use crate::Fingerprint;
let mut keys: HashMap<Fingerprint, key::UnspecifiedPublic> = HashMap::new();
for tsk in &[
Cert::from_bytes(crate::tests::key("testy-private.pgp")).unwrap(),
Cert::from_bytes(crate::tests::key("testy-new-private.pgp")).unwrap(),
] {
for key in tsk.keys().with_policy(p, crate::frozen_time())
.for_signing().map(|ka| ka.key())
{
keys.insert(key.fingerprint(), key.clone());
}
}
let mut o = vec![];
{
let mut signers = keys.iter().map(|(_, key)| {
key.clone().parts_into_secret().unwrap().into_keypair()
.expect("expected unencrypted secret key")
}).collect::<Vec<KeyPair>>();
let m = Message::new(&mut o);
let mut signer = Signer::new(m, signers.pop().unwrap()).unwrap();
for s in signers.into_iter() {
signer = signer.add_signer(s).unwrap();
}
let signer = signer.build().unwrap();
let mut ls = LiteralWriter::new(signer).build().unwrap();
ls.write_all(b"Tis, tis, tis. Tis is important.").unwrap();
let _ = ls.finalize().unwrap();
}
let mut ppr = PacketParser::from_bytes(&o).unwrap();
let mut good = 0;
while let PacketParserResult::Some(mut pp) = ppr {
if let Packet::Signature(sig) = &mut pp.packet {
let key = keys.get(sig.issuer_fingerprints().next().unwrap())
.unwrap();
sig.verify_document(key).unwrap();
good += 1;
}
ppr = pp.recurse().unwrap().1;
}
assert_eq!(good, 2);
}
#[test]
fn encryptor() {
let passwords = vec!["streng geheim".into(),
"top secret".into()];
let message = b"Hello world.";
let mut o = vec![];
{
let m = Message::new(&mut o);
let encryptor = Encryptor::with_passwords(m, passwords.clone())
.build().unwrap();
let mut literal = LiteralWriter::new(encryptor).build()
.unwrap();
literal.write_all(message).unwrap();
literal.finalize().unwrap();
}
#[derive(Debug, PartialEq)]
enum State {
Start,
Decrypted(Vec<(Option<SymmetricAlgorithm>, SessionKey)>),
Deciphered,
MDC,
Done,
}
for password in &passwords {
let mut state = State::Start;
let mut ppr = PacketParser::from_bytes(&o).unwrap();
while let PacketParserResult::Some(mut pp) = ppr {
state = match state {
State::Start =>
if let Packet::SKESK(ref skesk) = pp.packet {
match skesk.decrypt(password) {
Ok((algo, key))
=> State::Decrypted(
vec![(algo, key)]),
Err(e) =>
panic!("Decryption failed: {}", e),
}
} else {
panic!("Unexpected packet: {:?}", pp.packet)
},
State::Decrypted(mut keys) =>
match pp.packet {
Packet::SEIP(_) =>
loop {
if let Some((algo, key)) = keys.pop() {
let r = pp.decrypt(algo, &key);
if r.is_ok() {
break State::Deciphered;
}
} else {
panic!("seip decryption failed");
}
},
Packet::SKESK(ref skesk) =>
match skesk.decrypt(password) {
Ok((algo, key)) => {
keys.push((algo, key));
State::Decrypted(keys)
},
Err(e) =>
panic!("Decryption failed: {}", e),
},
_ =>
panic!("Unexpected packet: {:?}", pp.packet),
},
State::Deciphered =>
if let Packet::Literal(_) = pp.packet {
let mut body = Vec::new();
pp.read_to_end(&mut body).unwrap();
assert_eq!(&body, message);
State::MDC
} else {
panic!("Unexpected packet: {:?}", pp.packet)
},
#[allow(deprecated)]
State::MDC =>
if let Packet::MDC(ref mdc) = pp.packet {
assert_eq!(mdc.digest(), mdc.computed_digest());
State::Done
} else {
panic!("Unexpected packet: {:?}", pp.packet)
},
State::Done =>
panic!("Unexpected packet: {:?}", pp.packet),
};
ppr = pp.recurse().unwrap().1;
}
assert_eq!(state, State::Done);
}
}
#[test]
fn aead_eax() -> Result<()> {
test_aead_messages(AEADAlgorithm::EAX)
}
#[test]
fn aead_ocb() -> Result<()> {
test_aead_messages(AEADAlgorithm::OCB)
}
#[test]
fn aead_gcm() -> Result<()> {
test_aead_messages(AEADAlgorithm::GCM)
}
fn test_aead_messages(algo: AEADAlgorithm) -> Result<()> {
test_aead_messages_v(algo, Profile::RFC4880)?;
test_aead_messages_v(algo, Profile::RFC9580)?;
Ok(())
}
fn test_aead_messages_v(algo: AEADAlgorithm, profile: Profile)
-> Result<()>
{
eprintln!("Testing with {:?}", profile);
if ! algo.is_supported() {
eprintln!("Skipping because {} is not supported.", algo);
return Ok(());
}
use std::cmp;
use crate::parse::{
stream::{
DecryptorBuilder,
DecryptionHelper,
VerificationHelper,
MessageStructure,
},
};
use crate::cert::prelude::*;
let (tsk, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.set_profile(profile)?
.add_transport_encryption_subkey()
.generate().unwrap();
struct Helper<'a> {
policy: &'a dyn Policy,
tsk: &'a Cert,
}
impl<'a> VerificationHelper for Helper<'a> {
fn get_certs(&mut self, _ids: &[crate::KeyHandle])
-> Result<Vec<Cert>> {
Ok(Vec::new())
}
fn check(&mut self, _structure: MessageStructure) -> Result<()> {
Ok(())
}
fn inspect(&mut self, pp: &PacketParser<'_>) -> Result<()> {
assert!(! matches!(&pp.packet, Packet::Unknown(_)));
eprintln!("Parsed {:?}", pp.packet);
Ok(())
}
}
impl<'a> DecryptionHelper for Helper<'a> {
fn decrypt(&mut self, pkesks: &[PKESK], _skesks: &[SKESK],
sym_algo: Option<SymmetricAlgorithm>,
decrypt: &mut dyn FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool)
-> Result<Option<Cert>>
{
let mut keypair = self.tsk.keys().with_policy(self.policy, None)
.for_transport_encryption()
.map(|ka| ka.key()).next().unwrap()
.clone().parts_into_secret().unwrap()
.into_keypair().unwrap();
pkesks[0].decrypt(&mut keypair, sym_algo)
.map(|(algo, session_key)| decrypt(algo, &session_key));
Ok(None)
}
}
let p = unsafe { &crate::policy::NullPolicy::new() };
for chunks in 0..3 {
for msg_len in
cmp::max(24, chunks * Encryptor::AEAD_CHUNK_SIZE) - 24
..chunks * Encryptor::AEAD_CHUNK_SIZE + 24
{
eprintln!("Encrypting message of size: {}", msg_len);
let mut content : Vec<u8> = Vec::new();
for i in 0..msg_len {
content.push(b'0' + ((i % 10) as u8));
}
let mut msg = vec![];
{
let m = Message::new(&mut msg);
let recipients = tsk
.keys().with_policy(p, None)
.for_storage_encryption().for_transport_encryption();
let encryptor = Encryptor::for_recipients(m, recipients)
.aead_algo(algo)
.build().unwrap();
let mut literal = LiteralWriter::new(encryptor).build()
.unwrap();
literal.write_all(&content).unwrap();
literal.finalize().unwrap();
}
for &read_len in &[
37,
Encryptor::AEAD_CHUNK_SIZE - 1,
Encryptor::AEAD_CHUNK_SIZE,
100 * Encryptor::AEAD_CHUNK_SIZE
] {
for &do_err in &[ false, true ] {
let mut msg = msg.clone();
if do_err {
let l = msg.len() - 1;
if msg[l] == 0 {
msg[l] = 1;
} else {
msg[l] = 0;
}
}
let h = Helper { policy: p, tsk: &tsk };
let mut v = match DecryptorBuilder::from_bytes(&msg)?
.with_policy(p, None, h)
{
Ok(v) => v,
Err(_) if do_err => continue,
Err(err) => panic!("Decrypting message: {}", err),
};
let mut buffer = Vec::new();
buffer.resize(read_len, 0);
let mut decrypted_content = Vec::new();
loop {
match v.read(&mut buffer[..read_len]) {
Ok(0) if do_err =>
panic!("Expected an error, got EOF"),
Ok(0) => break,
Ok(len) =>
decrypted_content.extend_from_slice(
&buffer[..len]),
Err(_) if do_err => break,
Err(err) =>
panic!("Decrypting data: {:?}", err),
}
}
if do_err {
for _ in 0..3 {
assert!(v.read(&mut buffer[..read_len]).is_err());
}
}
assert_eq!(msg_len, decrypted_content.len());
assert_eq!(content, decrypted_content);
}
}
}
}
Ok(())
}
#[test]
fn signature_at_time() {
use crate::cert::prelude::*;
use crate::serialize::stream::{LiteralWriter, Message};
use crate::crypto::KeyPair;
let p = &P::new();
let (cert, _) = CertBuilder::new()
.add_signing_subkey()
.set_cipher_suite(CipherSuite::Cv25519)
.generate().unwrap();
let ka = cert.keys().with_policy(p, None).for_signing().next().unwrap();
let timestamp = ka.key().creation_time()
+ std::time::Duration::from_secs(14 * 24 * 60 * 60);
assert!(ka.key().creation_time() < timestamp);
let mut o = vec![];
{
let signer_keypair : KeyPair =
ka.key().clone().parts_into_secret().unwrap().into_keypair()
.expect("expected unencrypted secret key");
let m = Message::new(&mut o);
let signer = Signer::new(m, signer_keypair).unwrap();
let signer = signer.creation_time(timestamp);
let signer = signer.build().unwrap();
let mut ls = LiteralWriter::new(signer).build().unwrap();
ls.write_all(b"Tis, tis, tis. Tis is important.").unwrap();
let signer = ls.finalize_one().unwrap().unwrap();
let _ = signer.finalize_one().unwrap().unwrap();
}
let mut ppr = PacketParser::from_bytes(&o).unwrap();
let mut good = 0;
while let PacketParserResult::Some(mut pp) = ppr {
if let Packet::Signature(sig) = &mut pp.packet {
assert_eq!(sig.signature_creation_time(), Some(timestamp));
sig.verify_document(ka.key()).unwrap();
good += 1;
}
ppr = pp.recurse().unwrap().1;
}
assert_eq!(good, 1);
}
#[test]
fn issue_530_signing() -> Result<()> {
use std::io::Write;
use crate::*;
use crate::packet::signature;
use crate::serialize::stream::{Message, Signer};
use crate::policy::StandardPolicy;
use crate::{Result, Cert};
use crate::parse::Parse;
use crate::parse::stream::*;
let normalized_data = b"one\r\ntwo\r\nthree";
let p = &StandardPolicy::new();
let cert: Cert =
Cert::from_bytes(crate::tests::key("testy-new-private.pgp"))?;
for data in &[
&b"one\r\ntwo\r\nthree"[..], b"one\ntwo\nthree", b"one\ntwo\r\nthree", b"one\r\ntwo\nthree",
b"one\rtwo\rthree", ] {
eprintln!("{:?}", String::from_utf8(data.to_vec())?);
let signing_keypair = cert.keys().secret()
.with_policy(p, None).supported()
.alive().revoked(false).for_signing().next().unwrap()
.key().clone().into_keypair()?;
let mut signature = vec![];
{
let message = Message::new(&mut signature);
let mut message = Signer::with_template(
message, signing_keypair,
signature::SignatureBuilder::new(SignatureType::Text)
)?.detached().build()?;
message.write_all(data)?;
message.finalize()?;
}
struct Helper {}
impl VerificationHelper for Helper {
fn get_certs(&mut self, _ids: &[KeyHandle]) -> Result<Vec<Cert>>
{
Ok(vec![
Cert::from_bytes(crate::tests::key("testy-new.pgp"))?])
}
fn check(&mut self, structure: MessageStructure) -> Result<()> {
for (i, layer) in structure.iter().enumerate() {
assert_eq!(i, 0);
if let MessageLayer::SignatureGroup { results } = layer
{
assert_eq!(results.len(), 1);
results[0].as_ref().unwrap();
assert!(results[0].is_ok());
return Ok(());
} else {
unreachable!();
}
}
unreachable!()
}
}
let h = Helper {};
let mut v = DetachedVerifierBuilder::from_bytes(&signature)?
.with_policy(p, None, h)?;
v.verify_bytes(data)?;
v.verify_bytes(normalized_data)?;
}
Ok(())
}
struct BadSigner;
impl crypto::Signer for BadSigner {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
panic!("public not impl")
}
fn acceptable_hashes(&self) -> &[HashAlgorithm] {
&[]
}
fn sign(&mut self, _hash_algo: HashAlgorithm, _digest: &[u8])
-> Result<crypto::mpi::Signature> {
panic!("sign not impl")
}
}
struct GoodSigner(Vec<HashAlgorithm>, Key<key::PublicParts, key::UnspecifiedRole>);
impl crypto::Signer for GoodSigner {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
&self.1
}
fn acceptable_hashes(&self) -> &[HashAlgorithm] {
&self.0
}
fn sign(&mut self, _hash_algo: HashAlgorithm, _digest: &[u8])
-> Result<crypto::mpi::Signature> {
unimplemented!()
}
}
impl Default for GoodSigner {
fn default() -> Self {
let p = &P::new();
let (cert, _) = CertBuilder::new().generate().unwrap();
let ka = cert.keys().with_policy(p, None).next().unwrap();
Self(vec![HashAlgorithm::default()], ka.key().clone())
}
}
#[test]
fn overlapping_hashes() {
let mut signature = vec![];
let message = Message::new(&mut signature);
Signer::new(message, GoodSigner::default()).unwrap().build().unwrap();
}
#[test]
fn no_overlapping_hashes() {
let mut signature = vec![];
let message = Message::new(&mut signature);
if let Err(e) = Signer::new(message, BadSigner) {
assert_eq!(e.downcast_ref::<Error>(), Some(&Error::NoAcceptableHash));
} else {
unreachable!();
};
}
#[test]
fn no_overlapping_hashes_for_new_signer() {
let mut signature = vec![];
let message = Message::new(&mut signature);
let signer = Signer::new(message, GoodSigner::default()).unwrap();
if let Err(e) = signer.add_signer(BadSigner) {
assert_eq!(e.downcast_ref::<Error>(), Some(&Error::NoAcceptableHash));
} else {
unreachable!();
};
}
#[test]
fn issue_816() -> Result<()> {
use crate::{
packet::key::{Key4, PrimaryRole},
types::Curve,
KeyHandle,
};
let signer_a =
Key4::<_, PrimaryRole>::generate_ecc(true, Curve::Ed25519)?
.into_keypair()?;
let signer_b =
Key4::<_, PrimaryRole>::generate_ecc(true, Curve::Ed25519)?
.into_keypair()?;
let mut sink = Vec::new();
let message = Message::new(&mut sink);
let message = Signer::new(message, signer_a)?
.add_signer(signer_b)?
.build()?;
let mut message = LiteralWriter::new(message).build()?;
message.write_all(b"Make it so, number one!")?;
message.finalize()?;
let pp = crate::PacketPile::from_bytes(&sink)?;
assert_eq!(pp.children().count(), 5);
let first_signer: KeyHandle =
if let Packet::OnePassSig(ops) = pp.path_ref(&[0]).unwrap() {
ops.issuer().into()
} else {
panic!("expected ops packet")
};
let second_signer: KeyHandle =
if let Packet::OnePassSig(ops) = pp.path_ref(&[1]).unwrap() {
ops.issuer().into()
} else {
panic!("expected ops packet")
};
assert!(matches!(pp.path_ref(&[2]).unwrap(), Packet::Literal(_)));
if let Packet::Signature(sig) = pp.path_ref(&[3]).unwrap() {
assert!(sig.get_issuers()[0].aliases(&second_signer));
} else {
panic!("expected sig packet")
}
if let Packet::Signature(sig) = pp.path_ref(&[4]).unwrap() {
assert!(sig.get_issuers()[0].aliases(&first_signer));
} else {
panic!("expected sig packet")
}
Ok(())
}
#[test]
fn experimental_aead_encryptor() -> Result<()> {
use std::io::Write;
use crate::types::AEADAlgorithm;
use crate::policy::NullPolicy;
use crate::serialize::stream::{
Message, Encryptor, LiteralWriter,
};
use crate::parse::stream::{
DecryptorBuilder, VerificationHelper,
DecryptionHelper, MessageStructure,
};
let mut sink = vec![];
let message = Message::new(&mut sink);
let message =
Encryptor::with_passwords(message, Some("совершенно секретно"))
.aead_algo(AEADAlgorithm::default())
.build()?;
let mut message = LiteralWriter::new(message).build()?;
message.write_all(b"Hello world.")?;
message.finalize()?;
struct Helper;
impl VerificationHelper for Helper {
fn get_certs(&mut self, _ids: &[crate::KeyHandle]) -> Result<Vec<Cert>> where {
Ok(Vec::new())
}
fn check(&mut self, _structure: MessageStructure) -> Result<()> {
Ok(())
}
}
impl DecryptionHelper for Helper {
fn decrypt(&mut self, _: &[PKESK], skesks: &[SKESK],
_sym_algo: Option<SymmetricAlgorithm>,
decrypt: &mut dyn FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool)
-> Result<Option<Cert>>
{
skesks[0].decrypt(&"совершенно секретно".into())
.map(|(algo, session_key)| decrypt(algo, &session_key))?;
Ok(None)
}
}
let p = unsafe { &NullPolicy::new() };
let mut v = DecryptorBuilder::from_bytes(&sink)?.with_policy(p, None, Helper)?;
let mut content = vec![];
v.read_to_end(&mut content)?;
assert_eq!(content, b"Hello world.");
Ok(())
}
#[test]
fn signer() -> Result<()> {
use crate::policy::StandardPolicy;
use crate::parse::stream::{
VerifierBuilder,
test::VHelper,
};
let p = StandardPolicy::new();
for alg in &[
"rsa", "dsa",
"nistp256", "nistp384", "nistp521",
"brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1",
"secp256k1",
] {
eprintln!("Test vector {:?}...", alg);
let key = Cert::from_bytes(crate::tests::key(
&format!("signing/{}.pgp", alg)))?;
if let Some(k) = key.with_policy(&p, None).ok()
.and_then(|vcert| vcert.keys().for_signing().supported().next())
{
use crate::crypto::mpi::PublicKey;
match k.key().mpis() {
PublicKey::ECDSA { curve, .. } |
PublicKey::EdDSA { curve, .. }
if ! curve.is_supported() => {
eprintln!("Skipping {} because we don't support \
the curve {}", alg, curve);
continue;
},
_ => (),
}
} else {
eprintln!("Skipping {} because we don't support the algorithm",
alg);
continue;
}
let signing_keypair = key.keys().secret()
.with_policy(&p, None).supported()
.alive().revoked(false).for_signing()
.nth(0).unwrap()
.key().clone().into_keypair()?;
let mut sink = vec![];
let message = Message::new(&mut sink);
let message = Signer::new(message, signing_keypair)?
.build()?;
let mut message = LiteralWriter::new(message).build()?;
message.write_all(b"Hello world.")?;
message.finalize()?;
let h = VHelper::new(1, 0, 0, 0, vec![key]);
let mut d = VerifierBuilder::from_bytes(&sink)?
.with_policy(&p, None, h)?;
assert!(d.message_processed());
let mut content = Vec::new();
d.read_to_end(&mut content).unwrap();
assert_eq!(&b"Hello world."[..], &content[..]);
}
Ok(())
}
#[test]
fn pk_encryptor() -> Result<()> {
use crate::policy::StandardPolicy;
use crate::parse::stream::{
DecryptorBuilder,
test::VHelper,
};
let p = StandardPolicy::new();
for path in [
"rsa", "elg", "cv25519", "cv25519.unclamped",
"nistp256", "nistp384", "nistp521",
"brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1",
"secp256k1",
].iter().map(|alg| format!("messages/encrypted/{}.sec.pgp", alg))
.chain(vec![
"crypto-refresh/v6-minimal-secret.key".into(),
].into_iter())
{
eprintln!("Test vector {:?}...", path);
let key = Cert::from_bytes(crate::tests::file(&path))?;
if let Some(k) =
key.with_policy(&p, None)?.keys().subkeys().supported().next()
{
use crate::crypto::mpi::PublicKey;
match k.key().mpis() {
PublicKey::ECDH { curve, .. } if ! curve.is_supported() => {
eprintln!("Skipping {} because we don't support \
the curve {}", path, curve);
continue;
},
_ => (),
}
} else {
eprintln!("Skipping {} because we don't support the algorithm",
path);
continue;
}
let recipients =
key.with_policy(&p, None)?.keys().for_storage_encryption();
let mut sink = vec![];
let message = Message::new(&mut sink);
let message =
Encryptor::for_recipients(message, recipients)
.build()?;
let mut message = LiteralWriter::new(message).build()?;
message.write_all(b"Hello world.")?;
message.finalize()?;
let h = VHelper::for_decryption(0, 0, 0, 0, Vec::new(),
vec![key], Vec::new());
let mut d = DecryptorBuilder::from_bytes(&sink)?
.with_policy(&p, None, h)?;
assert!(d.message_processed());
let mut content = Vec::new();
d.read_to_end(&mut content).unwrap();
assert_eq!(&b"Hello world."[..], &content[..]);
}
Ok(())
}
#[test]
fn encryptor_lifetime()
{
pub fn _encrypt_data<'a, B: AsRef<[u8]>, R>(data: B, recipients: R)
-> anyhow::Result<Vec<u8>>
where
R: IntoIterator,
R::Item: Into<Recipient<'a>>,
{
let mut sink = vec![];
let message = Message::new(&mut sink);
let armorer = Armorer::new(message).build()?;
let encryptor = Encryptor::for_recipients(armorer, recipients).build()?;
let mut writer = LiteralWriter::new(encryptor).build()?;
writer.write_all(data.as_ref())?;
writer.finalize()?;
Ok(sink)
}
}
#[test]
fn mixed_recipients_seipd1() -> Result<()> {
let alice = CertBuilder::general_purpose(Some("alice"))
.set_profile(Profile::RFC9580)?
.generate()?.0;
let bob = CertBuilder::general_purpose(Some("bob"))
.set_profile(Profile::RFC4880)?
.set_features(Features::empty().set_seipdv1())?
.generate()?.0;
mixed_recipients_intern(alice, bob, 1)
}
#[test]
fn mixed_recipients_seipd2() -> Result<()> {
let alice = CertBuilder::general_purpose(Some("alice"))
.set_profile(Profile::RFC9580)?
.generate()?.0;
let bob = CertBuilder::general_purpose(Some("bob"))
.set_profile(Profile::RFC4880)?
.generate()?.0;
mixed_recipients_intern(alice, bob, 2)
}
fn mixed_recipients_intern(alice: Cert, bob: Cert, seipdv: u8)
-> Result<()>
{
use crate::policy::StandardPolicy;
use crate::parse::stream::{
DecryptorBuilder,
test::VHelper,
};
let p = StandardPolicy::new();
let recipients = [&alice, &bob].into_iter().flat_map(
|c| c.keys().with_policy(&p, None).for_storage_encryption());
let mut sink = vec![];
let message = Message::new(&mut sink);
let message =
Encryptor::for_recipients(message, recipients)
.build()?;
let mut message = LiteralWriter::new(message).build()?;
message.write_all(b"Hello world.")?;
message.finalize()?;
for key in [alice, bob] {
eprintln!("Decrypting with key version {}",
key.primary_key().key().version());
let h = VHelper::for_decryption(0, 0, 0, 0, Vec::new(),
vec![key], Vec::new());
let mut d = DecryptorBuilder::from_bytes(&sink)?
.with_policy(&p, None, h)?;
assert!(d.message_processed());
let mut content = Vec::new();
d.read_to_end(&mut content).unwrap();
assert_eq!(&b"Hello world."[..], &content[..]);
use Packet::*;
match seipdv {
1 => d.helper_ref().packets.iter().for_each(
|p| match p {
PKESK(p) => assert_eq!(p.version(), 3),
SKESK(p) => assert_eq!(p.version(), 4),
SEIP(p) => assert_eq!(p.version(), 1),
_ => (),
}),
2 => d.helper_ref().packets.iter().for_each(
|p| match p {
PKESK(p) => assert_eq!(p.version(), 6),
SKESK(p) => assert_eq!(p.version(), 6),
SEIP(p) => assert_eq!(p.version(), 2),
_ => (),
}),
_ => unreachable!(),
}
}
Ok(())
}
}