asn1_rs/asn1_types/strings/
bmpstring.rs

1// do not use the `asn1_string` macro, since types are not the same
2// X.680 section 37.15
3
4use crate::*;
5use alloc::borrow::Cow;
6#[cfg(not(feature = "std"))]
7use alloc::string::{String, ToString};
8#[cfg(not(feature = "std"))]
9use alloc::vec::Vec;
10
11/// ASN.1 `BMPSTRING` type
12///
13/// Note: parsing a `BmpString` allocates memory since the UTF-16 to UTF-8 conversion requires a memory allocation.
14/// (see `String::from_utf16` method).
15#[derive(Debug, PartialEq, Eq)]
16pub struct BmpString<'a> {
17    pub(crate) data: Cow<'a, str>,
18}
19
20impl<'a> BmpString<'a> {
21    pub const fn new(s: &'a str) -> Self {
22        BmpString {
23            data: Cow::Borrowed(s),
24        }
25    }
26
27    pub fn string(&self) -> String {
28        self.data.to_string()
29    }
30}
31
32impl<'a> AsRef<str> for BmpString<'a> {
33    fn as_ref(&self) -> &str {
34        &self.data
35    }
36}
37
38impl<'a> From<&'a str> for BmpString<'a> {
39    fn from(s: &'a str) -> Self {
40        Self::new(s)
41    }
42}
43
44impl From<String> for BmpString<'_> {
45    fn from(s: String) -> Self {
46        Self {
47            data: Cow::Owned(s),
48        }
49    }
50}
51
52impl<'a, 'r> core::convert::TryFrom<&'r Any<'a>> for BmpString<'a> {
53    type Error = Error;
54
55    fn try_from(any: &'r Any<'a>) -> Result<BmpString<'a>> {
56        any.tag().assert_eq(Self::TAG)?;
57
58        // read slice as big-endian UTF-16 string
59        let v = &any
60            .data
61            .chunks(2)
62            .map(|s| match s {
63                [a, b] => ((*a as u16) << 8) | (*b as u16),
64                [a] => *a as u16,
65                _ => unreachable!(),
66            })
67            .collect::<Vec<_>>();
68
69        let s = String::from_utf16(v)?;
70        let data = Cow::Owned(s);
71
72        Ok(BmpString { data })
73    }
74}
75
76impl<'a> core::convert::TryFrom<Any<'a>> for BmpString<'a> {
77    type Error = Error;
78
79    #[inline]
80    fn try_from(any: Any<'a>) -> Result<BmpString<'a>> {
81        BmpString::try_from(&any)
82    }
83}
84
85impl<'a> CheckDerConstraints for BmpString<'a> {
86    fn check_constraints(any: &Any) -> Result<()> {
87        any.header.assert_primitive()?;
88        Ok(())
89    }
90}
91
92impl DerAutoDerive for BmpString<'_> {}
93
94impl<'a> Tagged for BmpString<'a> {
95    const TAG: Tag = Tag::BmpString;
96}
97
98impl<'a> TestValidCharset for BmpString<'a> {
99    fn test_valid_charset(i: &[u8]) -> Result<()> {
100        if i.len() % 2 != 0 {
101            return Err(Error::StringInvalidCharset);
102        }
103        let iter = i.chunks(2).map(|s| ((s[0] as u16) << 8) | (s[1] as u16));
104        for c in char::decode_utf16(iter) {
105            if c.is_err() {
106                return Err(Error::StringInvalidCharset);
107            }
108        }
109        Ok(())
110    }
111}
112
113#[cfg(feature = "std")]
114impl ToDer for BmpString<'_> {
115    fn to_der_len(&self) -> Result<usize> {
116        // compute the UTF-16 length
117        let sz = self.data.encode_utf16().count() * 2;
118        if sz < 127 {
119            // 1 (class+tag) + 1 (length) + len
120            Ok(2 + sz)
121        } else {
122            // 1 (class+tag) + n (length) + len
123            let n = Length::Definite(sz).to_der_len()?;
124            Ok(1 + n + sz)
125        }
126    }
127
128    fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
129        // compute the UTF-16 length
130        let l = self.data.encode_utf16().count() * 2;
131        let header = Header::new(Class::Universal, false, Self::TAG, Length::Definite(l));
132        header.write_der_header(writer).map_err(Into::into)
133    }
134
135    fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
136        let mut v = Vec::new();
137        for u in self.data.encode_utf16() {
138            v.push((u >> 8) as u8);
139            v.push((u & 0xff) as u8);
140        }
141        writer.write(&v).map_err(Into::into)
142    }
143}