use alloc::vec;
use alloc::vec::Vec;
pub(crate) fn memory() -> structured_asn1::memory::Memory {
structured_asn1::memory::Memory::new(alloc::sync::Arc::new(
structured_asn1::memory::MemoryAccount::new(256 * 1024 * 1024, 1_000_000_000),
))
}
pub fn tlv(tag: u8, contents: &[u8]) -> Vec<u8> {
let mut out = vec![tag];
let length = contents.len();
if let Ok(short) = u8::try_from(length)
&& short < 0x80
{
out.push(short);
} else {
let bytes = length.to_be_bytes();
let significant = bytes.iter().position(|byte| *byte != 0).unwrap_or(0);
let bytes = &bytes[significant..];
let count = u8::try_from(bytes.len()).expect("a length is at most eight bytes");
out.push(0x80 | count);
out.extend_from_slice(bytes);
}
out.extend_from_slice(contents);
out
}
pub fn cat(parts: &[Vec<u8>]) -> Vec<u8> {
parts.iter().flatten().copied().collect()
}
pub fn seq(parts: &[Vec<u8>]) -> Vec<u8> {
tlv(0x30, &cat(parts))
}
pub fn set(parts: &[Vec<u8>]) -> Vec<u8> {
tlv(0x31, &cat(parts))
}
pub fn context(number: u8, parts: &[Vec<u8>]) -> Vec<u8> {
tlv(0xa0 | number, &cat(parts))
}
pub fn implicit(number: u8, contents: &[u8]) -> Vec<u8> {
tlv(0x80 | number, contents)
}
pub fn int(value: u32) -> Vec<u8> {
let bytes = value.to_be_bytes();
let significant = bytes.iter().position(|byte| *byte != 0).unwrap_or(3);
let mut contents = bytes[significant..].to_vec();
if contents.first().is_some_and(|first| first & 0x80 != 0) {
contents.insert(0, 0x00);
}
tlv(0x02, &contents)
}
pub fn oid(identifier: structured_asn1::Oid<'_>) -> Vec<u8> {
tlv(0x06, identifier.as_bytes())
}
pub fn bits(contents: &[u8]) -> Vec<u8> {
tlv(0x03, &cat(&[vec![0x00], contents.to_vec()]))
}
pub fn octets(contents: &[u8]) -> Vec<u8> {
tlv(0x04, contents)
}
pub fn boolean(value: bool) -> Vec<u8> {
tlv(0x01, &[if value { 0xff } else { 0x00 }])
}
pub fn null() -> Vec<u8> {
tlv(0x05, &[])
}
pub fn utf8(text: &str) -> Vec<u8> {
tlv(0x0c, text.as_bytes())
}
pub fn printable(text: &str) -> Vec<u8> {
tlv(0x13, text.as_bytes())
}
pub fn ia5(text: &str) -> Vec<u8> {
tlv(0x16, text.as_bytes())
}
pub fn utc_time(text: &str) -> Vec<u8> {
tlv(0x17, text.as_bytes())
}
pub fn generalized_time(text: &str) -> Vec<u8> {
tlv(0x18, text.as_bytes())
}
pub fn extension(kind: structured_asn1::Oid<'_>, critical: bool, value: &[u8]) -> Vec<u8> {
let mut parts = vec![oid(kind)];
if critical {
parts.push(boolean(true));
}
parts.push(octets(value));
seq(&parts)
}
pub fn algorithm(kind: structured_asn1::Oid<'_>) -> Vec<u8> {
seq(&[oid(kind), null()])
}
pub struct CertificateParts {
pub version: Option<u32>,
pub serial: Vec<u8>,
pub inner_algorithm: Vec<u8>,
pub issuer: Vec<u8>,
pub validity: Vec<u8>,
pub subject: Vec<u8>,
pub key: Vec<u8>,
pub issuer_unique_id: Option<Vec<u8>>,
pub subject_unique_id: Option<Vec<u8>>,
pub extensions: Option<Vec<u8>>,
pub outer_algorithm: Vec<u8>,
pub signature: Vec<u8>,
}
impl Default for CertificateParts {
fn default() -> Self {
let name =
|common: &str| seq(&[set(&[seq(&[oid(crate::oid::COMMON_NAME), utf8(common)])])]);
Self {
version: Some(2),
serial: int(0x0102_0304),
inner_algorithm: algorithm(crate::oid::RSA_ENCRYPTION),
issuer: name("Test Issuer"),
validity: seq(&[utc_time("240101000000Z"), utc_time("260101000000Z")]),
subject: name("Test Subject"),
key: seq(&[
algorithm(crate::oid::RSA_ENCRYPTION),
bits(&[0xde, 0xad, 0xbe, 0xef]),
]),
issuer_unique_id: None,
subject_unique_id: None,
extensions: Some(seq(&[extension(
crate::oid::BASIC_CONSTRAINTS,
true,
&seq(&[boolean(true)]),
)])),
outer_algorithm: algorithm(crate::oid::RSA_ENCRYPTION),
signature: bits(&[0x5f, 0x1c]),
}
}
}
impl CertificateParts {
pub fn tbs(&self) -> Vec<u8> {
let mut parts = Vec::new();
if let Some(version) = self.version {
parts.push(context(0, &[int(version)]));
}
parts.push(self.serial.clone());
parts.push(self.inner_algorithm.clone());
parts.push(self.issuer.clone());
parts.push(self.validity.clone());
parts.push(self.subject.clone());
parts.push(self.key.clone());
if let Some(identifier) = &self.issuer_unique_id {
parts.push(implicit(1, identifier));
}
if let Some(identifier) = &self.subject_unique_id {
parts.push(implicit(2, identifier));
}
if let Some(extensions) = &self.extensions {
parts.push(context(3, core::slice::from_ref(extensions)));
}
seq(&parts)
}
pub fn build(&self) -> Vec<u8> {
seq(&[
self.tbs(),
self.outer_algorithm.clone(),
self.signature.clone(),
])
}
}
pub fn with_extension<R>(value: Vec<u8>, read: impl FnOnce(&crate::Extensions<'_>) -> R) -> R {
let der = CertificateParts {
extensions: Some(seq(&[value])),
..CertificateParts::default()
}
.build();
let parser = structured_asn1::Parser::der(crate::fixtures::memory());
let certificate = crate::Certificate::read(&parser, &der, 0).unwrap();
read(certificate.tbs().extensions())
}
macro_rules! extension_error {
($value:expr, $accessor:ident) => {
crate::fixtures::with_extension($value, |list| list.$accessor(list.memory()).unwrap_err())
};
}
pub(crate) use extension_error;