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