use chrono::{DateTime, TimeZone, Utc};
use nom::bytes::streaming::tag;
use nom::combinator::{map, map_opt};
use nom::multi::length_data;
use nom::number::streaming::{be_u16, be_u32, be_u8};
use nom::sequence::{pair, tuple};
use num_traits::FromPrimitive;
use crate::crypto::ecc_curve::ecc_curve_from_oid;
use crate::crypto::hash::HashAlgorithm;
use crate::crypto::public_key::PublicKeyAlgorithm;
use crate::crypto::sym::SymmetricKeyAlgorithm;
use crate::errors::IResult;
use crate::types::{mpi, EcdsaPublicParams, KeyVersion, Mpi, MpiRef, PublicParams};
#[inline]
fn to_owned(mref: MpiRef<'_>) -> Mpi {
mref.to_owned()
}
fn ecdsa(i: &[u8]) -> IResult<&[u8], PublicParams> {
let (i, curve) = map_opt(
length_data(be_u8),
ecc_curve_from_oid,
)(i)?;
let (i, p) = mpi(i)?;
Ok((
i,
PublicParams::ECDSA(EcdsaPublicParams::try_from_mpi(p, curve)?),
))
}
fn eddsa(i: &[u8]) -> IResult<&[u8], PublicParams> {
let (i, curve) = map_opt(
length_data(be_u8),
ecc_curve_from_oid,
)(i)?;
let (i, q) = mpi(i)?;
Ok((
i,
PublicParams::EdDSA {
curve,
q: q.to_owned(),
},
))
}
fn ecdh(i: &[u8]) -> IResult<&[u8], PublicParams> {
map(
tuple((
map_opt(length_data(be_u8), ecc_curve_from_oid),
mpi,
be_u8,
tag(&[1][..]),
map_opt(be_u8, HashAlgorithm::from_u8),
map_opt(be_u8, SymmetricKeyAlgorithm::from_u8),
)),
|(curve, p, _len2, _tag, hash, alg_sym)| PublicParams::ECDH {
curve,
p: p.to_owned(),
hash,
alg_sym,
},
)(i)
}
fn elgamal(i: &[u8]) -> IResult<&[u8], PublicParams> {
map(
tuple((
map(mpi, to_owned),
map(mpi, to_owned),
map(mpi, to_owned),
)),
|(p, g, y)| PublicParams::Elgamal { p, g, y },
)(i)
}
fn dsa(i: &[u8]) -> IResult<&[u8], PublicParams> {
map(
tuple((
map(mpi, to_owned),
map(mpi, to_owned),
map(mpi, to_owned),
map(mpi, to_owned),
)),
|(p, q, g, y)| PublicParams::DSA { p, q, g, y },
)(i)
}
fn rsa(i: &[u8]) -> IResult<&[u8], PublicParams> {
map(pair(map(mpi, to_owned), map(mpi, to_owned)), |(n, e)| {
PublicParams::RSA { n, e }
})(i)
}
pub fn parse_pub_fields(typ: PublicKeyAlgorithm) -> impl Fn(&[u8]) -> IResult<&[u8], PublicParams> {
move |i: &[u8]| match typ {
PublicKeyAlgorithm::RSA | PublicKeyAlgorithm::RSAEncrypt | PublicKeyAlgorithm::RSASign => {
rsa(i)
}
PublicKeyAlgorithm::DSA => dsa(i),
PublicKeyAlgorithm::ECDSA => ecdsa(i),
PublicKeyAlgorithm::ECDH => ecdh(i),
PublicKeyAlgorithm::Elgamal | PublicKeyAlgorithm::ElgamalSign => elgamal(i),
PublicKeyAlgorithm::EdDSA => eddsa(i),
_ => Err(nom::Err::Error(crate::errors::Error::ParsingError(
nom::error::ErrorKind::Switch,
))),
}
}
fn new_public_key_parser(
key_ver: &KeyVersion,
) -> impl Fn(
&[u8],
) -> IResult<
&[u8],
(
KeyVersion,
PublicKeyAlgorithm,
DateTime<Utc>,
Option<u16>,
PublicParams,
),
> + '_ {
|i: &[u8]| {
let (i, created_at) = map_opt(be_u32, |v| Utc.timestamp_opt(i64::from(v), 0).single())(i)?;
let (i, alg) = map_opt(be_u8, PublicKeyAlgorithm::from_u8)(i)?;
let (i, params) = parse_pub_fields(alg)(i)?;
Ok((i, (*key_ver, alg, created_at, None, params)))
}
}
fn old_public_key_parser(
key_ver: &KeyVersion,
) -> impl Fn(
&[u8],
) -> IResult<
&[u8],
(
KeyVersion,
PublicKeyAlgorithm,
DateTime<Utc>,
Option<u16>,
PublicParams,
),
> + '_ {
|i: &[u8]| {
let (i, created_at) = map_opt(be_u32, |v| Utc.timestamp_opt(i64::from(v), 0).single())(i)?;
let (i, exp) = be_u16(i)?;
let (i, alg) = map_opt(be_u8, PublicKeyAlgorithm::from_u8)(i)?;
let (i, params) = parse_pub_fields(alg)(i)?;
Ok((i, (*key_ver, alg, created_at, Some(exp), params)))
}
}
#[allow(clippy::type_complexity)]
pub(crate) fn parse(
i: &[u8],
) -> IResult<
&[u8],
(
KeyVersion,
PublicKeyAlgorithm,
DateTime<Utc>,
Option<u16>,
PublicParams,
),
> {
let (i, key_ver) = map_opt(be_u8, KeyVersion::from_u8)(i)?;
let (i, key) = match &key_ver {
&KeyVersion::V2 | &KeyVersion::V3 => old_public_key_parser(&key_ver)(i)?,
&KeyVersion::V4 => new_public_key_parser(&key_ver)(i)?,
KeyVersion::V5 => {
return Err(nom::Err::Error(crate::errors::Error::ParsingError(
nom::error::ErrorKind::Switch,
)))
}
};
Ok((i, key))
}