1 use oid::ObjectIdentifier; 2 use picky_asn1::restricted_string::{CharSetError, IA5String}; 3 use picky_asn1::wrapper::{Asn1SequenceOf, IA5StringAsn1}; 4 use picky_asn1_x509::{ 5 DirectoryString, GeneralName as SerdeGeneralName, GeneralNames as SerdeGeneralNames, Name, NamePrettyFormatter, 6 }; 7 use std::fmt; 8 9 // === DirectoryName === 10 11 pub use picky_asn1_x509::NameAttr; 12 13 #[derive(Clone, Debug, PartialEq)] 14 pub struct DirectoryName(Name); 15 16 impl Default for DirectoryName { default() -> Self17 fn default() -> Self { 18 Self::new() 19 } 20 } 21 22 impl DirectoryName { new() -> Self23 pub fn new() -> Self { 24 Self(Name::new()) 25 } 26 new_common_name<S: Into<DirectoryString>>(name: S) -> Self27 pub fn new_common_name<S: Into<DirectoryString>>(name: S) -> Self { 28 Self(Name::new_common_name(name)) 29 } 30 31 /// Find the first common name contained in this `Name` find_common_name(&self) -> Option<&DirectoryString>32 pub fn find_common_name(&self) -> Option<&DirectoryString> { 33 self.0.find_common_name() 34 } 35 add_attr<S: Into<DirectoryString>>(&mut self, attr: NameAttr, value: S)36 pub fn add_attr<S: Into<DirectoryString>>(&mut self, attr: NameAttr, value: S) { 37 self.0.add_attr(attr, value) 38 } 39 40 /// Add an emailAddress attribute. 41 /// NOTE: this attribute does not conform with the RFC 5280, email should be placed in SAN instead add_email<S: Into<IA5StringAsn1>>(&mut self, value: S)42 pub fn add_email<S: Into<IA5StringAsn1>>(&mut self, value: S) { 43 self.0.add_email(value) 44 } 45 } 46 47 impl fmt::Display for DirectoryName { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 49 NamePrettyFormatter(&self.0).fmt(f) 50 } 51 } 52 53 impl From<Name> for DirectoryName { from(name: Name) -> Self54 fn from(name: Name) -> Self { 55 Self(name) 56 } 57 } 58 59 impl From<DirectoryName> for Name { from(name: DirectoryName) -> Self60 fn from(name: DirectoryName) -> Self { 61 name.0 62 } 63 } 64 65 // === GeneralNames === // 66 67 #[derive(Debug, PartialEq, Clone)] 68 pub enum GeneralName { 69 RFC822Name(IA5String), 70 DNSName(IA5String), 71 DirectoryName(DirectoryName), 72 EDIPartyName { 73 name_assigner: Option<DirectoryString>, 74 party_name: DirectoryString, 75 }, 76 URI(IA5String), 77 IpAddress(Vec<u8>), 78 RegisteredId(ObjectIdentifier), 79 } 80 81 impl GeneralName { new_rfc822_name<S: Into<String>>(name: S) -> Result<Self, CharSetError>82 pub fn new_rfc822_name<S: Into<String>>(name: S) -> Result<Self, CharSetError> { 83 Ok(Self::RFC822Name(IA5String::from_string(name.into())?)) 84 } 85 new_dns_name<S: Into<String>>(name: S) -> Result<Self, CharSetError>86 pub fn new_dns_name<S: Into<String>>(name: S) -> Result<Self, CharSetError> { 87 Ok(Self::DNSName(IA5String::from_string(name.into())?)) 88 } 89 new_directory_name<N: Into<DirectoryName>>(name: N) -> Self90 pub fn new_directory_name<N: Into<DirectoryName>>(name: N) -> Self { 91 Self::DirectoryName(name.into()) 92 } 93 new_edi_party_name<PN, NA>(party_name: PN, name_assigner: Option<NA>) -> Self where PN: Into<DirectoryString>, NA: Into<DirectoryString>,94 pub fn new_edi_party_name<PN, NA>(party_name: PN, name_assigner: Option<NA>) -> Self 95 where 96 PN: Into<DirectoryString>, 97 NA: Into<DirectoryString>, 98 { 99 Self::EDIPartyName { 100 name_assigner: name_assigner.map(Into::into), 101 party_name: party_name.into(), 102 } 103 } 104 new_uri<S: Into<String>>(uri: S) -> Result<Self, CharSetError>105 pub fn new_uri<S: Into<String>>(uri: S) -> Result<Self, CharSetError> { 106 Ok(Self::URI(IA5String::from_string(uri.into())?)) 107 } 108 new_ip_address<ADDR: Into<Vec<u8>>>(ip_address: ADDR) -> Self109 pub fn new_ip_address<ADDR: Into<Vec<u8>>>(ip_address: ADDR) -> Self { 110 Self::IpAddress(ip_address.into()) 111 } 112 new_registered_id<OID: Into<ObjectIdentifier>>(oid: OID) -> Self113 pub fn new_registered_id<OID: Into<ObjectIdentifier>>(oid: OID) -> Self { 114 Self::RegisteredId(oid.into()) 115 } 116 } 117 118 impl From<SerdeGeneralName> for GeneralName { from(gn: SerdeGeneralName) -> Self119 fn from(gn: SerdeGeneralName) -> Self { 120 match gn { 121 SerdeGeneralName::RFC822Name(name) => Self::RFC822Name(name.0), 122 SerdeGeneralName::DNSName(name) => Self::DNSName(name.0), 123 SerdeGeneralName::DirectoryName(name) => Self::DirectoryName(name.into()), 124 SerdeGeneralName::EDIPartyName(edi_pn) => Self::EDIPartyName { 125 name_assigner: edi_pn.name_assigner.0.map(|na| na.0), 126 party_name: edi_pn.party_name.0, 127 }, 128 SerdeGeneralName::URI(uri) => Self::URI(uri.0), 129 SerdeGeneralName::IpAddress(ip_addr) => Self::IpAddress(ip_addr.0), 130 SerdeGeneralName::RegisteredId(id) => Self::RegisteredId(id.0), 131 } 132 } 133 } 134 135 impl From<GeneralName> for SerdeGeneralName { from(gn: GeneralName) -> Self136 fn from(gn: GeneralName) -> Self { 137 match gn { 138 GeneralName::RFC822Name(name) => SerdeGeneralName::RFC822Name(name.into()), 139 GeneralName::DNSName(name) => SerdeGeneralName::DNSName(name.into()), 140 GeneralName::DirectoryName(name) => SerdeGeneralName::DirectoryName(name.into()), 141 GeneralName::EDIPartyName { 142 name_assigner, 143 party_name, 144 } => SerdeGeneralName::new_edi_party_name(party_name, name_assigner), 145 GeneralName::URI(uri) => SerdeGeneralName::URI(uri.into()), 146 GeneralName::IpAddress(ip_addr) => SerdeGeneralName::IpAddress(ip_addr.into()), 147 GeneralName::RegisteredId(id) => SerdeGeneralName::RegisteredId(id.into()), 148 } 149 } 150 } 151 152 impl From<GeneralName> for SerdeGeneralNames { from(gn: GeneralName) -> Self153 fn from(gn: GeneralName) -> Self { 154 GeneralNames::new(gn).into() 155 } 156 } 157 158 /// Wraps x509 `GeneralNames` into an easy to use API. 159 /// 160 /// # Example 161 /// 162 /// ``` 163 /// use picky::x509::name::{GeneralNames, GeneralName, DirectoryName}; 164 /// 165 /// let common_name = GeneralName::new_directory_name(DirectoryName::new_common_name("MyName")); 166 /// let dns_name = GeneralName::new_dns_name("localhost").expect("invalid name string"); 167 /// let names = GeneralNames::from(vec![common_name, dns_name]); 168 /// ``` 169 #[derive(Clone, Debug, PartialEq)] 170 pub struct GeneralNames(SerdeGeneralNames); 171 172 impl GeneralNames { 173 /// # Example 174 /// 175 /// ``` 176 /// use picky::x509::name::{GeneralName, GeneralNames}; 177 /// 178 /// let dns_name = GeneralName::new_dns_name("localhost").expect("invalid name string"); 179 /// let names = GeneralNames::new(dns_name); 180 /// ``` new<GN: Into<GeneralName>>(gn: GN) -> Self181 pub fn new<GN: Into<GeneralName>>(gn: GN) -> Self { 182 let gn = gn.into(); 183 Self(Asn1SequenceOf(vec![gn.into()])) 184 } 185 new_directory_name<DN: Into<DirectoryName>>(name: DN) -> Self186 pub fn new_directory_name<DN: Into<DirectoryName>>(name: DN) -> Self { 187 let gn = GeneralName::new_directory_name(name); 188 Self::new(gn) 189 } 190 with_directory_name<DN: Into<DirectoryName>>(mut self, name: DN) -> Self191 pub fn with_directory_name<DN: Into<DirectoryName>>(mut self, name: DN) -> Self { 192 let gn = GeneralName::new_directory_name(name); 193 (self.0).0.push(gn.into()); 194 self 195 } 196 find_directory_name(&self) -> Option<DirectoryName>197 pub fn find_directory_name(&self) -> Option<DirectoryName> { 198 for name in &(self.0).0 { 199 if let SerdeGeneralName::DirectoryName(name) = name { 200 return Some(name.clone().into()); 201 } 202 } 203 None 204 } 205 206 /// # Example 207 /// 208 /// ``` 209 /// use picky::x509::name::GeneralNames; 210 /// use picky_asn1::restricted_string::IA5String; 211 /// 212 /// let names = GeneralNames::new_dns_name(IA5String::new("localhost").unwrap()); 213 /// ``` new_dns_name<IA5: Into<IA5String>>(dns_name: IA5) -> Self214 pub fn new_dns_name<IA5: Into<IA5String>>(dns_name: IA5) -> Self { 215 let gn = GeneralName::DNSName(dns_name.into()); 216 Self::new(gn) 217 } 218 219 /// # Example 220 /// 221 /// ``` 222 /// use picky::x509::name::{GeneralNames, DirectoryName}; 223 /// use picky_asn1::restricted_string::IA5String; 224 /// 225 /// let names = GeneralNames::new_directory_name(DirectoryName::new_common_name("MyName")) 226 /// .with_dns_name(IA5String::new("localhost").unwrap()); 227 /// ``` with_dns_name<IA5: Into<IA5String>>(mut self, dns_name: IA5) -> Self228 pub fn with_dns_name<IA5: Into<IA5String>>(mut self, dns_name: IA5) -> Self { 229 let gn = GeneralName::DNSName(dns_name.into()); 230 (self.0).0.push(gn.into()); 231 self 232 } 233 find_dns_name(&self) -> Option<&IA5String>234 pub fn find_dns_name(&self) -> Option<&IA5String> { 235 for name in &(self.0).0 { 236 if let SerdeGeneralName::DNSName(name) = name { 237 return Some(&name.0); 238 } 239 } 240 None 241 } 242 add_name<GN: Into<GeneralName>>(&mut self, name: GN)243 pub fn add_name<GN: Into<GeneralName>>(&mut self, name: GN) { 244 let gn = name.into(); 245 (self.0).0.push(gn.into()); 246 } 247 248 /// # Example 249 /// 250 /// ``` 251 /// use picky::x509::name::{GeneralNames, GeneralName, DirectoryName}; 252 /// 253 /// let common_name = GeneralName::new_directory_name(DirectoryName::new_common_name("MyName")); 254 /// let dns_name = GeneralName::new_dns_name("localhost").expect("invalid name string"); 255 /// let names = GeneralNames::new(common_name).with_name(dns_name); 256 /// ``` with_name<GN: Into<GeneralName>>(mut self, name: GN) -> Self257 pub fn with_name<GN: Into<GeneralName>>(mut self, name: GN) -> Self { 258 let gn = name.into(); 259 (self.0).0.push(gn.into()); 260 self 261 } 262 into_general_names(self) -> Vec<GeneralName>263 pub fn into_general_names(self) -> Vec<GeneralName> { 264 (self.0).0.into_iter().map(|gn| gn.into()).collect() 265 } 266 to_general_names(&self) -> Vec<GeneralName>267 pub fn to_general_names(&self) -> Vec<GeneralName> { 268 (self.0).0.iter().map(|gn| gn.clone().into()).collect() 269 } 270 } 271 272 impl From<SerdeGeneralNames> for GeneralNames { from(gn: SerdeGeneralNames) -> Self273 fn from(gn: SerdeGeneralNames) -> Self { 274 Self(gn) 275 } 276 } 277 278 impl From<GeneralNames> for SerdeGeneralNames { from(gn: GeneralNames) -> Self279 fn from(gn: GeneralNames) -> Self { 280 gn.0 281 } 282 } 283 284 impl From<Vec<GeneralName>> for GeneralNames { from(names: Vec<GeneralName>) -> Self285 fn from(names: Vec<GeneralName>) -> Self { 286 let serde_names = names.into_iter().map(|n| n.into()).collect(); 287 Self(Asn1SequenceOf(serde_names)) 288 } 289 } 290 291 #[cfg(test)] 292 mod tests { 293 use super::*; 294 295 #[test] build_and_format_directory_name()296 fn build_and_format_directory_name() { 297 let mut my_name = DirectoryName::new_common_name("CommonName"); 298 my_name.add_attr(NameAttr::StateOrProvinceName, "SomeState"); 299 my_name.add_attr(NameAttr::CountryName, "SomeCountry"); 300 assert_eq!(my_name.to_string(), "CN=CommonName,ST=SomeState,C=SomeCountry"); 301 } 302 303 #[test] find_common_name()304 fn find_common_name() { 305 let my_name = DirectoryName::new_common_name("CommonName"); 306 let cn = my_name.find_common_name().unwrap(); 307 assert_eq!(cn.to_utf8_lossy(), "CommonName"); 308 } 309 } 310