1use crate::{CborError, CborValue, MAX_DAG_CBOR_INPUT_LEN, MAX_NESTING_DEPTH};
6use std::cmp::Ordering;
7use std::str;
8use zeroize::Zeroizing;
9
10const MT_UINT: u8 = 0;
11const MT_NEGINT: u8 = 1;
12const MT_BYTES: u8 = 2;
13const MT_STRING: u8 = 3;
14const MT_ARRAY: u8 = 4;
15const MT_MAP: u8 = 5;
16const CONTAINER_INITIAL_RESERVE: usize = 8;
17
18const MIN_ELEMENT_ENCODED_LEN: usize = 1;
23
24pub fn decode_dag_cbor(bytes: &[u8]) -> Result<CborValue, CborError> {
34 if bytes.len() > MAX_DAG_CBOR_INPUT_LEN {
35 return Err(CborError::InputTooLarge);
36 }
37 let (value, offset) = decode_value(bytes, 0, 0)?;
38 let mut value = Zeroizing::new(value);
39 if offset != bytes.len() {
40 return Err(CborError::TrailingBytes);
41 }
42 Ok(core::mem::replace(&mut *value, CborValue::Null))
43}
44
45fn decode_value(
49 bytes: &[u8],
50 mut offset: usize,
51 depth: usize,
52) -> Result<(CborValue, usize), CborError> {
53 if offset >= bytes.len() {
54 return Err(CborError::UnexpectedEnd);
55 }
56
57 let first = bytes[offset];
58 offset = offset.checked_add(1).ok_or(CborError::OffsetOverflow)?;
59
60 let major = first >> 5;
61 let ai = first & 0x1f;
62
63 if major == 7 && (25..=27).contains(&ai) {
67 return Err(CborError::DisallowedSimpleValue {
68 value: u64::from(ai),
69 });
70 }
71
72 let (arg, new_offset) = read_argument(bytes, offset, ai)?;
73 offset = new_offset;
74
75 match major {
76 MT_UINT => Ok((
77 CborValue::Int(i64::try_from(arg).map_err(|_| CborError::IntegerOutOfRange)?),
78 offset,
79 )),
80
81 MT_NEGINT => {
82 let magnitude = i128::from(arg);
83 let value = (-1_i128)
84 .checked_sub(magnitude)
85 .ok_or(CborError::IntegerOutOfRange)?;
86 Ok((
87 CborValue::Int(i64::try_from(value).map_err(|_| CborError::IntegerOutOfRange)?),
88 offset,
89 ))
90 }
91
92 MT_BYTES => {
93 let (b, off) = extract_bytes(bytes, offset, arg)?;
94 Ok((CborValue::Bytes(b), off))
95 }
96
97 MT_STRING => {
98 let (s, off) = extract_string(bytes, offset, arg)?;
99 Ok((CborValue::String(s), off))
100 }
101
102 MT_ARRAY => {
103 let child_depth = descend(depth)?;
104 let item_count = usize::try_from(arg).map_err(|_| CborError::LengthTooLarge)?;
105 bounded_capacity(item_count, bytes.len(), offset)?;
111 let capacity = initial_container_capacity(item_count);
112 let mut items = Zeroizing::new(Vec::with_capacity(capacity));
113 let mut off = offset;
114 for _ in 0..item_count {
115 let (v, next) = decode_value(bytes, off, child_depth)?;
116 items.push(v);
117 off = next;
118 }
119 Ok((CborValue::Array(core::mem::take(&mut *items)), off))
120 }
121
122 MT_MAP => {
123 let child_depth = descend(depth)?;
124 let entry_count = usize::try_from(arg).map_err(|_| CborError::LengthTooLarge)?;
125 let entry_min = MIN_ELEMENT_ENCODED_LEN
128 .checked_mul(2)
129 .ok_or(CborError::OffsetOverflow)?;
130 bounded_capacity_with_min(entry_count, bytes.len(), offset, entry_min)?;
131 let capacity = initial_container_capacity(entry_count);
132 let mut entries = Zeroizing::new(Vec::with_capacity(capacity));
133 let mut off = offset;
134 let mut last_key_bytes: Option<Zeroizing<Vec<u8>>> = None;
135
136 for _ in 0..entry_count {
137 let (key_val, key_off) = decode_value(bytes, off, child_depth)?;
138 off = key_off;
139
140 let mut key_val = Zeroizing::new(key_val);
141 let mut key = Zeroizing::new(match &mut *key_val {
142 CborValue::String(s) => core::mem::take(s),
143 _ => return Err(CborError::MapKeyMustBeString),
144 });
145
146 let key_bytes = Zeroizing::new(key.as_bytes().to_vec());
147 if let Some(prev) = &last_key_bytes {
148 match compare_bytes(prev, &key_bytes) {
149 Ordering::Less => {}
150 Ordering::Equal => return Err(CborError::DuplicateMapKey),
151 Ordering::Greater => return Err(CborError::MapKeysOutOfOrder),
152 }
153 }
154 last_key_bytes = Some(key_bytes);
155
156 let (val, val_off) = decode_value(bytes, off, child_depth)?;
157 off = val_off;
158
159 entries.push((core::mem::take(&mut *key), val));
160 }
161
162 Ok((CborValue::Map(core::mem::take(&mut *entries)), off))
163 }
164
165 7 => match arg {
166 20 => Ok((CborValue::Bool(false), offset)),
167 21 => Ok((CborValue::Bool(true), offset)),
168 22 => Ok((CborValue::Null, offset)),
169 _ => Err(CborError::DisallowedSimpleValue { value: arg }),
170 },
171
172 _ => Err(CborError::DisallowedMajorType { major }),
173 }
174}
175
176fn read_argument(bytes: &[u8], offset: usize, ai: u8) -> Result<(u64, usize), CborError> {
177 match ai {
178 n @ 0..=23 => Ok((u64::from(n), offset)),
179
180 24 => {
181 let end = checked_end(offset, 1)?;
182 if end > bytes.len() {
183 return Err(CborError::TruncatedArgument);
184 }
185 let value = u64::from(bytes[offset]);
186 if value < 24 {
187 return Err(CborError::NonCanonicalInteger);
188 }
189 Ok((value, end))
190 }
191
192 25 => {
193 let end = checked_end(offset, 2)?;
194 if end > bytes.len() {
195 return Err(CborError::TruncatedArgument);
196 }
197 let val = u16::from_be_bytes([bytes[offset], bytes[offset + 1]]);
198 if val < 256 {
199 return Err(CborError::NonCanonicalInteger);
200 }
201 Ok((u64::from(val), end))
202 }
203
204 26 => {
205 let end = checked_end(offset, 4)?;
206 if end > bytes.len() {
207 return Err(CborError::TruncatedArgument);
208 }
209 let val = u32::from_be_bytes([
210 bytes[offset],
211 bytes[offset + 1],
212 bytes[offset + 2],
213 bytes[offset + 3],
214 ]);
215 if val < 65536 {
216 return Err(CborError::NonCanonicalInteger);
217 }
218 Ok((u64::from(val), end))
219 }
220
221 27 => {
222 let end = checked_end(offset, 8)?;
223 if end > bytes.len() {
224 return Err(CborError::TruncatedArgument);
225 }
226 let val = u64::from_be_bytes([
227 bytes[offset],
228 bytes[offset + 1],
229 bytes[offset + 2],
230 bytes[offset + 3],
231 bytes[offset + 4],
232 bytes[offset + 5],
233 bytes[offset + 6],
234 bytes[offset + 7],
235 ]);
236 if val < 0x1_0000_0000 {
237 return Err(CborError::NonCanonicalInteger);
238 }
239 Ok((val, end))
240 }
241
242 _ => Err(CborError::UnsupportedAdditionalInfo),
243 }
244}
245
246fn extract_bytes(bytes: &[u8], offset: usize, len: u64) -> Result<(Vec<u8>, usize), CborError> {
247 let len = usize::try_from(len).map_err(|_| CborError::LengthTooLarge)?;
248 let end = checked_end(offset, len)?;
249 if end > bytes.len() {
250 return Err(CborError::TruncatedBytes);
251 }
252 Ok((bytes[offset..end].to_vec(), end))
253}
254
255fn extract_string(bytes: &[u8], offset: usize, len: u64) -> Result<(String, usize), CborError> {
256 let len = usize::try_from(len).map_err(|_| CborError::LengthTooLarge)?;
257 let end = checked_end(offset, len)?;
258 let raw = bytes.get(offset..end).ok_or(CborError::TruncatedBytes)?;
259 let s = str::from_utf8(raw).map_err(|_| CborError::InvalidUtf8)?;
260 Ok((s.to_string(), end))
261}
262
263fn checked_end(offset: usize, len: usize) -> Result<usize, CborError> {
264 offset.checked_add(len).ok_or(CborError::OffsetOverflow)
265}
266
267fn descend(depth: usize) -> Result<usize, CborError> {
270 let next = depth.checked_add(1).ok_or(CborError::OffsetOverflow)?;
271 if next > MAX_NESTING_DEPTH {
272 return Err(CborError::DepthExceeded);
273 }
274 Ok(next)
275}
276
277fn bounded_capacity(count: usize, total_len: usize, offset: usize) -> Result<usize, CborError> {
280 bounded_capacity_with_min(count, total_len, offset, MIN_ELEMENT_ENCODED_LEN)
281}
282
283fn bounded_capacity_with_min(
289 count: usize,
290 total_len: usize,
291 offset: usize,
292 min_element_len: usize,
293) -> Result<usize, CborError> {
294 let remaining = total_len.saturating_sub(offset);
295 let max_possible = remaining / min_element_len.max(1);
296 if count > max_possible {
297 return Err(CborError::ContainerLengthExceedsInput);
298 }
299 Ok(count)
300}
301
302fn initial_container_capacity(count: usize) -> usize {
303 count.min(CONTAINER_INITIAL_RESERVE)
308}
309
310fn compare_bytes(a: &[u8], b: &[u8]) -> Ordering {
311 match a.len().cmp(&b.len()) {
312 Ordering::Equal => a.cmp(b),
313 ordering => ordering,
314 }
315}