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