diff --git a/crates/formats/src/der.rs b/crates/formats/src/der.rs index 587900ad..ed7cc50e 100644 --- a/crates/formats/src/der.rs +++ b/crates/formats/src/der.rs @@ -88,10 +88,19 @@ pub fn children(data: &[u8], parent: &Tlv) -> Vec { /// Dotted form of an OBJECT IDENTIFIER's content. pub fn oid(content: &[u8]) -> String { + if content.is_empty() || content.len() > 64 { + return "invalid".into(); + } let mut parts: Vec = Vec::new(); let mut v: u64 = 0; let mut first = true; - for &b in content.iter().take(64) { + let mut arc_start = true; + for &b in content { + // DER subidentifiers use the shortest base-128 encoding. + if arc_start && b == 0x80 { + return "invalid".into(); + } + arc_start = b & 0x80 == 0; v = match v.checked_mul(128) { Some(x) => x | u64::from(b & 0x7f), None => return "invalid".into(), @@ -114,6 +123,9 @@ pub fn oid(content: &[u8]) -> String { v = 0; } } + if !arc_start { + return "invalid".into(); + } parts.join(".") } diff --git a/crates/formats/tests/der_oid_validation.rs b/crates/formats/tests/der_oid_validation.rs new file mode 100644 index 00000000..cecdcb68 --- /dev/null +++ b/crates/formats/tests/der_oid_validation.rs @@ -0,0 +1,28 @@ +use craft_anything_formats::der::{oid, oid_name}; + +#[test] +fn unfinished_oid_does_not_alias_a_known_algorithm() { + let mut algorithm = vec![0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 1, 1, 0x0b]; + assert_eq!(oid_name(&oid(&algorithm)), Some("sha256WithRSAEncryption")); + algorithm.push(0x80); + assert_eq!(oid(&algorithm), "invalid"); + assert!(oid_name(&oid(&algorithm)).is_none()); +} + +#[test] +fn malformed_and_oversized_oids_are_not_returned_as_prefixes() { + assert_eq!(oid(&[]), "invalid"); + assert_eq!(oid(&[0x80, 0x2a]), "invalid"); + assert_eq!(oid(&[0x2a, 0x80, 0]), "invalid"); + assert_eq!(oid(&[0; 65]), "invalid"); + assert_eq!(oid(&[0xff; 11]), "invalid"); +} + +#[test] +fn complete_oid_arcs_and_the_length_boundary_remain_supported() { + assert_eq!(oid(&[0x88, 0x37, 3]), "2.999.3"); + let mut boundary = vec![0x2a]; + boundary.resize(64, 0); + assert!(oid(&boundary).starts_with("1.2.0.0.")); + assert_eq!(oid(&boundary).split('.').count(), 65); +}