diff --git a/x509-cert/src/ext/pkix/name/dirstr.rs b/x509-cert/src/ext/pkix/name/dirstr.rs index 045cb19cc..385cdc5e7 100644 --- a/x509-cert/src/ext/pkix/name/dirstr.rs +++ b/x509-cert/src/ext/pkix/name/dirstr.rs @@ -1,7 +1,9 @@ +use alloc::borrow::Cow; use alloc::string::String; +use alloc::string::ToString; use der::{ Choice, FixedTag, Header, Reader, ValueOrd, - asn1::{Any, PrintableString, TeletexString}, + asn1::{Any, BmpString, PrintableString, TeletexString}, }; /// DirectoryString as defined in [RFC 5280 Section 4.2.1.4]. @@ -52,6 +54,9 @@ pub enum DirectoryString { #[asn1(type = "UTF8String")] Utf8String(String), + + #[asn1(type = "BMPString")] + BmpString(BmpString), } impl<'a> TryFrom<&'a Any> for DirectoryString { @@ -73,6 +78,7 @@ impl<'a> der::DecodeValue<'a> for DirectoryString { TeletexString::decode_value(reader, header).map(Self::TeletexString) } String::TAG => String::decode_value(reader, header).map(Self::Utf8String), + BmpString::TAG => BmpString::decode_value(reader, header).map(Self::BmpString), actual => Err(der::ErrorKind::TagUnexpected { expected: None, actual, @@ -81,13 +87,36 @@ impl<'a> der::DecodeValue<'a> for DirectoryString { } } } +impl DirectoryString { + /// Returns `Borrowed` variant for UTF-8 compatible strings + /// and `Owned` variant otherwise. + pub fn value(&self) -> Cow<'_, str> { + match self { + Self::PrintableString(s) => Cow::Borrowed(s.as_ref()), + Self::TeletexString(s) => Cow::Borrowed(s.as_ref()), + Self::Utf8String(s) => Cow::Borrowed(s.as_ref()), + Self::BmpString(s) => Cow::Owned(s.to_string()), + } + } -impl AsRef for DirectoryString { - fn as_ref(&self) -> &str { + /// Returns `&str` for `PrintableString`, `TeletexString` and `Utf8String` + /// + /// Warning: Returns `""` empty string for [`DirectoryString::BmpString`] variant + #[deprecated(since = "0.3.0-pre.0", note = "use `DirectoryString::value` instead")] + #[allow(clippy::should_implement_trait)] + pub fn as_ref(&self) -> &str { match self { Self::PrintableString(s) => s.as_ref(), Self::TeletexString(s) => s.as_ref(), Self::Utf8String(s) => s.as_ref(), + // BMPString is not str-compatible + Self::BmpString(_s) => "", } } } + +impl From for String { + fn from(value: DirectoryString) -> Self { + value.value().into_owned() + } +} diff --git a/x509-cert/tests/certificate.rs b/x509-cert/tests/certificate.rs index bd4c73b9b..417286628 100644 --- a/x509-cert/tests/certificate.rs +++ b/x509-cert/tests/certificate.rs @@ -468,3 +468,18 @@ fn certificate_arbitrary() { check_arbitrary(certificate); } } + +#[cfg(feature = "pem")] +#[test] +fn decode_cert_bmpstring() { + let der_encoded_cert = include_bytes!("examples/windows_bmpstring.pem"); + let result = Certificate::from_pem(der_encoded_cert); + let cert: Certificate = result.unwrap(); + + let common_name = cert.tbs_certificate().issuer().common_name().unwrap(); + + assert_eq!( + common_name.unwrap().value(), + "WDKTestCert 混沌,133906716390833193" + ); +} diff --git a/x509-cert/tests/examples/windows_bmpstring.pem b/x509-cert/tests/examples/windows_bmpstring.pem new file mode 100644 index 000000000..a29c4cb4f --- /dev/null +++ b/x509-cert/tests/examples/windows_bmpstring.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIQKPABcjfrcIpG1K6NEhSpIjANBgkqhkiG9w0BAQUFADBN +MUswSQYDVQQDHkIAVwBEAEsAVABlAHMAdABDAGUAcgB0ACBt92yMACwAMQAzADMA +OQAwADYANwAxADYAMwA5ADAAOAAzADMAMQA5ADMwHhcNMjUwNTAyMTUwMDM5WhcN +MzUwNTAyMDAwMDAwWjBNMUswSQYDVQQDHkIAVwBEAEsAVABlAHMAdABDAGUAcgB0 +ACBt92yMACwAMQAzADMAOQAwADYANwAxADYAMwA5ADAAOAAzADMAMQA5ADMwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlgz7PDdlf6UzTtcWMRpaGGCdg +KYQOBOJWg6lnTQMxALfWD2bzVrbLM8N2HlMUi1AhDmr3EX6xgFi3gdXB9DgZKwEB +C1yq9CtU3QEyb/BKdsSaBasZPwfNEak8shAacvi0ssDhd05CIPYpvAlyO1RP1+m9 +g3LaHaTYnlPM1PYGr3yJ8qMprGsMepgrLf1AlHQRhWyEml5C0s/1WaflYo/NJnBy +thyxksCEM2+AeO8A6CtwkL1yL47i6WImqk68NbeDiYalGwFt1Nb1Yd8PVbE7FT36 +4vdMJ7jdQxzsCDiIq+KORZGSF/nVVpDeWRy8W0YtcfQ0LRbpIdEpRJOpK2UZAgMB +AAGjJDAiMAsGA1UdDwQEAwIEMDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG +9w0BAQUFAAOCAQEAycUVQ5SKUGsdQL5hkvJchs5I43/zEfEHRaT75lYlMn2lAMsa +qX4vDBanWs0no9+0/7j0hPRBamNZGFThE6MswXcYdJrxhfp0ZjHntK0IMWO8riVO +Y6O/4xHF8rUPQkOBFIcRBF19Ads2Qdfgtjzc+288YRCLiZ4TmWjYlg9TVst7x/Fo +Hl1xH2Nbvm8AYPX9iPoqEzbq9aPy2MWi9Pf+R0lfqAjsdvj85A4dZKD2ovjOYzPX +PBSJQizmCHjovucot2iB5MumoSGloHbIFZl2fe/2jqDjPwX4wDoYO94kenlhxedo +lX4Tf6VRXWA0hVpJiE7NQMaVtO6rO/5P0pE1Pg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/x509-cert/tests/name.rs b/x509-cert/tests/name.rs index f8eba5784..a4eb6b474 100644 --- a/x509-cert/tests/name.rs +++ b/x509-cert/tests/name.rs @@ -381,7 +381,7 @@ fn access_attributes() { let name = Name::from_str("emailAddress=foo@example.com,UID=identity:ds.group.3891111,OU=management:ds.group.3891111,CN=OQFAvDNDWs.google.com,O=Google LLC,L=Mountain View,ST=California,C=US").unwrap(); assert_eq!( - <_ as AsRef>::as_ref(&name.common_name().unwrap().unwrap()), + name.common_name().unwrap().unwrap().value(), "OQFAvDNDWs.google.com" ); @@ -391,22 +391,16 @@ fn access_attributes() { ); assert_eq!( - <_ as AsRef>::as_ref(&name.state_or_province().unwrap().unwrap()), + name.state_or_province().unwrap().unwrap().value(), "California" ); - assert_eq!( - <_ as AsRef>::as_ref(&name.locality().unwrap().unwrap()), - "Mountain View" - ); + assert_eq!(name.locality().unwrap().unwrap().value(), "Mountain View"); - assert_eq!( - <_ as AsRef>::as_ref(&name.organization().unwrap().unwrap()), - "Google LLC" - ); + assert_eq!(name.organization().unwrap().unwrap().value(), "Google LLC"); assert_eq!( - <_ as AsRef>::as_ref(&name.organization_unit().unwrap().unwrap()), + name.organization_unit().unwrap().unwrap().value(), "management:ds.group.3891111" );