certificate: parseable
This commit is contained in:
parent
ed2b73389a
commit
d241e93d8e
@ -3,23 +3,25 @@ use num_enum::TryFromPrimitive;
|
|||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Certificate<'a> {
|
pub struct Certificate<'a> {
|
||||||
tbs_certificate: TBSCertificate<'a>,
|
pub tbs_certificate: TBSCertificate<'a>,
|
||||||
signature_algorithm: AlgorithmIdentifier<'a>,
|
pub signature_algorithm: AlgorithmIdentifier<'a>,
|
||||||
signature_value: &'a [u8]
|
pub signature_value: &'a [u8]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct TBSCertificate<'a> {
|
pub struct TBSCertificate<'a> {
|
||||||
version: Version,
|
pub version: Version,
|
||||||
serial_number: &'a [u8],
|
pub serial_number: &'a [u8],
|
||||||
signature: AlgorithmIdentifier<'a>,
|
pub signature: AlgorithmIdentifier<'a>,
|
||||||
issuer: &'a [u8],
|
pub issuer: &'a [u8],
|
||||||
validity: Validity<'a>,
|
pub validity: Validity<'a>,
|
||||||
subject: &'a [u8],
|
pub subject: &'a [u8],
|
||||||
subject_public_key_info: SubjectPublicKeyInfo<'a>,
|
pub subject_public_key_info: SubjectPublicKeyInfo<'a>,
|
||||||
issuer_unique_id: Option<&'a [u8]>,
|
pub issuer_unique_id: Option<&'a [u8]>,
|
||||||
subject_unique_id: Option<&'a [u8]>,
|
pub subject_unique_id: Option<&'a [u8]>,
|
||||||
extensions: Extensions<'a>,
|
pub extensions: Extensions<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
|
||||||
@ -31,44 +33,98 @@ pub enum Version {
|
|||||||
v3 = 2,
|
v3 = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Validity<'a> {
|
pub struct Validity<'a> {
|
||||||
pub not_before: Time<'a>,
|
pub not_before: Time<'a>,
|
||||||
pub not_after: Time<'a>,
|
pub not_after: Time<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum Time<'a> {
|
pub enum Time<'a> {
|
||||||
UTCTime(&'a [u8]),
|
UTCTime(&'a [u8]),
|
||||||
GeneralizedTime(&'a [u8]),
|
GeneralizedTime(&'a [u8]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct SubjectPublicKeyInfo<'a> {
|
pub struct SubjectPublicKeyInfo<'a> {
|
||||||
pub algorithm: AlgorithmIdentifier<'a>,
|
pub algorithm: AlgorithmIdentifier<'a>,
|
||||||
pub subject_public_key: &'a [u8],
|
pub subject_public_key: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Extensions<'a> {
|
pub struct Extensions<'a> {
|
||||||
extensions: Vec<Extension<'a>>
|
// TODO: Give a limit to the number of policies, migrate to heapless vec
|
||||||
|
// An arbitrary upper limit does not violate RFC5280
|
||||||
|
pub extensions: Vec<Extension<'a>>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Extension<'a> {
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Extension<'a> {
|
||||||
|
pub extension_id: &'a [u8],
|
||||||
|
pub critical: bool,
|
||||||
|
pub extension_value: ExtensionValue<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ExtensionValue<'a> {
|
||||||
KeyUsage {
|
KeyUsage {
|
||||||
// Acceptable usage of this certificate
|
// Acceptable usage of this certificate
|
||||||
// Cross verify with ExtendedKeyUsage
|
// Cross verify with ExtendedKeyUsage
|
||||||
usage: u8
|
// MSb is bit 0
|
||||||
|
usage: u16
|
||||||
},
|
},
|
||||||
|
|
||||||
CertificatePolicies {
|
CertificatePolicies {
|
||||||
// Policies listed in an extension
|
// Policies listed in an extension
|
||||||
// Need to verify its validity
|
// Need to verify its validity
|
||||||
policies: Vec<&'a [u8]>
|
// TODO: Give a limit to the number of policies, migrate to heapless vec
|
||||||
|
// An arbitrary upper limit does not violate RFC5280
|
||||||
|
info: Vec<PolicyInformation<'a>>
|
||||||
},
|
},
|
||||||
SubjectAlternativeName,
|
|
||||||
|
// Permitted subtrees and excluded subtrees are not implemented
|
||||||
|
// SubjectAlternativeName,
|
||||||
|
|
||||||
BasicConstraints {
|
BasicConstraints {
|
||||||
is_ca: bool,
|
is_ca: bool,
|
||||||
path_len_constraint: Option<u8>,
|
path_len_constraint: Option<u8>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Permitted subtrees and excluded subtrees are not implemented
|
||||||
|
// NameConstraints,
|
||||||
|
|
||||||
|
// Policy mapping will not be supported
|
||||||
|
// PolicyConstraints,
|
||||||
|
|
||||||
|
ExtendedKeyUsage {
|
||||||
|
// A list of all possible extended key usage in OID
|
||||||
|
// Cross check validity with regular KeyUsage
|
||||||
|
any_extended_key_usage: bool,
|
||||||
|
id_kp_server_auth: bool,
|
||||||
|
id_kp_client_auth: bool,
|
||||||
|
id_kp_code_signing: bool,
|
||||||
|
id_kp_email_protection: bool,
|
||||||
|
id_kp_time_stamping: bool,
|
||||||
|
id_kp_oscp_signing: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
InhibitAnyPolicy {
|
||||||
|
// Number of certificates in the path that may still allow AnyPolicy
|
||||||
|
// Certificate chain size should be limited to a small number
|
||||||
|
skip_certs: u8
|
||||||
|
},
|
||||||
|
|
||||||
|
// Extension data from an unsupported extension type
|
||||||
|
Unrecognized,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PolicyInformation<'a> {
|
||||||
|
pub id: &'a [u8],
|
||||||
|
pub qualifier: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct AlgorithmIdentifier<'a> {
|
pub struct AlgorithmIdentifier<'a> {
|
||||||
pub algorithm: &'a [u8],
|
pub algorithm: &'a [u8],
|
||||||
pub parameters: &'a [u8],
|
pub parameters: &'a [u8],
|
||||||
|
395
src/parse.rs
395
src/parse.rs
@ -3,6 +3,7 @@ use nom::bytes::complete::take;
|
|||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::take_till;
|
use nom::bytes::complete::take_till;
|
||||||
use nom::combinator::complete;
|
use nom::combinator::complete;
|
||||||
|
use nom::combinator::opt;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
use nom::error::make_error;
|
use nom::error::make_error;
|
||||||
@ -13,13 +14,20 @@ use smoltcp::Result;
|
|||||||
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
use byteorder::{ByteOrder, NetworkEndian, BigEndian};
|
||||||
|
|
||||||
use crate::tls_packet::*;
|
use crate::tls_packet::*;
|
||||||
use crate::certificate::Certificate as Asn1DerCertificate;
|
|
||||||
use crate::certificate::Version as Asn1DerVersion;
|
use crate::certificate::{
|
||||||
use crate::certificate::AlgorithmIdentifier as Asn1DerAlgId;
|
Certificate as Asn1DerCertificate,
|
||||||
use crate::certificate::Time as Asn1DerTime;
|
Version as Asn1DerVersion,
|
||||||
use crate::certificate::Validity as Asn1DerValidity;
|
AlgorithmIdentifier as Asn1DerAlgId,
|
||||||
use crate::certificate::SubjectPublicKeyInfo as Asn1DerSubjectPublicKeyInfo;
|
Time as Asn1DerTime,
|
||||||
use crate::certificate::Extensions as Asn1DerExtensions;
|
Validity as Asn1DerValidity,
|
||||||
|
SubjectPublicKeyInfo as Asn1DerSubjectPublicKeyInfo,
|
||||||
|
Extensions as Asn1DerExtensions,
|
||||||
|
Extension as Asn1DerExtension,
|
||||||
|
ExtensionValue as Asn1DerExtensionValue,
|
||||||
|
PolicyInformation as Asn1DerPolicyInformation,
|
||||||
|
TBSCertificate as Asn1DerTBSCertificate,
|
||||||
|
};
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
@ -368,22 +376,73 @@ pub fn parse_asn1_der_header(bytes: &[u8]) -> IResult<&[u8], (u8, usize)> {
|
|||||||
|
|
||||||
// TODO: Not return length
|
// TODO: Not return length
|
||||||
// It is quite useless when the value slice of the exact same length is returned
|
// It is quite useless when the value slice of the exact same length is returned
|
||||||
|
// i.e. `length` can be replaced by `value.len()`
|
||||||
pub fn parse_asn1_der_object(bytes: &[u8]) -> IResult<&[u8], (u8, usize, &[u8])> {
|
pub fn parse_asn1_der_object(bytes: &[u8]) -> IResult<&[u8], (u8, usize, &[u8])> {
|
||||||
let (rest, (tag, length)) = parse_asn1_der_header(bytes)?;
|
let (rest, (tag, length)) = parse_asn1_der_header(bytes)?;
|
||||||
let (rest, value) = take(length)(rest)?;
|
let (rest, value) = take(length)(rest)?;
|
||||||
Ok((rest, (tag, length, value)))
|
Ok((rest, (tag, length, value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_asn1_der_certificate(bytes: &[u8]) -> IResult<&[u8], (&[u8], &[u8], &[u8])> {
|
pub fn parse_asn1_der_certificate(bytes: &[u8]) -> IResult<&[u8], Asn1DerCertificate> {
|
||||||
let (_, (_, _, rest)) = parse_asn1_der_object(bytes)?;
|
let (excluded, (_, _, rest)) = parse_asn1_der_object(bytes)?;
|
||||||
let (rest, (_, _, tbscertificate_slice)) = parse_asn1_der_object(rest)?;
|
let (_, (tbs_certificate, sig_alg, sig_value)) = complete(
|
||||||
let (rest, (_, _, signature_alg)) = parse_asn1_der_object(rest)?;
|
tuple((
|
||||||
let (rest, (_, _, sig_val)) = parse_asn1_der_object(rest)?;
|
parse_asn1_der_tbs_certificate,
|
||||||
Ok((rest, (tbscertificate_slice, signature_alg, sig_val)))
|
parse_asn1_der_algorithm_identifier,
|
||||||
|
parse_asn1_der_bit_string
|
||||||
|
))
|
||||||
|
)(rest)?;
|
||||||
|
Ok((
|
||||||
|
excluded,
|
||||||
|
Asn1DerCertificate {
|
||||||
|
tbs_certificate,
|
||||||
|
signature_algorithm: sig_alg,
|
||||||
|
signature_value: sig_value,
|
||||||
|
}
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_asn1_der_tbs_certificate(bytes: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
|
// Parser for TBSCertificate (Sequence: 0x30)
|
||||||
todo!()
|
pub fn parse_asn1_der_tbs_certificate(bytes: &[u8]) -> IResult<&[u8], Asn1DerTBSCertificate> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify the tag is indeed 0x30
|
||||||
|
if tag_val != 0x30 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, (
|
||||||
|
version, serial_number, signature, issuer, validity, subject,
|
||||||
|
subject_public_key_info, issuer_unique_id, subject_unique_id, extensions
|
||||||
|
)) = complete(
|
||||||
|
tuple((
|
||||||
|
parse_asn1_der_version,
|
||||||
|
parse_asn1_der_serial_number,
|
||||||
|
parse_asn1_der_algorithm_identifier,
|
||||||
|
parse_asn1_der_sequence,
|
||||||
|
parse_asn1_der_validity,
|
||||||
|
parse_asn1_der_sequence,
|
||||||
|
parse_asn1_der_subject_key_public_info,
|
||||||
|
opt(parse_asn1_der_bit_string),
|
||||||
|
opt(parse_asn1_der_bit_string),
|
||||||
|
parse_asn1_der_extensions
|
||||||
|
))
|
||||||
|
)(value)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerTBSCertificate {
|
||||||
|
version,
|
||||||
|
serial_number,
|
||||||
|
signature,
|
||||||
|
issuer,
|
||||||
|
validity,
|
||||||
|
subject,
|
||||||
|
subject_public_key_info,
|
||||||
|
issuer_unique_id,
|
||||||
|
subject_unique_id,
|
||||||
|
extensions,
|
||||||
|
}
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// version: [0] EXPLICIT Version DEFAULT V1
|
// version: [0] EXPLICIT Version DEFAULT V1
|
||||||
@ -398,9 +457,7 @@ pub fn parse_asn1_der_version(bytes: &[u8]) -> IResult<&[u8], Asn1DerVersion> {
|
|||||||
// Parse the encapsulated INTEGER, force completeness
|
// Parse the encapsulated INTEGER, force completeness
|
||||||
let (_, integer) = complete(parse_asn1_der_integer)(value)?;
|
let (_, integer) = complete(parse_asn1_der_integer)(value)?;
|
||||||
// Either 0, 1, or 2, take the last byte and assert all former bytes to be 0
|
// Either 0, 1, or 2, take the last byte and assert all former bytes to be 0
|
||||||
let (zeroes, version_byte) = take(integer.len()-1)(integer)?;
|
Ok((rest, Asn1DerVersion::try_from(integer[0]).unwrap()))
|
||||||
complete(take_till(|byte| byte != 0))(zeroes)?;
|
|
||||||
Ok((rest, Asn1DerVersion::try_from(version_byte[0]).unwrap()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTEGER: tag: 0x02
|
// INTEGER: tag: 0x02
|
||||||
@ -410,8 +467,6 @@ pub fn parse_asn1_der_integer(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
|
|||||||
if tag_val != 0x02 {
|
if tag_val != 0x02 {
|
||||||
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
}
|
}
|
||||||
// Consume the leading 0x00 byte
|
|
||||||
let (value, _) = tag(&[0x00])(value)?;
|
|
||||||
Ok((rest, value))
|
Ok((rest, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,12 +477,35 @@ pub fn parse_asn1_der_bit_string(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
|
|||||||
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
// Verify the tag is indeed 0x03
|
// Verify the tag is indeed 0x03
|
||||||
if tag_val != 0x03 {
|
if tag_val != 0x03 {
|
||||||
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
return Err(nom::Err::Error((bytes, ErrorKind::Verify)));
|
||||||
}
|
}
|
||||||
// Dump `unused_bit` field
|
// Dump `unused_bit` field
|
||||||
let (value, unused_bit_byte) = take(1_usize)(value)?;
|
let (value, unused_bit_byte) = take(1_usize)(value)?;
|
||||||
// Assert no unused bits, otherwise it is a malformatted key
|
// Assert no unused bits, otherwise it is a malformatted key
|
||||||
if value[0] != 0 {
|
if unused_bit_byte[0] != 0 {
|
||||||
|
return Err(nom::Err::Error((bytes, ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
Ok((rest, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOOLEAN: tag: 0x01
|
||||||
|
// Length should be 1
|
||||||
|
// 0x00 -> false; 0xFF -> true
|
||||||
|
pub fn parse_asn1_der_boolean(bytes: &[u8]) -> IResult<&[u8], bool> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify the tag is indeed 0x01 and the length is 1
|
||||||
|
// The value should be 0x00 or 0xFF
|
||||||
|
if tag_val != 0x01 || length != 1 || (value[0] != 0x00 && value[0] != 0xFF) {
|
||||||
|
return Err(nom::Err::Error((bytes, ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
Ok((rest, value[0] == 0xFF))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SEQUENCE: tag: 0x30
|
||||||
|
pub fn parse_asn1_der_sequence(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify the tag is indeed 0x03
|
||||||
|
if tag_val != 0x30 {
|
||||||
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
}
|
}
|
||||||
Ok((rest, value))
|
Ok((rest, value))
|
||||||
@ -537,12 +615,281 @@ pub fn parse_asn1_der_subject_key_public_info(bytes: &[u8]) -> IResult<&[u8], As
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser for extensions (Sequence: 0xA3)
|
// Parser for extensions (Context-specific Sequence: 0xA3, then universal Sequence: 0x30)
|
||||||
pub fn parse_asn1_der_extensions(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensions> {
|
pub fn parse_asn1_der_extensions(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensions> {
|
||||||
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
// Verify the tag_val is indeed 0xA3
|
// Verify the tag_val is indeed 0xA3
|
||||||
if tag_val != 0xA3 {
|
if tag_val != 0xA3 {
|
||||||
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
}
|
}
|
||||||
todo!()
|
|
||||||
|
let (_, (tag_val, length, mut value)) = complete(
|
||||||
|
parse_asn1_der_object
|
||||||
|
)(value)?;
|
||||||
|
// Verify the tag_val is indeed 0x30
|
||||||
|
if tag_val != 0x30 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut extensions = Vec::new();
|
||||||
|
while value.len() != 0 {
|
||||||
|
let (rem, extension) = parse_asn1_der_extension(value)?;
|
||||||
|
value = rem;
|
||||||
|
extensions.push(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerExtensions { extensions }
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for an extension (Sequence: 0x30)
|
||||||
|
pub fn parse_asn1_der_extension(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtension> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify the tag_val is indeed 0x30
|
||||||
|
if tag_val != 0x30 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse an appropriate extension according to OID and critical-ness
|
||||||
|
let (_, (oid, critical, rem_ext_data)) = complete(
|
||||||
|
tuple((
|
||||||
|
parse_asn1_der_oid,
|
||||||
|
opt(parse_asn1_der_boolean),
|
||||||
|
parse_asn1_der_octet_string
|
||||||
|
))
|
||||||
|
)(value)?;
|
||||||
|
|
||||||
|
let extension_value = match oid {
|
||||||
|
oid::CERT_KEY_USAGE => {
|
||||||
|
let (_, extension_value) = complete(
|
||||||
|
parse_asn1_der_key_usage
|
||||||
|
)(rem_ext_data)?;
|
||||||
|
extension_value
|
||||||
|
},
|
||||||
|
oid::CERT_POLICIES => {
|
||||||
|
let (_, extension_value) = complete(
|
||||||
|
parse_asn1_der_certificate_policies
|
||||||
|
)(rem_ext_data)?;
|
||||||
|
extension_value
|
||||||
|
},
|
||||||
|
oid::CERT_BASIC_CONSTRAINTS => {
|
||||||
|
let (_, extension_value) = complete(
|
||||||
|
parse_asn1_der_basic_constraints
|
||||||
|
)(rem_ext_data)?;
|
||||||
|
extension_value
|
||||||
|
},
|
||||||
|
oid::CERT_EXT_KEY_USAGE => {
|
||||||
|
let (_, extension_value) = complete(
|
||||||
|
parse_asn1_der_extended_key_usage
|
||||||
|
)(rem_ext_data)?;
|
||||||
|
extension_value
|
||||||
|
},
|
||||||
|
oid::CERT_INHIBIT_ANY_POLICY => {
|
||||||
|
let (_, extension_value) = complete(
|
||||||
|
parse_inhibit_any_policy
|
||||||
|
)(rem_ext_data)?;
|
||||||
|
extension_value
|
||||||
|
},
|
||||||
|
// TODO: Parse extension value for recognized extensions
|
||||||
|
_ => Asn1DerExtensionValue::Unrecognized
|
||||||
|
};
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerExtension {
|
||||||
|
extension_id: oid,
|
||||||
|
critical: critical.map_or(false, |b| b),
|
||||||
|
extension_value
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for KeyUsage Extension, may have bit padding
|
||||||
|
// Do not use parse_asn1_der_bit_string, that assumes no bit padding
|
||||||
|
pub fn parse_asn1_der_key_usage(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify the tag_val represents a bitstring, and it must have length 2
|
||||||
|
// i.e. bit-padding | bit-string
|
||||||
|
if tag_val != 0x03 || (length != 2 && length != 3) {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
// Erase the padded bits
|
||||||
|
let padding = value[0];
|
||||||
|
let usage_array: [u8; 2] = if length == 2 {
|
||||||
|
[value[1], 0]
|
||||||
|
} else {
|
||||||
|
[value[1], value[2]]
|
||||||
|
};
|
||||||
|
let usage = (NetworkEndian::read_u16(&usage_array) >> padding) << padding;
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerExtensionValue::KeyUsage {
|
||||||
|
usage
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for CertificatePolicies Extension (sequence: 0x30)
|
||||||
|
pub fn parse_asn1_der_certificate_policies(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
|
||||||
|
let (rest, (tag_val, length, mut value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify tag value
|
||||||
|
if tag_val != 0x30 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vec: Vec<Asn1DerPolicyInformation> = Vec::new();
|
||||||
|
|
||||||
|
while value.len() != 0 {
|
||||||
|
let (rem, info) = parse_asn1_der_policy_information(value)?;
|
||||||
|
value = rem;
|
||||||
|
vec.push(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerExtensionValue::CertificatePolicies {
|
||||||
|
info: vec,
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for PolicyInformation (Sequence: 0x30)
|
||||||
|
pub fn parse_asn1_der_policy_information(bytes: &[u8]) -> IResult<&[u8], Asn1DerPolicyInformation> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify tag value
|
||||||
|
if tag_val != 0x30 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, (oid, (_, _, qualifier))) = complete(
|
||||||
|
tuple((
|
||||||
|
parse_asn1_der_oid,
|
||||||
|
parse_asn1_der_object
|
||||||
|
))
|
||||||
|
)(value)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerPolicyInformation {
|
||||||
|
id: oid,
|
||||||
|
qualifier
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for BasicConstraints (Sequence: 0x30)
|
||||||
|
pub fn parse_asn1_der_basic_constraints(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify tag value
|
||||||
|
if tag_val != 0x30 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
let (_, (is_ca, path_len_constraint)) = complete(
|
||||||
|
tuple((
|
||||||
|
opt(parse_asn1_der_boolean),
|
||||||
|
opt(parse_asn1_der_integer)
|
||||||
|
))
|
||||||
|
)(value)?;
|
||||||
|
let is_ca = is_ca.map_or(false, |b| b);
|
||||||
|
let path_len_constraint = path_len_constraint.map(
|
||||||
|
|slice| {
|
||||||
|
if slice.len() != 1 {
|
||||||
|
255
|
||||||
|
} else {
|
||||||
|
slice[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerExtensionValue::BasicConstraints {
|
||||||
|
is_ca,
|
||||||
|
path_len_constraint
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for Extended Key Usage Extension (Sequence: 0x30)
|
||||||
|
pub fn parse_asn1_der_extended_key_usage(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
|
||||||
|
let (rest, (tag_val, length, mut value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify tag value
|
||||||
|
if tag_val != 0x30 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut flags: [bool; 7] = [false; 7];
|
||||||
|
|
||||||
|
while value.len() != 0 {
|
||||||
|
let (rem, oid_val) = parse_asn1_der_oid(value)?;
|
||||||
|
value = rem;
|
||||||
|
match oid_val {
|
||||||
|
oid::ANY_EXTENDED_KEY_USAGE => flags[0] = true,
|
||||||
|
oid::ID_KP_SERVER_AUTH => flags[1] = true,
|
||||||
|
oid::ID_KP_CLIENT_AUTH => flags[2] = true,
|
||||||
|
oid::ID_KP_CODE_SIGNING => flags[3] = true,
|
||||||
|
oid::ID_KP_EMAIL_PROTECTION => flags[4] = true,
|
||||||
|
oid::ID_KP_TIME_STAMPING => flags[5] = true,
|
||||||
|
oid::ID_KP_OCSP_SIGNING => flags[6] = true,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerExtensionValue::ExtendedKeyUsage {
|
||||||
|
any_extended_key_usage: flags[0],
|
||||||
|
id_kp_server_auth: flags[1],
|
||||||
|
id_kp_client_auth: flags[2],
|
||||||
|
id_kp_code_signing: flags[3],
|
||||||
|
id_kp_email_protection: flags[4],
|
||||||
|
id_kp_time_stamping: flags[5],
|
||||||
|
id_kp_oscp_signing: flags[6],
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for inhibit anyPolicy extension (integer)
|
||||||
|
pub fn parse_inhibit_any_policy(bytes: &[u8]) -> IResult<&[u8], Asn1DerExtensionValue> {
|
||||||
|
let (rest, integer_slice) = parse_asn1_der_integer(bytes)?;
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Asn1DerExtensionValue::InhibitAnyPolicy {
|
||||||
|
skip_certs: {
|
||||||
|
if integer_slice.len() == 1 {
|
||||||
|
integer_slice[0]
|
||||||
|
} else {
|
||||||
|
255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parser for octet string (tag: 0x04)
|
||||||
|
pub fn parse_asn1_der_octet_string(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||||
|
let (rest, (tag_val, length, value)) = parse_asn1_der_object(bytes)?;
|
||||||
|
// Verify tag value
|
||||||
|
if tag_val != 0x04 {
|
||||||
|
return Err(nom::Err::Failure((&[], ErrorKind::Verify)));
|
||||||
|
}
|
||||||
|
Ok((rest, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
mod oid {
|
||||||
|
// Extensions
|
||||||
|
pub const CERT_KEY_USAGE: &'static [u8] = &[85, 29, 15]; // 2.5.29.15
|
||||||
|
pub const CERT_POLICIES: &'static [u8] = &[85, 29, 32]; // 2.5.29.32
|
||||||
|
pub const CERT_BASIC_CONSTRAINTS: &'static [u8] = &[85, 29, 19]; // 2.5.29.19
|
||||||
|
pub const CERT_EXT_KEY_USAGE: &'static [u8] = &[85, 29, 37]; // 2.5.29.37
|
||||||
|
pub const CERT_INHIBIT_ANY_POLICY: &'static [u8] = &[85, 29, 54]; // 2.5.29.54
|
||||||
|
// Extended Key Extensions
|
||||||
|
pub const ANY_EXTENDED_KEY_USAGE: &'static [u8] = &[85, 29, 37, 0]; // 2.5.29.37.0
|
||||||
|
pub const ID_KP_SERVER_AUTH: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 1]; // 1.3.6.1.5.5.7.3.1
|
||||||
|
pub const ID_KP_CLIENT_AUTH: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 2]; // 1.3.6.1.5.5.7.3.2
|
||||||
|
pub const ID_KP_CODE_SIGNING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 3]; // 1.3.6.1.5.5.7.3.3
|
||||||
|
pub const ID_KP_EMAIL_PROTECTION: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 4]; // 1.3.6.1.5.5.7.3.4
|
||||||
|
pub const ID_KP_TIME_STAMPING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 8]; // 1.3.6.1.5.5.7.3.8
|
||||||
|
pub const ID_KP_OCSP_SIGNING: &'static [u8] = &[43, 6, 1, 5, 5, 7, 3, 9]; // 1.3.6.1.5.5.7.3.9
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user