use std::str;
use bstr::BString;
use chrono::{DateTime, Duration, TimeZone, Utc};
use log::{debug, warn};
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 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::errors::{IResult, Result};
use crate::packet::signature::types::*;
use crate::types::{
mpi, CompressionAlgorithm, Fingerprint, KeyId, KeyVersion, Mpi, MpiRef, RevocationKey,
RevocationKeyClass, SignatureBytes, Version,
};
use crate::util::{clone_into_array, packet_length};
impl Signature {
pub 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>> {
DateTime::from_timestamp(i64::from(ts), 0)
}
fn duration_from_timestamp(ts: u32) -> Option<Duration> {
Duration::try_seconds(i64::from(ts))
}
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| duration_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| Ok(SymmetricKeyAlgorithm::from(*v)))
.collect::<Result<_>>()?;
Ok((&b""[..], SubpacketData::PreferredSymmetricAlgorithms(list)))
}
fn pref_aead_alg(body: &[u8]) -> IResult<&[u8], SubpacketData> {
if body.len() % 2 != 0 {
return Err(nom::Err::Error(crate::errors::Error::Message(format!(
"Illegal preferred aead subpacket len {} must be a multiple of 2",
body.len(),
))));
}
let list: SmallVec<[(SymmetricKeyAlgorithm, AeadAlgorithm); 4]> = body
.chunks(2)
.map(|v| (SymmetricKeyAlgorithm::from(v[0]), AeadAlgorithm::from(v[1])))
.collect();
Ok((&b""[..], SubpacketData::PreferredAeadAlgorithms(list)))
}
fn pref_hash_alg(body: &[u8]) -> IResult<&[u8], SubpacketData> {
let list: SmallVec<[HashAlgorithm; 8]> = body
.iter()
.map(|v| Ok(HashAlgorithm::from(*v)))
.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| Ok(CompressionAlgorithm::from(*v)))
.collect::<Result<_>>()?;
Ok((
&b""[..],
SubpacketData::PreferredCompressionAlgorithms(list),
))
}
fn signature_expiration_time(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map_opt(
be_u32,
|date| duration_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_res(be_u8, RevocationKeyClass::try_from),
map(be_u8, PublicKeyAlgorithm::from),
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 rev_reason(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(
pair(map(be_u8, RevocationCode::from), map(rest, BString::from)),
|(code, reason)| SubpacketData::RevocationReason(code, reason),
)(i)
}
fn features(body: &[u8]) -> IResult<&[u8], SubpacketData> {
Ok((
&b""[..],
SubpacketData::Features(SmallVec::from_slice(body)),
))
}
fn sig_target(i: &[u8]) -> IResult<&[u8], SubpacketData> {
map(
tuple((
map(be_u8, PublicKeyAlgorithm::from),
map(be_u8, HashAlgorithm::from),
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> {
let (i, version) = map(be_u8, KeyVersion::from)(i)?;
if version != KeyVersion::V4 && version != KeyVersion::V5 && version != KeyVersion::V6 {
return Err(invalid_key_version(version));
}
if let Some(fingerprint_len) = version.fingerprint_len() {
let (i, fingerprint) = take(fingerprint_len)(i)?;
let fp = Fingerprint::new(version, fingerprint)?;
Ok((i, SubpacketData::IssuerFingerprint(fp)))
} else {
Err(invalid_key_version(version))
}
}
fn preferred_encryption_modes(body: &[u8]) -> IResult<&[u8], SubpacketData> {
let list: SmallVec<[AeadAlgorithm; 2]> = body.iter().map(|v| AeadAlgorithm::from(*v)).collect();
Ok((&b""[..], SubpacketData::PreferredEncryptionModes(list)))
}
fn intended_recipient_fingerprint(i: &[u8]) -> IResult<&[u8], SubpacketData> {
let (i, version) = map(be_u8, KeyVersion::from)(i)?;
if version != KeyVersion::V4 && version != KeyVersion::V5 && version != KeyVersion::V6 {
return Err(invalid_key_version(version));
}
if let Some(fingerprint_len) = version.fingerprint_len() {
let (i, fingerprint) = take(fingerprint_len)(i)?;
let fp = Fingerprint::new(version, fingerprint)?;
Ok((i, SubpacketData::IntendedRecipientFingerprint(fp)))
} else {
Err(invalid_key_version(version))
}
}
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),
PreferredEncryptionModes => preferred_encryption_modes(body),
IntendedRecipientFingerprint => intended_recipient_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)?;
if len == 0 {
return Err(nom::Err::Error(crate::errors::Error::InvalidInput));
}
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], SignatureBytes> + '_ {
move |i: &[u8]| match typ {
&PublicKeyAlgorithm::RSA | &PublicKeyAlgorithm::RSASign => {
map(mpi, |v| vec![v.to_owned()].into())(i)
}
&PublicKeyAlgorithm::DSA
| &PublicKeyAlgorithm::ECDSA
| &PublicKeyAlgorithm::EdDSALegacy => fold_many_m_n(
2,
2,
mpi,
Vec::new,
|mut acc: Vec<Mpi>, item: MpiRef<'_>| {
acc.push(item.to_owned());
acc
},
)(i)
.map(|(i, sig)| (i, sig.into())),
&PublicKeyAlgorithm::Ed25519 => {
let (i, sig) = nom::bytes::complete::take(64u8)(i)?;
Ok((i, SignatureBytes::Native(sig.to_vec())))
}
&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()].into())(i),
_ => Ok((i, SignatureBytes::Native(vec![]))), }
}
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_res(be_u8, SignatureType::try_from),
map_opt(be_u32, |v| Utc.timestamp_opt(i64::from(v), 0).single()),
map_res(take(8usize), KeyId::from_slice),
map(be_u8, PublicKeyAlgorithm::from),
map(be_u8, HashAlgorithm::from),
take(2usize),
))(i)?;
let (i, sig) = actual_signature(&pub_alg)(i)?;
match version {
SignatureVersion::V2 => Ok((i, {
Signature::v2(
packet_version,
typ,
pub_alg,
hash_alg,
created,
issuer,
clone_into_array(ls_hash),
sig,
)
})),
SignatureVersion::V3 => Ok((i, {
Signature::v3(
packet_version,
typ,
pub_alg,
hash_alg,
created,
issuer,
clone_into_array(ls_hash),
sig,
)
})),
_ => Err(nom::Err::Error(crate::errors::Error::Message(
"must only be called for V2/V3".to_string(),
))),
}
}
}
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_res(be_u8, SignatureType::try_from),
map(be_u8, PublicKeyAlgorithm::from),
map(be_u8, HashAlgorithm::from),
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)?;
if version != SignatureVersion::V4 {
return Err(nom::Err::Error(crate::errors::Error::Message(format!(
"Unsupported version {:?}",
version
))));
}
Ok((
i,
Signature::v4(
packet_version,
typ,
pub_alg,
hash_alg,
clone_into_array(ls_hash),
sig,
hsub,
usub,
),
))
}
}
fn v6_parser(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], Signature> {
move |i: &[u8]| {
let (i, (typ, pub_alg, hash_alg, hsub, usub, ls_hash)) = tuple((
map_res(be_u8, SignatureType::try_from),
map(be_u8, PublicKeyAlgorithm::from),
map(be_u8, HashAlgorithm::from),
map_parser(length_data(be_u32), subpackets),
map_parser(length_data(be_u32), subpackets),
take(2usize),
))(i)?;
let (i, len) = be_u8(i)?;
let (i, salt) = take(len)(i)?;
if hash_alg.salt_len() != Some(salt.len()) {
return Err(nom::Err::Error(crate::errors::Error::Message(format!(
"Illegal salt length {} found for {:?}",
salt.len(),
hash_alg
))));
}
let (i, sig) = actual_signature(&pub_alg)(i)?;
Ok((
i,
Signature::v6(
packet_version,
typ,
pub_alg,
hash_alg,
clone_into_array(ls_hash),
sig,
hsub,
usub,
salt.to_vec(),
),
))
}
}
fn invalid_sig_version(version: SignatureVersion) -> nom::Err<crate::errors::Error> {
nom::Err::Error(crate::errors::Error::Unsupported(format!(
"invalid signature version {version:?}"
)))
}
fn invalid_key_version(version: KeyVersion) -> nom::Err<crate::errors::Error> {
nom::Err::Error(crate::errors::Error::Unsupported(format!(
"invalid key version {version:?}"
)))
}
fn parse(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], Signature> {
move |i: &[u8]| {
let (i, version) = map(be_u8, SignatureVersion::from)(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),
&SignatureVersion::V6 => v6_parser(packet_version)(i),
_ => Err(invalid_sig_version(version)),
}?;
Ok((i, signature))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
use crate::{Deserializable, StandaloneSignature};
#[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(*i))
.collect()
)
);
}
#[test]
fn test_unknown_revocation_code() {
let revocation = "-----BEGIN PGP SIGNATURE-----
wsASBCAWCgCEBYJlrwiYCRACvMqAWdPpHUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u
cy5zZXF1b2lhLXBncC5vcmfPfjVZJ9PXSt4854s05WU+Tj5QZwuhA5+LEHEUborP
PxQdQnJldm9jYXRpb24gbWVzc2FnZRYhBKfuT6/w5BLl1XTGUgK8yoBZ0+kdAABi
lQEAkpvZ3A2RGtRdCne/dOZtqoX7oCCZKCPyfZS9I9roc5oBAOj4aklEBejYuTKF
SW+kj0jFDKC2xb/o8hbkTpwPtsoI
=0ajX
-----END PGP SIGNATURE-----";
let (sig, _) = StandaloneSignature::from_armor_single(revocation.as_bytes()).unwrap();
let rc = sig.signature.revocation_reason_code();
assert!(rc.is_some());
assert!(matches!(rc.unwrap(), RevocationCode::Other(0x42)));
}
}