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<T> core::convert::From<cbor::de::Error<T>> for CoseError {
from(e: cbor::de::Error<T>) -> Self61     fn from(e: cbor::de::Error<T>) -> Self {
62         // Make sure we use our [`EndOfFile`] marker.
63         use cbor::de::Error::{Io, RecursionLimitExceeded, Semantic, Syntax};
64         let e = match e {
65             Io(_) => Io(EndOfFile),
66             Syntax(x) => Syntax(x),
67             Semantic(a, b) => Semantic(a, b),
68             RecursionLimitExceeded => RecursionLimitExceeded,
69         };
70         CoseError::DecodeFailed(e)
71     }
72 }
73 
74 impl<T> core::convert::From<cbor::ser::Error<T>> for CoseError {
from(_e: cbor::ser::Error<T>) -> Self75     fn from(_e: cbor::ser::Error<T>) -> Self {
76         CoseError::EncodeFailed
77     }
78 }
79 
80 impl core::convert::From<core::num::TryFromIntError> for CoseError {
from(_: core::num::TryFromIntError) -> Self81     fn from(_: core::num::TryFromIntError) -> Self {
82         CoseError::OutOfRangeIntegerValue
83     }
84 }
85 
86 impl core::fmt::Debug for CoseError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result87     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88         self.fmt_msg(f)
89     }
90 }
91 
92 impl core::fmt::Display for CoseError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result93     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94         self.fmt_msg(f)
95     }
96 }
97 
98 #[cfg(feature = "std")]
99 impl std::error::Error for CoseError {}
100 
101 impl CoseError {
fmt_msg(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result102     fn fmt_msg(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103         match self {
104             CoseError::DecodeFailed(e) => write!(f, "decode CBOR failure: {}", e),
105             CoseError::DuplicateMapKey => write!(f, "duplicate map key"),
106             CoseError::EncodeFailed => write!(f, "encode CBOR failure"),
107             CoseError::ExtraneousData => write!(f, "extraneous data in CBOR input"),
108             CoseError::OutOfRangeIntegerValue => write!(f, "out of range integer value"),
109             CoseError::UnexpectedItem(got, want) => write!(f, "got {}, expected {}", got, want),
110             CoseError::UnregisteredIanaValue => write!(f, "expected recognized IANA value"),
111             CoseError::UnregisteredIanaNonPrivateValue => {
112                 write!(f, "expected value in IANA or private use range")
113             }
114         }
115     }
116 }
117 
118 /// Read a CBOR [`Value`] from a byte slice, failing if any extra data remains after the `Value` has
119 /// been read.
read_to_value(mut slice: &[u8]) -> Result<Value>120 fn read_to_value(mut slice: &[u8]) -> Result<Value> {
121     let value = cbor::de::from_reader(&mut slice)?;
122     if slice.is_empty() {
123         Ok(value)
124     } else {
125         Err(CoseError::ExtraneousData)
126     }
127 }
128 
129 /// Trait for types that can be converted to/from a [`Value`].
130 pub trait AsCborValue: Sized {
131     /// Convert a [`Value`] into an instance of the type.
from_cbor_value(value: Value) -> Result<Self>132     fn from_cbor_value(value: Value) -> Result<Self>;
133     /// Convert the object into a [`Value`], consuming it along the way.
to_cbor_value(self) -> Result<Value>134     fn to_cbor_value(self) -> Result<Value>;
135 }
136 
137 /// Extension trait that adds serialization/deserialization methods.
138 pub trait CborSerializable: AsCborValue {
139     /// Create an object instance from serialized CBOR data in a slice.  This method will fail (with
140     /// `CoseError::ExtraneousData`) if there is additional CBOR data after the object.
from_slice(slice: &[u8]) -> Result<Self>141     fn from_slice(slice: &[u8]) -> Result<Self> {
142         Self::from_cbor_value(read_to_value(slice)?)
143     }
144 
145     /// Serialize this object to a vector, consuming it along the way.
to_vec(self) -> Result<Vec<u8>>146     fn to_vec(self) -> Result<Vec<u8>> {
147         let mut data = Vec::new();
148         cbor::ser::into_writer(&self.to_cbor_value()?, &mut data)?;
149         Ok(data)
150     }
151 }
152 
153 /// Extension trait that adds tagged serialization/deserialization methods.
154 pub trait TaggedCborSerializable: AsCborValue {
155     /// The associated tag value.
156     const TAG: u64;
157 
158     /// Create an object instance from serialized CBOR data in a slice, expecting an initial
159     /// tag value.
from_tagged_slice(slice: &[u8]) -> Result<Self>160     fn from_tagged_slice(slice: &[u8]) -> Result<Self> {
161         let (t, v) = read_to_value(slice)?.try_as_tag()?;
162         if t != Self::TAG {
163             return Err(CoseError::UnexpectedItem("tag", "other tag"));
164         }
165         Self::from_cbor_value(*v)
166     }
167 
168     /// Serialize this object to a vector, including initial tag, consuming the object along the
169     /// way.
to_tagged_vec(self) -> Result<Vec<u8>>170     fn to_tagged_vec(self) -> Result<Vec<u8>> {
171         let mut data = Vec::new();
172         cbor::ser::into_writer(
173             &Value::Tag(Self::TAG, Box::new(self.to_cbor_value()?)),
174             &mut data,
175         )?;
176         Ok(data)
177     }
178 }
179 
180 /// Trivial implementation of [`AsCborValue`] for [`Value`].
181 impl AsCborValue for Value {
from_cbor_value(value: Value) -> Result<Self>182     fn from_cbor_value(value: Value) -> Result<Self> {
183         Ok(value)
184     }
to_cbor_value(self) -> Result<Value>185     fn to_cbor_value(self) -> Result<Value> {
186         Ok(self)
187     }
188 }
189 
190 impl CborSerializable for Value {}
191 
192 /// Algorithm identifier.
193 pub type Algorithm = crate::RegisteredLabelWithPrivate<iana::Algorithm>;
194 
195 impl Default for Algorithm {
default() -> Self196     fn default() -> Self {
197         Algorithm::Assigned(iana::Algorithm::Reserved)
198     }
199 }
200 
201 /// A COSE label may be either a signed integer value or a string.
202 #[derive(Clone, Debug, Eq, PartialEq)]
203 pub enum Label {
204     Int(i64),
205     Text(String),
206 }
207 
208 impl CborSerializable for Label {}
209 
210 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
211 ///
212 /// Note that this uses the ordering given by RFC 8949 section 4.2.1 (lexicographic ordering of
213 /// encoded form), which is *different* from the canonical ordering defined in RFC 7049 section 3.9
214 /// (where the primary sorting criterion is the length of the encoded form)
215 impl Ord for Label {
cmp(&self, other: &Self) -> Ordering216     fn cmp(&self, other: &Self) -> Ordering {
217         match (self, other) {
218             (Label::Int(i1), Label::Int(i2)) => match (i1.signum(), i2.signum()) {
219                 (-1, -1) => i2.cmp(i1),
220                 (-1, 0) => Ordering::Greater,
221                 (-1, 1) => Ordering::Greater,
222                 (0, -1) => Ordering::Less,
223                 (0, 0) => Ordering::Equal,
224                 (0, 1) => Ordering::Less,
225                 (1, -1) => Ordering::Less,
226                 (1, 0) => Ordering::Greater,
227                 (1, 1) => i1.cmp(i2),
228                 (_, _) => unreachable!(), // safe: all possibilies covered
229             },
230             (Label::Int(_i1), Label::Text(_t2)) => Ordering::Less,
231             (Label::Text(_t1), Label::Int(_i2)) => Ordering::Greater,
232             (Label::Text(t1), Label::Text(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
233         }
234     }
235 }
236 
237 impl PartialOrd for Label {
partial_cmp(&self, other: &Self) -> Option<Ordering>238     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
239         Some(self.cmp(other))
240     }
241 }
242 
243 impl Label {
244     /// Alternative ordering for `Label`, using the canonical ordering criteria from RFC 7049
245     /// section 3.9 (where the primary sorting criterion is the length of the encoded form), rather
246     /// than the ordering given by RFC 8949 section 4.2.1 (lexicographic ordering of encoded form).
247     ///
248     /// # Panics
249     ///
250     /// Panics if either `Label` fails to serialize.
cmp_canonical(&self, other: &Self) -> Ordering251     pub fn cmp_canonical(&self, other: &Self) -> Ordering {
252         let encoded_self = self.clone().to_vec().unwrap(); /* safe: documented */
253         let encoded_other = other.clone().to_vec().unwrap(); /* safe: documented */
254         if encoded_self.len() != encoded_other.len() {
255             // Shorter encoding sorts first.
256             encoded_self.len().cmp(&encoded_other.len())
257         } else {
258             // Both encode to the same length, sort lexicographically on encoded form.
259             encoded_self.cmp(&encoded_other)
260         }
261     }
262 }
263 
264 /// Indicate which ordering should be applied to CBOR values.
265 pub enum CborOrdering {
266     /// Order values lexicographically, as per RFC 8949 section 4.2.1 (Core Deterministic Encoding
267     /// Requirements)
268     Lexicographic,
269     /// Order values by encoded length, then by lexicographic ordering of encoded form, as per RFC
270     /// 7049 section 3.9 (Canonical CBOR) / RFC 8949 section 4.2.3 (Length-First Map Key Ordering).
271     LengthFirstLexicographic,
272 }
273 
274 impl AsCborValue for Label {
from_cbor_value(value: Value) -> Result<Self>275     fn from_cbor_value(value: Value) -> Result<Self> {
276         match value {
277             Value::Integer(i) => Ok(Label::Int(i.try_into()?)),
278             Value::Text(t) => Ok(Label::Text(t)),
279             v => cbor_type_error(&v, "int/tstr"),
280         }
281     }
to_cbor_value(self) -> Result<Value>282     fn to_cbor_value(self) -> Result<Value> {
283         Ok(match self {
284             Label::Int(i) => Value::from(i),
285             Label::Text(t) => Value::Text(t),
286         })
287     }
288 }
289 
290 /// A COSE label which can be either a signed integer value or a string, but
291 /// where the allowed integer values are governed by IANA.
292 #[derive(Clone, Debug, Eq, PartialEq)]
293 pub enum RegisteredLabel<T: EnumI64> {
294     Assigned(T),
295     Text(String),
296 }
297 
298 impl<T: EnumI64> CborSerializable for RegisteredLabel<T> {}
299 
300 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
301 impl<T: EnumI64> Ord for RegisteredLabel<T> {
cmp(&self, other: &Self) -> Ordering302     fn cmp(&self, other: &Self) -> Ordering {
303         match (self, other) {
304             (RegisteredLabel::Assigned(i1), RegisteredLabel::Assigned(i2)) => {
305                 Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64()))
306             }
307             (RegisteredLabel::Assigned(_i1), RegisteredLabel::Text(_t2)) => Ordering::Less,
308             (RegisteredLabel::Text(_t1), RegisteredLabel::Assigned(_i2)) => Ordering::Greater,
309             (RegisteredLabel::Text(t1), RegisteredLabel::Text(t2)) => {
310                 t1.len().cmp(&t2.len()).then(t1.cmp(t2))
311             }
312         }
313     }
314 }
315 
316 impl<T: EnumI64> PartialOrd for RegisteredLabel<T> {
partial_cmp(&self, other: &Self) -> Option<Ordering>317     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
318         Some(self.cmp(other))
319     }
320 }
321 
322 impl<T: EnumI64> AsCborValue for RegisteredLabel<T> {
from_cbor_value(value: Value) -> Result<Self>323     fn from_cbor_value(value: Value) -> Result<Self> {
324         match value {
325             Value::Integer(i) => {
326                 if let Some(a) = T::from_i64(i.try_into()?) {
327                     Ok(RegisteredLabel::Assigned(a))
328                 } else {
329                     Err(CoseError::UnregisteredIanaValue)
330                 }
331             }
332             Value::Text(t) => Ok(RegisteredLabel::Text(t)),
333             v => cbor_type_error(&v, "int/tstr"),
334         }
335     }
336 
to_cbor_value(self) -> Result<Value>337     fn to_cbor_value(self) -> Result<Value> {
338         Ok(match self {
339             RegisteredLabel::Assigned(e) => Value::from(e.to_i64()),
340             RegisteredLabel::Text(t) => Value::Text(t),
341         })
342     }
343 }
344 
345 /// A COSE label which can be either a signed integer value or a string, and
346 /// where the allowed integer values are governed by IANA but include a private
347 /// use range.
348 #[derive(Clone, Debug, Eq, PartialEq)]
349 pub enum RegisteredLabelWithPrivate<T: EnumI64 + WithPrivateRange> {
350     PrivateUse(i64),
351     Assigned(T),
352     Text(String),
353 }
354 
355 impl<T: EnumI64 + WithPrivateRange> CborSerializable for RegisteredLabelWithPrivate<T> {}
356 
357 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
358 impl<T: EnumI64 + WithPrivateRange> Ord for RegisteredLabelWithPrivate<T> {
cmp(&self, other: &Self) -> Ordering359     fn cmp(&self, other: &Self) -> Ordering {
360         use RegisteredLabelWithPrivate::{Assigned, PrivateUse, Text};
361         match (self, other) {
362             (Assigned(i1), Assigned(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64())),
363             (Assigned(i1), PrivateUse(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(*i2)),
364             (PrivateUse(i1), Assigned(i2)) => Label::Int(*i1).cmp(&Label::Int(i2.to_i64())),
365             (PrivateUse(i1), PrivateUse(i2)) => Label::Int(*i1).cmp(&Label::Int(*i2)),
366             (Assigned(_i1), Text(_t2)) => Ordering::Less,
367             (PrivateUse(_i1), Text(_t2)) => Ordering::Less,
368             (Text(_t1), Assigned(_i2)) => Ordering::Greater,
369             (Text(_t1), PrivateUse(_i2)) => Ordering::Greater,
370             (Text(t1), Text(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
371         }
372     }
373 }
374 
375 impl<T: EnumI64 + WithPrivateRange> PartialOrd for RegisteredLabelWithPrivate<T> {
partial_cmp(&self, other: &Self) -> Option<Ordering>376     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
377         Some(self.cmp(other))
378     }
379 }
380 
381 impl<T: EnumI64 + WithPrivateRange> AsCborValue for RegisteredLabelWithPrivate<T> {
from_cbor_value(value: Value) -> Result<Self>382     fn from_cbor_value(value: Value) -> Result<Self> {
383         match value {
384             Value::Integer(i) => {
385                 let i = i.try_into()?;
386                 if let Some(a) = T::from_i64(i) {
387                     Ok(RegisteredLabelWithPrivate::Assigned(a))
388                 } else if T::is_private(i) {
389                     Ok(RegisteredLabelWithPrivate::PrivateUse(i))
390                 } else {
391                     Err(CoseError::UnregisteredIanaNonPrivateValue)
392                 }
393             }
394             Value::Text(t) => Ok(RegisteredLabelWithPrivate::Text(t)),
395             v => cbor_type_error(&v, "int/tstr"),
396         }
397     }
to_cbor_value(self) -> Result<Value>398     fn to_cbor_value(self) -> Result<Value> {
399         Ok(match self {
400             RegisteredLabelWithPrivate::PrivateUse(i) => Value::from(i),
401             RegisteredLabelWithPrivate::Assigned(i) => Value::from(i.to_i64()),
402             RegisteredLabelWithPrivate::Text(t) => Value::Text(t),
403         })
404     }
405 }
406