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

View file

@ -1,10 +1,8 @@
use x509_parser::{error::X509Error, prelude::X509Certificate}; use x509_parser::{error::X509Error, prelude::X509Certificate};
use super::{ use super::
structures::{parse_tbs_certificate_der, parse_x509_der}, structures::{parse_opaque_asn1_cert, parse_precert}
LeafParsingEnumType, ;
LeafParsingError
};
use crate::merkle::types::{ use crate::merkle::types::{
ChainEntry, ChainEntry,
EntryExtraData, EntryExtraData,
@ -14,6 +12,65 @@ use crate::merkle::types::{
Version 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): /// Parses a MerkleTreeLeaf structure as specified in [RFC6962](https://datatracker.ietf.org/doc/html/rfc6962):
/// ```txt /// ```txt
/// struct { /// struct {
@ -28,96 +85,22 @@ use crate::merkle::types::{
/// This function assumes a binary format for the leaf, rather than a /// 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 /// base64-encoded version, so make sure to manually decode it from the HTTP
/// response before use. /// response before use.
pub fn parse_merkle_tree_leaf( pub fn parse_merkle_tree_leaf(input: &[u8]) -> nom::IResult<&[u8], MerkleTreeLeaf, X509Error> {
input: &[u8] nom::combinator::map(
) -> nom::IResult<&[u8], MerkleTreeLeaf, LeafParsingError<&[u8]>> { nom::sequence::pair(
let (input, version /* Version version; */) = nom::number::complete::u8(input) // Parse version byte
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?; nom::combinator::map_opt(nom::number::complete::u8, |v| match v {
let (input, leaf_type /* MerkleLeafType leaf_type; */) = 0 => Some(Version::V1),
nom::number::complete::u8(input) _ => None
.map_err(|e| e.map(|e| LeafParsingError::Nom(e)))?; }),
// Parse leaf type byte
let version = match version { nom::branch::alt((nom::sequence::pair(
0 => Version::V1, nom::bytes::complete::tag([0u8]),
_ => parse_timestamped_entry
return Err(nom::Err::Failure(LeafParsingError::InvalidEnum { ),))
input, ),
enum_type: LeafParsingEnumType::Version |(version, (_, leaf_type))| MerkleTreeLeaf { version, leaf_type }
})), )(input)
};
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 }))
} }
/// Constructs a parser for an entry `extra_data`, with prior knowledge of the /// 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( pub fn parse_certificate_chain(
input: &[u8] input: &[u8]
) -> nom::IResult<&[u8], Vec<X509Certificate>, X509Error> { ) -> nom::IResult<&[u8], Vec<X509Certificate>, X509Error> {
dbg!(nom::combinator::map_parser( nom::combinator::map_parser(
nom::multi::length_data( nom::multi::length_data(
// Get slice containing the full chain // Get slice containing the full chain
nom::number::complete::be_u24 // certificate_chain has a length field of u24 nom::number::complete::be_u24 // certificate_chain has a length field of u24
), ),
nom::multi::many0( nom::multi::many0(
// Parse a length-data x509 DER as many times as possible // 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]: /// 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 /// [RFC6962]: https://datatracker.ietf.org/doc/html/rfc6962
pub fn parse_chain_entry(input: &[u8]) -> nom::IResult<&[u8], ChainEntry, X509Error> { pub fn parse_chain_entry(input: &[u8]) -> nom::IResult<&[u8], ChainEntry, X509Error> {
nom::combinator::map( 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)| ChainEntry {
main_certificate, main_certificate,
certificate_chain certificate_chain

View file

@ -3,8 +3,10 @@ use x509_parser::{
prelude::{TbsCertificate, X509Certificate} prelude::{TbsCertificate, X509Certificate}
}; };
use crate::merkle::types::PreCert;
/// Parses a type `opaque ASN.1Cert<1..2^24-1>;` /// 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::combinator::map_parser(
nom::multi::length_data(nom::number::complete::be_u24), nom::multi::length_data(nom::number::complete::be_u24),
x509_parser::parse_x509_certificate 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>;` /// 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::combinator::map_parser(
nom::multi::length_data(nom::number::complete::be_u24), nom::multi::length_data(nom::number::complete::be_u24),
x509_parser::certificate::TbsCertificateParser::new() x509_parser::certificate::TbsCertificateParser::new()
.with_deep_parse_extensions(true) .with_deep_parse_extensions(true)
)(input) )(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)
}