asn1_rs/asn1_types/strings/
universalstring.rs

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