-
Notifications
You must be signed in to change notification settings - Fork 147
Multiple domain components in issuer #301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -33,7 +33,6 @@ println!("{}", key_pair.serialize_pem()); | |||||
| #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] | ||||||
| #![warn(unreachable_pub)] | ||||||
|
|
||||||
| use std::collections::HashMap; | ||||||
| use std::fmt; | ||||||
| use std::hash::Hash; | ||||||
| use std::net::IpAddr; | ||||||
|
|
@@ -299,8 +298,7 @@ See also the RFC 5280 sections on the [issuer](https://tools.ietf.org/html/rfc52 | |||||
| and [subject](https://tools.ietf.org/html/rfc5280#section-4.1.2.6) fields. | ||||||
| */ | ||||||
| pub struct DistinguishedName { | ||||||
| entries: HashMap<DnType, DnValue>, | ||||||
| order: Vec<DnType>, | ||||||
| entries: Vec<(DnType, DnValue)>, | ||||||
| } | ||||||
|
|
||||||
| impl DistinguishedName { | ||||||
|
|
@@ -309,20 +307,22 @@ impl DistinguishedName { | |||||
| Self::default() | ||||||
| } | ||||||
| /// Obtains the attribute value for the given attribute type | ||||||
| pub fn get(&self, ty: &DnType) -> Option<&DnValue> { | ||||||
| self.entries.get(ty) | ||||||
| pub fn get(&self, ty: &DnType) -> Vec<&DnValue> { | ||||||
| self.entries | ||||||
| .iter() | ||||||
| .filter_map(|(dn_type, dn_value)| if ty == dn_type { Some(dn_value) } else { None }) | ||||||
| .collect() | ||||||
| } | ||||||
| /// Removes the attribute with the specified DnType | ||||||
| /// Removes all attributes with the specified DnType | ||||||
| /// | ||||||
| /// Returns true when an actual removal happened, false | ||||||
| /// when no attribute with the specified DnType was | ||||||
| /// found. | ||||||
| pub fn remove(&mut self, ty: DnType) -> bool { | ||||||
| let removed = self.entries.remove(&ty).is_some(); | ||||||
| if removed { | ||||||
| self.order.retain(|ty_o| &ty != ty_o); | ||||||
| } | ||||||
| removed | ||||||
| let prev_len = self.entries.len(); | ||||||
| self.entries.retain(|(dn_type, _dn_val)| dn_type != &ty); | ||||||
|
|
||||||
| prev_len != self.entries.len() | ||||||
| } | ||||||
| /// Inserts or updates an attribute that consists of type and name | ||||||
| /// | ||||||
|
|
@@ -331,20 +331,41 @@ impl DistinguishedName { | |||||
| /// let mut dn = DistinguishedName::new(); | ||||||
| /// dn.push(DnType::OrganizationName, "Crab widgits SE"); | ||||||
| /// dn.push(DnType::CommonName, DnValue::PrintableString("Master Cert".try_into().unwrap())); | ||||||
| /// assert_eq!(dn.get(&DnType::OrganizationName), Some(&DnValue::Utf8String("Crab widgits SE".to_string()))); | ||||||
| /// assert_eq!(dn.get(&DnType::CommonName), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap()))); | ||||||
| /// assert_eq!(dn.get(&DnType::OrganizationName).get(0), Some(&DnValue::Utf8String("Crab widgits SE".to_string())).as_ref()); | ||||||
| /// assert_eq!(dn.get(&DnType::CommonName).get(0), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref()); | ||||||
| /// ``` | ||||||
| pub fn push(&mut self, ty: DnType, s: impl Into<DnValue>) { | ||||||
| if !self.entries.contains_key(&ty) { | ||||||
| self.order.push(ty.clone()); | ||||||
| self.entries.push((ty, s.into())); | ||||||
| } | ||||||
|
|
||||||
| /// Replaces the *first occurrence* of a type with a new value. | ||||||
| /// This is a convenience function to avoid duplicating values. | ||||||
| /// | ||||||
| /// If there are multiple occurrences of a type there is currently no way of changing them besides iterating over the types and values of an existing instance and creating a new instance. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| /// | ||||||
| /// ``` | ||||||
| /// # use rcgen::{DistinguishedName, DnType, DnValue}; | ||||||
| /// let mut dn = DistinguishedName::new(); | ||||||
| /// dn.push(DnType::CommonName, DnValue::PrintableString("Master Cert".try_into().unwrap())); | ||||||
| /// assert_eq!(dn.get(&DnType::CommonName).get(0), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref()); | ||||||
| /// dn.push(DnType::CommonName, DnValue::PrintableString("Other Master Cert".try_into().unwrap())); | ||||||
| /// assert_eq!(dn.get(&DnType::CommonName).get(1), Some(&DnValue::PrintableString("Other Master Cert".try_into().unwrap())).as_ref()); | ||||||
| /// ``` | ||||||
| pub fn replace_or_push(&mut self, ty: DnType, s: impl Into<DnValue>) { | ||||||
| for (dn_type, dn_value) in self.entries.iter_mut() { | ||||||
| if *dn_type == ty { | ||||||
| *dn_value = s.into(); | ||||||
| return; | ||||||
| } | ||||||
| } | ||||||
| self.entries.insert(ty, s.into()); | ||||||
|
|
||||||
| self.push(ty, s) | ||||||
| } | ||||||
|
|
||||||
| /// Iterate over the entries | ||||||
| pub fn iter(&self) -> DistinguishedNameIterator<'_> { | ||||||
djc marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| DistinguishedNameIterator { | ||||||
| distinguished_name: self, | ||||||
| iter: self.order.iter(), | ||||||
| iter: self.entries.iter(), | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -397,17 +418,14 @@ impl DistinguishedName { | |||||
| Iterator over [`DistinguishedName`] entries | ||||||
| */ | ||||||
| pub struct DistinguishedNameIterator<'a> { | ||||||
| distinguished_name: &'a DistinguishedName, | ||||||
| iter: std::slice::Iter<'a, DnType>, | ||||||
| iter: std::slice::Iter<'a, (DnType, DnValue)>, | ||||||
| } | ||||||
|
|
||||||
| impl<'a> Iterator for DistinguishedNameIterator<'a> { | ||||||
| type Item = (&'a DnType, &'a DnValue); | ||||||
|
|
||||||
| fn next(&mut self) -> Option<Self::Item> { | ||||||
| self.iter | ||||||
| .next() | ||||||
| .and_then(|ty| self.distinguished_name.entries.get(ty).map(|v| (ty, v))) | ||||||
| self.iter.next().map(|(key, value)| (key, value)) | ||||||
djc marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -596,7 +614,7 @@ fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) { | |||||
| .write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| { | ||||||
| writer.write_bytes(s.as_bytes()) | ||||||
| }), | ||||||
| DnValue::Utf8String(s) => writer.next().write_utf8_string(s), | ||||||
| DnValue::Utf8String(s) => writer.next().write_utf8_string(s.as_str()), | ||||||
| } | ||||||
| }); | ||||||
| }); | ||||||
|
|
@@ -776,6 +794,83 @@ mod tests { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| #[test] | ||||||
| fn distinguished_name_remove_no_match() { | ||||||
| let mut dn = DistinguishedName::new(); | ||||||
| // Domain Component (DC) | ||||||
| let dc_type = DnType::CustomDnType(vec![0, 9, 2342, 19200300, 100, 1, 25]); | ||||||
|
Comment on lines
+800
to
+801
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems unimportant for the test added in this file that the |
||||||
|
|
||||||
| dn.push( | ||||||
| DnType::CommonName, | ||||||
| DnValue::PrintableString("Master Cert".try_into().unwrap()), | ||||||
| ); | ||||||
|
|
||||||
| let removed = dn.remove(dc_type.clone()); | ||||||
| assert!(!removed); | ||||||
| assert_eq!( | ||||||
| dn.get(&DnType::CommonName).get(0), | ||||||
| Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref() | ||||||
| ); | ||||||
| assert_eq!(dn.entries.len(), 1); | ||||||
| assert_eq!(dn.get(&dc_type).get(0), None); | ||||||
| } | ||||||
|
|
||||||
| #[test] | ||||||
| fn distinguished_name_remove_single_attribute() { | ||||||
| let mut dn = DistinguishedName::new(); | ||||||
| // Domain Component (DC) | ||||||
| let dc_type = DnType::CustomDnType(vec![0, 9, 2342, 19200300, 100, 1, 25]); | ||||||
|
|
||||||
| dn.push( | ||||||
| DnType::CommonName, | ||||||
| DnValue::PrintableString("Master Cert".try_into().unwrap()), | ||||||
| ); | ||||||
| dn.push( | ||||||
| dc_type.clone(), | ||||||
| DnValue::PrintableString("example".try_into().unwrap()), | ||||||
| ); | ||||||
|
|
||||||
| let removed = dn.remove(dc_type.clone()); | ||||||
| assert!(removed); | ||||||
| assert_eq!( | ||||||
| dn.get(&DnType::CommonName).get(0), | ||||||
| Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref() | ||||||
| ); | ||||||
| assert_eq!(dn.entries.len(), 1); | ||||||
| assert_eq!(dn.get(&dc_type).get(0), None); | ||||||
| } | ||||||
|
|
||||||
| #[test] | ||||||
| fn distinguished_name_remove_multiple_attributes_of_same_type() { | ||||||
| let mut dn = DistinguishedName::new(); | ||||||
| // Domain Component (DC) | ||||||
| let dc_type = DnType::CustomDnType(vec![0, 9, 2342, 19200300, 100, 1, 25]); | ||||||
|
|
||||||
| dn.push( | ||||||
| DnType::CommonName, | ||||||
| DnValue::PrintableString("Master Cert".try_into().unwrap()), | ||||||
| ); | ||||||
| dn.push( | ||||||
| // Domain Component (DC) | ||||||
| dc_type.clone(), | ||||||
| DnValue::PrintableString("example".try_into().unwrap()), | ||||||
| ); | ||||||
| dn.push( | ||||||
| dc_type.clone(), | ||||||
| DnValue::PrintableString("com".try_into().unwrap()), | ||||||
| ); | ||||||
|
|
||||||
| let removed = dn.remove(dc_type.clone()); | ||||||
| assert!(removed); | ||||||
| assert_eq!( | ||||||
| dn.get(&DnType::CommonName).get(0), | ||||||
| Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref() | ||||||
| ); | ||||||
| assert_eq!(dn.entries.len(), 1); | ||||||
| assert_eq!(dn.get(&dc_type).get(0), None); | ||||||
| assert_eq!(dn.get(&dc_type).get(1), None); | ||||||
| } | ||||||
|
|
||||||
| #[cfg(feature = "x509-parser")] | ||||||
| mod test_ip_address_from_octets { | ||||||
| use super::super::ip_addr_from_octets; | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,8 @@ | |
| use std::cell::RefCell; | ||
| use std::io::{Error, ErrorKind, Read, Result as ioResult, Write}; | ||
| use std::rc::Rc; | ||
| #[cfg(feature = "x509-parser")] | ||
| use std::str::FromStr; | ||
|
|
||
| use openssl::asn1::{Asn1Integer, Asn1Time}; | ||
| use openssl::bn::BigNum; | ||
|
|
@@ -12,6 +14,8 @@ use openssl::stack::Stack; | |
| use openssl::x509::store::{X509Store, X509StoreBuilder}; | ||
| use openssl::x509::{CrlStatus, X509Crl, X509Req, X509StoreContext, X509}; | ||
|
|
||
| #[cfg(feature = "x509-parser")] | ||
| use rcgen::Ia5String; | ||
| use rcgen::{ | ||
| BasicConstraints, Certificate, CertificateParams, DnType, DnValue, GeneralSubtree, IsCa, | ||
| KeyPair, NameConstraints, | ||
|
|
@@ -540,3 +544,62 @@ fn test_openssl_pkcs1_and_sec1_keys() { | |
| let pkcs8_ec_key_der = PrivateKeyDer::try_from(ec_key.private_key_to_pkcs8().unwrap()).unwrap(); | ||
| KeyPair::try_from(&pkcs8_ec_key_der).unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| #[cfg(feature = "x509-parser")] | ||
| fn test_parse_certificate_with_multiple_domain_components() { | ||
|
Comment on lines
+548
to
+550
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this test should be moved to While the static test certificate was generated with the |
||
| /// Command used to generate: | ||
| /// `openssl req -x509 -newkey rsa:4096 -nodes -out mycert.pem -keyout mykey.pem -days 365 -subj "/C=US/ST=California/L=San Francisco/O=Example Company/OU=IT Department/CN=www.example.com/DC=example/DC=com"` | ||
| /// Contains two distinct "DC" entries. | ||
| const CERT_WITH_MULTI_DC: &str = r#"-----BEGIN CERTIFICATE----- | ||
| MIIGSzCCBDOgAwIBAgIUECjoFzATY6rTCtu7HKjBtfXnB/owDQYJKoZIhvcNAQEL | ||
| BQAwgbQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH | ||
| DA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQKDA9FeGFtcGxlIENvbXBhbnkxFjAUBgNV | ||
| BAsMDUlUIERlcGFydG1lbnQxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEXMBUG | ||
| CgmSJomT8ixkARkWB2V4YW1wbGUxEzARBgoJkiaJk/IsZAEZFgNjb20wHhcNMjQx | ||
| MTIxMDkxNTE2WhcNMjUxMTIxMDkxNTE2WjCBtDELMAkGA1UEBhMCVVMxEzARBgNV | ||
| BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoM | ||
| D0V4YW1wbGUgQ29tcGFueTEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDEYMBYGA1UE | ||
| AwwPd3d3LmV4YW1wbGUuY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTETMBEG | ||
| CgmSJomT8ixkARkWA2NvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB | ||
| ANla4cBCTS+6JdEw6kVQHskanjtHbw7F25TZ2tZWC1f/UJuQnpF/JJqADdV6R3ta | ||
| xjcGj2ubJnKS1npcdiVN6A95jYggbQqjfZV+Z0cxjL8L4dQ+UPDsNyP8W0+S6UnK | ||
| +W813DG/QGXxEFrT8nZIfhyD4qZEtOSFGgp/ZA2f687Svx1+SKiutHeRovEf/OTb | ||
| fK4NHhewa1IxiV7shYNy7hhJmDqcsRIhVfuiWn4TU++qB6JTiPATYmzFRALli7B6 | ||
| g5m8KhvWcdAssgb2+bNpbs3fTcytrqwiNnNYtZ5a7DV0WWH4+wfor7KlomPMviPg | ||
| jiFwWWKW/N5dQ+f9vpo7SDOT9Jl26BWj0vJYTceLgkOGwYMXsg7pbWmPH4sL+GNv | ||
| WpRG7fDmual98y4DFwD8vHp4Mvax2OWKxfxe6xPqdn7or7D3ZSSyBu//ZlhQ6yMd | ||
| F6tLTl2/5VcWdJy0W+FDEnZIHnPm3zyCiplEP4bxY2Blpdnqf5Cx80mz8YSQhddn | ||
| gVNrM7iaNnIvRLjFS88w4KMOKbYSPbxEt2eWO4ggVcn1akcifDFTpyInRKQxQkXa | ||
| SXH/iu2dm7kuyGwSwrIW1l41vUkT+Lsm/9TFQ3a+UWWzut4oux9oGmcuUP5EiUZb | ||
| rWw2GIP2DaluKsZNUh8QIWVccBmX6AaKw3+K0r/tFqShAgMBAAGjUzBRMB0GA1Ud | ||
| DgQWBBTru/FFL1lBGB6d1a1xe3Tn3wV/RzAfBgNVHSMEGDAWgBTru/FFL1lBGB6d | ||
| 1a1xe3Tn3wV/RzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCY | ||
| dKu+CYxHb7drJqDhfMXUq2ogZiYI0fYyPEf+rpNpM5A8C0PyG9Um7WZwKAfp38IE | ||
| a/McBidxI7TuNq9ojruIyY79LCThz64Z1Nc5rb3sthcygZLd1t98Zh8vaG07kk7s | ||
| n2/BMLgHPvm47cUJ1VaQpLwx2tSBaFB+Osroq0ZXMqyO6s7Gyk+hrI+l6b+gqryA | ||
| b8kHzbeslxPK6QkDz9Kt+qPkZVRgfKgyqyd0YGoe1LaAwctMdrTPZRzkFRDLYDls | ||
| JK/PFi027oljJJzFZ07k9c8WJBeM3xiIHFlxIJ5XehVpLLFEhxX1ypnvku7GeINq | ||
| I9356ueSmMPn1BIsLonTOYR3k1hue+giO5AiD6J3yl8OhJStouG3FOZbB5dDRae+ | ||
| 9bdhU4npsmKTmBX/CDUFYJl4yqavEGfvw40p77gaqIOShEBB54ASKDaSyuLSeYbi | ||
| 3TQsa+JyWmJ5iNmqVsAy8YfioKveNmyl023hRTjtqJgKQY1UzY6M0bnHa0IlgZq/ | ||
| l4A7hDDsvi3rDFiqvKg/WTEZd5G87E9hwIcHF/bJPc+0+MjelRoxFTSty2bpbniR | ||
| p3mmtsYxi+XCHdwUwRLhbBrdu93z5Iy3AWIb7vGeTKznnnDweJzYpfHCXuWZdr/d | ||
| z6cbmudPzN1l99Op5eH9i1JikA+DQ8BQv1OgkNBw2A== | ||
| -----END CERTIFICATE----- | ||
| "#; | ||
|
|
||
| let param = CertificateParams::from_ca_cert_pem(CERT_WITH_MULTI_DC).unwrap(); | ||
|
|
||
| let domain_component_values = param.distinguished_name.get(&DnType::CustomDnType(vec![ | ||
| 0, 9, 2342, 19200300, 100, 1, 25, | ||
| ])); | ||
|
Comment on lines
+594
to
+596
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you have let domain_component_values = param.distinguished_name.get(&DnType::CustomDnType(
OID_DOMAIN_COMPONENT.iter().unwrap().collect(),
)); |
||
|
|
||
| assert_eq!( | ||
| domain_component_values, | ||
| vec![ | ||
| &DnValue::Ia5String(Ia5String::from_str("example").unwrap()), | ||
| &DnValue::Ia5String(Ia5String::from_str("com").unwrap()), | ||
| ] | ||
| ) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this a breaking API change? That conclusion also seems to be supported by the necessity to change the example code on
push().There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, it is -- sorry I missed that.