1 //! ASN.1 `BMPString` support. 2 3 use crate::{ 4 BytesOwned, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Tag, 5 Writer, 6 }; 7 use alloc::{boxed::Box, vec::Vec}; 8 use core::{fmt, str::FromStr}; 9 10 /// ASN.1 `BMPString` type. 11 /// 12 /// Encodes Basic Multilingual Plane (BMP) subset of Unicode (ISO 10646), 13 /// a.k.a. UCS-2. 14 #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] 15 pub struct BmpString { 16 bytes: BytesOwned, 17 } 18 19 impl BmpString { 20 /// Create a new [`BmpString`] from its UCS-2 encoding. from_ucs2(bytes: impl Into<Box<[u8]>>) -> Result<Self>21 pub fn from_ucs2(bytes: impl Into<Box<[u8]>>) -> Result<Self> { 22 let bytes = bytes.into(); 23 24 if bytes.len() % 2 != 0 { 25 return Err(Tag::BmpString.length_error()); 26 } 27 28 let ret = Self { 29 bytes: bytes.try_into()?, 30 }; 31 32 for maybe_char in char::decode_utf16(ret.codepoints()) { 33 match maybe_char { 34 // All surrogates paired and character is in the Basic Multilingual Plane 35 Ok(c) if (c as u64) < u64::from(u16::MAX) => (), 36 // Unpaired surrogates or characters outside Basic Multilingual Plane 37 _ => return Err(Tag::BmpString.value_error()), 38 } 39 } 40 41 Ok(ret) 42 } 43 44 /// Create a new [`BmpString`] from a UTF-8 string. from_utf8(utf8: &str) -> Result<Self>45 pub fn from_utf8(utf8: &str) -> Result<Self> { 46 let capacity = utf8 47 .len() 48 .checked_mul(2) 49 .ok_or_else(|| Tag::BmpString.length_error())?; 50 51 let mut bytes = Vec::with_capacity(capacity); 52 53 for code_point in utf8.encode_utf16() { 54 bytes.extend(code_point.to_be_bytes()); 55 } 56 57 Self::from_ucs2(bytes) 58 } 59 60 /// Borrow the encoded UCS-2 as bytes. as_bytes(&self) -> &[u8]61 pub fn as_bytes(&self) -> &[u8] { 62 self.bytes.as_ref() 63 } 64 65 /// Obtain the inner bytes. 66 #[inline] into_bytes(self) -> Box<[u8]>67 pub fn into_bytes(self) -> Box<[u8]> { 68 self.bytes.into() 69 } 70 71 /// Get an iterator over characters in the string. chars(&self) -> impl Iterator<Item = char> + '_72 pub fn chars(&self) -> impl Iterator<Item = char> + '_ { 73 char::decode_utf16(self.codepoints()) 74 .map(|maybe_char| maybe_char.expect("unpaired surrogates checked in constructor")) 75 } 76 77 /// Get an iterator over the `u16` codepoints. codepoints(&self) -> impl Iterator<Item = u16> + '_78 pub fn codepoints(&self) -> impl Iterator<Item = u16> + '_ { 79 // TODO(tarcieri): use `array_chunks` 80 self.as_bytes() 81 .chunks_exact(2) 82 .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]])) 83 } 84 } 85 86 impl AsRef<[u8]> for BmpString { as_ref(&self) -> &[u8]87 fn as_ref(&self) -> &[u8] { 88 self.as_bytes() 89 } 90 } 91 92 impl<'a> DecodeValue<'a> for BmpString { decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>93 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> { 94 Self::from_ucs2(reader.read_vec(header.length)?) 95 } 96 } 97 98 impl EncodeValue for BmpString { value_len(&self) -> Result<Length>99 fn value_len(&self) -> Result<Length> { 100 Ok(self.bytes.len()) 101 } 102 encode_value(&self, writer: &mut impl Writer) -> Result<()>103 fn encode_value(&self, writer: &mut impl Writer) -> Result<()> { 104 writer.write(self.as_bytes()) 105 } 106 } 107 108 impl FixedTag for BmpString { 109 const TAG: Tag = Tag::BmpString; 110 } 111 112 impl FromStr for BmpString { 113 type Err = Error; 114 from_str(s: &str) -> Result<Self>115 fn from_str(s: &str) -> Result<Self> { 116 Self::from_utf8(s) 117 } 118 } 119 120 impl fmt::Debug for BmpString { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 122 write!(f, "BmpString(\"{}\")", self) 123 } 124 } 125 126 impl fmt::Display for BmpString { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 128 for c in self.chars() { 129 write!(f, "{}", c)?; 130 } 131 Ok(()) 132 } 133 } 134 135 #[cfg(test)] 136 mod tests { 137 use super::BmpString; 138 use crate::{Decode, Encode}; 139 use alloc::string::ToString; 140 use hex_literal::hex; 141 142 const EXAMPLE_BYTES: &[u8] = &hex!( 143 "1e 26 00 43 00 65 00 72 00 74" 144 " 00 69 00 66 00 69 00 63" 145 " 00 61 00 74 00 65 00 54" 146 " 00 65 00 6d 00 70 00 6c" 147 " 00 61 00 74 00 65" 148 ); 149 150 const EXAMPLE_UTF8: &str = "CertificateTemplate"; 151 152 #[test] decode()153 fn decode() { 154 let bmp_string = BmpString::from_der(EXAMPLE_BYTES).unwrap(); 155 assert_eq!(bmp_string.to_string(), EXAMPLE_UTF8); 156 } 157 158 #[test] encode()159 fn encode() { 160 let bmp_string = BmpString::from_utf8(EXAMPLE_UTF8).unwrap(); 161 let encoded = bmp_string.to_der().unwrap(); 162 assert_eq!(encoded, EXAMPLE_BYTES); 163 } 164 } 165