refactor(parsing): leaf_input parsing to use combinators properly, rather than messy descructuring
This commit is contained in:
parent
8d335fe4d7
commit
d8e4faa9ff
3 changed files with 118 additions and 111 deletions
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
Loading…
Reference in a new issue