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