1use crate::{
20 cbor,
21 cbor::value::Value,
22 common::AsCborValue,
23 iana,
24 util::{cbor_type_error, to_cbor_array, ValueTryAs},
25 CoseError, CoseRecipient, Header, ProtectedHeader, Result,
26};
27use alloc::{borrow::ToOwned, vec, vec::Vec};
28
29#[cfg(test)]
30mod tests;
31
32#[derive(Clone, Debug, Default, PartialEq)]
43pub struct CoseMac {
44 pub protected: ProtectedHeader,
45 pub unprotected: Header,
46 pub payload: Option<Vec<u8>>,
47 pub tag: Vec<u8>,
48 pub recipients: Vec<CoseRecipient>,
49}
50
51impl crate::CborSerializable for CoseMac {}
52
53impl crate::TaggedCborSerializable for CoseMac {
54 const TAG: u64 = iana::CborTag::CoseMac as u64;
55}
56
57impl AsCborValue for CoseMac {
58 fn from_cbor_value(value: Value) -> Result<Self> {
59 let mut a = value.try_as_array()?;
60 if a.len() != 5 {
61 return Err(CoseError::UnexpectedItem("array", "array with 5 items"));
62 }
63
64 let recipients = a
66 .remove(4)
67 .try_as_array_then_convert(CoseRecipient::from_cbor_value)?;
68
69 Ok(Self {
70 recipients,
71 tag: a.remove(3).try_as_bytes()?,
72 payload: match a.remove(2) {
73 Value::Bytes(b) => Some(b),
74 Value::Null => None,
75 v => return cbor_type_error(&v, "bstr"),
76 },
77 unprotected: Header::from_cbor_value(a.remove(1))?,
78 protected: ProtectedHeader::from_cbor_bstr(a.remove(0))?,
79 })
80 }
81
82 fn to_cbor_value(self) -> Result<Value> {
83 Ok(Value::Array(vec![
84 self.protected.cbor_bstr()?,
85 self.unprotected.to_cbor_value()?,
86 match self.payload {
87 None => Value::Null,
88 Some(b) => Value::Bytes(b),
89 },
90 Value::Bytes(self.tag),
91 to_cbor_array(self.recipients)?,
92 ]))
93 }
94}
95
96impl CoseMac {
97 #[deprecated = "Use verify_payload_tag() to ensure payload is present"]
104 pub fn verify_tag<F, E>(&self, external_aad: &[u8], verify: F) -> Result<(), E>
105 where
106 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
107 {
108 let tbm = self.tbm(external_aad);
109 verify(&self.tag, &tbm)
110 }
111
112 pub fn verify_payload_tag<F, E, G>(
117 &self,
118 external_aad: &[u8],
119 missing_payload_error: G,
120 verify: F,
121 ) -> Result<(), E>
122 where
123 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
124 G: FnOnce() -> E,
125 {
126 if self.payload.is_none() {
127 return Err(missing_payload_error());
128 }
129 let tbm = self.tbm(external_aad);
130 verify(&self.tag, &tbm)
131 }
132
133 fn tbm(&self, external_aad: &[u8]) -> Vec<u8> {
140 mac_structure_data(
141 MacContext::CoseMac,
142 self.protected.clone(),
143 external_aad,
144 self.payload.as_ref().expect("payload missing"), )
146 }
147}
148
149#[derive(Debug, Default)]
151pub struct CoseMacBuilder(CoseMac);
152
153impl CoseMacBuilder {
154 builder! {CoseMac}
155 builder_set_protected! {protected}
156 builder_set! {unprotected: Header}
157 builder_set! {tag: Vec<u8>}
158 builder_set_optional! {payload: Vec<u8>}
159
160 #[must_use]
162 pub fn add_recipient(mut self, recipient: CoseRecipient) -> Self {
163 self.0.recipients.push(recipient);
164 self
165 }
166
167 #[must_use]
174 pub fn create_tag<F>(self, external_aad: &[u8], create: F) -> Self
175 where
176 F: FnOnce(&[u8]) -> Vec<u8>,
177 {
178 let tbm = self.0.tbm(external_aad);
179 self.tag(create(&tbm))
180 }
181
182 pub fn try_create_tag<F, E>(self, external_aad: &[u8], create: F) -> Result<Self, E>
189 where
190 F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
191 {
192 let tbm = self.0.tbm(external_aad);
193 Ok(self.tag(create(&tbm)?))
194 }
195}
196
197#[derive(Clone, Debug, Default, PartialEq)]
208pub struct CoseMac0 {
209 pub protected: ProtectedHeader,
210 pub unprotected: Header,
211 pub payload: Option<Vec<u8>>,
212 pub tag: Vec<u8>,
213}
214
215impl crate::CborSerializable for CoseMac0 {}
216
217impl crate::TaggedCborSerializable for CoseMac0 {
218 const TAG: u64 = iana::CborTag::CoseMac0 as u64;
219}
220
221impl AsCborValue for CoseMac0 {
222 fn from_cbor_value(value: Value) -> Result<Self> {
223 let mut a = value.try_as_array()?;
224 if a.len() != 4 {
225 return Err(CoseError::UnexpectedItem("array", "array with 4 items"));
226 }
227
228 Ok(Self {
230 tag: a.remove(3).try_as_bytes()?,
231 payload: match a.remove(2) {
232 Value::Bytes(b) => Some(b),
233 Value::Null => None,
234 v => return cbor_type_error(&v, "bstr"),
235 },
236 unprotected: Header::from_cbor_value(a.remove(1))?,
237 protected: ProtectedHeader::from_cbor_bstr(a.remove(0))?,
238 })
239 }
240
241 fn to_cbor_value(self) -> Result<Value> {
242 Ok(Value::Array(vec![
243 self.protected.cbor_bstr()?,
244 self.unprotected.to_cbor_value()?,
245 match self.payload {
246 None => Value::Null,
247 Some(b) => Value::Bytes(b),
248 },
249 Value::Bytes(self.tag),
250 ]))
251 }
252}
253
254impl CoseMac0 {
255 #[deprecated = "Use verify_payload_tag() to ensure payload is present"]
262 pub fn verify_tag<F, E>(&self, external_aad: &[u8], verify: F) -> Result<(), E>
263 where
264 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
265 {
266 let tbm = self.tbm(external_aad);
267 verify(&self.tag, &tbm)
268 }
269
270 pub fn verify_payload_tag<F, E, G>(
275 &self,
276 external_aad: &[u8],
277 missing_payload_error: G,
278 verify: F,
279 ) -> Result<(), E>
280 where
281 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
282 G: FnOnce() -> E,
283 {
284 if self.payload.is_none() {
285 return Err(missing_payload_error());
286 }
287 let tbm = self.tbm(external_aad);
288 verify(&self.tag, &tbm)
289 }
290
291 fn tbm(&self, external_aad: &[u8]) -> Vec<u8> {
298 mac_structure_data(
299 MacContext::CoseMac0,
300 self.protected.clone(),
301 external_aad,
302 self.payload.as_ref().expect("payload missing"), )
304 }
305}
306
307#[derive(Debug, Default)]
309pub struct CoseMac0Builder(CoseMac0);
310
311impl CoseMac0Builder {
312 builder! {CoseMac0}
313 builder_set_protected! {protected}
314 builder_set! {unprotected: Header}
315 builder_set! {tag: Vec<u8>}
316 builder_set_optional! {payload: Vec<u8>}
317
318 #[must_use]
325 pub fn create_tag<F>(self, external_aad: &[u8], create: F) -> Self
326 where
327 F: FnOnce(&[u8]) -> Vec<u8>,
328 {
329 let tbm = self.0.tbm(external_aad);
330 self.tag(create(&tbm))
331 }
332
333 pub fn try_create_tag<F, E>(self, external_aad: &[u8], create: F) -> Result<Self, E>
340 where
341 F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
342 {
343 let tbm = self.0.tbm(external_aad);
344 Ok(self.tag(create(&tbm)?))
345 }
346}
347
348#[derive(Clone, Copy, Debug)]
350pub enum MacContext {
351 CoseMac,
352 CoseMac0,
353}
354
355impl MacContext {
356 fn text(&self) -> &'static str {
358 match self {
359 MacContext::CoseMac => "MAC",
360 MacContext::CoseMac0 => "MAC0",
361 }
362 }
363}
364
365pub fn mac_structure_data(
376 context: MacContext,
377 protected: ProtectedHeader,
378 external_aad: &[u8],
379 payload: &[u8],
380) -> Vec<u8> {
381 let arr = vec![
382 Value::Text(context.text().to_owned()),
383 protected.cbor_bstr().expect("failed to serialize header"), Value::Bytes(external_aad.to_vec()),
385 Value::Bytes(payload.to_vec()),
386 ];
387
388 let mut data = Vec::new();
389 cbor::ser::into_writer(&Value::Array(arr), &mut data).unwrap(); data
391}