use std::boxed::Box;
use std::str;
use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
use nom::{be_u16, be_u32, be_u8, rest, IResult};
use num_traits::FromPrimitive;
use smallvec::SmallVec;
use crate::crypto::aead::AeadAlgorithm;
use crate::crypto::hash::HashAlgorithm;
use crate::crypto::public_key::PublicKeyAlgorithm;
use crate::crypto::sym::SymmetricKeyAlgorithm;
use crate::de::Deserialize;
use crate::errors::Result;
use crate::packet::signature::types::*;
use crate::types::{
mpi, CompressionAlgorithm, KeyId, KeyVersion, Mpi, MpiRef, RevocationKey, RevocationKeyClass,
Version,
};
use crate::util::{clone_into_array, packet_length, read_string};
impl Deserialize for Signature {
fn from_slice(packet_version: Version, input: &[u8]) -> Result<Self> {
let (_, pk) = parse(input, packet_version)?;
Ok(pk)
}
}
fn dt_from_timestamp(ts: u32) -> DateTime<Utc> {
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(i64::from(ts), 0), Utc)
}
named!(
signature_creation_time<Subpacket>,
map!(
be_u32,
|date| Subpacket::SignatureCreationTime(dt_from_timestamp(date))
)
);
#[rustfmt::skip]
named!(issuer<Subpacket>, map!(
map_res!(complete!(take!(8)), KeyId::from_slice),
Subpacket::Issuer
));
#[rustfmt::skip]
named!(key_expiration<Subpacket>, map!(
be_u32,
|date| Subpacket::KeyExpirationTime(dt_from_timestamp(date))
));
fn pref_sym_alg(body: &[u8]) -> IResult<&[u8], Subpacket> {
let list: SmallVec<[SymmetricKeyAlgorithm; 8]> = body
.iter()
.map(|v| {
SymmetricKeyAlgorithm::from_u8(*v)
.ok_or_else(|| format_err!("Invalid SymmetricKeyAlgorithm"))
})
.collect::<Result<_>>()?;
Ok((&b""[..], Subpacket::PreferredSymmetricAlgorithms(list)))
}
fn pref_hash_alg(body: &[u8]) -> IResult<&[u8], Subpacket> {
let list: SmallVec<[HashAlgorithm; 8]> = body
.iter()
.map(|v| HashAlgorithm::from_u8(*v).ok_or_else(|| format_err!("Invalid HashAlgorithm")))
.collect::<Result<_>>()?;
Ok((&b""[..], Subpacket::PreferredHashAlgorithms(list)))
}
fn pref_com_alg(body: &[u8]) -> IResult<&[u8], Subpacket> {
let list: SmallVec<[CompressionAlgorithm; 8]> = body
.iter()
.map(|v| {
CompressionAlgorithm::from_u8(*v)
.ok_or_else(|| format_err!("Invalid CompressionAlgorithm"))
})
.collect::<Result<_>>()?;
Ok((&b""[..], Subpacket::PreferredCompressionAlgorithms(list)))
}
#[rustfmt::skip]
named!(signature_expiration_time<Subpacket>, map!(
be_u32,
|date| Subpacket::SignatureExpirationTime(dt_from_timestamp(date))
));
named!(
exportable_certification<Subpacket>,
map!(complete!(be_u8), |v| Subpacket::ExportableCertification(
v == 1
))
);
named!(
revocable<Subpacket>,
map!(complete!(be_u8), |v| Subpacket::Revocable(v == 1))
);
#[rustfmt::skip]
named!(trust_signature<Subpacket>, do_parse!(
depth: be_u8
>> value: be_u8
>> (Subpacket::TrustSignature(depth, value))
));
#[rustfmt::skip]
named!(regular_expression<Subpacket>, map!(
map!(rest, read_string), Subpacket::RegularExpression
));
#[rustfmt::skip]
named!(revocation_key<Subpacket>, do_parse!(
class: map_opt!(be_u8, RevocationKeyClass::from_u8)
>> algorithm: map_opt!(be_u8, PublicKeyAlgorithm::from_u8)
>> fp: take!(20)
>> (Subpacket::RevocationKey(RevocationKey::new(
class,
algorithm,
fp,
)))
));
#[rustfmt::skip]
named!(notation_data<Subpacket>, do_parse!(
readable: map!(be_u8, |v| v == 0x80)
>> tag!(&[0, 0, 0])
>> name_len: be_u16
>> value_len: be_u16
>> name: map!(take!(name_len), read_string)
>> value: map!(take!(value_len), read_string)
>> (Subpacket::Notation(Notation { readable, name, value }))
));
fn key_server_prefs(body: &[u8]) -> IResult<&[u8], Subpacket> {
Ok((
&b""[..],
Subpacket::KeyServerPreferences(SmallVec::from_slice(body)),
))
}
#[rustfmt::skip]
named!(preferred_key_server<Subpacket>,do_parse!(
body: map_res!(rest, str::from_utf8)
>> ({ Subpacket::PreferredKeyServer(body.to_string()) })
));
named!(
primary_userid<Subpacket>,
map!(be_u8, |a| Subpacket::IsPrimary(a == 1))
);
#[rustfmt::skip]
named!(policy_uri<Subpacket>, map!(
map!(rest, read_string), Subpacket::PolicyURI
));
fn key_flags(body: &[u8]) -> IResult<&[u8], Subpacket> {
Ok((&b""[..], Subpacket::KeyFlags(SmallVec::from_slice(body))))
}
#[rustfmt::skip]
named!(signers_userid<Subpacket>, do_parse!(
body: map_res!(rest, str::from_utf8)
>> (Subpacket::SignersUserID(body.to_string())))
);
fn features(body: &[u8]) -> IResult<&[u8], Subpacket> {
Ok((&b""[..], Subpacket::Features(SmallVec::from_slice(body))))
}
#[rustfmt::skip]
named!(rev_reason<Subpacket>, do_parse!(
code: map_opt!(be_u8, RevocationCode::from_u8)
>> reason: map!(rest, read_string)
>> (Subpacket::RevocationReason(code, reason))
));
#[rustfmt::skip]
named!(sig_target<Subpacket>, do_parse!(
pub_alg: map_opt!(be_u8, PublicKeyAlgorithm::from_u8)
>> hash_alg: map_opt!(be_u8, HashAlgorithm::from_u8)
>> hash: rest
>> (Subpacket::SignatureTarget(pub_alg, hash_alg, hash.to_vec()))
));
#[rustfmt::skip]
named!(embedded_sig<Subpacket>, map!(call!(parse, Version::New), |sig| {
Subpacket::EmbeddedSignature(Box::new(sig))
}));
#[rustfmt::skip]
named!(issuer_fingerprint<Subpacket>, do_parse!(
version: map_opt!(be_u8, KeyVersion::from_u8)
>> fingerprint: rest
>> (Subpacket::IssuerFingerprint(version, SmallVec::from_slice(fingerprint)))
));
fn pref_aead_alg(body: &[u8]) -> IResult<&[u8], Subpacket> {
let list: SmallVec<[AeadAlgorithm; 2]> = body
.iter()
.map(|v| AeadAlgorithm::from_u8(*v).ok_or_else(|| format_err!("Invalid AeadAlgorithm")))
.collect::<Result<_>>()?;
Ok((&b""[..], Subpacket::PreferredAeadAlgorithms(list)))
}
fn subpacket<'a>(typ: SubpacketType, body: &'a [u8]) -> IResult<&'a [u8], Subpacket> {
use self::SubpacketType::*;
debug!("parsing subpacket: {:?} {}", typ, hex::encode(body));
let res = match typ {
SignatureCreationTime => signature_creation_time(body),
SignatureExpirationTime => signature_expiration_time(body),
ExportableCertification => exportable_certification(body),
TrustSignature => trust_signature(body),
RegularExpression => regular_expression(body),
Revocable => revocable(body),
KeyExpirationTime => key_expiration(body),
PreferredSymmetricAlgorithms => pref_sym_alg(body),
RevocationKey => revocation_key(body),
Issuer => issuer(body),
Notation => notation_data(body),
PreferredHashAlgorithms => pref_hash_alg(body),
PreferredCompressionAlgorithms => pref_com_alg(body),
KeyServerPreferences => key_server_prefs(body),
PreferredKeyServer => preferred_key_server(body),
PrimaryUserId => primary_userid(body),
PolicyURI => policy_uri(body),
KeyFlags => key_flags(body),
SignersUserID => signers_userid(body),
RevocationReason => rev_reason(body),
Features => features(body),
SignatureTarget => sig_target(body),
EmbeddedSignature => embedded_sig(body),
IssuerFingerprint => issuer_fingerprint(body),
PreferredAead => pref_aead_alg(body),
Experimental(n) => Ok((
&body[..],
Subpacket::Experimental(n, SmallVec::from_slice(body)),
)),
Other(n) => Ok((&body[..], Subpacket::Other(n, body.to_vec()))),
};
if res.is_err() {
warn!("invalid subpacket: {:?} {:?}", typ, res);
}
res
}
#[rustfmt::skip]
named!(subpackets(&[u8]) -> Vec<Subpacket>, many0!(complete!(do_parse!(
len: packet_length
>> typ: map_opt!(be_u8, SubpacketType::from_u8)
>> p: flat_map!(take!(len - 1), |b| subpacket(typ, b))
>> (p)
))));
named_args!(actual_signature<'a>(typ: &PublicKeyAlgorithm) <&'a [u8], Vec<Mpi>>, switch!(
value!(typ),
&PublicKeyAlgorithm::RSA |
&PublicKeyAlgorithm::RSASign => map!(call!(mpi), |v| vec![v.to_owned()]) |
&PublicKeyAlgorithm::DSA |
&PublicKeyAlgorithm::ECDSA |
&PublicKeyAlgorithm::EdDSA => fold_many_m_n!(2, 2, mpi, Vec::new(), |mut acc: Vec<Mpi>, item: MpiRef<'_> | {
acc.push(item.to_owned());
acc
}) |
&PublicKeyAlgorithm::Private100 |
&PublicKeyAlgorithm::Private101 |
&PublicKeyAlgorithm::Private102 |
&PublicKeyAlgorithm::Private103 |
&PublicKeyAlgorithm::Private104 |
&PublicKeyAlgorithm::Private105 |
&PublicKeyAlgorithm::Private106 |
&PublicKeyAlgorithm::Private107 |
&PublicKeyAlgorithm::Private108 |
&PublicKeyAlgorithm::Private109 |
&PublicKeyAlgorithm::Private110 => map!(call!(mpi), |v| vec![v.to_owned()]) |
_ => map!(call!(mpi), |v| vec![v.to_owned()])
));
#[rustfmt::skip]
named_args!(v3_parser(packet_version: Version, version: SignatureVersion) <Signature>, do_parse!(
tag!(&[5])
>> typ: map_opt!(be_u8, SignatureType::from_u8)
>> created: map!(be_u32, |v| Utc.timestamp(i64::from(v), 0))
>> issuer: map_res!(take!(8), KeyId::from_slice)
>> pub_alg: map_opt!(be_u8, PublicKeyAlgorithm::from_u8)
>> hash_alg: map_opt!(be_u8, HashAlgorithm::from_u8)
>> ls_hash: take!(2)
>> sig: call!(actual_signature, &pub_alg)
>> ({
let mut s = Signature::new(
packet_version,
version,
typ,
pub_alg,
hash_alg,
clone_into_array(ls_hash),
sig,
vec![],
vec![],
);
s.config.created = Some(created);
s.config.issuer = Some(issuer);
s
})
));
#[rustfmt::skip]
named_args!(v4_parser(packet_version: Version, version: SignatureVersion) <Signature>, do_parse!(
typ: map_opt!(be_u8, SignatureType::from_u8)
>> pub_alg: map_opt!(be_u8, PublicKeyAlgorithm::from_u8)
>> hash_alg: map_opt!(be_u8, HashAlgorithm::from_u8)
>> hsub_len: be_u16
>> hsub: flat_map!(take!(hsub_len), subpackets)
>> usub_len: be_u16
>> usub: flat_map!(take!(usub_len), subpackets)
>> ls_hash: take!(2)
>> sig: call!(actual_signature, &pub_alg)
>> (Signature::new(
packet_version,
version,
typ,
pub_alg,
hash_alg,
clone_into_array(ls_hash),
sig,
hsub,
usub,
))
));
fn invalid_version<'a>(_body: &'a [u8], version: SignatureVersion) -> IResult<&'a [u8], Signature> {
unimplemented!("unknown signature version {:?}", version);
}
#[rustfmt::skip]
named_args!(parse(packet_version: Version) <Signature>, do_parse!(
version: map_opt!(be_u8, SignatureVersion::from_u8)
>> signature: switch!(value!(&version),
&SignatureVersion::V2 => call!(v3_parser, packet_version, version) |
&SignatureVersion::V3 => call!(v3_parser, packet_version, version) |
&SignatureVersion::V4 => call!(v4_parser, packet_version, version) |
&SignatureVersion::V5 => call!(v4_parser, packet_version, version) |
_ => call!(invalid_version, version)
)
>> (signature)
));
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subpacket_pref_sym_alg() {
let input = vec![9, 8, 7, 3, 2];
let (_, res) = pref_sym_alg(input.as_slice()).unwrap();
assert_eq!(
res,
Subpacket::PreferredSymmetricAlgorithms(
input
.iter()
.map(|i| SymmetricKeyAlgorithm::from_u8(*i).unwrap())
.collect()
)
);
}
}