use std::fmt;
use std::io::{self, Write};
use std::time;
use crate::{
crypto,
Error,
Fingerprint,
HashAlgorithm,
KeyID,
Result,
crypto::Password,
crypto::SessionKey,
packet::prelude::*,
packet::signature,
packet::key::{
PublicParts,
UnspecifiedRole,
},
Cert,
};
use crate::packet::header::CTB;
use crate::packet::header::BodyLength;
use super::{
PartialBodyFilter,
Serialize,
writer,
};
use crate::types::{
AEADAlgorithm,
CompressionAlgorithm,
DataFormat,
SignatureType,
SymmetricAlgorithm,
};
#[doc(hidden)]
#[derive(Debug)]
pub struct Cookie {
pub(crate)
level: usize,
private: Private,
}
#[derive(Debug)]
enum Private {
Nothing,
Signer,
}
impl Cookie {
pub(crate)
fn new(level: usize) -> Self {
Cookie {
level: level,
private: Private::Nothing,
}
}
}
impl Default for Cookie {
fn default() -> Self {
Cookie::new(0)
}
}
pub struct Message {
}
impl Message {
pub fn new<'a, W: 'a + io::Write>(w: W) -> writer::Stack<'a, Cookie> {
writer::Generic::new(w, Cookie::new(0))
}
}
impl<'a> From<&'a mut dyn io::Write> for writer::Stack<'a, Cookie> {
fn from(w: &'a mut dyn io::Write) -> Self {
writer::Generic::new(w, Cookie::new(0))
}
}
pub struct ArbitraryWriter<'a> {
inner: writer::BoxStack<'a, Cookie>,
}
impl<'a> ArbitraryWriter<'a> {
pub fn new(mut inner: writer::Stack<'a, Cookie>, tag: Tag)
-> Result<writer::Stack<'a, Cookie>> {
let level = inner.as_ref().cookie_ref().level + 1;
CTB::new(tag).serialize(&mut inner)?;
Ok(writer::Stack::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>> {
Some(self.inner.as_ref())
}
fn inner_mut(&mut self) -> Option<&mut dyn writer::Stackable<'a, Cookie>> {
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 + 'a>>,
intended_recipients: Vec<Fingerprint>,
detached: bool,
hash: crypto::hash::Context,
cookie: Cookie,
position: u64,
}
impl<'a> Signer<'a> {
pub fn new<S>(inner: writer::Stack<'a, Cookie>, signer: S) -> Self
where S: crypto::Signer + 'a
{
let inner = writer::BoxStack::from(inner);
let level = inner.cookie_ref().level + 1;
Signer {
inner: Some(inner),
signers: vec![Box::new(signer)],
intended_recipients: Vec::new(),
detached: false,
hash: HashAlgorithm::default().context().unwrap(),
cookie: Cookie {
level: level,
private: Private::Signer,
},
position: 0,
}
}
pub fn hash_algo(mut self, algo: HashAlgorithm) -> Result<Self> {
self.hash = algo.context()?;
Ok(self)
}
pub fn add_signer<S>(mut self, signer: S) -> Self
where S: crypto::Signer + 'a
{
self.signers.push(Box::new(signer));
self
}
pub fn add_intended_recipient(mut self, recipient: &Cert) -> Self {
self.intended_recipients.push(recipient.fingerprint());
self
}
pub fn detached(mut self) -> Self {
self.detached = true;
self
}
pub fn build(mut self) -> Result<writer::Stack<'a, Cookie>>
{
assert!(self.signers.len() > 0, "The constructor adds a signer.");
assert!(self.inner.is_some(), "The constructor adds an inner writer.");
if ! self.detached {
for (i, keypair) in self.signers.iter().enumerate() {
let key = keypair.public();
let mut ops = OnePassSig3::new(SignatureType::Binary);
ops.set_pk_algo(key.pk_algo());
ops.set_hash_algo(self.hash.algo());
ops.set_issuer(key.keyid());
ops.set_last(i == self.signers.len() - 1);
Packet::OnePassSig(ops.into())
.serialize(self.inner.as_mut().unwrap())?;
}
}
Ok(writer::Stack::from(Box::new(self)))
}
fn emit_signatures(&mut self) -> Result<()> {
if let Some(ref mut sink) = self.inner {
for signer in self.signers.iter_mut() {
let hash = self.hash.clone();
let mut sig = signature::Builder::new(SignatureType::Binary)
.set_signature_creation_time(
std::time::SystemTime::now())?
.set_issuer_fingerprint(signer.public().fingerprint())?
.set_issuer(signer.public().keyid())?;
if ! self.intended_recipients.is_empty() {
sig = sig.set_intended_recipients(
self.intended_recipients.clone())?;
}
let sig = sig.sign_hash(signer.as_mut(), hash)?;
Packet::Signature(sig).serialize(sink)?;
}
}
Ok(())
}
}
impl<'a> Drop for Signer<'a> {
fn drop(&mut self) {
let _ = self.emit_signatures();
}
}
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)
.finish()
}
}
impl<'a> Write for Signer<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let written = match self.inner.as_mut() {
Some(ref mut w) if ! self.detached => w.write(buf),
Some(_) => Ok(buf.len()),
None => Ok(buf.len()),
};
if let Ok(amount) = written {
self.hash.update(&buf[..amount]);
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>> {
if let Some(ref mut i) = self.inner {
Some(i)
} else {
None
}
}
fn inner_ref(&self) -> Option<&dyn writer::Stackable<'a, Cookie>> {
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>>,
}
impl<'a> LiteralWriter<'a> {
pub fn new(inner: writer::Stack<'a, Cookie>) -> 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(mut self, timestamp: time::SystemTime) -> Result<Self>
{
self.template.set_date(Some(timestamp))?;
Ok(self)
}
pub fn build(mut self) -> Result<writer::Stack<'a, Cookie>> {
let level = self.inner.cookie_ref().level + 1;
let signer_above =
if let &Cookie {
private: Private::Signer{..},
..
} = self.inner.cookie_ref() {
true
} else {
false
};
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(writer::Stack::from(self.inner),
Cookie::new(level)).into();
self.template.serialize_headers(&mut self.inner, false)?;
Ok(writer::Stack::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>> {
Some(self.inner.as_ref())
}
fn inner_mut(&mut self) -> Option<&mut dyn writer::Stackable<'a, Cookie>> {
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: writer::CompressionLevel,
inner: writer::BoxStack<'a, Cookie>,
}
impl<'a> Compressor<'a> {
pub fn new(inner: writer::Stack<'a, Cookie>) -> 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: writer::CompressionLevel) -> Self {
self.level = level;
self
}
pub fn build(mut self) -> Result<writer::Stack<'a, Cookie>> {
let level = self.inner.cookie_ref().level + 1;
CTB::new(Tag::CompressedData).serialize(&mut self.inner)?;
let inner: writer::Stack<'a, Cookie>
= PartialBodyFilter::new(writer::Stack::from(self.inner),
Cookie::new(level));
Self::new_naked(inner, self.algo, self.level, level)
}
pub(crate)
fn new_naked(mut inner: writer::Stack<'a, Cookie>,
algo: CompressionAlgorithm,
compression_level: writer::CompressionLevel,
level: usize)
-> Result<writer::Stack<'a, Cookie>>
{
inner.as_mut().write_u8(algo.into())?;
let inner: writer::Stack<'a, Cookie> = 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(writer::Stack::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>> {
Some(self.inner.as_ref())
}
fn inner_mut(&mut self) -> Option<&mut dyn writer::Stackable<'a, Cookie>> {
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> {
keyid: KeyID,
key: &'a Key<PublicParts, UnspecifiedRole>,
}
impl<'a> From<&'a Key<PublicParts, UnspecifiedRole>> for Recipient<'a> {
fn from(key: &'a Key<PublicParts, UnspecifiedRole>) -> Self {
Self::new(key.keyid(), key)
}
}
impl<'a> Recipient<'a> {
pub fn new(keyid: KeyID, key: &'a Key<PublicParts, UnspecifiedRole>)
-> Recipient<'a> {
Recipient { keyid, key }
}
pub fn keyid(&self) -> &KeyID {
&self.keyid
}
pub fn set_keyid(&mut self, keyid: KeyID) -> KeyID {
std::mem::replace(&mut self.keyid, keyid)
}
}
pub struct Encryptor<'a> {
inner: Option<writer::BoxStack<'a, Cookie>>,
recipients: Vec<Recipient<'a>>,
passwords: Vec<Password>,
sym_algo: SymmetricAlgorithm,
aead_algo: Option<AEADAlgorithm>,
hash: crypto::hash::Context,
cookie: Cookie,
}
impl<'a> Encryptor<'a> {
pub fn for_recipient(inner: writer::Stack<'a, Cookie>,
recipient: Recipient<'a>) -> Self {
Self {
inner: Some(inner.into()),
recipients: vec![recipient],
passwords: Vec::new(),
sym_algo: Default::default(),
aead_algo: Default::default(),
hash: HashAlgorithm::SHA1.context().unwrap(),
cookie: Default::default(),
}
}
pub fn with_password(inner: writer::Stack<'a, Cookie>,
password: Password) -> Self {
Self {
inner: Some(inner.into()),
recipients: Vec::new(),
passwords: vec![password],
sym_algo: Default::default(),
aead_algo: Default::default(),
hash: HashAlgorithm::SHA1.context().unwrap(),
cookie: Default::default(),
}
}
pub fn add_recipient(mut self, recipient: Recipient<'a>) -> Self {
self.recipients.push(recipient);
self
}
pub fn add_password(mut self, password: Password) -> Self {
self.passwords.push(password);
self
}
pub fn sym_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<writer::Stack<'a, Cookie>> {
assert!(self.recipients.len() + self.passwords.len() > 0,
"The constructors add at least one recipient or password");
struct AEADParameters {
algo: AEADAlgorithm,
chunk_size: usize,
nonce: Box<[u8]>,
}
let aead = if let Some(algo) = self.aead_algo {
let mut nonce = vec![0; algo.iv_size()?];
crypto::random(&mut nonce);
Some(AEADParameters {
algo: algo,
chunk_size: Self::AEAD_CHUNK_SIZE,
nonce: nonce.into_boxed_slice(),
})
} else {
None
};
let mut inner = self.inner.take().expect("Added in constructors");
let level = inner.as_ref().cookie_ref().level + 1;
let sk = SessionKey::new(self.sym_algo.key_size()?);
for recipient in self.recipients.iter() {
let mut pkesk =
PKESK3::for_recipient(self.sym_algo, &sk, recipient.key)?;
pkesk.set_recipient(recipient.keyid.clone());
Packet::PKESK(pkesk.into()).serialize(&mut inner)?;
}
for password in self.passwords.iter() {
if let Some(aead) = aead.as_ref() {
let skesk = SKESK5::with_password(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,
Default::default(),
&sk, password).unwrap();
Packet::SKESK(skesk.into()).serialize(&mut inner)?;
}
}
if let Some(aead) = aead {
CTB::new(Tag::AED).serialize(&mut inner)?;
let mut inner = PartialBodyFilter::new(writer::Stack::from(inner),
Cookie::new(level));
let aed = AED1::new(self.sym_algo, aead.algo, aead.chunk_size, aead.nonce)?;
aed.serialize_headers(&mut inner)?;
writer::AEADEncryptor::new(
inner.into(),
Cookie::new(level),
aed.symmetric_algo(),
aed.aead(),
aed.chunk_size(),
aed.iv(),
&sk,
)
} else {
CTB::new(Tag::SEIP).serialize(&mut inner)?;
let mut inner = PartialBodyFilter::new(writer::Stack::from(inner),
Cookie::new(level));
inner.write_all(&[1])?;
self.inner = Some(writer::Encryptor::new(
inner.into(),
Cookie::new(level),
self.sym_algo,
&sk,
)?.into());
self.cookie = Cookie::new(level);
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(writer::Stack::from(Box::new(self)))
}
}
fn emit_mdc(&mut self) -> Result<writer::BoxStack<'a, Cookie>> {
if let Some(mut w) = self.inner.take() {
let mut header = Vec::new();
CTB::new(Tag::MDC).serialize(&mut header)?;
BodyLength::Full(20).serialize(&mut header)?;
self.hash.update(&header);
Packet::MDC(MDC::from(self.hash.clone())).serialize(&mut w)?;
let w = w.into_inner()?.unwrap();
let w = w.into_inner()?.unwrap();
Ok(w)
} else {
Err(Error::InvalidOperation(
"Inner writer already taken".into()).into())
}
}
}
impl<'a> Drop for Encryptor<'a> {
fn drop(&mut self) {
let _ = self.emit_mdc();
}
}
impl<'a> fmt::Debug for Encryptor<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Encryptor")
.field("inner", &self.inner)
.finish()
}
}
impl<'a> Write for Encryptor<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let written = match self.inner.as_mut() {
Some(ref mut w) => w.write(buf),
None => Ok(buf.len()),
};
if let Ok(amount) = written {
self.hash.update(&buf[..amount]);
}
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 Encryptor<'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>> {
self.inner.as_ref().map(|r| r.as_ref())
}
fn inner_mut(&mut self) -> Option<&mut dyn writer::Stackable<'a, Cookie>> {
if let Some(ref mut i) = self.inner {
Some(i)
} else {
None
}
}
fn into_inner(mut 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.as_ref().map(|i| i.position()).unwrap_or(0)
}
}
#[cfg(test)]
mod test {
use std::io::Read;
use crate::{Packet, PacketPile, packet::CompressedData};
use crate::parse::{Parse, PacketParserResult, PacketParser};
use super::*;
use crate::types::DataFormat::Text as T;
#[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"t").unwrap();
ustr.write_all(b"\x00").unwrap();
ustr.write_all(b"\x00\x00\x00\x00").unwrap();
ustr.write_all(b"Hello world.").unwrap();
}
let mut pp = PacketParser::from_bytes(&o).unwrap().unwrap();
if let Packet::Literal(ref l) = pp.packet {
assert_eq!(l.format(), DataFormat::Text);
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_none());
}
#[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();
}
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();
}
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() {
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().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().mark_parts_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());
for s in signers.into_iter() {
signer = signer.add_signer(s);
}
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(pp) = ppr {
if let Packet::Signature(ref sig) = pp.packet {
let key = keys.get(&sig.issuer_fingerprint().unwrap())
.unwrap();
let result = sig.verify(key).unwrap();
assert!(result);
good += 1;
}
ppr = pp.recurse().unwrap().1;
}
assert_eq!(good, 2);
}
#[test]
fn encryptor() {
let passwords: [Password; 2] = ["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_password(m, passwords[0].clone())
.add_password(passwords[1].clone())
.build().unwrap();
let mut literal = LiteralWriter::new(encryptor).build()
.unwrap();
literal.write_all(message).unwrap();
}
#[derive(Debug, PartialEq)]
enum State {
Start,
Decrypted(Vec<(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)
},
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_messages() {
use std::cmp;
use crate::types::KeyFlags;
use crate::parse::{
stream::{
Decryptor,
DecryptionHelper,
VerificationHelper,
MessageStructure,
},
};
use crate::cert::{CertBuilder, CipherSuite};
use crate::serialize::stream::{LiteralWriter, Message};
let (tsk, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.add_transport_encryption_subkey()
.generate().unwrap();
struct Helper<'a> {
tsk: &'a Cert,
};
impl<'a> VerificationHelper for Helper<'a> {
fn get_public_keys(&mut self, _ids: &[crate::KeyHandle])
-> Result<Vec<Cert>> {
Ok(Vec::new())
}
fn check(&mut self, _structure: &MessageStructure) -> Result<()> {
Ok(())
}
}
impl<'a> DecryptionHelper for Helper<'a> {
fn decrypt<D>(&mut self, pkesks: &[PKESK], _skesks: &[SKESK],
mut decrypt: D) -> Result<Option<crate::Fingerprint>>
where D: FnMut(SymmetricAlgorithm, &SessionKey) -> Result<()>
{
let mut keypair = self.tsk.keys()
.key_flags(
KeyFlags::default().set_transport_encryption(true))
.map(|ka| ka.key()).next().unwrap()
.clone().mark_parts_secret().unwrap()
.into_keypair().unwrap();
pkesks[0].decrypt(&mut keypair)
.and_then(|(algo, session_key)| decrypt(algo, &session_key))
.map(|_| None)
}
}
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 recipient =
tsk.keys()
.key_flags(KeyFlags::default()
.set_storage_encryption(true)
.set_transport_encryption(true))
.nth(0).unwrap().key().into();
let encryptor = Encryptor::for_recipient(m, recipient)
.aead_algo(AEADAlgorithm::EAX)
.build().unwrap();
let mut literal = LiteralWriter::new(encryptor).build()
.unwrap();
literal.write_all(&content).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 { tsk: &tsk };
let mut v = match Decryptor::from_bytes(&msg, h, None) {
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);
}
}
}
}
}
}