monero_epee/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![deny(missing_docs)]
4#![no_std]
5
6use core::convert::TryFrom;
7
8/// An error incurred when decoding.
9#[derive(Clone, Copy, Debug)]
10pub enum EpeeError {
11  /// The `epee`-encoded blob did not have the expected header.
12  InvalidHeader,
13  /// The `epee`-encoded blob was short, as discovered when trying to read `{0}` bytes.
14  Short(usize),
15  /// Unrecognized type specified.
16  UnrecognizedType,
17  /// Array found when a unit was expected.
18  ArrayWhenUnit,
19  /// The `epee`-encoded blob had {0} trailing bytes.
20  TrailingBytes(usize),
21  /// The depth limit was exceeded.
22  DepthLimitExceeded,
23}
24
25// epee header, an 8-byte magic and a version
26const HEADER: &[u8] = b"\x01\x11\x01\x01\x01\x01\x02\x01\x01";
27
28/// The type of the field being read.
29#[derive(Clone, Copy, PartialEq, Eq, Debug)]
30#[repr(u8)]
31pub enum Type {
32  /// An `i64`.
33  Int64 = 1,
34  /// An `i32`.
35  Int32 = 2,
36  /// An `i16`.
37  Int16 = 3,
38  /// An `i8`.
39  Int8 = 4,
40  /// A `u64`.
41  Uint64 = 5,
42  /// A `u32`.
43  Uint32 = 6,
44  /// A `u16`.
45  Uint16 = 7,
46  /// A `u8`.
47  Uint8 = 8,
48  /// A `f64`.
49  Double = 9,
50  /// A length-prefixed collection of bytes.
51  String = 10,
52  /// A `bool`.
53  Bool = 11,
54  /// An object.
55  Object = 12,
56  // Array = 13, // Unused and unsupported
57}
58
59/// A bitflag for if the field represents an array.
60#[derive(Clone, Copy, Debug)]
61#[repr(u8)]
62pub enum Array {
63  /// A unit type.
64  Unit = 0,
65  /// An array.
66  Array = 1 << 7,
67}
68
69enum TypeOrEntry {
70  // An epee-defined type
71  Type(Type),
72  // An entry (name, type, value)
73  Entry,
74}
75
76impl TypeOrEntry {
77  fn to_u8(self) -> u8 {
78    match self {
79      TypeOrEntry::Type(kind) => kind as u8,
80      TypeOrEntry::Entry => u8::MAX,
81    }
82  }
83
84  // Panics if not called with a valid serialization for a `TypeOrEntry`.
85  fn from_u8(kind: u8) -> Self {
86    match kind {
87      0xff => TypeOrEntry::Entry,
88      _ => TypeOrEntry::Type(
89        Type::read(&mut (&[kind] as &[u8]))
90          .expect("Type we converted to u8 could not be converted back")
91          .0,
92      ),
93    }
94  }
95}
96
97fn read_byte(reader: &mut &[u8]) -> Result<u8, EpeeError> {
98  #[allow(clippy::len_zero)]
99  if reader.len() < 1 {
100    Err(EpeeError::Short(1))?;
101  }
102  let byte = reader[0];
103  *reader = &reader[1 ..];
104  Ok(byte)
105}
106
107fn read_bytes<'a, const N: usize>(reader: &mut &'a [u8]) -> Result<&'a [u8], EpeeError> {
108  if reader.len() < N {
109    Err(EpeeError::Short(N))?;
110  }
111  let res = &reader[.. N];
112  *reader = &reader[N ..];
113  Ok(res)
114}
115
116// Read a VarInt
117fn read_varint(reader: &mut &[u8]) -> Result<u64, EpeeError> {
118  let vi_start = read_byte(reader)?;
119  let len = match vi_start & 0b11 {
120    0 => 1,
121    1 => 2,
122    2 => 4,
123    3 => 8,
124    _ => unreachable!(),
125  };
126  let mut vi = u64::from(vi_start >> 2);
127  for i in 1 .. len {
128    vi |= u64::from(read_byte(reader)?) << (((i - 1) * 8) + 6);
129  }
130  Ok(vi)
131}
132
133impl Type {
134  // Read a type specification, including its length
135  fn read(reader: &mut &[u8]) -> Result<(Self, u64), EpeeError> {
136    let kind = read_byte(reader)?;
137    let array = kind & (Array::Array as u8);
138    let kind = kind & (!(Array::Array as u8));
139
140    let kind = match kind {
141      1 => Type::Int64,
142      2 => Type::Int32,
143      3 => Type::Int16,
144      4 => Type::Int8,
145      5 => Type::Uint64,
146      6 => Type::Uint32,
147      7 => Type::Uint16,
148      8 => Type::Uint8,
149      9 => Type::Double,
150      10 => Type::String,
151      11 => Type::Bool,
152      12 => Type::Object,
153      _ => Err(EpeeError::UnrecognizedType)?,
154    };
155
156    let len = if array != 0 { read_varint(reader)? } else { 1 };
157
158    Ok((kind, len))
159  }
160}
161
162// Read a entry's key
163fn read_key<'a>(reader: &mut &'a [u8]) -> Result<&'a [u8], EpeeError> {
164  let len = usize::from(read_byte(reader)?);
165  if reader.len() < len {
166    Err(EpeeError::Short(len))?;
167  }
168  let res = &reader[.. len];
169  *reader = &reader[len ..];
170  Ok(res)
171}
172
173// https://github.com/monero-project/monero/blob/8d4c625713e3419573dfcc7119c8848f47cabbaa/
174//   contrib/epee/include/storages/portable_storage_from_bin.h#L42
175const EPEE_LIB_MAX_OBJECT_DEPTH: usize = 100;
176// Explicitly set a larger depth in case we have slight differences in counting, so we can likely
177// handle at least the set of objects Monero's `epee` library would handle
178const MAX_OBJECT_DEPTH: usize = EPEE_LIB_MAX_OBJECT_DEPTH + 3;
179
180#[repr(C, packed)]
181struct PackedTypes([u8; MAX_OBJECT_DEPTH]);
182
183/*
184  epee allows nested objects, when we don't want to write a recursive function. The following
185  `Seek::next` only reads a single item per iteration of its loop, using this stack to keep track
186  of its depth.
187
188  In order to not represent an array of 100 items as `Type::*; 100]`, which would enable a DoS by
189  simply claiming there's 100 items when there isn't, we associate each item with its length as
190  `(Type::*, 100)`. This causes our stack to grow only with depth, not width.
191*/
192struct Stack {
193  remaining: [u64; MAX_OBJECT_DEPTH],
194  types: PackedTypes,
195  len: usize,
196}
197#[cfg(test)]
198const _ASSERT_KIBIBYTE_STACK: [(); 1024 - core::mem::size_of::<Stack>()] =
199  [(); 1024 - core::mem::size_of::<Stack>()];
200
201impl Stack {
202  fn new(initial_item: (TypeOrEntry, u64)) -> Self {
203    let (kind, amount) = initial_item;
204    Self {
205      types: PackedTypes([kind.to_u8(); MAX_OBJECT_DEPTH]),
206      remaining: [amount; MAX_OBJECT_DEPTH],
207      len: 1,
208    }
209  }
210
211  fn len(&self) -> usize {
212    self.len
213  }
214
215  /// Panics if the stack is empty, if range checks are on.
216  fn last(&mut self) -> (TypeOrEntry, &mut u64) {
217    let i = self.len - 1;
218    let kind = TypeOrEntry::from_u8(self.types.0[i]);
219    let amount = &mut self.remaining[i];
220    (kind, amount)
221  }
222
223  fn push(&mut self, kind: TypeOrEntry, amount: u64) -> Result<(), EpeeError> {
224    if self.len == MAX_OBJECT_DEPTH {
225      Err(EpeeError::DepthLimitExceeded)?;
226    }
227    self.types.0[self.len] = kind.to_u8();
228    self.remaining[self.len] = amount;
229    self.len += 1;
230    Ok(())
231  }
232
233  /// Panics if the stack is empty, if range checks are on.
234  fn pop(&mut self) {
235    self.len -= 1;
236  }
237}
238
239/// An iterator which seeks to all values of desired `(type, name)`.
240struct Seek<'a> {
241  reader: &'a [u8],
242  kind: Type,
243  array: Array,
244  field_name: &'static str,
245  stack: Stack,
246}
247#[cfg(test)]
248const _ASSERT_KIBIBYTE_SEEK: [(); 1024 - core::mem::size_of::<Seek>()] =
249  [(); 1024 - core::mem::size_of::<Seek>()];
250
251impl<'a> Seek<'a> {
252  fn new(
253    mut reader: &'a [u8],
254    kind: Type,
255    array: Array,
256    field_name: &'static str,
257  ) -> Result<Self, EpeeError> {
258    if read_bytes::<{ HEADER.len() }>(&mut reader).ok() != Some(HEADER) {
259      Err(EpeeError::InvalidHeader)?;
260    }
261    let stack = Stack::new((TypeOrEntry::Type(Type::Object), 1u64));
262    Ok(Seek { reader, kind, array, field_name, stack })
263  }
264}
265
266impl<'a> Iterator for Seek<'a> {
267  type Item = Result<(u64, &'a [u8]), EpeeError>;
268  fn next(&mut self) -> Option<Self::Item> {
269    (|| -> Result<_, EpeeError> {
270      let mut result = None;
271
272      while self.stack.len() > 0 {
273        let (kind, remaining) = self.stack.last();
274
275        // Decrement the amount remaining by one
276        *remaining -= 1;
277        if *remaining == 0 {
278          self.stack.pop();
279        }
280
281        match kind {
282          TypeOrEntry::Type(Type::Int64) => {
283            read_bytes::<{ core::mem::size_of::<i64>() }>(&mut self.reader)?;
284          }
285          TypeOrEntry::Type(Type::Int32) => {
286            read_bytes::<{ core::mem::size_of::<i32>() }>(&mut self.reader)?;
287          }
288          TypeOrEntry::Type(Type::Int16) => {
289            read_bytes::<{ core::mem::size_of::<i16>() }>(&mut self.reader)?;
290          }
291          TypeOrEntry::Type(Type::Int8) => {
292            read_bytes::<{ core::mem::size_of::<i8>() }>(&mut self.reader)?;
293          }
294          TypeOrEntry::Type(Type::Uint64) => {
295            read_bytes::<{ core::mem::size_of::<u64>() }>(&mut self.reader)?;
296          }
297          TypeOrEntry::Type(Type::Uint32) => {
298            read_bytes::<{ core::mem::size_of::<u32>() }>(&mut self.reader)?;
299          }
300          TypeOrEntry::Type(Type::Uint16) => {
301            read_bytes::<{ core::mem::size_of::<u16>() }>(&mut self.reader)?;
302          }
303          TypeOrEntry::Type(Type::Uint8) => {
304            read_bytes::<{ core::mem::size_of::<u8>() }>(&mut self.reader)?;
305          }
306          TypeOrEntry::Type(Type::Double) => {
307            read_bytes::<{ core::mem::size_of::<f64>() }>(&mut self.reader)?;
308          }
309          TypeOrEntry::Type(Type::String) => {
310            let len = usize::try_from(read_varint(&mut self.reader)?)
311              .map_err(|_| EpeeError::Short(usize::MAX))?;
312            if self.reader.len() < len {
313              Err(EpeeError::Short(len))?;
314            }
315            self.reader = &self.reader[len ..];
316          }
317          TypeOrEntry::Type(Type::Bool) => {
318            read_bytes::<{ core::mem::size_of::<bool>() }>(&mut self.reader)?;
319          }
320          TypeOrEntry::Type(Type::Object) => {
321            let amount_of_entries = read_varint(&mut self.reader)?;
322            self.stack.push(TypeOrEntry::Entry, amount_of_entries)?;
323          }
324          TypeOrEntry::Entry => {
325            let key = read_key(&mut self.reader)?;
326            let (kind, len) = Type::read(&mut self.reader)?;
327            let result_stack_depth = self.stack.len();
328            self.stack.push(TypeOrEntry::Type(kind), len)?;
329            // If this is the requested `(name, type)`, yield it
330            if (key == self.field_name.as_bytes()) && (kind == self.kind) {
331              // Check if this was unexpectedly an array
332              // Note this is imperfect in that an array of length 1 will be accepted as a unit
333              if matches!(self.array, Array::Unit) && (len != 1) {
334                Err(EpeeError::ArrayWhenUnit)?;
335              }
336              result = Some(((len, self.reader), result_stack_depth));
337            }
338          }
339        }
340
341        if let Some(((epee_len, bytes), stack_depth)) = result {
342          if stack_depth == self.stack.len() {
343            let remaining_bytes = self.reader.len();
344            let bytes_used_by_field = bytes.len() - remaining_bytes;
345            return Ok(Some((epee_len, &bytes[.. bytes_used_by_field])));
346          }
347        }
348      }
349
350      if !self.reader.is_empty() {
351        Err(EpeeError::TrailingBytes(self.reader.len()))?;
352      }
353
354      Ok(None)
355    })()
356    .transpose()
357  }
358}
359
360/// Seek all instances of a field with the desired `(type, name)`.
361///
362/// This yields the length of the item _as an `epee` value_ and a slice for the bytes of the
363/// `epee`-encoded item. This will validate the resulting item is complete to the claimed length.
364pub fn seek_all<'a>(
365  reader: &'a [u8],
366  kind: Type,
367  array: Array,
368  field_name: &'static str,
369) -> Result<impl Iterator<Item = Result<(u64, &'a [u8]), EpeeError>>, EpeeError> {
370  Seek::new(reader, kind, array, field_name)
371}