1use 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#[derive(Debug)]
34pub struct EndOfFile;
35
36pub enum CoseError {
38 DecodeFailed(cbor::de::Error<EndOfFile>),
40 DuplicateMapKey,
42 EncodeFailed,
44 ExtraneousData,
46 OutOfRangeIntegerValue,
49 UnexpectedItem(&'static str, &'static str),
51 UnregisteredIanaValue,
53 UnregisteredIanaNonPrivateValue,
55}
56
57pub 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 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
118fn 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
129pub trait AsCborValue: Sized {
131 fn from_cbor_value(value: Value) -> Result<Self>;
133 fn to_cbor_value(self) -> Result<Value>;
135}
136
137pub trait CborSerializable: AsCborValue {
139 fn from_slice(slice: &[u8]) -> Result<Self> {
142 Self::from_cbor_value(read_to_value(slice)?)
143 }
144
145 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
153pub trait TaggedCborSerializable: AsCborValue {
155 const TAG: u64;
157
158 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 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
180impl 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
192pub 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#[derive(Clone, Debug, Eq, PartialEq)]
203pub enum Label {
204 Int(i64),
205 Text(String),
206}
207
208impl CborSerializable for Label {}
209
210impl 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!(), },
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 pub fn cmp_canonical(&self, other: &Self) -> Ordering {
252 let encoded_self = self.clone().to_vec().unwrap(); let encoded_other = other.clone().to_vec().unwrap(); if encoded_self.len() != encoded_other.len() {
255 encoded_self.len().cmp(&encoded_other.len())
257 } else {
258 encoded_self.cmp(&encoded_other)
260 }
261 }
262}
263
264pub enum CborOrdering {
266 Lexicographic,
269 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#[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
306impl<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#[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
369impl<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}