use std::boxed::Box;
use std::str;
use bstr::BString;
use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
use nom::bytes::streaming::{tag, take};
use nom::combinator::{complete, map, map_opt, map_parser, map_res, rest};
use nom::multi::{fold_many_m_n, length_data, many0};
use nom::number::streaming::{be_u16, be_u32, be_u8};
use nom::sequence::{pair, tuple};
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::{IResult, 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};
impl Deserialize for Signature {
fn from_slice(packet_version: Version, input: &[u8]) -> Result<Self> {
let (_, pk) = parse(packet_version)(input)?;
Ok(pk)
}
}
fn dt_from_timestamp(ts: u32) -> Option<DateTime<Utc>> {
NaiveDateTime::from_timestamp_opt(i64::from(ts), 0).map(|ts| DateTime::<Utc>::from_utc(ts, Utc))
}
fn signature_creation_time(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map_opt(
be_u32,
|date| dt_from_timestamp(date).map(SubpacketData::SignatureCreationTime),
)(i)
}
fn issuer(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(
map_res(complete(take(8u8)), KeyId::from_slice),
SubpacketData::Issuer,
)(i)
}
fn key_expiration(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map_opt(
be_u32,
|date| dt_from_timestamp(date).map(SubpacketData::KeyExpirationTime),
)(i)
}
fn pref_sym_alg(body: &[u8]) -> IResult<&[u8], SubpacketData> {
let list: SmallVec<[SymmetricKeyAlgorithm; 8]> = body
.iter()
.map(|v| {
SymmetricKeyAlgorithm::from_u8(*v)
.ok_or_else(|| format_err!("Invalid SymmetricKeyAlgorithm"))
})
.collect::<Result<_>>()?;
Ok((&b""[..], SubpacketData::PreferredSymmetricAlgorithms(list)))
}
fn pref_hash_alg(body: &[u8]) -> IResult<&[u8], SubpacketData> {
let list: SmallVec<[HashAlgorithm; 8]> = body
.iter()
.map(|v| HashAlgorithm::from_u8(*v).ok_or_else(|| format_err!("Invalid HashAlgorithm")))
.collect::<Result<_>>()?;
Ok((&b""[..], SubpacketData::PreferredHashAlgorithms(list)))
}
fn pref_com_alg(body: &[u8]) -> IResult<&[u8], SubpacketData> {
let list: SmallVec<[CompressionAlgorithm; 8]> = body
.iter()
.map(|v| {
CompressionAlgorithm::from_u8(*v)
.ok_or_else(|| format_err!("Invalid CompressionAlgorithm"))
})
.collect::<Result<_>>()?;
Ok((
&b""[..],
SubpacketData::PreferredCompressionAlgorithms(list),
))
}
fn signature_expiration_time(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map_opt(
be_u32,
|date| dt_from_timestamp(date).map(SubpacketData::SignatureExpirationTime),
)(i)
}
fn exportable_certification(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(complete(be_u8), |v| {
SubpacketData::ExportableCertification(v == 1)
})(i)
}
fn revocable(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(complete(be_u8), |v| SubpacketData::Revocable(v == 1))(i)
}
fn trust_signature(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(pair(be_u8, be_u8), |(depth, value)| {
SubpacketData::TrustSignature(depth, value)
})(i)
}
fn regular_expression(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(map(rest, BString::from), SubpacketData::RegularExpression)(i)
}
fn revocation_key(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(
tuple((
map_opt(be_u8, RevocationKeyClass::from_u8),
map_opt(be_u8, PublicKeyAlgorithm::from_u8),
take(20u8),
)),
|(class, algorithm, fp)| {
SubpacketData::RevocationKey(RevocationKey::new(class, algorithm, fp))
},
)(i)
}
fn notation_data(i: &[u8]) -> IResult<&[u8], SubpacketData> {
let (i, readable) = map(be_u8, |v| v == 0x80)(i)?;
let (i, _) = tag(&[0, 0, 0])(i)?;
let (i, name_len) = be_u16(i)?;
let (i, value_len) = be_u16(i)?;
let (i, name) = map(take(name_len), BString::from)(i)?;
let (i, value) = map(take(value_len), BString::from)(i)?;
Ok((
i,
SubpacketData::Notation(Notation {
readable,
name,
value,
}),
))
}
fn key_server_prefs(body: &[u8]) -> IResult<&[u8], SubpacketData> {
Ok((
&b""[..],
SubpacketData::KeyServerPreferences(SmallVec::from_slice(body)),
))
}
fn preferred_key_server(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(map_res(rest, str::from_utf8), |body| {
SubpacketData::PreferredKeyServer(body.to_string())
})(i)
}
fn primary_userid(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(be_u8, |a| SubpacketData::IsPrimary(a == 1))(i)
}
fn policy_uri(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(map_res(rest, str::from_utf8), |body| {
SubpacketData::PolicyURI(body.to_owned())
})(i)
}
fn key_flags(body: &[u8]) -> IResult<&[u8], SubpacketData> {
Ok((
&b""[..],
SubpacketData::KeyFlags(SmallVec::from_slice(body)),
))
}
fn signers_userid(i: &[u8]) -> IResult<&[u8], SubpacketData> {
Ok((&[], SubpacketData::SignersUserID(BString::from(i))))
}
fn features(body: &[u8]) -> IResult<&[u8], SubpacketData> {
Ok((
&b""[..],
SubpacketData::Features(SmallVec::from_slice(body)),
))
}
fn rev_reason(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(
pair(
map_opt(be_u8, RevocationCode::from_u8),
map(rest, BString::from),
),
|(code, reason)| SubpacketData::RevocationReason(code, reason),
)(i)
}
fn sig_target(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(
tuple((
map_opt(be_u8, PublicKeyAlgorithm::from_u8),
map_opt(be_u8, HashAlgorithm::from_u8),
rest,
)),
|(pub_alg, hash_alg, hash): (_, _, &[u8])| {
SubpacketData::SignatureTarget(pub_alg, hash_alg, hash.to_vec())
},
)(i)
}
fn embedded_sig(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(parse(Version::New), |sig| {
SubpacketData::EmbeddedSignature(Box::new(sig))
})(i)
}
fn issuer_fingerprint(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(
pair(map_opt(be_u8, KeyVersion::from_u8), rest),
|(version, fingerprint)| {
SubpacketData::IssuerFingerprint(version, SmallVec::from_slice(fingerprint))
},
)(i)
}
fn pref_aead_alg(body: &[u8]) -> IResult<&[u8], SubpacketData> {
let list: SmallVec<[AeadAlgorithm; 2]> = body
.iter()
.map(|v| AeadAlgorithm::from_u8(*v).ok_or_else(|| format_err!("Invalid AeadAlgorithm")))
.collect::<Result<_>>()?;
Ok((&b""[..], SubpacketData::PreferredAeadAlgorithms(list)))
}
fn subpacket(typ: SubpacketType, is_critical: bool, body: &[u8]) -> IResult<&[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,
SubpacketData::Experimental(n, SmallVec::from_slice(body)),
)),
Other(n) => Ok((body, SubpacketData::Other(n, body.to_vec()))),
};
let res = res.map(|(body, data)| (body, Subpacket { is_critical, data }));
if res.is_err() {
warn!("invalid subpacket: {:?} {:?}", typ, res);
}
res
}
fn subpackets<'a>(i: &'a [u8]) -> IResult<&'a [u8], Vec<Subpacket>> {
many0(complete(|i: &'a [u8]| {
let (i, len) = packet_length(i)?;
let (i, typ) = map(be_u8, SubpacketType::from_u8)(i)?;
map_parser(take(len - 1), move |b| subpacket(typ.0, typ.1, b))(i)
}))(i)
}
fn actual_signature(typ: &PublicKeyAlgorithm) -> impl Fn(&[u8]) -> IResult<&[u8], Vec<Mpi>> + '_ {
move |i: &[u8]| match typ {
&PublicKeyAlgorithm::RSA | &PublicKeyAlgorithm::RSASign => {
map(mpi, |v| vec![v.to_owned()])(i)
}
&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
},
)(i)
}
&PublicKeyAlgorithm::Private100
| &PublicKeyAlgorithm::Private101
| &PublicKeyAlgorithm::Private102
| &PublicKeyAlgorithm::Private103
| &PublicKeyAlgorithm::Private104
| &PublicKeyAlgorithm::Private105
| &PublicKeyAlgorithm::Private106
| &PublicKeyAlgorithm::Private107
| &PublicKeyAlgorithm::Private108
| &PublicKeyAlgorithm::Private109
| &PublicKeyAlgorithm::Private110 => map(mpi, |v| vec![v.to_owned()])(i),
_ => map(mpi, |v| vec![v.to_owned()])(i),
}
}
fn v3_parser(
packet_version: Version,
version: SignatureVersion,
) -> impl Fn(&[u8]) -> IResult<&[u8], Signature> {
move |i: &[u8]| {
let (i, (_tag, typ, created, issuer, pub_alg, hash_alg, ls_hash)) = tuple((
tag(&[5]),
map_opt(be_u8, SignatureType::from_u8),
map_opt(be_u32, |v| Utc.timestamp_opt(i64::from(v), 0).single()),
map_res(take(8usize), KeyId::from_slice),
map_opt(be_u8, PublicKeyAlgorithm::from_u8),
map_opt(be_u8, HashAlgorithm::from_u8),
take(2usize),
))(i)?;
let (i, sig) = actual_signature(&pub_alg)(i)?;
Ok((i, {
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
}))
}
}
fn v4_parser(
packet_version: Version,
version: SignatureVersion,
) -> impl Fn(&[u8]) -> IResult<&[u8], Signature> {
move |i: &[u8]| {
let (i, (typ, pub_alg, hash_alg, hsub, usub, ls_hash)) = tuple((
map_opt(be_u8, SignatureType::from_u8),
map_opt(be_u8, PublicKeyAlgorithm::from_u8),
map_opt(be_u8, HashAlgorithm::from_u8),
map_parser(length_data(be_u16), subpackets),
map_parser(length_data(be_u16), subpackets),
take(2usize),
))(i)?;
let (i, sig) = actual_signature(&pub_alg)(i)?;
Ok((
i,
Signature::new(
packet_version,
version,
typ,
pub_alg,
hash_alg,
clone_into_array(ls_hash),
sig,
hsub,
usub,
),
))
}
}
fn invalid_version(_body: &[u8], version: SignatureVersion) -> IResult<&[u8], Signature> {
unimplemented!("unknown signature version {:?}", version);
}
fn parse(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], Signature> {
move |i: &[u8]| {
let (i, version) = map_opt(be_u8, SignatureVersion::from_u8)(i)?;
let (i, signature) = match &version {
&SignatureVersion::V2 | &SignatureVersion::V3 => v3_parser(packet_version, version)(i),
&SignatureVersion::V4 | &SignatureVersion::V5 => v4_parser(packet_version, version)(i),
#[allow(unreachable_patterns)]
_ => invalid_version(i, version),
}?;
Ok((i, signature))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
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,
SubpacketData::PreferredSymmetricAlgorithms(
input
.iter()
.map(|i| SymmetricKeyAlgorithm::from_u8(*i).unwrap())
.collect()
)
);
}
}