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