1use crate::{
6 CborError, CborValue, MAX_DAG_CBOR_CONTAINER_ENTRIES, MAX_DAG_CBOR_INPUT_LEN,
7 MAX_DAG_CBOR_NODES, MAX_NESTING_DEPTH,
8};
9use std::cmp::Ordering;
10use std::str;
11use zeroize::Zeroizing;
12
13const MT_UINT: u8 = 0;
14const MT_NEGINT: u8 = 1;
15const MT_BYTES: u8 = 2;
16const MT_STRING: u8 = 3;
17const MT_ARRAY: u8 = 4;
18const MT_MAP: u8 = 5;
19
20const MIN_ELEMENT_ENCODED_LEN: usize = 1;
25
26#[derive(Default)]
27struct DecodeBudget {
28 seen: usize,
29 pending: usize,
30}
31
32impl DecodeBudget {
33 fn enter(&mut self) -> Result<(), CborError> {
34 if self.seen != 0 {
35 self.pending = self
36 .pending
37 .checked_sub(1)
38 .ok_or(CborError::NodeLimitExceeded)?;
39 }
40 self.seen = self
41 .seen
42 .checked_add(1)
43 .ok_or(CborError::NodeLimitExceeded)?;
44 if self.seen > MAX_DAG_CBOR_NODES {
45 return Err(CborError::NodeLimitExceeded);
46 }
47 Ok(())
48 }
49
50 fn reserve_children(&mut self, count: usize) -> Result<(), CborError> {
51 let pending = self
52 .pending
53 .checked_add(count)
54 .ok_or(CborError::NodeLimitExceeded)?;
55 let total = self
56 .seen
57 .checked_add(pending)
58 .ok_or(CborError::NodeLimitExceeded)?;
59 if total > MAX_DAG_CBOR_NODES {
60 return Err(CborError::NodeLimitExceeded);
61 }
62 self.pending = pending;
63 Ok(())
64 }
65}
66
67pub fn decode_dag_cbor(bytes: &[u8]) -> Result<CborValue, CborError> {
77 if bytes.len() > MAX_DAG_CBOR_INPUT_LEN {
78 return Err(CborError::InputTooLarge);
79 }
80 let mut budget = DecodeBudget::default();
81 let (value, offset) = decode_value(bytes, 0, 0, &mut budget)?;
82 let mut value = Zeroizing::new(value);
83 if offset != bytes.len() {
84 return Err(CborError::TrailingBytes);
85 }
86 Ok(core::mem::replace(&mut *value, CborValue::Null))
87}
88
89fn decode_value(
93 bytes: &[u8],
94 mut offset: usize,
95 depth: usize,
96 budget: &mut DecodeBudget,
97) -> Result<(CborValue, usize), CborError> {
98 if offset >= bytes.len() {
99 return Err(CborError::UnexpectedEnd);
100 }
101 budget.enter()?;
102
103 let first = bytes[offset];
104 offset = offset.checked_add(1).ok_or(CborError::OffsetOverflow)?;
105
106 let major = first >> 5;
107 let ai = first & 0x1f;
108
109 if major == 7 && (25..=27).contains(&ai) {
113 return Err(CborError::DisallowedSimpleValue {
114 value: u64::from(ai),
115 });
116 }
117
118 let (arg, new_offset) = read_argument(bytes, offset, ai)?;
119 offset = new_offset;
120
121 match major {
122 MT_UINT => Ok((
123 CborValue::Int(i64::try_from(arg).map_err(|_| CborError::IntegerOutOfRange)?),
124 offset,
125 )),
126
127 MT_NEGINT => {
128 let magnitude = i128::from(arg);
129 let value = (-1_i128)
130 .checked_sub(magnitude)
131 .ok_or(CborError::IntegerOutOfRange)?;
132 Ok((
133 CborValue::Int(i64::try_from(value).map_err(|_| CborError::IntegerOutOfRange)?),
134 offset,
135 ))
136 }
137
138 MT_BYTES => {
139 let (b, off) = extract_bytes(bytes, offset, arg)?;
140 Ok((CborValue::Bytes(b), off))
141 }
142
143 MT_STRING => {
144 let (s, off) = extract_string(bytes, offset, arg)?;
145 Ok((CborValue::String(s), off))
146 }
147
148 MT_ARRAY => {
149 let child_depth = descend(depth)?;
150 let item_count = usize::try_from(arg).map_err(|_| CborError::LengthTooLarge)?;
151 bounded_capacity(item_count, bytes.len(), offset)?;
157 if item_count > MAX_DAG_CBOR_CONTAINER_ENTRIES {
158 return Err(CborError::ContainerEntriesExceeded);
159 }
160 budget.reserve_children(item_count)?;
161 let mut items = Zeroizing::new(Vec::with_capacity(item_count));
164 let mut off = offset;
165 for _ in 0..item_count {
166 let (v, next) = decode_value(bytes, off, child_depth, budget)?;
167 items.push(v);
168 off = next;
169 }
170 Ok((CborValue::Array(core::mem::take(&mut *items)), off))
171 }
172
173 MT_MAP => {
174 let child_depth = descend(depth)?;
175 let entry_count = usize::try_from(arg).map_err(|_| CborError::LengthTooLarge)?;
176 let entry_min = MIN_ELEMENT_ENCODED_LEN
179 .checked_mul(2)
180 .ok_or(CborError::OffsetOverflow)?;
181 bounded_capacity_with_min(entry_count, bytes.len(), offset, entry_min)?;
182 if entry_count > MAX_DAG_CBOR_CONTAINER_ENTRIES {
183 return Err(CborError::ContainerEntriesExceeded);
184 }
185 let child_count = entry_count
186 .checked_mul(2)
187 .ok_or(CborError::NodeLimitExceeded)?;
188 budget.reserve_children(child_count)?;
189 let mut entries = Zeroizing::new(Vec::with_capacity(entry_count));
190 let mut off = offset;
191 let mut last_key_bytes: Option<Zeroizing<Vec<u8>>> = None;
192
193 for _ in 0..entry_count {
194 let (key_val, key_off) = decode_value(bytes, off, child_depth, budget)?;
195 off = key_off;
196
197 let mut key_val = Zeroizing::new(key_val);
198 let mut key = Zeroizing::new(match &mut *key_val {
199 CborValue::String(s) => core::mem::take(s),
200 _ => return Err(CborError::MapKeyMustBeString),
201 });
202
203 let key_bytes = Zeroizing::new(key.as_bytes().to_vec());
204 if let Some(prev) = &last_key_bytes {
205 match compare_bytes(prev, &key_bytes) {
206 Ordering::Less => {}
207 Ordering::Equal => return Err(CborError::DuplicateMapKey),
208 Ordering::Greater => return Err(CborError::MapKeysOutOfOrder),
209 }
210 }
211 last_key_bytes = Some(key_bytes);
212
213 let (val, val_off) = decode_value(bytes, off, child_depth, budget)?;
214 off = val_off;
215
216 entries.push((core::mem::take(&mut *key), val));
217 }
218
219 Ok((CborValue::Map(core::mem::take(&mut *entries)), off))
220 }
221
222 7 => match arg {
223 20 => Ok((CborValue::Bool(false), offset)),
224 21 => Ok((CborValue::Bool(true), offset)),
225 22 => Ok((CborValue::Null, offset)),
226 _ => Err(CborError::DisallowedSimpleValue { value: arg }),
227 },
228
229 _ => Err(CborError::DisallowedMajorType { major }),
230 }
231}
232
233fn read_argument(bytes: &[u8], offset: usize, ai: u8) -> Result<(u64, usize), CborError> {
234 match ai {
235 n @ 0..=23 => Ok((u64::from(n), offset)),
236
237 24 => {
238 let end = checked_end(offset, 1)?;
239 if end > bytes.len() {
240 return Err(CborError::TruncatedArgument);
241 }
242 let value = u64::from(bytes[offset]);
243 if value < 24 {
244 return Err(CborError::NonCanonicalInteger);
245 }
246 Ok((value, end))
247 }
248
249 25 => {
250 let end = checked_end(offset, 2)?;
251 if end > bytes.len() {
252 return Err(CborError::TruncatedArgument);
253 }
254 let val = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]);
255 if val < 256 {
256 return Err(CborError::NonCanonicalInteger);
257 }
258 Ok((u64::from(val), end))
259 }
260
261 26 => {
262 let end = checked_end(offset, 4)?;
263 if end > bytes.len() {
264 return Err(CborError::TruncatedArgument);
265 }
266 let val = u32::from_be_bytes([
267 bytes[offset],
268 bytes[offset + 1],
269 bytes[offset + 2],
270 bytes[offset + 3],
271 ]);
272 if val < 65536 {
273 return Err(CborError::NonCanonicalInteger);
274 }
275 Ok((u64::from(val), end))
276 }
277
278 27 => {
279 let end = checked_end(offset, 8)?;
280 if end > bytes.len() {
281 return Err(CborError::TruncatedArgument);
282 }
283 let val = u64::from_be_bytes([
284 bytes[offset],
285 bytes[offset + 1],
286 bytes[offset + 2],
287 bytes[offset + 3],
288 bytes[offset + 4],
289 bytes[offset + 5],
290 bytes[offset + 6],
291 bytes[offset + 7],
292 ]);
293 if val < 0x1_0000_0000 {
294 return Err(CborError::NonCanonicalInteger);
295 }
296 Ok((val, end))
297 }
298
299 _ => Err(CborError::UnsupportedAdditionalInfo),
300 }
301}
302
303fn extract_bytes(bytes: &[u8], offset: usize, len: u64) -> Result<(Vec<u8>, usize), CborError> {
304 let len = usize::try_from(len).map_err(|_| CborError::LengthTooLarge)?;
305 let end = checked_end(offset, len)?;
306 if end > bytes.len() {
307 return Err(CborError::TruncatedBytes);
308 }
309 Ok((bytes[offset..end].to_vec(), end))
310}
311
312fn extract_string(bytes: &[u8], offset: usize, len: u64) -> Result<(String, usize), CborError> {
313 let len = usize::try_from(len).map_err(|_| CborError::LengthTooLarge)?;
314 let end = checked_end(offset, len)?;
315 let raw = bytes.get(offset..end).ok_or(CborError::TruncatedBytes)?;
316 let s = str::from_utf8(raw).map_err(|_| CborError::InvalidUtf8)?;
317 Ok((s.to_string(), end))
318}
319
320fn checked_end(offset: usize, len: usize) -> Result<usize, CborError> {
321 offset.checked_add(len).ok_or(CborError::OffsetOverflow)
322}
323
324fn descend(depth: usize) -> Result<usize, CborError> {
327 let next = depth.checked_add(1).ok_or(CborError::OffsetOverflow)?;
328 if next > MAX_NESTING_DEPTH {
329 return Err(CborError::DepthExceeded);
330 }
331 Ok(next)
332}
333
334fn bounded_capacity(count: usize, total_len: usize, offset: usize) -> Result<usize, CborError> {
337 bounded_capacity_with_min(count, total_len, offset, MIN_ELEMENT_ENCODED_LEN)
338}
339
340fn bounded_capacity_with_min(
346 count: usize,
347 total_len: usize,
348 offset: usize,
349 min_element_len: usize,
350) -> Result<usize, CborError> {
351 let remaining = total_len.saturating_sub(offset);
352 let max_possible = remaining / min_element_len.max(1);
353 if count > max_possible {
354 return Err(CborError::ContainerLengthExceedsInput);
355 }
356 Ok(count)
357}
358
359fn compare_bytes(a: &[u8], b: &[u8]) -> Ordering {
360 match a.len().cmp(&b.len()) {
361 Ordering::Equal => a.cmp(b),
362 ordering => ordering,
363 }
364}