summaryrefslogtreecommitdiffstats
path: root/vendor/der/src/asn1/bmp_string.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
commit2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch)
tree033cc839730fda84ff08db877037977be94e5e3a /vendor/der/src/asn1/bmp_string.rs
parentInitial commit. (diff)
downloadcargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.tar.xz
cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.zip
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/der/src/asn1/bmp_string.rs')
-rw-r--r--vendor/der/src/asn1/bmp_string.rs164
1 files changed, 164 insertions, 0 deletions
diff --git a/vendor/der/src/asn1/bmp_string.rs b/vendor/der/src/asn1/bmp_string.rs
new file mode 100644
index 0000000..b4135d5
--- /dev/null
+++ b/vendor/der/src/asn1/bmp_string.rs
@@ -0,0 +1,164 @@
+//! ASN.1 `BMPString` support.
+
+use crate::{
+ BytesOwned, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Tag,
+ Writer,
+};
+use alloc::{boxed::Box, vec::Vec};
+use core::{fmt, str::FromStr};
+
+/// ASN.1 `BMPString` type.
+///
+/// Encodes Basic Multilingual Plane (BMP) subset of Unicode (ISO 10646),
+/// a.k.a. UCS-2.
+#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
+pub struct BmpString {
+ bytes: BytesOwned,
+}
+
+impl BmpString {
+ /// Create a new [`BmpString`] from its UCS-2 encoding.
+ pub fn from_ucs2(bytes: impl Into<Box<[u8]>>) -> Result<Self> {
+ let bytes = bytes.into();
+
+ if bytes.len() % 2 != 0 {
+ return Err(Tag::BmpString.length_error());
+ }
+
+ let ret = Self {
+ bytes: bytes.try_into()?,
+ };
+
+ for maybe_char in char::decode_utf16(ret.codepoints()) {
+ match maybe_char {
+ // All surrogates paired and character is in the Basic Multilingual Plane
+ Ok(c) if (c as u64) < u64::from(u16::MAX) => (),
+ // Unpaired surrogates or characters outside Basic Multilingual Plane
+ _ => return Err(Tag::BmpString.value_error()),
+ }
+ }
+
+ Ok(ret)
+ }
+
+ /// Create a new [`BmpString`] from a UTF-8 string.
+ pub fn from_utf8(utf8: &str) -> Result<Self> {
+ let capacity = utf8
+ .len()
+ .checked_mul(2)
+ .ok_or_else(|| Tag::BmpString.length_error())?;
+
+ let mut bytes = Vec::with_capacity(capacity);
+
+ for code_point in utf8.encode_utf16() {
+ bytes.extend(code_point.to_be_bytes());
+ }
+
+ Self::from_ucs2(bytes)
+ }
+
+ /// Borrow the encoded UCS-2 as bytes.
+ pub fn as_bytes(&self) -> &[u8] {
+ self.bytes.as_ref()
+ }
+
+ /// Obtain the inner bytes.
+ #[inline]
+ pub fn into_bytes(self) -> Box<[u8]> {
+ self.bytes.into()
+ }
+
+ /// Get an iterator over characters in the string.
+ pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
+ char::decode_utf16(self.codepoints())
+ .map(|maybe_char| maybe_char.expect("unpaired surrogates checked in constructor"))
+ }
+
+ /// Get an iterator over the `u16` codepoints.
+ pub fn codepoints(&self) -> impl Iterator<Item = u16> + '_ {
+ // TODO(tarcieri): use `array_chunks`
+ self.as_bytes()
+ .chunks_exact(2)
+ .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
+ }
+}
+
+impl AsRef<[u8]> for BmpString {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a> DecodeValue<'a> for BmpString {
+ fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
+ Self::from_ucs2(reader.read_vec(header.length)?)
+ }
+}
+
+impl EncodeValue for BmpString {
+ fn value_len(&self) -> Result<Length> {
+ Ok(self.bytes.len())
+ }
+
+ fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
+ writer.write(self.as_bytes())
+ }
+}
+
+impl FixedTag for BmpString {
+ const TAG: Tag = Tag::BmpString;
+}
+
+impl FromStr for BmpString {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self> {
+ Self::from_utf8(s)
+ }
+}
+
+impl fmt::Debug for BmpString {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "BmpString(\"{}\")", self)
+ }
+}
+
+impl fmt::Display for BmpString {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for c in self.chars() {
+ write!(f, "{}", c)?;
+ }
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::BmpString;
+ use crate::{Decode, Encode};
+ use alloc::string::ToString;
+ use hex_literal::hex;
+
+ const EXAMPLE_BYTES: &[u8] = &hex!(
+ "1e 26 00 43 00 65 00 72 00 74"
+ " 00 69 00 66 00 69 00 63"
+ " 00 61 00 74 00 65 00 54"
+ " 00 65 00 6d 00 70 00 6c"
+ " 00 61 00 74 00 65"
+ );
+
+ const EXAMPLE_UTF8: &str = "CertificateTemplate";
+
+ #[test]
+ fn decode() {
+ let bmp_string = BmpString::from_der(EXAMPLE_BYTES).unwrap();
+ assert_eq!(bmp_string.to_string(), EXAMPLE_UTF8);
+ }
+
+ #[test]
+ fn encode() {
+ let bmp_string = BmpString::from_utf8(EXAMPLE_UTF8).unwrap();
+ let encoded = bmp_string.to_der().unwrap();
+ assert_eq!(encoded, EXAMPLE_BYTES);
+ }
+}