• 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 #[cfg(feature = "std")]
91 impl std::error::Error for CoseError {}
92 
93 impl CoseError {
fmt_msg(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result94     fn fmt_msg(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95         match self {
96             CoseError::DecodeFailed(e) => write!(f, "decode CBOR failure: {}", e),
97             CoseError::DuplicateMapKey => write!(f, "duplicate map key"),
98             CoseError::EncodeFailed => write!(f, "encode CBOR failure"),
99             CoseError::ExtraneousData => write!(f, "extraneous data in CBOR input"),
100             CoseError::OutOfRangeIntegerValue => write!(f, "out of range integer value"),
101             CoseError::UnexpectedItem(got, want) => write!(f, "got {}, expected {}", got, want),
102             CoseError::UnregisteredIanaValue => write!(f, "expected recognized IANA value"),
103             CoseError::UnregisteredIanaNonPrivateValue => {
104                 write!(f, "expected value in IANA or private use range")
105             }
106         }
107     }
108 }
109 
110 /// Newtype wrapper around a byte slice to allow left-over data to be detected.
111 struct MeasuringReader<'a>(&'a [u8]);
112 
113 impl<'a> MeasuringReader<'a> {
new(buf: &'a [u8]) -> MeasuringReader<'a>114     fn new(buf: &'a [u8]) -> MeasuringReader<'a> {
115         MeasuringReader(buf)
116     }
117 
is_empty(&self) -> bool118     fn is_empty(&self) -> bool {
119         self.0.is_empty()
120     }
121 }
122 
123 impl<'a> ciborium_io::Read for &mut MeasuringReader<'a> {
124     type Error = EndOfFile;
125 
read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error>126     fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
127         if data.len() > self.0.len() {
128             return Err(EndOfFile);
129         }
130 
131         let (prefix, suffix) = self.0.split_at(data.len());
132         data.copy_from_slice(prefix);
133         self.0 = suffix;
134         Ok(())
135     }
136 }
137 
138 /// Read a CBOR [`Value`] from a byte slice, failing if any extra data remains after the `Value` has
139 /// been read.
read_to_value(slice: &[u8]) -> Result<Value>140 fn read_to_value(slice: &[u8]) -> Result<Value> {
141     let mut mr = MeasuringReader::new(slice);
142     let value = cbor::de::from_reader(&mut mr)?;
143     if mr.is_empty() {
144         Ok(value)
145     } else {
146         Err(CoseError::ExtraneousData)
147     }
148 }
149 
150 /// Trait for types that can be converted to/from a [`Value`].
151 pub trait AsCborValue: Sized {
152     /// Convert a [`Value`] into an instance of the type.
from_cbor_value(value: Value) -> Result<Self>153     fn from_cbor_value(value: Value) -> Result<Self>;
154     /// Convert the object into a [`Value`], consuming it along the way.
to_cbor_value(self) -> Result<Value>155     fn to_cbor_value(self) -> Result<Value>;
156 }
157 
158 /// Extension trait that adds serialization/deserialization methods.
159 pub trait CborSerializable: AsCborValue {
160     /// Create an object instance from serialized CBOR data in a slice.
from_slice(slice: &[u8]) -> Result<Self>161     fn from_slice(slice: &[u8]) -> Result<Self> {
162         Self::from_cbor_value(read_to_value(slice)?)
163     }
164 
165     /// Serialize this object to a vector, consuming it along the way.
to_vec(self) -> Result<Vec<u8>>166     fn to_vec(self) -> Result<Vec<u8>> {
167         let mut data = Vec::new();
168         cbor::ser::into_writer(&self.to_cbor_value()?, &mut data)?;
169         Ok(data)
170     }
171 }
172 
173 /// Extension trait that adds tagged serialization/deserialization methods.
174 pub trait TaggedCborSerializable: AsCborValue {
175     /// The associated tag value.
176     const TAG: u64;
177 
178     /// Create an object instance from serialized CBOR data in a slice, expecting an initial
179     /// tag value.
from_tagged_slice(slice: &[u8]) -> Result<Self>180     fn from_tagged_slice(slice: &[u8]) -> Result<Self> {
181         let (t, v) = read_to_value(slice)?.try_as_tag()?;
182         if t != Self::TAG {
183             return Err(CoseError::UnexpectedItem("tag", "other tag"));
184         }
185         Self::from_cbor_value(*v)
186     }
187 
188     /// Serialize this object to a vector, including initial tag, consuming the object along the
189     /// way.
to_tagged_vec(self) -> Result<Vec<u8>>190     fn to_tagged_vec(self) -> Result<Vec<u8>> {
191         let mut data = Vec::new();
192         cbor::ser::into_writer(
193             &Value::Tag(Self::TAG, Box::new(self.to_cbor_value()?)),
194             &mut data,
195         )?;
196         Ok(data)
197     }
198 }
199 
200 /// Algorithm identifier.
201 pub type Algorithm = crate::RegisteredLabelWithPrivate<iana::Algorithm>;
202 
203 impl Default for Algorithm {
default() -> Self204     fn default() -> Self {
205         Algorithm::Assigned(iana::Algorithm::Reserved)
206     }
207 }
208 
209 /// A COSE label may be either a signed integer value or a string.
210 #[derive(Clone, Debug, Eq, PartialEq)]
211 pub enum Label {
212     Int(i64),
213     Text(String),
214 }
215 
216 impl CborSerializable for Label {}
217 
218 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
219 ///
220 /// Note that this uses the ordering given by RFC 8949 section 4.2.1 (lexicographic ordering of
221 /// encoded form), which is *different* from the canonical ordering defined in RFC 7049 section 3.9
222 /// (where the primary sorting criterion is the length of the encoded form)
223 impl Ord for Label {
cmp(&self, other: &Self) -> Ordering224     fn cmp(&self, other: &Self) -> Ordering {
225         match (self, other) {
226             (Label::Int(i1), Label::Int(i2)) => match (i1.signum(), i2.signum()) {
227                 (-1, -1) => (-i1).cmp(&(-i2)),
228                 (-1, 0) => Ordering::Greater,
229                 (-1, 1) => Ordering::Greater,
230                 (0, -1) => Ordering::Less,
231                 (0, 0) => Ordering::Equal,
232                 (0, 1) => Ordering::Less,
233                 (1, -1) => Ordering::Less,
234                 (1, 0) => Ordering::Greater,
235                 (1, 1) => i1.cmp(i2),
236                 (_, _) => unreachable!(), // safe: all possibilies covered
237             },
238             (Label::Int(_i1), Label::Text(_t2)) => Ordering::Less,
239             (Label::Text(_t1), Label::Int(_i2)) => Ordering::Greater,
240             (Label::Text(t1), Label::Text(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
241         }
242     }
243 }
244 
245 impl PartialOrd for Label {
partial_cmp(&self, other: &Self) -> Option<Ordering>246     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
247         Some(self.cmp(other))
248     }
249 }
250 
251 impl AsCborValue for Label {
from_cbor_value(value: Value) -> Result<Self>252     fn from_cbor_value(value: Value) -> Result<Self> {
253         match value {
254             Value::Integer(i) => Ok(Label::Int(i.try_into()?)),
255             Value::Text(t) => Ok(Label::Text(t)),
256             v => cbor_type_error(&v, "int/tstr"),
257         }
258     }
to_cbor_value(self) -> Result<Value>259     fn to_cbor_value(self) -> Result<Value> {
260         Ok(match self {
261             Label::Int(i) => Value::from(i),
262             Label::Text(t) => Value::Text(t),
263         })
264     }
265 }
266 
267 /// A COSE label which can be either a signed integer value or a string, but
268 /// where the allowed integer values are governed by IANA.
269 #[derive(Clone, Debug, Eq, PartialEq)]
270 pub enum RegisteredLabel<T: EnumI64> {
271     Assigned(T),
272     Text(String),
273 }
274 
275 impl<T: EnumI64> CborSerializable for RegisteredLabel<T> {}
276 
277 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
278 impl<T: EnumI64> Ord for RegisteredLabel<T> {
cmp(&self, other: &Self) -> Ordering279     fn cmp(&self, other: &Self) -> Ordering {
280         match (self, other) {
281             (RegisteredLabel::Assigned(i1), RegisteredLabel::Assigned(i2)) => {
282                 Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64()))
283             }
284             (RegisteredLabel::Assigned(_i1), RegisteredLabel::Text(_t2)) => Ordering::Less,
285             (RegisteredLabel::Text(_t1), RegisteredLabel::Assigned(_i2)) => Ordering::Greater,
286             (RegisteredLabel::Text(t1), RegisteredLabel::Text(t2)) => {
287                 t1.len().cmp(&t2.len()).then(t1.cmp(t2))
288             }
289         }
290     }
291 }
292 
293 impl<T: EnumI64> PartialOrd for RegisteredLabel<T> {
partial_cmp(&self, other: &Self) -> Option<Ordering>294     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
295         Some(self.cmp(other))
296     }
297 }
298 
299 impl<T: EnumI64> AsCborValue for RegisteredLabel<T> {
from_cbor_value(value: Value) -> Result<Self>300     fn from_cbor_value(value: Value) -> Result<Self> {
301         match value {
302             Value::Integer(i) => {
303                 if let Some(a) = T::from_i64(i.try_into()?) {
304                     Ok(RegisteredLabel::Assigned(a))
305                 } else {
306                     Err(CoseError::UnregisteredIanaValue)
307                 }
308             }
309             Value::Text(t) => Ok(RegisteredLabel::Text(t)),
310             v => cbor_type_error(&v, "int/tstr"),
311         }
312     }
313 
to_cbor_value(self) -> Result<Value>314     fn to_cbor_value(self) -> Result<Value> {
315         Ok(match self {
316             RegisteredLabel::Assigned(e) => Value::from(e.to_i64()),
317             RegisteredLabel::Text(t) => Value::Text(t),
318         })
319     }
320 }
321 
322 /// A COSE label which can be either a signed integer value or a string, and
323 /// where the allowed integer values are governed by IANA but include a private
324 /// use range.
325 #[derive(Clone, Debug, Eq, PartialEq)]
326 pub enum RegisteredLabelWithPrivate<T: EnumI64 + WithPrivateRange> {
327     PrivateUse(i64),
328     Assigned(T),
329     Text(String),
330 }
331 
332 impl<T: EnumI64 + WithPrivateRange> CborSerializable for RegisteredLabelWithPrivate<T> {}
333 
334 /// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
335 impl<T: EnumI64 + WithPrivateRange> Ord for RegisteredLabelWithPrivate<T> {
cmp(&self, other: &Self) -> Ordering336     fn cmp(&self, other: &Self) -> Ordering {
337         use RegisteredLabelWithPrivate::{Assigned, PrivateUse, Text};
338         match (self, other) {
339             (Assigned(i1), Assigned(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64())),
340             (Assigned(i1), PrivateUse(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(*i2)),
341             (PrivateUse(i1), Assigned(i2)) => Label::Int(*i1).cmp(&Label::Int(i2.to_i64())),
342             (PrivateUse(i1), PrivateUse(i2)) => Label::Int(*i1).cmp(&Label::Int(*i2)),
343             (Assigned(_i1), Text(_t2)) => Ordering::Less,
344             (PrivateUse(_i1), Text(_t2)) => Ordering::Less,
345             (Text(_t1), Assigned(_i2)) => Ordering::Greater,
346             (Text(_t1), PrivateUse(_i2)) => Ordering::Greater,
347             (Text(t1), Text(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
348         }
349     }
350 }
351 
352 impl<T: EnumI64 + WithPrivateRange> PartialOrd for RegisteredLabelWithPrivate<T> {
partial_cmp(&self, other: &Self) -> Option<Ordering>353     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
354         Some(self.cmp(other))
355     }
356 }
357 
358 impl<T: EnumI64 + WithPrivateRange> AsCborValue for RegisteredLabelWithPrivate<T> {
from_cbor_value(value: Value) -> Result<Self>359     fn from_cbor_value(value: Value) -> Result<Self> {
360         match value {
361             Value::Integer(i) => {
362                 let i = i.try_into()?;
363                 if let Some(a) = T::from_i64(i) {
364                     Ok(RegisteredLabelWithPrivate::Assigned(a))
365                 } else if T::is_private(i) {
366                     Ok(RegisteredLabelWithPrivate::PrivateUse(i))
367                 } else {
368                     Err(CoseError::UnregisteredIanaNonPrivateValue)
369                 }
370             }
371             Value::Text(t) => Ok(RegisteredLabelWithPrivate::Text(t)),
372             v => cbor_type_error(&v, "int/tstr"),
373         }
374     }
to_cbor_value(self) -> Result<Value>375     fn to_cbor_value(self) -> Result<Value> {
376         Ok(match self {
377             RegisteredLabelWithPrivate::PrivateUse(i) => Value::from(i),
378             RegisteredLabelWithPrivate::Assigned(i) => Value::from(i.to_i64()),
379             RegisteredLabelWithPrivate::Text(t) => Value::Text(t),
380         })
381     }
382 }
383