Skip to main content

ember_plus/ber/
mod.rs

1//! BER (Basic Encoding Rules) codec for ASN.1.
2//!
3//! This module implements the ASN.1 BER encoding used by Ember+ protocol.
4//! The Ember+ protocol uses BER encoding for all data serialization.
5
6mod tag;
7mod length;
8mod reader;
9mod writer;
10mod value;
11
12pub use tag::{Tag, TagClass, TagType};
13pub use length::Length;
14pub use reader::BerReader;
15pub use writer::BerWriter;
16pub use value::BerValue;
17
18use crate::error::{BerError, Result};
19
20/// Universal tag numbers for ASN.1 primitive types.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[repr(u8)]
23pub enum UniversalTag {
24    /// Boolean type (tag 1)
25    Boolean = 1,
26    /// Integer type (tag 2)
27    Integer = 2,
28    /// Bit string type (tag 3)
29    BitString = 3,
30    /// Octet string type (tag 4)
31    OctetString = 4,
32    /// Null type (tag 5)
33    Null = 5,
34    /// Object identifier type (tag 6)
35    ObjectIdentifier = 6,
36    /// Object descriptor type (tag 7)
37    ObjectDescriptor = 7,
38    /// External type (tag 8)
39    External = 8,
40    /// Real type (tag 9)
41    Real = 9,
42    /// Enumerated type (tag 10)
43    Enumerated = 10,
44    /// Embedded PDV type (tag 11)
45    EmbeddedPdv = 11,
46    /// UTF-8 string type (tag 12)
47    Utf8String = 12,
48    /// Relative OID type (tag 13)
49    RelativeOid = 13,
50    /// Sequence type (tag 16)
51    Sequence = 16,
52    /// Set type (tag 17)
53    Set = 17,
54    /// Numeric string type (tag 18)
55    NumericString = 18,
56    /// Printable string type (tag 19)
57    PrintableString = 19,
58    /// T61 string type (tag 20)
59    T61String = 20,
60    /// Videotex string type (tag 21)
61    VideotexString = 21,
62    /// IA5 string type (tag 22)
63    Ia5String = 22,
64    /// UTC time type (tag 23)
65    UtcTime = 23,
66    /// Generalized time type (tag 24)
67    GeneralizedTime = 24,
68    /// Graphic string type (tag 25)
69    GraphicString = 25,
70    /// Visible string type (tag 26)
71    VisibleString = 26,
72    /// General string type (tag 27)
73    GeneralString = 27,
74    /// Universal string type (tag 28)
75    UniversalString = 28,
76    /// Character string type (tag 29)
77    CharacterString = 29,
78    /// BMP string type (tag 30)
79    BmpString = 30,
80}
81
82impl TryFrom<u32> for UniversalTag {
83    type Error = BerError;
84
85    fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
86        match value {
87            1 => Ok(UniversalTag::Boolean),
88            2 => Ok(UniversalTag::Integer),
89            3 => Ok(UniversalTag::BitString),
90            4 => Ok(UniversalTag::OctetString),
91            5 => Ok(UniversalTag::Null),
92            6 => Ok(UniversalTag::ObjectIdentifier),
93            7 => Ok(UniversalTag::ObjectDescriptor),
94            8 => Ok(UniversalTag::External),
95            9 => Ok(UniversalTag::Real),
96            10 => Ok(UniversalTag::Enumerated),
97            11 => Ok(UniversalTag::EmbeddedPdv),
98            12 => Ok(UniversalTag::Utf8String),
99            13 => Ok(UniversalTag::RelativeOid),
100            16 => Ok(UniversalTag::Sequence),
101            17 => Ok(UniversalTag::Set),
102            18 => Ok(UniversalTag::NumericString),
103            19 => Ok(UniversalTag::PrintableString),
104            20 => Ok(UniversalTag::T61String),
105            21 => Ok(UniversalTag::VideotexString),
106            22 => Ok(UniversalTag::Ia5String),
107            23 => Ok(UniversalTag::UtcTime),
108            24 => Ok(UniversalTag::GeneralizedTime),
109            25 => Ok(UniversalTag::GraphicString),
110            26 => Ok(UniversalTag::VisibleString),
111            27 => Ok(UniversalTag::GeneralString),
112            28 => Ok(UniversalTag::UniversalString),
113            29 => Ok(UniversalTag::CharacterString),
114            30 => Ok(UniversalTag::BmpString),
115            _ => Err(BerError::InvalidTag(format!("unknown universal tag: {}", value))),
116        }
117    }
118}
119
120/// Encode a value to BER format.
121pub fn encode<T: BerEncode>(value: &T) -> Result<Vec<u8>> {
122    let mut writer = BerWriter::new();
123    value.encode(&mut writer)?;
124    Ok(writer.into_bytes())
125}
126
127/// Decode a value from BER format.
128pub fn decode<T: BerDecode>(data: &[u8]) -> Result<T> {
129    let mut reader = BerReader::new(data);
130    T::decode(&mut reader)
131}
132
133/// Trait for types that can be encoded to BER format.
134pub trait BerEncode {
135    /// Encode this value to the writer.
136    fn encode(&self, writer: &mut BerWriter) -> Result<()>;
137}
138
139/// Trait for types that can be decoded from BER format.
140pub trait BerDecode: Sized {
141    /// Decode a value from the reader.
142    fn decode(reader: &mut BerReader) -> Result<Self>;
143}
144
145// Implement BerEncode for primitive types
146impl BerEncode for bool {
147    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
148        writer.write_boolean(*self)
149    }
150}
151
152impl BerEncode for i64 {
153    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
154        writer.write_integer(*self)
155    }
156}
157
158impl BerEncode for i32 {
159    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
160        writer.write_integer(*self as i64)
161    }
162}
163
164impl BerEncode for f64 {
165    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
166        writer.write_real(*self)
167    }
168}
169
170impl BerEncode for str {
171    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
172        writer.write_utf8_string(self)
173    }
174}
175
176impl BerEncode for String {
177    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
178        writer.write_utf8_string(self)
179    }
180}
181
182impl BerEncode for [u8] {
183    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
184        writer.write_octet_string(self)
185    }
186}
187
188impl BerEncode for Vec<u8> {
189    fn encode(&self, writer: &mut BerWriter) -> Result<()> {
190        writer.write_octet_string(self)
191    }
192}
193
194// Implement BerDecode for primitive types
195impl BerDecode for bool {
196    fn decode(reader: &mut BerReader) -> Result<Self> {
197        reader.read_boolean()
198    }
199}
200
201impl BerDecode for i64 {
202    fn decode(reader: &mut BerReader) -> Result<Self> {
203        reader.read_integer()
204    }
205}
206
207impl BerDecode for i32 {
208    fn decode(reader: &mut BerReader) -> Result<Self> {
209        let value = reader.read_integer()?;
210        i32::try_from(value).map_err(|_| BerError::IntegerConversion.into())
211    }
212}
213
214impl BerDecode for f64 {
215    fn decode(reader: &mut BerReader) -> Result<Self> {
216        reader.read_real()
217    }
218}
219
220impl BerDecode for String {
221    fn decode(reader: &mut BerReader) -> Result<Self> {
222        reader.read_utf8_string()
223    }
224}
225
226impl BerDecode for Vec<u8> {
227    fn decode(reader: &mut BerReader) -> Result<Self> {
228        reader.read_octet_string()
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use super::*;
235
236    #[test]
237    fn test_boolean_roundtrip() {
238        let encoded = encode(&true).unwrap();
239        let decoded: bool = decode(&encoded).unwrap();
240        assert!(decoded);
241
242        let encoded = encode(&false).unwrap();
243        let decoded: bool = decode(&encoded).unwrap();
244        assert!(!decoded);
245    }
246
247    #[test]
248    fn test_integer_roundtrip() {
249        for value in [0i64, 1, -1, 127, 128, -128, -129, 32767, -32768, i64::MAX, i64::MIN] {
250            let encoded = encode(&value).unwrap();
251            let decoded: i64 = decode(&encoded).unwrap();
252            assert_eq!(value, decoded);
253        }
254    }
255
256    #[test]
257    fn test_string_roundtrip() {
258        let value = "Hello, Ember+!".to_string();
259        let encoded = encode(&value).unwrap();
260        let decoded: String = decode(&encoded).unwrap();
261        assert_eq!(value, decoded);
262    }
263}