bcder/encode/
primitive.rs

1//! PrimitiveContent and related types.
2//!
3//! This is an internal module. Its public types are re-exported by the
4//! parent.
5
6use std::io;
7use bytes::Bytes;
8use crate::length::Length;
9use crate::mode::Mode;
10use crate::tag::Tag;
11use super::values::Values;
12
13
14//------------ PrimitiveContent ----------------------------------------------
15
16/// A type that is encoded as a primitive value.
17///
18/// This trait should be implemented for types that use primitive encoding.
19/// It defines, how the content octets of a single primitive value containing
20/// a value of the type are to be created. As a consequence, these types
21/// gain the [`encode`] and [`encode_as`] methods from their implementation
22/// of this trait.
23///
24/// Note that the trait requires implementing types to be `Copy` to
25/// avoid unnecessary lifetime parameters on the encoder type. For types that
26/// aren’t `Copy`, `PrimitiveContent` should be implemented on a reference to
27/// the type instead.
28///
29/// [`encode`]: #tymethod.encode
30/// [`encode_as`]: #tymethod.encode_as
31pub trait PrimitiveContent: Sized {
32    /// The natural tag of an encoded value of this type.
33    const TAG: Tag;
34
35    /// Returns the length of the encoded content of this type.
36    fn encoded_len(&self, mode: Mode) -> usize;
37
38    /// Writes the encoded content to a writer.
39    fn write_encoded<W: io::Write>(
40        &self,
41        mode: Mode,
42        target: &mut W
43    ) -> Result<(), io::Error>;
44
45    /// Encodes the value to bytes (useful when you need to sign a structure)
46    fn to_encoded_bytes(&self, mode: Mode) -> Bytes {
47        let l = self.encoded_len(mode);
48        let mut w = Vec::with_capacity(l);
49        self.write_encoded(mode, &mut w).unwrap();
50        w.into()
51    }
52
53    /// Returns a value encoder for this content using the natural tag.
54    ///
55    /// This is identical to `self.encode_as(Self::TAG)`
56    fn encode(self) -> Primitive<Self> {
57        self.encode_as(Self::TAG)
58    }
59
60    /// Returns a value encoder for this content using the given tag.
61    ///
62    /// The returned value is a content encoder that produces a single
63    /// primitive BER encoded value. The tag for this value is explicitely
64    /// given via the `tag` argument.
65    fn encode_as(self, tag: Tag) -> Primitive<Self> {
66        Primitive { tag, prim: self }
67    }
68
69    /// Returns a value encoder for a reference using the natural tag.
70    fn encode_ref(&self) -> Primitive<&Self> {
71        self.encode_ref_as(Self::TAG)
72    }
73
74    /// Returns a value encoder for a reference using the given tag.
75    fn encode_ref_as(&self, tag: Tag) -> Primitive<&Self> {
76        Primitive { tag, prim: self }
77    }
78}
79
80//--- Blanket impls
81
82impl<T: PrimitiveContent> PrimitiveContent for &'_ T {
83    const TAG: Tag = T::TAG;
84
85    fn encoded_len(&self, mode: Mode) -> usize {
86        (*self).encoded_len(mode)
87    }
88
89    fn write_encoded<W: io::Write>(
90        &self,
91        mode: Mode,
92        target: &mut W
93    ) -> Result<(), io::Error> {
94        (*self).write_encoded(mode, target)
95    }
96}
97
98
99//--- impl for built-in types
100
101impl PrimitiveContent for u8 {
102    const TAG: Tag = Tag::INTEGER;
103
104    fn encoded_len(&self, _: Mode) -> usize {
105        if *self > 0x7F { 2 }
106        else { 1 }
107    }
108
109    fn write_encoded<W: io::Write>(
110        &self,
111        _: Mode,
112        target: &mut W
113    ) -> Result<(), io::Error> {
114        if *self > 0x7F {
115            target.write_all(&[0])?;
116        }
117        target.write_all(&[*self])?;
118        Ok(())
119    }
120}
121
122macro_rules! unsigned_content {
123    ( $type:ident, $len:expr) => {
124        impl PrimitiveContent for $type {
125            const TAG: Tag = Tag::INTEGER;
126
127            fn encoded_len(&self, _: Mode) -> usize {
128                if *self == 0 {
129                    1
130                }
131                else {
132                    let zeros = self.leading_zeros() as usize;
133                    if zeros % 8 == 0 {
134                        $len - (zeros >> 3) + 1
135                    }
136                    else {
137                        $len - (zeros >> 3)
138                    }
139                }
140            }
141
142            fn write_encoded<W: io::Write>(
143                &self,
144                _: Mode,
145                target: &mut W
146            ) -> Result<(), io::Error> {
147                if *self == 0 {
148                    target.write_all(&[0x00])?;
149                }
150                else {
151                    let mut val = self.swap_bytes();
152                    let mut i = 0;
153                    while i < $len {
154                        if val as u8 != 0 {
155                            break
156                        }
157                        val >>= 8;
158                        i += 1
159                    }
160                    if val & 0x80 != 0 {
161                        target.write_all(&[0x00])?;
162                    }
163                    while i < $len {
164                        target.write_all(&[val as u8])?;
165                        val >>= 8;
166                        i += 1
167                    }
168                }
169                Ok(())
170            }
171        }
172    }
173}
174
175unsigned_content!(u16, 2);
176unsigned_content!(u32, 4);
177unsigned_content!(u64, 8);
178unsigned_content!(u128, 16);
179
180
181impl PrimitiveContent for i8 {
182    const TAG: Tag = Tag::INTEGER;
183
184    fn encoded_len(&self, _: Mode) -> usize {
185        1
186    }
187
188    fn write_encoded<W: io::Write>(
189        &self,
190        _: Mode,
191        target: &mut W
192    ) -> Result<(), io::Error> {
193        target.write_all(&[*self as u8])?;
194        Ok(())
195    }
196}
197
198macro_rules! signed_content {
199    ($type:ident, $len:expr) => {
200        impl PrimitiveContent for $type {
201            const TAG: Tag = Tag::INTEGER;
202
203            #[allow(clippy::verbose_bit_mask)]
204            fn encoded_len(&self, _: Mode) -> usize {
205                if *self == 0 || *self == -1 {
206                    return 1
207                }
208                let zeros = if *self < 0 {
209                    (!*self).leading_zeros() as usize
210                }
211                else {
212                    self.leading_zeros() as usize
213                };
214                if zeros & 0x07 == 0 { // i.e., zeros % 8 == 1
215                    $len + 1 - (zeros >> 3)
216                }
217                else {
218                    $len - (zeros >> 3)
219                }
220            }
221
222            fn write_encoded<W: io::Write>(
223                &self,
224                _: Mode,
225                target: &mut W
226            ) -> Result<(), io::Error> {
227                if *self == 0 {
228                    target.write_all(&[0x00])?;
229                }
230                else if *self == -1 {
231                    target.write_all(&[0xFF])?;
232                }
233                else if *self < 0 {
234                    let mut val = self.swap_bytes();
235                    let mut i = 0;
236                    // Skip over leading 0xFF.
237                    while i < $len {
238                        if val as u8 != 0xFF {
239                            break
240                        }
241                        val >>= 8;
242                        i += 1;
243                    }
244                    // If the first non-0xFF doesn’t have the left-most bit
245                    // set, we need an 0xFF for the sign.
246                    if val & 0x80 != 0x80 {
247                        target.write_all(&[0xFF])?;
248                    }
249                    while i < $len {
250                        target.write_all(&[val as u8])?;
251                        val >>= 8;
252                        i += 1
253                    }
254                }
255                else {
256                    let mut val = self.swap_bytes();
257                    let mut i = 0;
258                    // Skip over leading zero bytes.
259                    while i < $len {
260                        if val as u8 != 0x00 {
261                            break
262                        }
263                        val >>= 8;
264                        i += 1;
265                    }
266                    // If the first non-zero has the left-most bit
267                    // set, we need an 0x00 for the sign.
268                    if val & 0x80 == 0x80 {
269                        target.write_all(&[0x00])?;
270                    }
271                    while i < $len {
272                        target.write_all(&[val as u8])?;
273                        val >>= 8;
274                        i += 1
275                    }
276                }
277                Ok(())
278            }
279        }
280    }
281}
282
283signed_content!(i16, 2);
284signed_content!(i32, 4);
285signed_content!(i64, 8);
286signed_content!(i128, 16);
287
288
289impl PrimitiveContent for () {
290    const TAG: Tag = Tag::NULL;
291
292    fn encoded_len(&self, _: Mode) -> usize {
293        0
294    }
295
296    fn write_encoded<W: io::Write>(
297        &self,
298        _: Mode,
299        _: &mut W
300    ) -> Result<(), io::Error> {
301        Ok(())
302    }
303}
304
305impl PrimitiveContent for bool {
306    const TAG: Tag = Tag::BOOLEAN;
307
308    fn encoded_len(&self, _: Mode) -> usize {
309        1
310    }
311
312    fn write_encoded<W: io::Write>(
313        &self,
314        _: Mode,
315        target: &mut W
316    ) -> Result<(), io::Error> {
317        if *self {
318            target.write_all(&[0xff])
319        }
320        else {
321            target.write_all(&[0])
322        }
323    }
324}
325
326impl PrimitiveContent for &'_ [u8] {
327    const TAG: Tag = Tag::OCTET_STRING;
328
329    fn encoded_len(&self, _: Mode) -> usize {
330        self.len()
331    }
332
333    fn write_encoded<W: io::Write>(
334        &self,
335        _: Mode,
336        target: &mut W
337    ) -> Result<(), io::Error> {
338        target.write_all(self)
339    }
340}
341
342
343//------------ Primitive -----------------------------------------------------
344
345/// A value encoder for primitively encoded types.
346///
347/// This type is returned by [`PrimitiveContent::encode`] and
348/// [`PrimitiveContent::encode_as`].
349///
350/// [`PrimitiveContent::encode`]: trait.PrimitiveContent.html#tymethod_encode
351/// [`PrimitiveContent::encode_as`]: trait.PrimitiveContent.html#tymethod_encode_as
352pub struct Primitive<P> {
353    /// The tag for this value.
354    tag: Tag,
355
356    /// The primitive content.
357    prim: P
358}
359
360impl<P: PrimitiveContent> Values for Primitive<P> {
361    fn encoded_len(&self, mode: Mode) -> usize {
362        let len = self.prim.encoded_len(mode);
363        self.tag.encoded_len()
364        + Length::Definite(len).encoded_len()
365        + len
366    }
367
368    fn write_encoded<W: io::Write>(
369        &self,
370        mode: Mode,
371        target: &mut W
372    ) -> Result<(), io::Error> {
373        self.tag.write_encoded(false, target)?;
374        Length::Definite(self.prim.encoded_len(mode)).write_encoded(target)?;
375        self.prim.write_encoded(mode, target)
376    }
377}
378
379
380//============ Tests =========================================================
381
382#[cfg(test)]
383mod test {
384    use super::*;
385
386    fn test_der<T: PrimitiveContent>(value: T, expected: &[u8]) {
387        assert_eq!(value.encoded_len(Mode::Der), expected.len());
388        let mut target = Vec::new();
389        value.write_encoded(Mode::Der, &mut target).unwrap();
390        assert_eq!(target, expected);
391    }
392
393    #[test]
394    fn encode_u32() {
395        test_der(0u32, b"\0");
396        test_der(0x12u32, b"\x12");
397        test_der(0xf2u32, b"\0\xf2");
398        test_der(0x1234u32, b"\x12\x34");
399        test_der(0xf234u32, b"\0\xf2\x34");
400        test_der(0x123400u32, b"\x12\x34\x00");
401        test_der(0xf23400u32, b"\0\xf2\x34\x00");
402        test_der(0x12345678u32, b"\x12\x34\x56\x78");
403        test_der(0xA2345678u32, b"\x00\xA2\x34\x56\x78");
404    }
405
406    #[test]
407    fn encode_i32() {
408        test_der(0i32, b"\0");
409        test_der(0x12i32, b"\x12");
410        test_der(0xf2i32, b"\0\xf2");
411        test_der(0x1234i32, b"\x12\x34");
412        test_der(0xf234i32, b"\0\xf2\x34");
413        test_der(0x123400i32, b"\x12\x34\x00");
414        test_der(0xf23400i32, b"\0\xf2\x34\x00");
415        test_der(0x12345678i32, b"\x12\x34\x56\x78");
416        test_der(-1i32, b"\xFF");
417        test_der(-0xF0i32, b"\xFF\x10");
418        test_der(-0xF000i32, b"\xFF\x10\x00");
419        test_der(-12000i32, b"\xD1\x20");
420        test_der(-1200000i32, b"\xED\xB0\x80");
421    }
422}