refactor(parsing): leaf_input parsing to use combinators properly, rather than messy descructuring

This commit is contained in:
Tyler Beckman 2024-10-26 17:51:25 -06:00
parent e2f4de8603
commit f888aa47d0
Signed by: Ty
GPG key ID: 2813440C772555A4
3 changed files with 118 additions and 111 deletions

View file

@ -1,15 +1,13 @@
use x509_parser::prelude::{TbsCertificate, X509Certificate};
#[repr(u8)]
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Version {
V1 = 0
V1
}
pub type CtExtensions<'a> = &'a [u8];
#[repr(u8)]
#[non_exhaustive]
#[derive(Debug)]
pub enum MerkleLeafType<'a> {
@ -17,17 +15,19 @@ pub enum MerkleLeafType<'a> {
timestamp: u64,
entry_type: LogEntryType<'a>,
extensions: CtExtensions<'a>
} = 0
}
}
#[derive(Debug)]
pub struct PreCert<'a> {
pub issuer_key_hash: &'a [u8],
pub tbs_certificate: TbsCertificate<'a>
}
#[repr(u16)]
#[derive(Debug)]
pub enum LogEntryType<'a> {
X509Entry(x509_parser::certificate::X509Certificate<'a>) = 0,
PrecertEntry {
issuer_key_hash: [u8; 32],
tbs_certificate: TbsCertificate<'a>
} = 1
X509Entry(x509_parser::certificate::X509Certificate<'a>),
PrecertEntry(PreCert<'a>)
}
#[derive(Debug)]

View file

@ -1,10 +1,8 @@
use x509_parser::{error::X509Error, prelude::X509Certificate};
use super::{
structures::{parse_tbs_certificate_der, parse_x509_der},
LeafParsingEnumType,
LeafParsingError
};
use super::
structures::{parse_opaque_asn1_cert, parse_precert}
;
use crate::merkle::types::{
ChainEntry,
EntryExtraData,
@ -14,6 +12,65 @@ use crate::merkle::types::{
Version
};
#[derive(Debug, PartialEq)]
pub enum CtErrorKind {
InvalidVersion
}
#[derive(Debug, PartialEq)]
pub struct CtError<I> {
pub input: I,
pub code: CtErrorKind
}
/// Parses a TimestampedEntry structure as specified in [RFC6962]:
/// ```txt
/// struct {
/// uint64 timestamp;
/// LogEntryType entry_type;
/// select(entry_type) {
/// case x509_entry: ASN.1Cert;
/// case precert_entry: PreCert;
/// } signed_entry;
/// CtExtensions extensions;
/// } TimestampedEntry;
/// ```
///
/// [RFC6962]: https://datatracker.ietf.org/doc/html/rfc6962
pub fn parse_timestamped_entry(
input: &[u8]
) -> nom::IResult<&[u8], MerkleLeafType, X509Error> {
nom::combinator::map(
nom::sequence::tuple((
nom::number::complete::be_u64,
nom::branch::alt((
// x509 entry
nom::combinator::map(
nom::sequence::pair(
nom::bytes::complete::tag(0u16.to_be_bytes()),
parse_opaque_asn1_cert
),
|(_, v)| LogEntryType::X509Entry(v)
),
// precert entry
nom::combinator::map(
nom::sequence::pair(
nom::bytes::complete::tag(1u16.to_be_bytes()),
parse_precert
),
|(_, v)| LogEntryType::PrecertEntry(v)
)
)),
nom::multi::length_data(nom::number::complete::be_u16)
)),
|(timestamp, entry_type, extensions)| MerkleLeafType::TimeStampedEntry {
timestamp,
entry_type,
extensions
}
)(input)
}
/// Parses a MerkleTreeLeaf structure as specified in [RFC6962](https://datatracker.ietf.org/doc/html/rfc6962):
/// ```txt
/// struct {
@ -28,96 +85,22 @@ use crate::merkle::types::{
/// This function assumes a binary format for the leaf, rather than a
/// base64-encoded version, so make sure to manually decode it from the HTTP
/// response before use.
pub fn parse_merkle_tree_leaf(
input: &[u8]
) -> nom::IResult<&[u8], MerkleTreeLeaf, LeafParsingError<&[u8]>> {
let (input, version /* Version version; */) = nom::number::complete::u8(input)
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?;
let (input, leaf_type /* MerkleLeafType leaf_type; */) =
nom::number::complete::u8(input)
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?;
let version = match version {
0 => Version::V1,
_ =>
return Err(nom::Err::Failure(LeafParsingError::InvalidEnum {
input,
enum_type: LeafParsingEnumType::Version
})),
};
let (input, leaf_type) = match leaf_type {
// struct {
// uint64 timestamp;
// LogEntryType entry_type; /* 2 bytes */
// select(entry_type) {
// case x509_entry: ASN.1Cert;
// case precert_entry: PreCert;
// } signed_entry;
// CtExtensions extensions;
// } TimestampedEntry;
0 => {
let (input, timestamp /* uint64 timestamp; */) =
nom::number::complete::be_u64(input)
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?;
let (input, entry_type /* LogEntryType entry_type; */) =
nom::number::complete::be_u16(input)
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?;
let (input, entry_type) = match entry_type {
0 => {
let (input, der /* opaque ASN.1Cert<1..2^24-1> */) =
parse_x509_der(input)
.map_err(|e| e.map(|e| LeafParsingError::DerParsing(e)))?;
(input, LogEntryType::X509Entry(der))
}
// case precert_entry: PreCert;
1 => {
let (
input,
issuer_key_hash // opaque issuer_key_hash[32];
) = nom::bytes::complete::take(32usize)(input)
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?;
let (
input,
tbs_certificate // TBSCertificate tbs_certificate;
) = parse_tbs_certificate_der(input)
.map_err(|e| e.map(|e| LeafParsingError::DerParsing(e)))?;
(input, LogEntryType::PrecertEntry {
issuer_key_hash: issuer_key_hash.try_into().map_err(|_| {
nom::Err::Failure(LeafParsingError::InvalidTakeLength)
})?,
tbs_certificate
})
}
_ =>
return Err(nom::Err::Failure(LeafParsingError::InvalidEnum {
input,
enum_type: LeafParsingEnumType::LogEntryType
})),
};
let (
input,
extensions // opaque CtExtensions<0..2^16-1>
) = nom::multi::length_data(nom::number::complete::be_u16)(input)
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?;
(input, MerkleLeafType::TimeStampedEntry {
timestamp,
entry_type,
extensions
})
}
_ =>
return Err(nom::Err::Failure(LeafParsingError::InvalidEnum {
input,
enum_type: LeafParsingEnumType::MerkleLeafType
})),
};
Ok((input, MerkleTreeLeaf { version, leaf_type }))
pub fn parse_merkle_tree_leaf(input: &[u8]) -> nom::IResult<&[u8], MerkleTreeLeaf, X509Error> {
nom::combinator::map(
nom::sequence::pair(
// Parse version byte
nom::combinator::map_opt(nom::number::complete::u8, |v| match v {
0 => Some(Version::V1),
_ => None
}),
// Parse leaf type byte
nom::branch::alt((nom::sequence::pair(
nom::bytes::complete::tag([0u8]),
parse_timestamped_entry
),))
),
|(version, (_, leaf_type))| MerkleTreeLeaf { version, leaf_type }
)(input)
}
/// Constructs a parser for an entry `extra_data`, with prior knowledge of the
@ -145,16 +128,16 @@ pub fn parse_entry_extra_data_precert<'a>(
pub fn parse_certificate_chain(
input: &[u8]
) -> nom::IResult<&[u8], Vec<X509Certificate>, X509Error> {
dbg!(nom::combinator::map_parser(
nom::combinator::map_parser(
nom::multi::length_data(
// Get slice containing the full chain
nom::number::complete::be_u24 // certificate_chain has a length field of u24
),
nom::multi::many0(
// Parse a length-data x509 DER as many times as possible
parse_x509_der
parse_opaque_asn1_cert
)
)(input))
)(input)
}
/// Pares an X509ChainEntry or PrecertChainEntry as specified in [RFC6962]:
@ -173,7 +156,7 @@ pub fn parse_certificate_chain(
/// [RFC6962]: https://datatracker.ietf.org/doc/html/rfc6962
pub fn parse_chain_entry(input: &[u8]) -> nom::IResult<&[u8], ChainEntry, X509Error> {
nom::combinator::map(
nom::sequence::pair(parse_x509_der, parse_certificate_chain),
nom::sequence::pair(parse_opaque_asn1_cert, parse_certificate_chain),
|(main_certificate, certificate_chain)| ChainEntry {
main_certificate,
certificate_chain

View file

@ -3,8 +3,10 @@ use x509_parser::{
prelude::{TbsCertificate, X509Certificate}
};
use crate::merkle::types::PreCert;
/// Parses a type `opaque ASN.1Cert<1..2^24-1>;`
pub fn parse_x509_der(input: &[u8]) -> X509Result<X509Certificate> {
pub fn parse_opaque_asn1_cert(input: &[u8]) -> X509Result<X509Certificate> {
nom::combinator::map_parser(
nom::multi::length_data(nom::number::complete::be_u24),
x509_parser::parse_x509_certificate
@ -12,10 +14,32 @@ pub fn parse_x509_der(input: &[u8]) -> X509Result<X509Certificate> {
}
/// Parses a type `opaque TBSCertificate<1..2^24-1>;`
pub fn parse_tbs_certificate_der(input: &[u8]) -> X509Result<TbsCertificate> {
pub fn parse_opaque_tbs_certificate(input: &[u8]) -> X509Result<TbsCertificate> {
nom::combinator::map_parser(
nom::multi::length_data(nom::number::complete::be_u24),
x509_parser::certificate::TbsCertificateParser::new()
.with_deep_parse_extensions(true)
)(input)
}
/// Parses the PreCert structure from [RFC6962]:
/// ```txt
/// struct {
/// opaque issuer_key_hash[32];
/// TBSCertificate tbs_certificate;
/// } PreCert;
/// ```
///
/// [RFC6962]: https://datatracker.ietf.org/doc/html/rfc6962
pub fn parse_precert(input: &[u8]) -> X509Result<PreCert> {
nom::combinator::map(
nom::sequence::pair(
nom::bytes::complete::take(32usize),
parse_opaque_tbs_certificate
),
|(issuer_key_hash, tbs_certificate)| PreCert {
issuer_key_hash,
tbs_certificate
}
)(input)
}