use quickcheck::{Arbitrary, Gen};
use Error;
use packet::Key;
use KeyID;
use crypto::mpis::{self, MPI, Ciphertext};
use Packet;
use PublicKeyAlgorithm;
use Result;
use SymmetricAlgorithm;
use crypto::SessionKey;
use crypto::ecdh;
use nettle::{rsa, Yarrow};
use packet;
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct PKESK3 {
pub(crate) common: packet::Common,
recipient: KeyID,
pk_algo: PublicKeyAlgorithm,
esk: Ciphertext,
}
impl PKESK3 {
pub fn new(recipient: KeyID, pk_algo: PublicKeyAlgorithm,
encrypted_session_key: Ciphertext)
-> Result<PKESK3> {
Ok(PKESK3 {
common: Default::default(),
recipient: recipient,
pk_algo: pk_algo,
esk: encrypted_session_key,
})
}
pub fn for_recipient(algo: SymmetricAlgorithm,
session_key: &SessionKey, recipient: &Key)
-> Result<PKESK3> {
use PublicKeyAlgorithm::*;
let mut rng = Yarrow::default();
let mut psk = Vec::with_capacity(1 + session_key.len() + 2);
psk.push(algo.into());
psk.extend_from_slice(session_key);
let checksum
= session_key.iter().map(|&x| x as usize).sum::<usize>() & 0xffff;
psk.push((checksum >> 8) as u8);
psk.push((checksum >> 0) as u8);
#[allow(deprecated)]
let esk = match recipient.pk_algo() {
RSAEncryptSign | RSAEncrypt => {
match recipient.mpis() {
&mpis::PublicKey::RSA { ref e, ref n } => {
let mut esk = vec![0u8; n.value.len()];
let pk = rsa::PublicKey::new(&n.value, &e.value)?;
rsa::encrypt_pkcs1(&pk, &mut rng, &psk, &mut esk)?;
Ciphertext::RSA {c: MPI::new(&esk)}
}
pk => {
return Err(
Error::MalformedPacket(
format!(
"Key: Expected RSA public key, got {:?}",
pk)).into());
}
}
},
ECDH => {
ecdh::wrap_session_key(recipient, &psk)?
}
algo =>
return Err(Error::UnsupportedPublicKeyAlgorithm(algo).into()),
};
Ok(PKESK3{
common: Default::default(),
recipient: recipient.keyid(),
pk_algo: recipient.pk_algo(),
esk: esk,
})
}
pub fn recipient(&self) -> &KeyID {
&self.recipient
}
pub fn set_recipient(&mut self, recipient: KeyID) -> KeyID {
::std::mem::replace(&mut self.recipient, recipient)
}
pub fn pk_algo(&self) -> PublicKeyAlgorithm {
self.pk_algo
}
pub fn set_pk_algo(&mut self, algo: PublicKeyAlgorithm) -> PublicKeyAlgorithm {
::std::mem::replace(&mut self.pk_algo, algo)
}
pub fn esk(&self) -> &Ciphertext {
&self.esk
}
pub fn set_esk(&mut self, esk: Ciphertext) -> Ciphertext {
::std::mem::replace(&mut self.esk, esk)
}
pub fn decrypt(&self, recipient: &Key, recipient_sec: &mpis::SecretKey)
-> Result<(SymmetricAlgorithm, SessionKey)>
{
use PublicKeyAlgorithm::*;
use crypto::mpis::PublicKey;
use nettle::rsa;
let plain: SessionKey = match
(self.pk_algo, recipient.mpis(), recipient_sec, &self.esk)
{
(RSAEncryptSign,
&PublicKey::RSA{ ref e, ref n },
&mpis::SecretKey::RSA{ ref p, ref q, ref d, .. },
&mpis::Ciphertext::RSA{ ref c }) => {
let public = rsa::PublicKey::new(&n.value, &e.value)?;
let secret = rsa::PrivateKey::new(&d.value, &p.value,
&q.value, Option::None)?;
let mut rand = Yarrow::default();
rsa::decrypt_pkcs1(&public, &secret, &mut rand, &c.value)?
}
(ElgamalEncrypt,
&PublicKey::Elgamal{ .. },
&mpis::SecretKey::Elgamal{ .. },
&mpis::Ciphertext::Elgamal{ .. }) =>
return Err(
Error::UnsupportedPublicKeyAlgorithm(self.pk_algo).into()),
(ECDH,
PublicKey::ECDH{ .. },
mpis::SecretKey::ECDH { .. },
mpis::Ciphertext::ECDH { .. }) =>
ecdh::unwrap_session_key(recipient, recipient_sec, &self.esk)?,
(algo, public, secret, cipher) =>
return Err(Error::MalformedPacket(format!(
"unsupported combination of algorithm {:?}, key pair {:?}/{:?} and ciphertext {:?}",
algo, public, secret, cipher)).into()),
}.into();
let key_rgn = 1..(plain.len() - 2);
let sym_algo: SymmetricAlgorithm = plain[0].into();
let mut key = vec![0u8; sym_algo.key_size()?];
if key_rgn.len() != sym_algo.key_size()? {
return Err(Error::MalformedPacket(
format!("session key has the wrong size")).into());
}
key.copy_from_slice(&plain[key_rgn]);
let our_checksum
= key.iter().map(|&x| x as usize).sum::<usize>() & 0xffff;
let their_checksum = (plain[plain.len() - 2] as usize) << 8
| (plain[plain.len() - 1] as usize);
if their_checksum == our_checksum {
Ok((sym_algo, key.into()))
} else {
Err(Error::MalformedPacket(format!("key checksum wrong"))
.into())
}
}
}
impl From<PKESK3> for super::PKESK {
fn from(p: PKESK3) -> Self {
super::PKESK::V3(p)
}
}
impl From<PKESK3> for Packet {
fn from(p: PKESK3) -> Self {
Packet::PKESK(p.into())
}
}
impl Arbitrary for PKESK3 {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let (ciphertext, pk_algo) = loop {
let ciphertext = Ciphertext::arbitrary(g);
if let Some(pk_algo) = ciphertext.pk_algo() {
break (ciphertext, pk_algo);
}
};
PKESK3::new(KeyID::arbitrary(g), pk_algo, ciphertext).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use TPK;
use PacketPile;
use packet::key::SecretKey;
use Packet;
use std::path::PathBuf;
use parse::Parse;
use serialize::SerializeInto;
quickcheck! {
fn roundtrip(p: PKESK3) -> bool {
let q = PKESK3::from_bytes(&p.to_vec().unwrap()).unwrap();
assert_eq!(p, q);
true
}
}
fn path_to_key(artifact: &str) -> PathBuf {
[env!("CARGO_MANIFEST_DIR"), "tests", "data", "keys", artifact]
.iter().collect()
}
fn path_to_msg(artifact: &str) -> PathBuf {
[env!("CARGO_MANIFEST_DIR"), "tests", "data", "messages", artifact]
.iter().collect()
}
#[test]
fn decrypt_rsa() {
let tpk = TPK::from_file(
path_to_key("testy-private.pgp")).unwrap();
let pile = PacketPile::from_file(
path_to_msg("encrypted-to-testy.gpg")).unwrap();
let pair = tpk.subkeys().next().unwrap().subkey();
if let Some(SecretKey::Unencrypted{ mpis: ref sec }) = pair.secret() {
let pkg = pile.descendants().skip(0).next().clone();
if let Some(Packet::PKESK(ref pkesk)) = pkg {
let plain = pkesk.decrypt(&pair, sec).unwrap();
eprintln!("plain: {:?}", plain);
} else {
panic!("message is not a PKESK packet");
}
} else {
panic!("secret key is encrypted/missing");
}
}
#[test]
fn decrypt_ecdh_cv25519() {
let tpk = TPK::from_file(
path_to_key("testy-new-private.pgp")).unwrap();
let pile = PacketPile::from_file(
path_to_msg("encrypted-to-testy-new.pgp")).unwrap();
let pair = tpk.subkeys().next().unwrap().subkey();
if let Some(SecretKey::Unencrypted{ mpis: ref sec }) = pair.secret() {
let pkg = pile.descendants().skip(0).next().clone();
if let Some(Packet::PKESK(ref pkesk)) = pkg {
let plain = pkesk.decrypt(&pair, sec).unwrap();
eprintln!("plain: {:?}", plain);
} else {
panic!("message is not a PKESK packet");
}
} else {
panic!("secret key is encrypted/missing");
}
}
#[test]
fn decrypt_ecdh_nistp256() {
let tpk = TPK::from_file(
path_to_key("testy-nistp256-private.pgp")).unwrap();
let pile = PacketPile::from_file(
path_to_msg("encrypted-to-testy-nistp256.pgp")).unwrap();
let pair = tpk.subkeys().next().unwrap().subkey();
if let Some(SecretKey::Unencrypted{ mpis: ref sec }) = pair.secret() {
let pkg = pile.descendants().skip(0).next().clone();
if let Some(Packet::PKESK(ref pkesk)) = pkg {
let plain = pkesk.decrypt(&pair, sec).unwrap();
eprintln!("plain: {:?}", plain);
} else {
panic!("message is not a PKESK packet");
}
} else {
panic!("secret key is encrypted/missing");
}
}
#[test]
fn decrypt_ecdh_nistp384() {
let tpk = TPK::from_file(
path_to_key("testy-nistp384-private.pgp")).unwrap();
let pile = PacketPile::from_file(
path_to_msg("encrypted-to-testy-nistp384.pgp")).unwrap();
let pair = tpk.subkeys().next().unwrap().subkey();
if let Some(SecretKey::Unencrypted{ mpis: ref sec }) = pair.secret() {
let pkg = pile.descendants().skip(0).next().clone();
if let Some(Packet::PKESK(ref pkesk)) = pkg {
let plain = pkesk.decrypt(&pair, sec).unwrap();
eprintln!("plain: {:?}", plain);
} else {
panic!("message is not a PKESK packet");
}
} else {
panic!("secret key is encrypted/missing");
}
}
#[test]
fn decrypt_ecdh_nistp521() {
let tpk = TPK::from_file(
path_to_key("testy-nistp521-private.pgp")).unwrap();
let pile = PacketPile::from_file(
path_to_msg("encrypted-to-testy-nistp521.pgp")).unwrap();
let pair = tpk.subkeys().next().unwrap().subkey();
if let Some(SecretKey::Unencrypted{ mpis: ref sec }) = pair.secret() {
let pkg = pile.descendants().skip(0).next().clone();
if let Some(Packet::PKESK(ref pkesk)) = pkg {
let plain = pkesk.decrypt(&pair, sec).unwrap();
eprintln!("plain: {:?}", plain);
} else {
panic!("message is not a PKESK packet");
}
} else {
panic!("secret key is encrypted/missing");
}
}
#[test]
fn decrypt_with_short_cv25519_secret_key() {
use conversions::Time;
use super::PKESK3;
use crypto::SessionKey;
use crypto::mpis::{self, MPI};
use PublicKeyAlgorithm;
use SymmetricAlgorithm;
use HashAlgorithm;
use constants::Curve;
use packet::Key;
use packet::key::Key4;
use nettle::{curve25519, Yarrow};
use time;
let mut sec = [
0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,
0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x0,0x0
];
let mut pnt = [0x40u8; curve25519::CURVE25519_SIZE + 1];
curve25519::mul_g(&mut pnt[1..], &sec[..]).unwrap();
sec.reverse();
let public_mpis = mpis::PublicKey::ECDH {
curve: Curve::Cv25519,
q: MPI::new(&pnt[..]),
hash: HashAlgorithm::SHA256,
sym: SymmetricAlgorithm::AES256,
};
let private_mpis = mpis::SecretKey::ECDH {
scalar: MPI::new(&sec[..]),
};
let key: Key = Key4::new(time::now().canonicalize(),
PublicKeyAlgorithm::ECDH, public_mpis, None)
.unwrap().into();
let mut rng = Yarrow::default();
let sess_key = SessionKey::new(&mut rng, 32);
let pkesk = PKESK3::for_recipient(SymmetricAlgorithm::AES256, &sess_key,
&key).unwrap();
pkesk.decrypt(&key, &private_mpis).unwrap();
}
}