use std::io;
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use nom::bytes::streaming::take;
use nom::combinator::{map, map_opt, map_res};
use nom::number::streaming::be_u8;
use nom::sequence::pair;
use num_traits::FromPrimitive;
use rand::{CryptoRng, Rng};
use crate::crypto::checksum;
use crate::crypto::public_key::PublicKeyAlgorithm;
use crate::crypto::sym::SymmetricKeyAlgorithm;
use crate::errors::{IResult, Result};
use crate::packet::PacketTrait;
use crate::ser::Serialize;
use crate::types::{mpi, KeyId, Mpi, PublicKeyTrait, Tag, Version};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKeyEncryptedSessionKey {
packet_version: Version,
version: u8,
id: KeyId,
algorithm: PublicKeyAlgorithm,
mpis: Vec<Mpi>,
}
impl PublicKeyEncryptedSessionKey {
pub fn from_slice(version: Version, input: &[u8]) -> Result<Self> {
let (_, pk) = parse(version)(input)?;
ensure_eq!(pk.version, 3, "invalid version");
Ok(pk)
}
pub fn from_session_key<R: CryptoRng + Rng>(
rng: &mut R,
session_key: &[u8],
alg: SymmetricKeyAlgorithm,
pkey: &impl PublicKeyTrait,
) -> Result<Self> {
let len = session_key.len();
let mut data = vec![0u8; len + 3];
data[0] = alg as u8;
data[1..=len].copy_from_slice(session_key);
BigEndian::write_u16(
&mut data[len + 1..],
checksum::calculate_simple(session_key),
);
let mpis = pkey.encrypt(rng, &data)?;
Ok(PublicKeyEncryptedSessionKey {
packet_version: Default::default(),
version: 3,
id: pkey.key_id(),
algorithm: pkey.algorithm(),
mpis,
})
}
pub fn id(&self) -> &KeyId {
&self.id
}
pub fn mpis(&self) -> &[Mpi] {
&self.mpis
}
pub fn packet_version(&self) -> Version {
self.packet_version
}
}
fn parse_mpis<'i>(alg: &PublicKeyAlgorithm, i: &'i [u8]) -> IResult<&'i [u8], Vec<Mpi>> {
match alg {
PublicKeyAlgorithm::RSA | PublicKeyAlgorithm::RSASign | PublicKeyAlgorithm::RSAEncrypt => {
map(mpi, |v| vec![v.to_owned()])(i)
}
PublicKeyAlgorithm::Elgamal | PublicKeyAlgorithm::ElgamalSign => {
map(pair(mpi, mpi), |(first, second)| {
vec![first.to_owned(), second.to_owned()]
})(i)
}
PublicKeyAlgorithm::ECDSA | PublicKeyAlgorithm::DSA | PublicKeyAlgorithm::DiffieHellman => {
Ok((i, vec![]))
}
PublicKeyAlgorithm::ECDH => {
let (i, a) = mpi(i)?;
let (i, blen) = be_u8(i)?;
let (i, b) = take(blen)(i)?;
let v: [u8; 1] = [blen];
Ok((i, vec![a.to_owned(), (&v[..]).into(), b.into()]))
}
_ => Err(nom::Err::Error(crate::errors::Error::ParsingError(
nom::error::ErrorKind::Switch,
))),
}
}
fn parse(
packet_version: Version,
) -> impl Fn(&[u8]) -> IResult<&[u8], PublicKeyEncryptedSessionKey> {
move |i: &[u8]| {
let (i, version) = be_u8(i)?;
let (i, id) = map_res(take(8u8), KeyId::from_slice)(i)?;
let (i, alg) = map_opt(be_u8, PublicKeyAlgorithm::from_u8)(i)?;
let (i, mpis) = parse_mpis(&alg, i)?;
Ok((
i,
PublicKeyEncryptedSessionKey {
packet_version,
version,
id,
algorithm: alg,
mpis,
},
))
}
}
impl Serialize for PublicKeyEncryptedSessionKey {
fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
writer.write_all(&[self.version])?;
writer.write_all(self.id.as_ref())?;
writer.write_all(&[self.algorithm as u8])?;
match self.algorithm {
PublicKeyAlgorithm::RSA
| PublicKeyAlgorithm::RSASign
| PublicKeyAlgorithm::RSAEncrypt
| PublicKeyAlgorithm::Elgamal
| PublicKeyAlgorithm::ElgamalSign => {
for mpi in &self.mpis {
mpi.to_writer(writer)?;
}
}
PublicKeyAlgorithm::ECDH => {
self.mpis[0].to_writer(writer)?;
let blen: usize = match self.mpis[1].first() {
Some(l) => *l as usize,
None => 0,
};
writer.write_all(&[blen as u8])?;
let padding_len = blen - self.mpis[2].as_bytes().len();
for _ in 0..padding_len {
writer.write_u8(0)?;
}
writer.write_all(self.mpis[2].as_bytes())?;
}
_ => {
unimplemented_err!("writing {:?}", self.algorithm);
}
}
Ok(())
}
}
impl PacketTrait for PublicKeyEncryptedSessionKey {
fn packet_version(&self) -> Version {
self.packet_version
}
fn tag(&self) -> Tag {
Tag::PublicKeyEncryptedSessionKey
}
}