diff --git a/catalyst-gateway-crates/c509-certificate/src/cbor_encoder.rs b/catalyst-gateway-crates/c509-certificate/src/cbor_encoder.rs index 7ea5ca7cbf..59cdc254a7 100644 --- a/catalyst-gateway-crates/c509-certificate/src/cbor_encoder.rs +++ b/catalyst-gateway-crates/c509-certificate/src/cbor_encoder.rs @@ -1,12 +1,30 @@ use hex::FromHexError; use minicbor::Encoder; +use oid::ObjectIdentifier; use regex::Regex; use thiserror::Error; use crate::c509_enum::AttributesRegistry; -trait CborEncoder { - fn encoder(&self, encoder: &mut Encoder<&mut Vec>); +pub(crate) trait CborEncoder { + fn encode(&self, encoder: &mut Encoder<&mut Vec>); + + fn encode_string(&self, encoder: &mut Encoder<&mut Vec>, s: &str) { + let _unused = encoder.str(s); + } + + fn encode_bytes(&self, encoder: &mut Encoder<&mut Vec>, b: &[u8]) { + let _unused = encoder.bytes(b); + } + + fn encode_oid(&self, encoder: &mut Encoder<&mut Vec>, s: &str) { + let oid = match ObjectIdentifier::try_from(s) { + Ok(oid) => oid, + Err(_) => return, + }; + let oid: Vec = (&oid).into(); + let _unused = encoder.bytes(&oid); + } } // --------------------------------------------------- @@ -17,7 +35,7 @@ pub enum UnwrappedBiguint { } impl CborEncoder for UnwrappedBiguint { - fn encoder(&self, encoder: &mut Encoder<&mut Vec>) { + fn encode(&self, encoder: &mut Encoder<&mut Vec>) { match self { UnwrappedBiguint::U64Value(u) => { // Convert the integer to bytes @@ -30,7 +48,8 @@ impl CborEncoder for UnwrappedBiguint { .collect::>(); // Encode the significant bytes as a byte string in CBOR format - let _unused = encoder.bytes(&significant_bytes); + // FIXME - Is it better to just use encoder.bytes(&significant_bytes)? + self.encode_bytes(encoder, &significant_bytes); }, } } @@ -38,16 +57,17 @@ impl CborEncoder for UnwrappedBiguint { // --------------------------------------------------- +const NOEXPDATE: u64 = 99991231235959; pub type Time = u64; impl CborEncoder for Time { - fn encoder(&self, encoder: &mut Encoder<&mut Vec>) { + fn encode(&self, encoder: &mut Encoder<&mut Vec>) { // The value "99991231235959Z" (no expiration date) is encoded as CBOR null // Also add an option for using 0 as no expiration date - if *self == 0 || *self == 99991231235959 { - let _unused_exp = encoder.null(); + if *self == 0 || *self == NOEXPDATE { + let _unused = encoder.null(); } else { - let _unused_time = encoder.u64(*self); + let _unused = encoder.u64(*self); } } } @@ -77,16 +97,16 @@ impl CborEncoder for Name { /// Encode type Name /// Since it is currently support only natively signed C509 certificates, /// all text strings are UTF-8 encoded and all attributeType SHALL be non-negative - fn encoder(&self, encoder: &mut Encoder<&mut Vec>) { + fn encode(&self, encoder: &mut Encoder<&mut Vec>) { // If type Name contain single attribute common-name if self.len() == 1 && self[0].name == AttributesRegistry::CommonName { let _unused = encode_common_name_cn(&self[0].value.str_value, encoder); } else { - let _unused_arr = encoder.array(self.len() as u64 * 2); + let _unused = encoder.array(self.len() as u64 * 2); // a (CBOR int, CBOR text string) pair, for data in self { - let _unused_u8 = encoder.u8(data.name as u8); - let _unused_str = encoder.str(&data.value.str_value); + let _unused = encoder.u8(data.name as u8); + let _unused = encoder.str(&data.value.str_value); } } } @@ -141,7 +161,7 @@ mod test_cbor_encoder { let mut buffer: Vec = Vec::new(); let mut encoder: Encoder<&mut Vec> = Encoder::new(&mut buffer); let number = UnwrappedBiguint::U64Value(128269); - number.encoder(&mut encoder); + number.encode(&mut encoder); let result = hex::encode(buffer); assert_eq!(result, "4301f50d"); } @@ -151,9 +171,9 @@ mod test_cbor_encoder { let mut buffer: Vec = Vec::new(); let mut encoder: Encoder<&mut Vec> = Encoder::new(&mut buffer); let time: Time = 1672531200; - time.encoder(&mut encoder); + time.encode(&mut encoder); let exp_time: Time = 99991231235959; - exp_time.encoder(&mut encoder); + exp_time.encode(&mut encoder); assert_eq!(hex::encode(buffer), "1a63b0cd00f6"); } @@ -168,7 +188,7 @@ mod test_cbor_encoder { str_value: "RFC test CA".to_string(), }, }]; - name1.encoder(&mut encoder); + name1.encode(&mut encoder); let name2: Name = vec![RelativeDistinguishedName { name: AttributesRegistry::CommonName, @@ -177,8 +197,8 @@ mod test_cbor_encoder { str_value: "01-23-45-FF-FE-67-89-AB".to_string(), }, }]; - name2.encoder(&mut encoder); - + name2.encode(&mut encoder); + assert_eq!( hex::encode(buffer), "6b524643207465737420434147010123456789ab" @@ -191,7 +211,7 @@ mod test_cbor_encoder { let mut encoder: Encoder<&mut Vec> = Encoder::new(&mut buffer); // Issuer: C=US, ST=CA, O=Example Inc, OU=certification, CN=802.1AR CA - let name: Name = vec![ + let name: Name = vec![ RelativeDistinguishedName { name: AttributesRegistry::Country, value: AttributeRegistryValue { @@ -228,7 +248,7 @@ mod test_cbor_encoder { }, }, ]; - name.encoder(&mut encoder); + name.encode(&mut encoder); assert_eq!( hex::encode(buffer), "8a0462555306624341086b4578616d706c6520496e63096d63657274696669636174696f6e016a3830322e314152204341" diff --git a/catalyst-gateway-crates/c509-certificate/src/extensions/alt_name.rs b/catalyst-gateway-crates/c509-certificate/src/extensions/alt_name.rs index 29e63b123a..ba99a48ac4 100644 --- a/catalyst-gateway-crates/c509-certificate/src/extensions/alt_name.rs +++ b/catalyst-gateway-crates/c509-certificate/src/extensions/alt_name.rs @@ -1,12 +1,14 @@ +use crate::cbor_encoder::CborEncoder; use minicbor::Encoder; -use oid::prelude::*; +use oid::ObjectIdentifier; use super::is_critical; -// Define the GeneralNames enum +/// Define the GeneralNamesRegistry enum +/// Section 9.9 https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/ #[allow(unused)] #[derive(Debug, PartialEq, Clone)] -enum GeneralNames { +enum GeneralNamesRegistry { OtherNameBundleEID = -3, // eid-structure from RFC 9171 OtherNameSmtpUTF8Mailbox = -2, // text OtherNameHardwareModuleName = -1, // [ ~oid, bytes ] @@ -21,7 +23,7 @@ enum GeneralNames { #[allow(unused)] #[derive(Clone)] -pub(crate) enum GeneralNamesType { +pub(crate) enum GeneralNamesRegistryType { String(String), Bytes(Vec), Oid(String), @@ -36,40 +38,39 @@ pub(crate) struct OtherNameType { hw_serial_num: Vec, } +impl CborEncoder for Vec { + fn encode(&self, encoder: &mut Encoder<&mut Vec>) { + let _unused = encoder.array(self.len() as u64); + + for item in self { + let oid = match ObjectIdentifier::try_from(item.hw_type.as_str()) { + Ok(oid) => oid, + Err(_) => return, + }; + let oid: Vec = (&oid).into(); + let _unused = encoder.bytes(&oid); + let _unused = encoder.bytes(&item.hw_serial_num); + } + } +} + // Define the GeneralName struct #[derive(Clone)] pub(crate) struct GeneralName { - gn_name: GeneralNames, - gn_value: GeneralNamesType, + gn_name: GeneralNamesRegistry, + gn_value: GeneralNamesRegistryType, } -impl GeneralNamesType { +impl CborEncoder for GeneralNamesRegistryType { fn encode(&self, encoder: &mut Encoder<&mut Vec>) { match self { - GeneralNamesType::String(s) => { - encoder.str(s).unwrap(); - }, - GeneralNamesType::Bytes(b) => { - encoder.bytes(b).unwrap(); - }, - GeneralNamesType::Oid(s) => { - let oid = ObjectIdentifier::try_from(s.as_str()).unwrap(); - let oid: Vec = (&oid).into(); - encoder.bytes(&oid).unwrap(); - }, - GeneralNamesType::OidAndBytes(b) => { - encoder.array(b.len() as u64).unwrap(); - for x in b { - let oid = ObjectIdentifier::try_from(x.hw_type.clone()).unwrap(); - let oid: Vec = (&oid).into(); - encoder.bytes(&oid).unwrap(); - encoder.bytes(&x.hw_serial_num).unwrap(); - } - }, + GeneralNamesRegistryType::String(s) => self.encode_string(encoder, s), + GeneralNamesRegistryType::Bytes(b) => self.encode_bytes(encoder, b), + GeneralNamesRegistryType::Oid(s) => self.encode_oid(encoder, s), + GeneralNamesRegistryType::OidAndBytes(b) => b.encode(encoder), } } } - #[allow(unused)] impl GeneralName { fn encode(&self, encoder: &mut Encoder<&mut Vec>) { @@ -82,12 +83,13 @@ impl GeneralName { fn encode_alt_name(b: Vec, critical: bool, encoder: &mut Encoder<&mut Vec>) { // If subjectAltName contains exactly one dNSName, the array and the int // are omitted and extensionValue is the dNSName encoded as a CBOR text string. - if b.len() == 1 && b[0].gn_name == GeneralNames::DNSName { + if b.len() == 1 && b[0].gn_name == GeneralNamesRegistry::DNSName { let c = is_critical(critical); encoder.i16(b[0].gn_name.clone() as i16 * c); b[0].gn_value.encode(encoder); } else { - encoder.array(b.len() as u64); + // A pair of ( hwType, hwSerialNum ) is encoded as a CBOR array of two items. + encoder.array(b.len() as u64 * 2); for gn in b { encoder.i8(gn.gn_name.clone() as i8).unwrap(); gn.gn_value.encode(encoder); @@ -106,8 +108,8 @@ mod tests { let mut buffer = Vec::new(); let mut encoder = Encoder::new(&mut buffer); let general_name = GeneralName { - gn_name: GeneralNames::DNSName, - gn_value: GeneralNamesType::String("example.com".to_string()), + gn_name: GeneralNamesRegistry::DNSName, + gn_value: GeneralNamesRegistryType::String("example.com".to_string()), }; encode_alt_name(vec![general_name], false, &mut encoder); println!("{:?}", hex::encode(buffer)); @@ -120,22 +122,20 @@ mod tests { // value: // hwType: 1.3.6.1.4.1.6175.10.1 // hwSerialNum: 01:02:03:04 - // 3, [-1, [h'2B06010401B01F0A01', h'01020304']] - // 03 82 20 82 49 2B 06 01 04 01 B0 1F 0A 01 44 01 02 03 04 #[test] - fn test_encode_general_name_hw_module_name() { + fn test() { let mut buffer = Vec::new(); let mut encoder = Encoder::new(&mut buffer); let general_name = GeneralName { - gn_name: GeneralNames::OtherNameHardwareModuleName, - gn_value: GeneralNamesType::OidAndBytes(vec![OtherNameType { + gn_name: GeneralNamesRegistry::OtherNameHardwareModuleName, + gn_value: GeneralNamesRegistryType::OidAndBytes(vec![OtherNameType { hw_type: "1.3.6.1.4.1.6175.10.1".to_string(), hw_serial_num: vec![0x01, 0x02, 0x03, 0x04], }]), }; encode_alt_name(vec![general_name], false, &mut encoder); - println!("{:?}", hex::encode(buffer)); + assert_eq!(hex::encode(buffer), "822081492b06010401b01f0a014401020304"); } }