coset/common/
mod.rs

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
19use crate::{
20    cbor,
21    cbor::value::Value,
22    iana,
23    iana::{EnumI64, WithPrivateRange},
24    util::{cbor_type_error, ValueTryAs},
25};
26use alloc::{boxed::Box, string::String, vec::Vec};
27use core::{cmp::Ordering, convert::TryInto};
28
29#[cfg(test)]
30mod tests;
31
32/// Marker structure indicating that the EOF was encountered when reading CBOR data.
33#[derive(Debug)]
34pub struct EndOfFile;
35
36/// Error type for failures in encoding or decoding COSE types.
37pub 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
58pub type Result<T, E = CoseError> = core::result::Result<T, E>;
59
60impl<T> core::convert::From<cbor::de::Error<T>> for CoseError {
61    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
74impl<T> core::convert::From<cbor::ser::Error<T>> for CoseError {
75    fn from(_e: cbor::ser::Error<T>) -> Self {
76        CoseError::EncodeFailed
77    }
78}
79
80impl core::convert::From<core::num::TryFromIntError> for CoseError {
81    fn from(_: core::num::TryFromIntError) -> Self {
82        CoseError::OutOfRangeIntegerValue
83    }
84}
85
86impl core::fmt::Debug for CoseError {
87    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88        self.fmt_msg(f)
89    }
90}
91
92impl core::fmt::Display for CoseError {
93    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94        self.fmt_msg(f)
95    }
96}
97
98#[cfg(feature = "std")]
99impl std::error::Error for CoseError {}
100
101impl CoseError {
102    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 {got}, expected {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.
120fn 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`].
130pub trait AsCborValue: Sized {
131    /// Convert a [`Value`] into an instance of the type.
132    fn from_cbor_value(value: Value) -> Result<Self>;
133    /// Convert the object into a [`Value`], consuming it along the way.
134    fn to_cbor_value(self) -> Result<Value>;
135}
136
137/// Extension trait that adds serialization/deserialization methods.
138pub 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.
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.
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.
154pub 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.
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.
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`].
181impl AsCborValue for Value {
182    fn from_cbor_value(value: Value) -> Result<Self> {
183        Ok(value)
184    }
185    fn to_cbor_value(self) -> Result<Value> {
186        Ok(self)
187    }
188}
189
190impl CborSerializable for Value {}
191
192/// Algorithm identifier.
193pub type Algorithm = crate::RegisteredLabelWithPrivate<iana::Algorithm>;
194
195impl Default for Algorithm {
196    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)]
203pub enum Label {
204    Int(i64),
205    Text(String),
206}
207
208impl 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)
215impl Ord for Label {
216    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
237impl PartialOrd for Label {
238    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
239        Some(self.cmp(other))
240    }
241}
242
243impl 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.
251    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.
265pub 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
274impl AsCborValue for Label {
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    }
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)]
293pub enum RegisteredLabel<T: EnumI64> {
294    Assigned(T),
295    Text(String),
296}
297
298impl<T: EnumI64> From<T> for RegisteredLabel<T> {
299    fn from(val: T) -> Self {
300        Self::Assigned(val)
301    }
302}
303
304impl<T: EnumI64> CborSerializable for RegisteredLabel<T> {}
305
306/// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
307impl<T: EnumI64> Ord for RegisteredLabel<T> {
308    fn cmp(&self, other: &Self) -> Ordering {
309        match (self, other) {
310            (RegisteredLabel::Assigned(i1), RegisteredLabel::Assigned(i2)) => {
311                Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64()))
312            }
313            (RegisteredLabel::Assigned(_i1), RegisteredLabel::Text(_t2)) => Ordering::Less,
314            (RegisteredLabel::Text(_t1), RegisteredLabel::Assigned(_i2)) => Ordering::Greater,
315            (RegisteredLabel::Text(t1), RegisteredLabel::Text(t2)) => {
316                t1.len().cmp(&t2.len()).then(t1.cmp(t2))
317            }
318        }
319    }
320}
321
322impl<T: EnumI64> PartialOrd for RegisteredLabel<T> {
323    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
324        Some(self.cmp(other))
325    }
326}
327
328impl<T: EnumI64> AsCborValue for RegisteredLabel<T> {
329    fn from_cbor_value(value: Value) -> Result<Self> {
330        match value {
331            Value::Integer(i) => {
332                if let Some(a) = T::from_i64(i.try_into()?) {
333                    Ok(RegisteredLabel::Assigned(a))
334                } else {
335                    Err(CoseError::UnregisteredIanaValue)
336                }
337            }
338            Value::Text(t) => Ok(RegisteredLabel::Text(t)),
339            v => cbor_type_error(&v, "int/tstr"),
340        }
341    }
342
343    fn to_cbor_value(self) -> Result<Value> {
344        Ok(match self {
345            RegisteredLabel::Assigned(e) => Value::from(e.to_i64()),
346            RegisteredLabel::Text(t) => Value::Text(t),
347        })
348    }
349}
350
351/// A COSE label which can be either a signed integer value or a string, and
352/// where the allowed integer values are governed by IANA but include a private
353/// use range.
354#[derive(Clone, Debug, Eq, PartialEq)]
355pub enum RegisteredLabelWithPrivate<T: EnumI64 + WithPrivateRange> {
356    PrivateUse(i64),
357    Assigned(T),
358    Text(String),
359}
360
361impl<T: EnumI64 + WithPrivateRange> From<T> for RegisteredLabelWithPrivate<T> {
362    fn from(val: T) -> Self {
363        Self::Assigned(val)
364    }
365}
366
367impl<T: EnumI64 + WithPrivateRange> CborSerializable for RegisteredLabelWithPrivate<T> {}
368
369/// Manual implementation of [`Ord`] to ensure that CBOR canonical ordering is respected.
370impl<T: EnumI64 + WithPrivateRange> Ord for RegisteredLabelWithPrivate<T> {
371    fn cmp(&self, other: &Self) -> Ordering {
372        use RegisteredLabelWithPrivate::{Assigned, PrivateUse, Text};
373        match (self, other) {
374            (Assigned(i1), Assigned(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(i2.to_i64())),
375            (Assigned(i1), PrivateUse(i2)) => Label::Int(i1.to_i64()).cmp(&Label::Int(*i2)),
376            (PrivateUse(i1), Assigned(i2)) => Label::Int(*i1).cmp(&Label::Int(i2.to_i64())),
377            (PrivateUse(i1), PrivateUse(i2)) => Label::Int(*i1).cmp(&Label::Int(*i2)),
378            (Assigned(_i1), Text(_t2)) => Ordering::Less,
379            (PrivateUse(_i1), Text(_t2)) => Ordering::Less,
380            (Text(_t1), Assigned(_i2)) => Ordering::Greater,
381            (Text(_t1), PrivateUse(_i2)) => Ordering::Greater,
382            (Text(t1), Text(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
383        }
384    }
385}
386
387impl<T: EnumI64 + WithPrivateRange> PartialOrd for RegisteredLabelWithPrivate<T> {
388    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
389        Some(self.cmp(other))
390    }
391}
392
393impl<T: EnumI64 + WithPrivateRange> AsCborValue for RegisteredLabelWithPrivate<T> {
394    fn from_cbor_value(value: Value) -> Result<Self> {
395        match value {
396            Value::Integer(i) => {
397                let i = i.try_into()?;
398                if let Some(a) = T::from_i64(i) {
399                    Ok(RegisteredLabelWithPrivate::Assigned(a))
400                } else if T::is_private(i) {
401                    Ok(RegisteredLabelWithPrivate::PrivateUse(i))
402                } else {
403                    Err(CoseError::UnregisteredIanaNonPrivateValue)
404                }
405            }
406            Value::Text(t) => Ok(RegisteredLabelWithPrivate::Text(t)),
407            v => cbor_type_error(&v, "int/tstr"),
408        }
409    }
410    fn to_cbor_value(self) -> Result<Value> {
411        Ok(match self {
412            RegisteredLabelWithPrivate::PrivateUse(i) => Value::from(i),
413            RegisteredLabelWithPrivate::Assigned(i) => Value::from(i.to_i64()),
414            RegisteredLabelWithPrivate::Text(t) => Value::Text(t),
415        })
416    }
417}