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 //! COSE_Sign* functionality.
18
19 use crate::{
20 cbor,
21 cbor::value::Value,
22 common::AsCborValue,
23 iana,
24 util::{cbor_type_error, to_cbor_array, ValueTryAs},
25 CoseError, Header, ProtectedHeader, Result,
26 };
27 use alloc::{borrow::ToOwned, vec, vec::Vec};
28
29 #[cfg(test)]
30 mod tests;
31
32 /// Structure representing a cryptographic signature.
33 ///
34 /// ```cddl
35 /// COSE_Signature = [
36 /// Headers,
37 /// signature : bstr
38 /// ]
39 /// ```
40 #[derive(Clone, Debug, Default, PartialEq)]
41 pub struct CoseSignature {
42 pub protected: ProtectedHeader,
43 pub unprotected: Header,
44 pub signature: Vec<u8>,
45 }
46
47 impl crate::CborSerializable for CoseSignature {}
48
49 impl AsCborValue for CoseSignature {
from_cbor_value(value: Value) -> Result<Self>50 fn from_cbor_value(value: Value) -> Result<Self> {
51 let mut a = value.try_as_array()?;
52 if a.len() != 3 {
53 return Err(CoseError::UnexpectedItem("array", "array with 3 items"));
54 }
55
56 // Remove array elements in reverse order to avoid shifts.
57 Ok(Self {
58 signature: a.remove(2).try_as_bytes()?,
59 unprotected: Header::from_cbor_value(a.remove(1))?,
60 protected: ProtectedHeader::from_cbor_bstr(a.remove(0))?,
61 })
62 }
63
to_cbor_value(self) -> Result<Value>64 fn to_cbor_value(self) -> Result<Value> {
65 Ok(Value::Array(vec![
66 self.protected.cbor_bstr()?,
67 self.unprotected.to_cbor_value()?,
68 Value::Bytes(self.signature),
69 ]))
70 }
71 }
72
73 /// Builder for [`CoseSignature`] objects.
74 #[derive(Debug, Default)]
75 pub struct CoseSignatureBuilder(CoseSignature);
76
77 impl CoseSignatureBuilder {
78 builder! {CoseSignature}
79 builder_set_protected! {protected}
80 builder_set! {unprotected: Header}
81 builder_set! {signature: Vec<u8>}
82 }
83
84 /// Signed payload with signatures.
85 ///
86 /// ```cdl
87 /// COSE_Sign = [
88 /// Headers,
89 /// payload : bstr / nil,
90 /// signatures : [+ COSE_Signature]
91 /// ]
92 /// ```
93 #[derive(Clone, Debug, Default, PartialEq)]
94 pub struct CoseSign {
95 pub protected: ProtectedHeader,
96 pub unprotected: Header,
97 pub payload: Option<Vec<u8>>,
98 pub signatures: Vec<CoseSignature>,
99 }
100
101 impl crate::CborSerializable for CoseSign {}
102 impl crate::TaggedCborSerializable for CoseSign {
103 const TAG: u64 = iana::CborTag::CoseSign as u64;
104 }
105
106 impl AsCborValue for CoseSign {
from_cbor_value(value: Value) -> Result<Self>107 fn from_cbor_value(value: Value) -> Result<Self> {
108 let mut a = value.try_as_array()?;
109 if a.len() != 4 {
110 return Err(CoseError::UnexpectedItem("array", "array with 4 items"));
111 }
112
113 // Remove array elements in reverse order to avoid shifts.
114 let signatures = a.remove(3).try_as_array_then_convert(|v| {
115 CoseSignature::from_cbor_value(v)
116 .map_err(|_e| CoseError::UnexpectedItem("non-signature", "map for COSE_Signature"))
117 })?;
118
119 Ok(Self {
120 signatures,
121 payload: match a.remove(2) {
122 Value::Bytes(b) => Some(b),
123 Value::Null => None,
124 v => return cbor_type_error(&v, "bstr or nil"),
125 },
126 unprotected: Header::from_cbor_value(a.remove(1))?,
127 protected: ProtectedHeader::from_cbor_bstr(a.remove(0))?,
128 })
129 }
130
to_cbor_value(self) -> Result<Value>131 fn to_cbor_value(self) -> Result<Value> {
132 Ok(Value::Array(vec![
133 self.protected.cbor_bstr()?,
134 self.unprotected.to_cbor_value()?,
135 match self.payload {
136 Some(b) => Value::Bytes(b),
137 None => Value::Null,
138 },
139 to_cbor_array(self.signatures)?,
140 ]))
141 }
142 }
143
144 impl CoseSign {
145 /// Verify the indicated signature value, using `verifier` on the signature value and serialized
146 /// data (in that order).
147 ///
148 /// # Panics
149 ///
150 /// This method will panic if `which` is >= `self.signatures.len()`.
verify_signature<F, E>(&self, which: usize, aad: &[u8], verifier: F) -> Result<(), E> where F: FnOnce(&[u8], &[u8]) -> Result<(), E>,151 pub fn verify_signature<F, E>(&self, which: usize, aad: &[u8], verifier: F) -> Result<(), E>
152 where
153 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
154 {
155 let sig = &self.signatures[which];
156 let tbs_data = self.tbs_data(aad, sig);
157 verifier(&sig.signature, &tbs_data)
158 }
159
160 /// Verify the indicated signature value for a detached payload, using `verifier` on the
161 /// signature value and serialized data (in that order).
162 ///
163 /// # Panics
164 ///
165 /// This method will panic if `which` is >= `self.signatures.len()`.
166 ///
167 /// This method will panic if `self.payload.is_some()`.
verify_detached_signature<F, E>( &self, which: usize, payload: &[u8], aad: &[u8], verifier: F, ) -> Result<(), E> where F: FnOnce(&[u8], &[u8]) -> Result<(), E>,168 pub fn verify_detached_signature<F, E>(
169 &self,
170 which: usize,
171 payload: &[u8],
172 aad: &[u8],
173 verifier: F,
174 ) -> Result<(), E>
175 where
176 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
177 {
178 let sig = &self.signatures[which];
179 let tbs_data = self.tbs_detached_data(payload, aad, sig);
180 verifier(&sig.signature, &tbs_data)
181 }
182
183 /// Construct the to-be-signed data for this object.
tbs_data(&self, aad: &[u8], sig: &CoseSignature) -> Vec<u8>184 pub fn tbs_data(&self, aad: &[u8], sig: &CoseSignature) -> Vec<u8> {
185 sig_structure_data(
186 SignatureContext::CoseSignature,
187 self.protected.clone(),
188 Some(sig.protected.clone()),
189 aad,
190 self.payload.as_ref().unwrap_or(&vec![]),
191 )
192 }
193
194 /// Construct the to-be-signed data for this object, using a detached payload.
195 ///
196 /// # Panics
197 ///
198 /// This method will panic if `self.payload.is_some()`.
tbs_detached_data(&self, payload: &[u8], aad: &[u8], sig: &CoseSignature) -> Vec<u8>199 pub fn tbs_detached_data(&self, payload: &[u8], aad: &[u8], sig: &CoseSignature) -> Vec<u8> {
200 assert!(self.payload.is_none());
201 sig_structure_data(
202 SignatureContext::CoseSignature,
203 self.protected.clone(),
204 Some(sig.protected.clone()),
205 aad,
206 payload,
207 )
208 }
209 }
210
211 /// Builder for [`CoseSign`] objects.
212 #[derive(Debug, Default)]
213 pub struct CoseSignBuilder(CoseSign);
214
215 impl CoseSignBuilder {
216 builder! {CoseSign}
217 builder_set_protected! {protected}
218 builder_set! {unprotected: Header}
219 builder_set_optional! {payload: Vec<u8>}
220
221 /// Add a signature value.
222 #[must_use]
add_signature(mut self, sig: CoseSignature) -> Self223 pub fn add_signature(mut self, sig: CoseSignature) -> Self {
224 self.0.signatures.push(sig);
225 self
226 }
227
228 /// Calculate the signature value, using `signer` to generate the signature bytes that will be
229 /// used to complete `sig`. Any protected header values should be set before using this
230 /// method.
231 #[must_use]
add_created_signature<F>(self, mut sig: CoseSignature, aad: &[u8], signer: F) -> Self where F: FnOnce(&[u8]) -> Vec<u8>,232 pub fn add_created_signature<F>(self, mut sig: CoseSignature, aad: &[u8], signer: F) -> Self
233 where
234 F: FnOnce(&[u8]) -> Vec<u8>,
235 {
236 let tbs_data = self.0.tbs_data(aad, &sig);
237 sig.signature = signer(&tbs_data);
238 self.add_signature(sig)
239 }
240
241 /// Calculate the signature value for a detached payload, using `signer` to generate the
242 /// signature bytes that will be used to complete `sig`. Any protected header values should
243 /// be set before using this method.
244 ///
245 /// # Panics
246 ///
247 /// This method will panic if `self.payload.is_some()`.
248 #[must_use]
add_detached_signature<F>( self, mut sig: CoseSignature, payload: &[u8], aad: &[u8], signer: F, ) -> Self where F: FnOnce(&[u8]) -> Vec<u8>,249 pub fn add_detached_signature<F>(
250 self,
251 mut sig: CoseSignature,
252 payload: &[u8],
253 aad: &[u8],
254 signer: F,
255 ) -> Self
256 where
257 F: FnOnce(&[u8]) -> Vec<u8>,
258 {
259 let tbs_data = self.0.tbs_detached_data(payload, aad, &sig);
260 sig.signature = signer(&tbs_data);
261 self.add_signature(sig)
262 }
263
264 /// Calculate the signature value, using `signer` to generate the signature bytes that will be
265 /// used to complete `sig`. Any protected header values should be set before using this
266 /// method.
try_add_created_signature<F, E>( self, mut sig: CoseSignature, aad: &[u8], signer: F, ) -> Result<Self, E> where F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,267 pub fn try_add_created_signature<F, E>(
268 self,
269 mut sig: CoseSignature,
270 aad: &[u8],
271 signer: F,
272 ) -> Result<Self, E>
273 where
274 F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
275 {
276 let tbs_data = self.0.tbs_data(aad, &sig);
277 sig.signature = signer(&tbs_data)?;
278 Ok(self.add_signature(sig))
279 }
280
281 /// Calculate the signature value for a detached payload, using `signer` to generate the
282 /// signature bytes that will be used to complete `sig`. Any protected header values should
283 /// be set before using this method.
284 ///
285 /// # Panics
286 ///
287 /// This method will panic if `self.payload.is_some()`.
try_add_detached_signature<F, E>( self, mut sig: CoseSignature, payload: &[u8], aad: &[u8], signer: F, ) -> Result<Self, E> where F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,288 pub fn try_add_detached_signature<F, E>(
289 self,
290 mut sig: CoseSignature,
291 payload: &[u8],
292 aad: &[u8],
293 signer: F,
294 ) -> Result<Self, E>
295 where
296 F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
297 {
298 let tbs_data = self.0.tbs_detached_data(payload, aad, &sig);
299 sig.signature = signer(&tbs_data)?;
300 Ok(self.add_signature(sig))
301 }
302 }
303
304 /// Signed payload with a single signature.
305 ///
306 /// ```cddl
307 /// COSE_Sign1 = [
308 /// Headers,
309 /// payload : bstr / nil,
310 /// signature : bstr
311 /// ]
312 /// ```
313 #[derive(Clone, Debug, Default, PartialEq)]
314 pub struct CoseSign1 {
315 pub protected: ProtectedHeader,
316 pub unprotected: Header,
317 pub payload: Option<Vec<u8>>,
318 pub signature: Vec<u8>,
319 }
320
321 impl crate::CborSerializable for CoseSign1 {}
322 impl crate::TaggedCborSerializable for CoseSign1 {
323 const TAG: u64 = iana::CborTag::CoseSign1 as u64;
324 }
325
326 impl AsCborValue for CoseSign1 {
from_cbor_value(value: Value) -> Result<Self>327 fn from_cbor_value(value: Value) -> Result<Self> {
328 let mut a = value.try_as_array()?;
329 if a.len() != 4 {
330 return Err(CoseError::UnexpectedItem("array", "array with 4 items"));
331 }
332
333 // Remove array elements in reverse order to avoid shifts.
334 Ok(Self {
335 signature: a.remove(3).try_as_bytes()?,
336 payload: match a.remove(2) {
337 Value::Bytes(b) => Some(b),
338 Value::Null => None,
339 v => return cbor_type_error(&v, "bstr or nil"),
340 },
341 unprotected: Header::from_cbor_value(a.remove(1))?,
342 protected: ProtectedHeader::from_cbor_bstr(a.remove(0))?,
343 })
344 }
345
to_cbor_value(self) -> Result<Value>346 fn to_cbor_value(self) -> Result<Value> {
347 Ok(Value::Array(vec![
348 self.protected.cbor_bstr()?,
349 self.unprotected.to_cbor_value()?,
350 match self.payload {
351 Some(b) => Value::Bytes(b),
352 None => Value::Null,
353 },
354 Value::Bytes(self.signature),
355 ]))
356 }
357 }
358
359 impl CoseSign1 {
360 /// Verify the signature value, using `verifier` on the signature value and serialized data (in
361 /// that order).
verify_signature<F, E>(&self, aad: &[u8], verifier: F) -> Result<(), E> where F: FnOnce(&[u8], &[u8]) -> Result<(), E>,362 pub fn verify_signature<F, E>(&self, aad: &[u8], verifier: F) -> Result<(), E>
363 where
364 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
365 {
366 let tbs_data = self.tbs_data(aad);
367 verifier(&self.signature, &tbs_data)
368 }
369
370 /// Verify the indicated signature value for a detached payload, using `verifier` on the
371 /// signature value and serialized data (in that order).
372 ///
373 /// # Panics
374 ///
375 /// This method will panic if `self.payload.is_some()`.
verify_detached_signature<F, E>( &self, payload: &[u8], aad: &[u8], verifier: F, ) -> Result<(), E> where F: FnOnce(&[u8], &[u8]) -> Result<(), E>,376 pub fn verify_detached_signature<F, E>(
377 &self,
378 payload: &[u8],
379 aad: &[u8],
380 verifier: F,
381 ) -> Result<(), E>
382 where
383 F: FnOnce(&[u8], &[u8]) -> Result<(), E>,
384 {
385 let tbs_data = self.tbs_detached_data(payload, aad);
386 verifier(&self.signature, &tbs_data)
387 }
388
389 /// Construct the to-be-signed data for this object.
tbs_data(&self, aad: &[u8]) -> Vec<u8>390 pub fn tbs_data(&self, aad: &[u8]) -> Vec<u8> {
391 sig_structure_data(
392 SignatureContext::CoseSign1,
393 self.protected.clone(),
394 None,
395 aad,
396 self.payload.as_ref().unwrap_or(&vec![]),
397 )
398 }
399
400 /// Construct the to-be-signed data for this object, using a detached payload.
401 ///
402 /// # Panics
403 ///
404 /// This method will panic if `self.payload.is_some()`.
tbs_detached_data(&self, payload: &[u8], aad: &[u8]) -> Vec<u8>405 pub fn tbs_detached_data(&self, payload: &[u8], aad: &[u8]) -> Vec<u8> {
406 assert!(self.payload.is_none());
407 sig_structure_data(
408 SignatureContext::CoseSign1,
409 self.protected.clone(),
410 None,
411 aad,
412 payload,
413 )
414 }
415 }
416
417 /// Builder for [`CoseSign1`] objects.
418 #[derive(Debug, Default)]
419 pub struct CoseSign1Builder(CoseSign1);
420
421 impl CoseSign1Builder {
422 builder! {CoseSign1}
423 builder_set_protected! {protected}
424 builder_set! {unprotected: Header}
425 builder_set! {signature: Vec<u8>}
426 builder_set_optional! {payload: Vec<u8>}
427
428 /// Calculate the signature value, using `signer` to generate the signature bytes. Any
429 /// protected header values should be set before using this method.
430 #[must_use]
create_signature<F>(self, aad: &[u8], signer: F) -> Self where F: FnOnce(&[u8]) -> Vec<u8>,431 pub fn create_signature<F>(self, aad: &[u8], signer: F) -> Self
432 where
433 F: FnOnce(&[u8]) -> Vec<u8>,
434 {
435 let sig_data = signer(&self.0.tbs_data(aad));
436 self.signature(sig_data)
437 }
438
439 /// Calculate the signature value for a detached payload, using `signer` to generate the
440 /// signature bytes. Any protected header values should be set before using this method.
441 ///
442 /// # Panics
443 ///
444 /// This method will panic if `self.payload.is_some()`.
445 #[must_use]
create_detached_signature<F>(self, payload: &[u8], aad: &[u8], signer: F) -> Self where F: FnOnce(&[u8]) -> Vec<u8>,446 pub fn create_detached_signature<F>(self, payload: &[u8], aad: &[u8], signer: F) -> Self
447 where
448 F: FnOnce(&[u8]) -> Vec<u8>,
449 {
450 let sig_data = signer(&self.0.tbs_detached_data(payload, aad));
451 self.signature(sig_data)
452 }
453
454 /// Calculate the signature value, using `signer` to generate the signature bytes. Any
455 /// protected header values should be set before using this method.
try_create_signature<F, E>(self, aad: &[u8], signer: F) -> Result<Self, E> where F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,456 pub fn try_create_signature<F, E>(self, aad: &[u8], signer: F) -> Result<Self, E>
457 where
458 F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
459 {
460 let sig_data = signer(&self.0.tbs_data(aad))?;
461 Ok(self.signature(sig_data))
462 }
463
464 /// Calculate the signature value for a detached payload, using `signer` to generate the
465 /// signature bytes. Any protected header values should be set before using this method.
466 ///
467 /// # Panics
468 ///
469 /// This method will panic if `self.payload.is_some()`.
try_create_detached_signature<F, E>( self, payload: &[u8], aad: &[u8], signer: F, ) -> Result<Self, E> where F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,470 pub fn try_create_detached_signature<F, E>(
471 self,
472 payload: &[u8],
473 aad: &[u8],
474 signer: F,
475 ) -> Result<Self, E>
476 where
477 F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
478 {
479 let sig_data = signer(&self.0.tbs_detached_data(payload, aad))?;
480 Ok(self.signature(sig_data))
481 }
482 }
483
484 /// Possible signature contexts.
485 #[derive(Clone, Copy)]
486 pub enum SignatureContext {
487 CoseSignature,
488 CoseSign1,
489 CounterSignature,
490 }
491
492 impl SignatureContext {
493 /// Return the context string as per RFC 8152 section 4.4.
text(&self) -> &'static str494 fn text(&self) -> &'static str {
495 match self {
496 SignatureContext::CoseSignature => "Signature",
497 SignatureContext::CoseSign1 => "Signature1",
498 SignatureContext::CounterSignature => "CounterSignature",
499 }
500 }
501 }
502
503 /// Create a binary blob that will be signed.
504 ///
505 /// ```cddl
506 /// Sig_structure = [
507 /// context : "Signature" / "Signature1" / "CounterSignature",
508 /// body_protected : empty_or_serialized_map,
509 /// ? sign_protected : empty_or_serialized_map,
510 /// external_aad : bstr,
511 /// payload : bstr
512 /// ]
513 /// ```
sig_structure_data( context: SignatureContext, body: ProtectedHeader, sign: Option<ProtectedHeader>, aad: &[u8], payload: &[u8], ) -> Vec<u8>514 pub fn sig_structure_data(
515 context: SignatureContext,
516 body: ProtectedHeader,
517 sign: Option<ProtectedHeader>,
518 aad: &[u8],
519 payload: &[u8],
520 ) -> Vec<u8> {
521 let mut arr = vec![
522 Value::Text(context.text().to_owned()),
523 body.cbor_bstr().expect("failed to serialize header"), // safe: always serializable
524 ];
525 if let Some(sign) = sign {
526 arr.push(sign.cbor_bstr().expect("failed to serialize header")); // safe: always
527 // serializable
528 }
529 arr.push(Value::Bytes(aad.to_vec()));
530 arr.push(Value::Bytes(payload.to_vec()));
531 let mut data = Vec::new();
532 cbor::ser::into_writer(&Value::Array(arr), &mut data).unwrap(); // safe: always serializable
533 data
534 }
535