/rust/registry/src/index.crates.io-1949cf8c6b5b557f/const-oid-0.9.6/src/encoder.rs
Line | Count | Source |
1 | | //! OID encoder with `const` support. |
2 | | |
3 | | use crate::{ |
4 | | arcs::{ARC_MAX_FIRST, ARC_MAX_SECOND}, |
5 | | Arc, Error, ObjectIdentifier, Result, |
6 | | }; |
7 | | |
8 | | /// BER/DER encoder |
9 | | #[derive(Debug)] |
10 | | pub(crate) struct Encoder { |
11 | | /// Current state |
12 | | state: State, |
13 | | |
14 | | /// Bytes of the OID being encoded in-progress |
15 | | bytes: [u8; ObjectIdentifier::MAX_SIZE], |
16 | | |
17 | | /// Current position within the byte buffer |
18 | | cursor: usize, |
19 | | } |
20 | | |
21 | | /// Current state of the encoder |
22 | | #[derive(Debug)] |
23 | | enum State { |
24 | | /// Initial state - no arcs yet encoded |
25 | | Initial, |
26 | | |
27 | | /// First arc parsed |
28 | | FirstArc(Arc), |
29 | | |
30 | | /// Encoding base 128 body of the OID |
31 | | Body, |
32 | | } |
33 | | |
34 | | impl Encoder { |
35 | | /// Create a new encoder initialized to an empty default state. |
36 | 0 | pub(crate) const fn new() -> Self { |
37 | 0 | Self { |
38 | 0 | state: State::Initial, |
39 | 0 | bytes: [0u8; ObjectIdentifier::MAX_SIZE], |
40 | 0 | cursor: 0, |
41 | 0 | } |
42 | 0 | } |
43 | | |
44 | | /// Extend an existing OID. |
45 | 0 | pub(crate) const fn extend(oid: ObjectIdentifier) -> Self { |
46 | 0 | Self { |
47 | 0 | state: State::Body, |
48 | 0 | bytes: oid.bytes, |
49 | 0 | cursor: oid.length as usize, |
50 | 0 | } |
51 | 0 | } |
52 | | |
53 | | /// Encode an [`Arc`] as base 128 into the internal buffer. |
54 | 0 | pub(crate) const fn arc(mut self, arc: Arc) -> Result<Self> { |
55 | 0 | match self.state { |
56 | | State::Initial => { |
57 | 0 | if arc > ARC_MAX_FIRST { |
58 | 0 | return Err(Error::ArcInvalid { arc }); |
59 | 0 | } |
60 | | |
61 | 0 | self.state = State::FirstArc(arc); |
62 | 0 | Ok(self) |
63 | | } |
64 | | // Ensured not to overflow by `ARC_MAX_SECOND` check |
65 | | #[allow(clippy::integer_arithmetic)] |
66 | 0 | State::FirstArc(first_arc) => { |
67 | 0 | if arc > ARC_MAX_SECOND { |
68 | 0 | return Err(Error::ArcInvalid { arc }); |
69 | 0 | } |
70 | | |
71 | 0 | self.state = State::Body; |
72 | 0 | self.bytes[0] = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + arc as u8; |
73 | 0 | self.cursor = 1; |
74 | 0 | Ok(self) |
75 | | } |
76 | | // TODO(tarcieri): finer-grained overflow safety / checked arithmetic |
77 | | #[allow(clippy::integer_arithmetic)] |
78 | | State::Body => { |
79 | | // Total number of bytes in encoded arc - 1 |
80 | 0 | let nbytes = base128_len(arc); |
81 | | |
82 | | // Shouldn't overflow on any 16-bit+ architectures |
83 | 0 | if self.cursor + nbytes + 1 >= ObjectIdentifier::MAX_SIZE { |
84 | 0 | return Err(Error::Length); |
85 | 0 | } |
86 | | |
87 | 0 | let new_cursor = self.cursor + nbytes + 1; |
88 | | |
89 | | // TODO(tarcieri): use `?` when stable in `const fn` |
90 | 0 | match self.encode_base128_byte(arc, nbytes, false) { |
91 | 0 | Ok(mut encoder) => { |
92 | 0 | encoder.cursor = new_cursor; |
93 | 0 | Ok(encoder) |
94 | | } |
95 | 0 | Err(err) => Err(err), |
96 | | } |
97 | | } |
98 | | } |
99 | 0 | } |
100 | | |
101 | | /// Finish encoding an OID. |
102 | 0 | pub(crate) const fn finish(self) -> Result<ObjectIdentifier> { |
103 | 0 | if self.cursor >= 2 { |
104 | 0 | Ok(ObjectIdentifier { |
105 | 0 | bytes: self.bytes, |
106 | 0 | length: self.cursor as u8, |
107 | 0 | }) |
108 | | } else { |
109 | 0 | Err(Error::NotEnoughArcs) |
110 | | } |
111 | 0 | } |
112 | | |
113 | | /// Encode a single byte of a Base 128 value. |
114 | 0 | const fn encode_base128_byte(mut self, mut n: u32, i: usize, continued: bool) -> Result<Self> { |
115 | 0 | let mask = if continued { 0b10000000 } else { 0 }; |
116 | | |
117 | | // Underflow checked by branch |
118 | | #[allow(clippy::integer_arithmetic)] |
119 | 0 | if n > 0x80 { |
120 | 0 | self.bytes[checked_add!(self.cursor, i)] = (n & 0b1111111) as u8 | mask; |
121 | 0 | n >>= 7; |
122 | | |
123 | 0 | if i > 0 { |
124 | 0 | self.encode_base128_byte(n, i.saturating_sub(1), true) |
125 | | } else { |
126 | 0 | Err(Error::Base128) |
127 | | } |
128 | | } else { |
129 | 0 | self.bytes[self.cursor] = n as u8 | mask; |
130 | 0 | Ok(self) |
131 | | } |
132 | 0 | } |
133 | | } |
134 | | |
135 | | /// Compute the length - 1 of an arc when encoded in base 128. |
136 | 0 | const fn base128_len(arc: Arc) -> usize { |
137 | 0 | match arc { |
138 | 0 | 0..=0x7f => 0, |
139 | 0 | 0x80..=0x3fff => 1, |
140 | 0 | 0x4000..=0x1fffff => 2, |
141 | 0 | 0x200000..=0x1fffffff => 3, |
142 | 0 | _ => 4, |
143 | | } |
144 | 0 | } |
145 | | |
146 | | #[cfg(test)] |
147 | | mod tests { |
148 | | use super::Encoder; |
149 | | use hex_literal::hex; |
150 | | |
151 | | /// OID `1.2.840.10045.2.1` encoded as ASN.1 BER/DER |
152 | | const EXAMPLE_OID_BER: &[u8] = &hex!("2A8648CE3D0201"); |
153 | | |
154 | | #[test] |
155 | | fn encode() { |
156 | | let encoder = Encoder::new(); |
157 | | let encoder = encoder.arc(1).unwrap(); |
158 | | let encoder = encoder.arc(2).unwrap(); |
159 | | let encoder = encoder.arc(840).unwrap(); |
160 | | let encoder = encoder.arc(10045).unwrap(); |
161 | | let encoder = encoder.arc(2).unwrap(); |
162 | | let encoder = encoder.arc(1).unwrap(); |
163 | | assert_eq!(&encoder.bytes[..encoder.cursor], EXAMPLE_OID_BER); |
164 | | } |
165 | | } |