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