use alloc::string::String;
use alloc::vec::Vec;
use super::oid;
use crate::der::{
encode_boolean, encode_context, encode_integer, encode_octet_string, encode_sequence,
encode_tlv, oid_tlv, tag,
};
use crate::hash::{Digest, Sha1};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Extension {
pub oid: Vec<u64>,
pub critical: bool,
pub value: Vec<u8>,
}
impl Extension {
pub(crate) fn to_der(&self) -> Vec<u8> {
let mut body = oid_tlv(&self.oid);
if self.critical {
body.extend_from_slice(&encode_boolean(true));
}
body.extend_from_slice(&encode_octet_string(&self.value));
encode_sequence(&body)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GeneralName {
Dns(String),
IpV4([u8; 4]),
IpV6([u8; 16]),
Email(String),
Uri(String),
}
impl GeneralName {
pub(crate) fn to_der(&self) -> Vec<u8> {
match self {
GeneralName::Email(s) => encode_tlv(0x81, s.as_bytes()),
GeneralName::Dns(s) => encode_tlv(0x82, s.as_bytes()),
GeneralName::Uri(s) => encode_tlv(0x86, s.as_bytes()),
GeneralName::IpV4(ip) => encode_tlv(0x87, ip),
GeneralName::IpV6(ip) => encode_tlv(0x87, ip),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct KeyUsageBits(pub u16);
impl KeyUsageBits {
pub const DIGITAL_SIGNATURE: Self = Self(0x80);
pub const NON_REPUDIATION: Self = Self(0x40);
pub const KEY_ENCIPHERMENT: Self = Self(0x20);
pub const DATA_ENCIPHERMENT: Self = Self(0x10);
pub const KEY_AGREEMENT: Self = Self(0x08);
pub const KEY_CERT_SIGN: Self = Self(0x04);
pub const CRL_SIGN: Self = Self(0x02);
pub const ENCIPHER_ONLY: Self = Self(0x01);
pub const DECIPHER_ONLY: Self = Self(0x80_00);
pub fn empty() -> Self {
Self(0)
}
pub fn contains(&self, other: Self) -> bool {
self.0 & other.0 == other.0
}
pub fn insert(&mut self, other: Self) {
self.0 |= other.0;
}
pub fn is_empty(&self) -> bool {
self.0 == 0
}
}
impl core::ops::BitOr for KeyUsageBits {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl core::ops::BitOrAssign for KeyUsageBits {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
pub fn basic_constraints(is_ca: bool, path_len: Option<u32>) -> Extension {
let mut body = Vec::new();
if is_ca {
body.extend_from_slice(&encode_boolean(true));
if let Some(n) = path_len {
let be = n.to_be_bytes();
body.extend_from_slice(&encode_integer(&be));
}
}
Extension {
oid: oid::BASIC_CONSTRAINTS.to_vec(),
critical: true,
value: encode_sequence(&body),
}
}
pub fn key_usage(bits: KeyUsageBits) -> Extension {
let mut value = Vec::with_capacity(4);
let mask = bits.0;
let bytes = if mask & 0xff00 != 0 {
[(mask & 0xff) as u8, (mask >> 8) as u8].to_vec()
} else if mask & 0x00ff != 0 {
[(mask & 0xff) as u8].to_vec()
} else {
Vec::new()
};
let unused = if bytes.is_empty() {
0u8
} else {
let last = *bytes.last().unwrap();
let mut u = 0u8;
let mut b = last;
while b & 1 == 0 && u < 8 {
u += 1;
b >>= 1;
}
u
};
value.push(unused);
value.extend_from_slice(&bytes);
let bs = encode_tlv(tag::BIT_STRING, &value);
Extension {
oid: oid::KEY_USAGE.to_vec(),
critical: true,
value: bs,
}
}
pub fn extended_key_usage(oids: &[&[u64]]) -> Extension {
let mut body = Vec::new();
for o in oids {
body.extend_from_slice(&oid_tlv(o));
}
Extension {
oid: oid::EXT_KEY_USAGE.to_vec(),
critical: false,
value: encode_sequence(&body),
}
}
pub fn subject_alt_name(names: &[GeneralName]) -> Extension {
let mut body = Vec::new();
for n in names {
body.extend_from_slice(&n.to_der());
}
Extension {
oid: oid::SUBJECT_ALT_NAME.to_vec(),
critical: false,
value: encode_sequence(&body),
}
}
pub fn subject_key_identifier(spki_bit_string_contents: &[u8]) -> Extension {
let hash = Sha1::digest(spki_bit_string_contents);
Extension {
oid: oid::SUBJECT_KEY_IDENTIFIER.to_vec(),
critical: false,
value: encode_octet_string(&hash),
}
}
pub fn subject_key_identifier_raw(ski: &[u8]) -> Extension {
Extension {
oid: oid::SUBJECT_KEY_IDENTIFIER.to_vec(),
critical: false,
value: encode_octet_string(ski),
}
}
pub fn authority_key_identifier(issuer_ski: &[u8]) -> Extension {
let ki = encode_tlv(0x80, issuer_ski);
Extension {
oid: oid::AUTHORITY_KEY_IDENTIFIER.to_vec(),
critical: false,
value: encode_sequence(&ki),
}
}
pub fn name_constraints(permitted: &[GeneralName], excluded: &[GeneralName]) -> Extension {
let mut body = Vec::new();
if !permitted.is_empty() {
let subtrees = encode_subtrees(permitted);
body.extend_from_slice(&encode_tlv(tag::context(0), &subtrees));
}
if !excluded.is_empty() {
let subtrees = encode_subtrees(excluded);
body.extend_from_slice(&encode_tlv(tag::context(1), &subtrees));
}
Extension {
oid: oid::NAME_CONSTRAINTS.to_vec(),
critical: true,
value: encode_sequence(&body),
}
}
fn encode_subtrees(names: &[GeneralName]) -> Vec<u8> {
let mut out = Vec::new();
for n in names {
let subtree = encode_sequence(&n.to_der());
out.extend_from_slice(&subtree);
}
out
}
pub fn certificate_policies(policy_oids: &[&[u64]]) -> Extension {
let mut body = Vec::new();
for o in policy_oids {
body.extend_from_slice(&encode_sequence(&oid_tlv(o)));
}
Extension {
oid: oid::CERTIFICATE_POLICIES.to_vec(),
critical: false,
value: encode_sequence(&body),
}
}
pub fn crl_distribution_points(urls: &[&str]) -> Extension {
let mut body = Vec::new();
for url in urls {
let gn = encode_tlv(0x86, url.as_bytes());
let gnames = encode_sequence(&gn);
let dpn = encode_tlv(tag::context(0), &gnames);
let dp_inner = encode_context(0, &dpn);
body.extend_from_slice(&encode_sequence(&dp_inner));
}
Extension {
oid: oid::CRL_DISTRIBUTION_POINTS.to_vec(),
critical: false,
value: encode_sequence(&body),
}
}
pub(crate) fn encode_extensions_field(exts: &[Extension]) -> Vec<u8> {
let mut body = Vec::new();
for e in exts {
body.extend_from_slice(&e.to_der());
}
encode_context(3, &encode_sequence(&body))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::der::{Reader, parse_oid};
use alloc::vec;
#[test]
fn basic_constraints_ca_false() {
let ext = basic_constraints(false, None);
assert!(ext.critical);
assert_eq!(ext.oid, oid::BASIC_CONSTRAINTS);
assert_eq!(ext.value, &[0x30, 0x00]);
}
#[test]
fn basic_constraints_ca_with_path_len() {
let ext = basic_constraints(true, Some(2));
assert!(ext.critical);
let mut r = Reader::new(&ext.value);
let mut seq = r.read_sequence().unwrap();
let b = seq.read_boolean().unwrap();
assert!(b);
let i = seq.read_integer_bytes().unwrap();
assert_eq!(i, &[2]);
}
#[test]
fn basic_constraints_ca_path_len_high_bit() {
let ext = basic_constraints(true, Some(0xff));
let mut r = Reader::new(&ext.value);
let mut seq = r.read_sequence().unwrap();
let _ = seq.read_boolean().unwrap();
let i = seq.read_unsigned_integer_bytes().unwrap();
assert_eq!(i, &[0x00, 0xff]);
}
#[test]
fn key_usage_digital_signature_plus_key_encipherment() {
let bits = KeyUsageBits::DIGITAL_SIGNATURE | KeyUsageBits::KEY_ENCIPHERMENT;
let ext = key_usage(bits);
assert!(ext.critical);
assert_eq!(ext.value, &[0x03, 0x02, 0x05, 0xA0]);
}
#[test]
fn key_usage_key_cert_sign_plus_crl_sign() {
let bits = KeyUsageBits::KEY_CERT_SIGN | KeyUsageBits::CRL_SIGN;
let ext = key_usage(bits);
assert_eq!(ext.value, &[0x03, 0x02, 0x01, 0x06]);
}
#[test]
fn key_usage_decipher_only_uses_second_byte() {
let bits = KeyUsageBits::DIGITAL_SIGNATURE | KeyUsageBits::DECIPHER_ONLY;
let ext = key_usage(bits);
assert_eq!(ext.value, &[0x03, 0x03, 0x07, 0x80, 0x80]);
}
#[test]
fn extended_key_usage_server_and_client() {
let ext = extended_key_usage(&[oid::ID_KP_SERVER_AUTH, oid::ID_KP_CLIENT_AUTH]);
assert!(!ext.critical);
let mut r = Reader::new(&ext.value);
let mut seq = r.read_sequence().unwrap();
let a = parse_oid(seq.read_oid().unwrap()).unwrap();
let b = parse_oid(seq.read_oid().unwrap()).unwrap();
assert_eq!(a, oid::ID_KP_SERVER_AUTH);
assert_eq!(b, oid::ID_KP_CLIENT_AUTH);
}
#[test]
fn subject_alt_name_round_trip() {
let names = [
GeneralName::Dns("example.com".into()),
GeneralName::IpV4([10, 0, 0, 1]),
GeneralName::Email("admin@example.com".into()),
GeneralName::Uri("https://example.com/x".into()),
];
let ext = subject_alt_name(&names);
assert!(!ext.critical);
let mut r = Reader::new(&ext.value);
let mut seq = r.read_sequence().unwrap();
let (t1, v1) = seq.read_any().unwrap();
assert_eq!(t1, 0x82);
assert_eq!(v1, b"example.com");
let (t2, v2) = seq.read_any().unwrap();
assert_eq!(t2, 0x87);
assert_eq!(v2, &[10, 0, 0, 1]);
let (t3, v3) = seq.read_any().unwrap();
assert_eq!(t3, 0x81);
assert_eq!(v3, b"admin@example.com");
let (t4, v4) = seq.read_any().unwrap();
assert_eq!(t4, 0x86);
assert_eq!(v4, b"https://example.com/x");
}
#[test]
fn ski_matches_rfc5280_method1() {
let ext = subject_key_identifier(b"");
assert_eq!(ext.value[0], 0x04);
assert_eq!(ext.value[1], 20);
assert_eq!(
&ext.value[2..],
&[
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60,
0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
]
);
}
#[test]
fn aki_round_trip() {
let ski = [0xAAu8; 20];
let ext = authority_key_identifier(&ski);
assert!(!ext.critical);
assert_eq!(ext.value[0], 0x30);
assert_eq!(ext.value[1], 0x16);
assert_eq!(ext.value[2], 0x80);
assert_eq!(ext.value[3], 0x14);
assert_eq!(&ext.value[4..], &ski);
}
#[test]
fn name_constraints_permitted_only() {
let permitted = [GeneralName::Dns(".internal".into())];
let ext = name_constraints(&permitted, &[]);
assert!(ext.critical);
let mut r = Reader::new(&ext.value);
let mut seq = r.read_sequence().unwrap();
let (t, _) = seq.read_any().unwrap();
assert_eq!(t, tag::context(0)); }
#[test]
fn name_constraints_excluded_only() {
let excluded = [GeneralName::Dns(".bad".into())];
let ext = name_constraints(&[], &excluded);
let mut r = Reader::new(&ext.value);
let mut seq = r.read_sequence().unwrap();
let (t, _) = seq.read_any().unwrap();
assert_eq!(t, tag::context(1)); }
#[test]
fn certificate_policies_emits_one_per_oid() {
let p = certificate_policies(&[&[2, 23, 140, 1, 2, 1], &[2, 23, 140, 1, 2, 2]]);
assert!(!p.critical);
let mut r = Reader::new(&p.value);
let mut seq = r.read_sequence().unwrap();
let mut p1 = seq.read_sequence().unwrap();
let _ = parse_oid(p1.read_oid().unwrap()).unwrap();
let mut p2 = seq.read_sequence().unwrap();
let _ = parse_oid(p2.read_oid().unwrap()).unwrap();
assert!(seq.is_empty());
}
#[test]
fn crldp_single_url() {
let ext = crl_distribution_points(&["http://crl.example/r.crl"]);
assert!(!ext.critical);
let mut r = Reader::new(&ext.value);
let mut outer = r.read_sequence().unwrap();
let mut dp = outer.read_sequence().unwrap();
let dpn_explicit = dp.read_tlv(tag::context(0)).unwrap();
let mut dpn = Reader::new(dpn_explicit);
let names = dpn.read_tlv(tag::context(0)).unwrap();
let mut gnames = Reader::new(names);
let mut gns = gnames.read_sequence().unwrap();
let (t, v) = gns.read_any().unwrap();
assert_eq!(t, 0x86);
assert_eq!(v, b"http://crl.example/r.crl");
}
#[test]
fn encode_extensions_field_wraps_explicit() {
let exts = vec![basic_constraints(false, None)];
let der = encode_extensions_field(&exts);
assert_eq!(der[0], tag::context(3));
}
}