• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 //! Common types.
18 
19 use crate::{
20     cbor,
21     cbor::value::Value,
22     iana,
23     iana::{EnumI64, WithPrivateRange},
24     util::{cbor_type_error, ValueTryAs},
25 };
26 use alloc::{boxed::Box, string::String, vec::Vec};
27 use core::{cmp::Ordering, convert::TryInto};
28 
29 #[cfg(test)]
30 mod tests;
31 
32 /// Marker structure indicating that the EOF was encountered when reading CBOR data.
33 #[derive(Debug)]
34 pub struct EndOfFile;
35 
36 /// Error type for failures in encoding or decoding COSE types.
37 pub enum CoseError {
38     /// CBOR decoding failure.
39     DecodeFailed(cbor::de::Error<EndOfFile>),
40     /// Duplicate map key detected.
41     DuplicateMapKey,
42     /// CBOR encoding failure.
43     EncodeFailed,
44     /// CBOR input had extra data.
45     ExtraneousData,
46     /// Integer value on the wire is outside the range of integers representable in this crate.
47     /// See <https://crates.io/crates/coset/#integer-ranges>.
48     OutOfRangeIntegerValue,
49     /// Unexpected CBOR item encountered (got, want).
50     UnexpectedItem(&'static str, &'static str),
51     /// Unrecognized value in IANA-controlled range (with no private range).
52     UnregisteredIanaValue,
53     /// Unrecognized value in neither IANA-controlled range nor private range.
54     UnregisteredIanaNonPrivateValue,
55 }
56 
57 /// Crate-specific Result type
58 pub type Result<T, E = CoseError> = core::result::Result<T, E>;
59 
60 impl core::convert::From<cbor::de::Error<EndOfFile>> for CoseError {
from(e: cbor::de::Error<EndOfFile>) -> Self61     fn from(e: cbor::de::Error<EndOfFile>) -> Self {
62         CoseError::DecodeFailed(e)
63     }
64 }
65 
66 impl<T> core::convert::From<cbor::ser::Error<T>> for CoseError {
from(_e: cbor::ser::Error<T>) -> Self67     fn from(_e: cbor::ser::Error<T>) -> Self {
68         CoseError::EncodeFailed
69     }
70 }
71 
72 impl core::convert::From<core::num::TryFromIntError> for CoseError {
from(_: core::num::TryFromIntError) -> Self73     fn from(_: core::num::TryFromIntError) -> Self {
74         CoseError::OutOfRangeIntegerValue
75     }
76 }
77 
78 impl core::fmt::Debug for CoseError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result79     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80         self.fmt_msg(f)
81     }
82 }
83 
84 impl core::fmt::Display for CoseError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result85     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86         self.fmt_msg(f)
87     }
88 }
89 
90 impl CoseError {
fmt_msg(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result91     fn fmt_msg(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
92         match self {
93             CoseError::DecodeFailed(e) => write!(f, "decode CBOR failure: {}", e),
94             CoseError::DuplicateMapKey => write!(f, "duplicate map key"),
95             CoseError::EncodeFailed => write!(f, "encode CBOR failure"),
96             CoseError::ExtraneousData => write!(f, "extraneous data in CBOR input"),
97             CoseError::OutOfRangeIntegerValue => write!(f, "out of range integer value"),
98             CoseError::UnexpectedItem(got, want) => write!(f, "got {}, expected {}", got, want),
99             CoseError::UnregisteredIanaValue => write!(f, "expected recognized IANA value"),
100             CoseError::UnregisteredIanaNonPrivateValue => {
101                 write!(f, "expected value in IANA or private use range")
102             }
103         }
104     }
105 }
106 
107 /// Newtype wrapper around a byte slice to allow left-over data to be detected.
108 struct MeasuringReader<'a>(&'a [u8]);
109 
110 impl<'a> MeasuringReader<'a> {
new(buf: &'a [u8]) -> MeasuringReader<'a>111     fn new(buf: &'a [u8]) -> MeasuringReader<'a> {
112         MeasuringReader(buf)
113     }
114 
is_empty(&self) -> bool115     fn is_empty(&self) -> bool {
116         self.0.is_empty()
117     }
118 }
119 
120 impl<'a> ciborium_io::Read for &mut MeasuringReader<'a> {
121     type Error = EndOfFile;
122 
read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error>123     fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
124         if data.len() > self.0.len() {
125             return Err(EndOfFile);
126         }
127 
128         let (prefix, suffix) = self.0.split_at(data.len());
129         data.copy_from_slice(prefix);
130         self.0 = suffix;
131         Ok(())
132     }
133 }
134 
135 /// Read a CBOR [`Value`] from a byte slice, failing if any extra data remains after the `Value` has
136 /// been read.
read_to_value(slice: &[u8]) -> Result<Value>137 fn read_to_value(slice: &[u8]) -> Result<Value> {
138     let mut mr = MeasuringReader::new(slice);
139     let value = cbor::de::from_reader(&mut mr)?;
140     if mr.is_empty() {
141         Ok(value)
142     } else {
143         Err(CoseError::ExtraneousData)
144     }
145 }
146 
147 /// Trait for types that can be converted to/from a [`Value`].
148 pub trait AsCborValue: Sized {
149     /// Convert a [`Value`] into an instance of the type.
from_cbor_value(value: Value) -> Result<Self>150     fn from_cbor_value(value: Value) -> Result<Self>;
151     /// Convert the object into a [`Value`], consuming it along the way.
to_cbor_value(self) -> Result<Value>152     fn to_cbor_value(self) -> Result<Value>;
153 }
154 
155 /// Extension trait that adds serialization/deserialization methods.
156 pub trait CborSerializable: AsCborValue {
157     /// Create an object instance from serialized CBOR data in a slice.
from_slice(slice: &[u8]) -> Result<Self>158     fn from_slice(slice: &[u8]) -> Result<Self> {
159         Self::from_cbor_value(read_to_value(slice)?)
160     }
161 
162     /// Serialize this object to a vector, consuming it along the way.
to_vec(self) -> Result<Vec<u8>>163     fn to_vec(self) -> Result<Vec<u8>> {
164         let mut data = Vec::new();
165         cbor::ser::into_writer(&self.to_cbor_value()?, &mut data)?;
166         Ok(data)
167     }
168 }
169 
170 /// Extension trait that adds tagged serialization/deserialization methods.
171 pub trait TaggedCborSerializable: AsCborValue {
172     /// The associated tag value.
173     const TAG: u64;
174 
175     /// Create an object instance from serialized CBOR data in a slice, expecting an initial
176     /// tag value.
from_tagged_slice(slice: &[u8]) -> Result<Self>177     fn from_tagged_slice(slice: &[u8]) -> Result<Self> {
178         let (t, v) = read_to_value(slice)?.try_as_tag()?;
179         if t != Self::TAG {
180             return Err(CoseError::UnexpectedItem("tag", "other tag"));
181         }
182         Self::from_cbor_value(*v)
183     }
184 
185     /// Serialize this object to a vector, including initial tag, consuming the object along the
186     /// way.
to_tagged_vec(self) -> Result<Vec<u8>>187     fn to_tagged_vec(self) -> Result<Vec<u8>> {
188         let mut data = Vec::new();
189         cbor::ser::into_writer(
190             &Value::Tag(Self::TAG, Box::new(self.to_cbor_value()?)),
191             &mut data,
192         )?;
193         Ok(data)
194     }
195 }
196 
197 /// Algorithm identifier.
198 pub type Algorithm = crate::RegisteredLabelWithPrivate<iana::Algorithm>;
199 
200 impl Default for Algorithm {
default() -> Self201     fn default() -> Self {
202         Algorithm::Assigned(iana::Algorithm::Reserved)
203     }
204 }
205 
206 /// A COSE label may be either a signed integer value or a string.
207 #[derive(Clone, Debug, Eq, PartialEq)]
208 pub enum Label {
209     Int(i64),
210     Text(String),
211 }
212 
213 impl CborSerializable for Label {}
214 
215 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
216 ///
217 /// Note that this uses the ordering given by RFC 8949 section 4.2.1 (lexicographic ordering of
218 /// encoded form), which is *different* from the canonical ordering defined in RFC 7049 section 3.9
219 /// (where the primary sorting criterion is the length of the encoded form)
220 impl Ord for Label {
cmp(&self, other: &Self) -> Ordering221     fn cmp(&self, other: &Self) -> Ordering {
222         match (self, other) {
223             (Label::Int(i1), Label::Int(i2)) => match (i1.signum(), i2.signum()) {
224                 (-1, -1) => (-i1).cmp(&(-i2)),
225                 (-1, 0) => Ordering::Greater,
226                 (-1, 1) => Ordering::Greater,
227                 (0, -1) => Ordering::Less,
228                 (0, 0) => Ordering::Equal,
229                 (0, 1) => Ordering::Less,
230                 (1, -1) => Ordering::Less,
231                 (1, 0) => Ordering::Greater,
232                 (1, 1) => i1.cmp(i2),
233                 (_, _) => unreachable!(), // safe: all possibilies covered
234             },
235             (Label::Int(_i1), Label::Text(_t2)) => Ordering::Less,
236             (Label::Text(_t1), Label::Int(_i2)) => Ordering::Greater,
237             (Label::Text(t1), Label::Text(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
238         }
239     }
240 }
241 
242 impl PartialOrd for Label {
partial_cmp(&self, other: &Self) -> Option<Ordering>243     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
244         Some(self.cmp(other))
245     }
246 }
247 
248 impl AsCborValue for Label {
from_cbor_value(value: Value) -> Result<Self>249     fn from_cbor_value(value: Value) -> Result<Self> {
250         match value {
251             Value::Integer(i) => Ok(Label::Int(i.try_into()?)),
252             Value::Text(t) => Ok(Label::Text(t)),
253             v => cbor_type_error(&v, "int/tstr"),
254         }
255     }
to_cbor_value(self) -> Result<Value>256     fn to_cbor_value(self) -> Result<Value> {
257         Ok(match self {
258             Label::Int(i) => Value::from(i),
259             Label::Text(t) => Value::Text(t),
260         })
261     }
262 }
263 
264 /// A COSE label which can be either a signed integer value or a string, but
265 /// where the allowed integer values are governed by IANA.
266 #[derive(Clone, Debug, Eq, PartialEq)]
267 pub enum RegisteredLabel<T: EnumI64> {
268     Assigned(T),
269     Text(String),
270 }
271 
272 impl<T: EnumI64> CborSerializable for RegisteredLabel<T> {}
273 
274 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
275 impl<T: EnumI64> Ord for RegisteredLabel<T> {
cmp(&self, other: &Self) -> Ordering276     fn cmp(&self, other: &Self) -> Ordering {
277         match (self, other) {
278             (RegisteredLabel::Assigned(i1), RegisteredLabel::Assigned(i2)) => {
279                 Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64()))
280             }
281             (RegisteredLabel::Assigned(_i1), RegisteredLabel::Text(_t2)) => Ordering::Less,
282             (RegisteredLabel::Text(_t1), RegisteredLabel::Assigned(_i2)) => Ordering::Greater,
283             (RegisteredLabel::Text(t1), RegisteredLabel::Text(t2)) => {
284                 t1.len().cmp(&t2.len()).then(t1.cmp(t2))
285             }
286         }
287     }
288 }
289 
290 impl<T: EnumI64> PartialOrd for RegisteredLabel<T> {
partial_cmp(&self, other: &Self) -> Option<Ordering>291     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
292         Some(self.cmp(other))
293     }
294 }
295 
296 impl<T: EnumI64> AsCborValue for RegisteredLabel<T> {
from_cbor_value(value: Value) -> Result<Self>297     fn from_cbor_value(value: Value) -> Result<Self> {
298         match value {
299             Value::Integer(i) => {
300                 if let Some(a) = T::from_i64(i.try_into()?) {
301                     Ok(RegisteredLabel::Assigned(a))
302                 } else {
303                     Err(CoseError::UnregisteredIanaValue)
304                 }
305             }
306             Value::Text(t) => Ok(RegisteredLabel::Text(t)),
307             v => cbor_type_error(&v, "int/tstr"),
308         }
309     }
310 
to_cbor_value(self) -> Result<Value>311     fn to_cbor_value(self) -> Result<Value> {
312         Ok(match self {
313             RegisteredLabel::Assigned(e) => Value::from(e.to_i64()),
314             RegisteredLabel::Text(t) => Value::Text(t),
315         })
316     }
317 }
318 
319 /// A COSE label which can be either a signed integer value or a string, and
320 /// where the allowed integer values are governed by IANA but include a private
321 /// use range.
322 #[derive(Clone, Debug, Eq, PartialEq)]
323 pub enum RegisteredLabelWithPrivate<T: EnumI64 + WithPrivateRange> {
324     PrivateUse(i64),
325     Assigned(T),
326     Text(String),
327 }
328 
329 impl<T: EnumI64 + WithPrivateRange> CborSerializable for RegisteredLabelWithPrivate<T> {}
330 
331 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
332 impl<T: EnumI64 + WithPrivateRange> Ord for RegisteredLabelWithPrivate<T> {
cmp(&self, other: &Self) -> Ordering333     fn cmp(&self, other: &Self) -> Ordering {
334         use RegisteredLabelWithPrivate::{Assigned, PrivateUse, Text};
335         match (self, other) {
336             (Assigned(i1), Assigned(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64())),
337             (Assigned(i1), PrivateUse(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(*i2)),
338             (PrivateUse(i1), Assigned(i2)) => Label::Int(*i1).cmp(&Label::Int(i2.to_i64())),
339             (PrivateUse(i1), PrivateUse(i2)) => Label::Int(*i1).cmp(&Label::Int(*i2)),
340             (Assigned(_i1), Text(_t2)) => Ordering::Less,
341             (PrivateUse(_i1), Text(_t2)) => Ordering::Less,
342             (Text(_t1), Assigned(_i2)) => Ordering::Greater,
343             (Text(_t1), PrivateUse(_i2)) => Ordering::Greater,
344             (Text(t1), Text(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
345         }
346     }
347 }
348 
349 impl<T: EnumI64 + WithPrivateRange> PartialOrd for RegisteredLabelWithPrivate<T> {
partial_cmp(&self, other: &Self) -> Option<Ordering>350     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
351         Some(self.cmp(other))
352     }
353 }
354 
355 impl<T: EnumI64 + WithPrivateRange> AsCborValue for RegisteredLabelWithPrivate<T> {
from_cbor_value(value: Value) -> Result<Self>356     fn from_cbor_value(value: Value) -> Result<Self> {
357         match value {
358             Value::Integer(i) => {
359                 let i = i.try_into()?;
360                 if let Some(a) = T::from_i64(i) {
361                     Ok(RegisteredLabelWithPrivate::Assigned(a))
362                 } else if T::is_private(i) {
363                     Ok(RegisteredLabelWithPrivate::PrivateUse(i))
364                 } else {
365                     Err(CoseError::UnregisteredIanaNonPrivateValue)
366                 }
367             }
368             Value::Text(t) => Ok(RegisteredLabelWithPrivate::Text(t)),
369             v => cbor_type_error(&v, "int/tstr"),
370         }
371     }
to_cbor_value(self) -> Result<Value>372     fn to_cbor_value(self) -> Result<Value> {
373         Ok(match self {
374             RegisteredLabelWithPrivate::PrivateUse(i) => Value::from(i),
375             RegisteredLabelWithPrivate::Assigned(i) => Value::from(i.to_i64()),
376             RegisteredLabelWithPrivate::Text(t) => Value::Text(t),
377         })
378     }
379 }
380