-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor X.509 encoding library to use the der crate
The der crate-based implementation is significantly less code and follows a more consistent pattern. Since this allows defining types with encoding rules, this will make it much simpler to add structures for ML-DSA structures for Caliptra 2.0. The der crate is part of RustCrypto and is intended to be embedded/no_std friendly.
- Loading branch information
Showing
10 changed files
with
1,179 additions
and
1,914 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,298 @@ | ||
use der::{ | ||
Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, | ||
Tag, Writer, | ||
}; | ||
use crate::{ | ||
response::DpeErrorCode, x509::X509Error, | ||
}; | ||
use platform::ArrayVec; | ||
|
||
pub struct RawGeneralizedTimeRef<'a> { | ||
time: &'a [u8], | ||
} | ||
|
||
impl<'a> RawGeneralizedTimeRef<'a> { | ||
/// Length of an RFC 5280-flavored ASN.1 DER-encoded [`GeneralizedTime`]. | ||
const LENGTH: usize = 15; | ||
|
||
pub fn new(bytes: &'a [u8]) -> Result<Self, DpeErrorCode> { | ||
if bytes.len() != Self::LENGTH { | ||
return Err(DpeErrorCode::InternalError); | ||
} | ||
|
||
Ok(Self { time: bytes }) | ||
} | ||
} | ||
|
||
impl EncodeValue for RawGeneralizedTimeRef<'_> { | ||
fn value_len(&self) -> Result<Length, der::Error> { | ||
self.time.len().try_into() | ||
} | ||
|
||
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), der::Error> { | ||
writer.write(self.time)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<'a> DecodeValue<'a> for RawGeneralizedTimeRef<'a> { | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> Result<Self, der::Error> { | ||
let time = reader.read_slice(Self::LENGTH.try_into()?)?; | ||
Ok(Self { time }) | ||
} | ||
} | ||
|
||
impl FixedTag for RawGeneralizedTimeRef<'_> { | ||
const TAG: Tag = Tag::GeneralizedTime; | ||
} | ||
|
||
// Wraps any asn1 encodable/decodable type and encodes/decodes it as an octet | ||
// sring | ||
pub struct OctetStringContainer<T>(pub T); | ||
|
||
impl<'a, T> EncodeValue for OctetStringContainer<T> | ||
where | ||
T: der::Encode + der::Decode<'a>, | ||
{ | ||
fn value_len(&self) -> Result<Length, der::Error> { | ||
self.0.encoded_len() | ||
} | ||
|
||
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), der::Error> { | ||
self.0.encode(writer) | ||
} | ||
} | ||
|
||
impl<'a, T> DecodeValue<'a> for OctetStringContainer<T> | ||
where | ||
T: der::Encode + der::Decode<'a>, | ||
{ | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> Result<Self, der::Error> { | ||
Ok(OctetStringContainer::<T>(T::decode(reader)?)) | ||
} | ||
} | ||
|
||
impl<'a, T> FixedTag for OctetStringContainer<T> | ||
where | ||
T: der::Encode + der::Decode<'a>, | ||
{ | ||
const TAG: Tag = Tag::OctetString; | ||
} | ||
|
||
// Wraps any asn1 encodable/decodable type and encodes/decodes it as an octet | ||
// sring | ||
pub struct BitStringContainer<T>(pub T); | ||
|
||
impl<'a, T> EncodeValue for BitStringContainer<T> | ||
where | ||
T: der::Encode + der::Decode<'a>, | ||
{ | ||
fn value_len(&self) -> Result<Length, der::Error> { | ||
// Add 1 for unused bits | ||
Ok(self.0.encoded_len()?.saturating_add(Length::ONE)) | ||
} | ||
|
||
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), der::Error> { | ||
// Write unused bits | ||
writer.write_byte(0u8)?; | ||
self.0.encode(writer) | ||
} | ||
} | ||
|
||
impl<'a, T> DecodeValue<'a> for BitStringContainer<T> | ||
where | ||
T: der::Encode + der::Decode<'a>, | ||
{ | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> Result<Self, der::Error> { | ||
// Unused bits must be 0 for BitStringContainers. Skip unused bits byte. | ||
reader.read_byte()?; | ||
Ok(BitStringContainer::<T>(T::decode(reader)?)) | ||
} | ||
} | ||
|
||
impl<'a, T> FixedTag for BitStringContainer<T> | ||
where | ||
T: der::Encode + der::Decode<'a>, | ||
{ | ||
const TAG: Tag = Tag::BitString; | ||
} | ||
|
||
/// UncheckedSetOf provides a smaller SET OF implementation than der::SetOf | ||
/// It assumes the caller has already ensured the set items are ordered properly | ||
/// which removes the need to sort the items in the set. | ||
/// | ||
/// DPE certificates generally only have one or two items in SetOf collections, | ||
/// so keeping them manually sorted is trivial. | ||
/// | ||
/// At the time of this writing, this removes ~8KiB from the X.509 | ||
/// implementation. | ||
pub struct UncheckedSetOf<T, const N: usize> { | ||
values: ArrayVec<T, N>, | ||
} | ||
|
||
impl <T, const N: usize> UncheckedSetOf<T, N> { | ||
pub fn new() -> Self { | ||
Self { | ||
values: ArrayVec::default(), | ||
} | ||
} | ||
|
||
pub fn insert(&mut self, val: T) { | ||
self.values.push(val); | ||
} | ||
} | ||
|
||
impl<'a, T, const N: usize> DecodeValue<'a> for UncheckedSetOf<T, N> | ||
where | ||
T: Decode<'a>, | ||
{ | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self, der::Error> { | ||
reader.read_nested(header.length, |reader| { | ||
let mut result = Self::new(); | ||
|
||
while !reader.is_finished() { | ||
result.values.push(T::decode(reader)?); | ||
} | ||
|
||
Ok(result) | ||
}) | ||
} | ||
} | ||
|
||
impl<'a, T, const N: usize> EncodeValue for UncheckedSetOf<T, N> | ||
where | ||
T: 'a + Decode<'a> + Encode, | ||
{ | ||
fn value_len(&self) -> Result<Length, der::Error> { | ||
self.values.iter() | ||
.fold(Ok(Length::ZERO), |len, elem| len + elem.encoded_len()?) | ||
} | ||
|
||
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), der::Error> { | ||
for elem in self.values.iter() { | ||
elem.encode(writer)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl<'a, T, const N: usize> FixedTag for UncheckedSetOf<T, N> | ||
where | ||
T: Decode<'a>, | ||
{ | ||
const TAG: Tag = Tag::Set; | ||
} | ||
|
||
pub struct RawDerSequenceRef<'a> { | ||
val: &'a [u8], | ||
} | ||
|
||
impl<'a> RawDerSequenceRef<'a> { | ||
pub fn new(data: &'a [u8]) -> Result<Self, DpeErrorCode> { | ||
// Skip header | ||
let mut reader = der::SliceReader::new(data) | ||
.map_err(|_| DpeErrorCode::from(X509Error::InvalidRawDer))?; | ||
let header = Header::decode(&mut reader) | ||
.map_err(|_| DpeErrorCode::from(X509Error::InvalidRawDer))?; | ||
let len: usize = header | ||
.length | ||
.try_into() | ||
.map_err(|_| DpeErrorCode::from(X509Error::InvalidRawDer))?; | ||
let offset = reader | ||
.position() | ||
.try_into() | ||
.map_err(|_| DpeErrorCode::from(X509Error::InvalidRawDer))?; | ||
|
||
Ok(Self { | ||
val: &data[offset..offset + len], | ||
}) | ||
} | ||
} | ||
|
||
impl<'a> EncodeValue for RawDerSequenceRef<'a> { | ||
fn value_len(&self) -> Result<Length, der::Error> { | ||
self.val.len().try_into() | ||
} | ||
|
||
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), der::Error> { | ||
writer.write(self.val)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<'a> DecodeValue<'a> for RawDerSequenceRef<'a> { | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self, der::Error> { | ||
let val = reader.read_slice(header.length)?; | ||
Ok(Self { val }) | ||
} | ||
} | ||
|
||
impl<'a> FixedTag for RawDerSequenceRef<'a> { | ||
const TAG: Tag = Tag::Sequence; | ||
} | ||
|
||
// Saves a few-hundred bytes over using der crate PrintableStringRef | ||
pub struct UncheckedPrintableStringRef<'a> { | ||
s: &'a [u8], | ||
} | ||
|
||
impl<'a> UncheckedPrintableStringRef<'a> { | ||
pub fn new(s: &'a [u8]) -> Self { | ||
Self{ | ||
s, | ||
} | ||
} | ||
} | ||
|
||
impl<'a> EncodeValue for UncheckedPrintableStringRef<'a> { | ||
fn value_len(&self) -> Result<Length, der::Error> { | ||
// PANIC FREE: Values guaranteed to be less than u16 max | ||
Ok(self.s.len().try_into().unwrap()) | ||
} | ||
|
||
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), der::Error> { | ||
writer.write(self.s)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<'a> DecodeValue<'a> for UncheckedPrintableStringRef<'a> { | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self, der::Error> { | ||
let s = reader.read_slice(header.length)?; | ||
Ok(Self { s }) | ||
} | ||
} | ||
|
||
impl<'a> FixedTag for UncheckedPrintableStringRef<'a> { | ||
const TAG: Tag = Tag::PrintableString; | ||
} | ||
|
||
pub struct U32OctetString(u32); | ||
|
||
impl U32OctetString { | ||
const LENGTH: usize = 4; | ||
} | ||
|
||
impl FixedTag for U32OctetString { | ||
const TAG: Tag = Tag::OctetString; | ||
} | ||
|
||
impl EncodeValue for U32OctetString { | ||
fn value_len(&self) -> Result<Length, der::Error> { | ||
Self::LENGTH.try_into() | ||
} | ||
|
||
fn encode_value(&self, writer: &mut impl Writer) -> Result<(), der::Error> { | ||
writer.write(&self.0.to_be_bytes())?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<'a> DecodeValue<'a> for U32OctetString { | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> Result<Self, der::Error> { | ||
let val = reader.read_slice(Self::LENGTH.try_into()?)?; | ||
// PANIC FREE: val is guaranteed to be 4 bytes | ||
Ok(Self(u32::from_be_bytes(val.try_into().unwrap()))) | ||
} | ||
} |
Oops, something went wrong.