1 //! Parse and decode COSE signatures.
2 
3 use cbor::CborType;
4 use cbor::decoder::decode;
5 use {CoseError, SignatureAlgorithm};
6 use util::get_sig_struct_bytes;
7 use std::collections::BTreeMap;
8 
9 pub const COSE_SIGN_TAG: u64 = 98;
10 
11 /// The result of `decode_signature` holding a decoded COSE signature.
12 #[derive(Debug)]
13 pub struct CoseSignature {
14     pub signature_type: SignatureAlgorithm,
15     pub signature: Vec<u8>,
16     pub signer_cert: Vec<u8>,
17     pub certs: Vec<Vec<u8>>,
18     pub to_verify: Vec<u8>,
19 }
20 
21 pub const COSE_TYPE_ES256: i64 = -7;
22 pub const COSE_TYPE_ES384: i64 = -35;
23 pub const COSE_TYPE_ES512: i64 = -36;
24 pub const COSE_TYPE_PS256: i64 = -37;
25 
26 pub const COSE_HEADER_ALG: u64 = 1;
27 pub const COSE_HEADER_KID: u64 = 4;
28 
29 macro_rules! unpack {
30    ($to:tt, $var:ident) => (
31         match *$var {
32             CborType::$to(ref cbor_object) => {
33                 cbor_object
34             }
35             _ => return Err(CoseError::UnexpectedType),
36         };
37     )
38 }
39 
get_map_value( map: &BTreeMap<CborType, CborType>, key: &CborType, ) -> Result<CborType, CoseError>40 fn get_map_value(
41     map: &BTreeMap<CborType, CborType>,
42     key: &CborType,
43 ) -> Result<CborType, CoseError> {
44     match map.get(key) {
45         Some(x) => Ok(x.clone()),
46         _ => Err(CoseError::MissingHeader),
47     }
48 }
49 
50 /// Ensure that the referenced `CborType` is an empty map.
ensure_empty_map(map: &CborType) -> Result<(), CoseError>51 fn ensure_empty_map(map: &CborType) -> Result<(), CoseError> {
52     let unpacked = unpack!(Map, map);
53     if !unpacked.is_empty() {
54         return Err(CoseError::MalformedInput);
55     }
56     Ok(())
57 }
58 
59 // This syntax is a little unintuitive. Taken together, the two previous definitions essentially
60 // mean:
61 //
62 // COSE_Sign = [
63 //     protected : empty_or_serialized_map,
64 //     unprotected : header_map
65 //     payload : bstr / nil,
66 //     signatures : [+ COSE_Signature]
67 // ]
68 //
69 // (COSE_Sign is an array. The first element is an empty or serialized map (in our case, it is
70 // never expected to be empty). The second element is a map (it is expected to be empty. The third
71 // element is a bstr or nil (it is expected to be nil). The fourth element is an array of
72 // COSE_Signature.)
73 //
74 // COSE_Signature =  [
75 //     Headers,
76 //     signature : bstr
77 // ]
78 //
79 // but again, unpacking this:
80 //
81 // COSE_Signature =  [
82 //     protected : empty_or_serialized_map,
83 //     unprotected : header_map
84 //     signature : bstr
85 // ]
decode_signature_struct( cose_signature: &CborType, payload: &[u8], protected_body_head: &CborType, ) -> Result<CoseSignature, CoseError>86 fn decode_signature_struct(
87     cose_signature: &CborType,
88     payload: &[u8],
89     protected_body_head: &CborType,
90 ) -> Result<CoseSignature, CoseError> {
91     let cose_signature = unpack!(Array, cose_signature);
92     if cose_signature.len() != 3 {
93         return Err(CoseError::MalformedInput);
94     }
95     let protected_signature_header_serialized = &cose_signature[0];
96     let protected_signature_header_bytes = unpack!(Bytes, protected_signature_header_serialized);
97 
98     // Parse the protected signature header.
99     let protected_signature_header = &match decode(protected_signature_header_bytes) {
100         Err(_) => return Err(CoseError::DecodingFailure),
101         Ok(value) => value,
102     };
103     let protected_signature_header = unpack!(Map, protected_signature_header);
104     if protected_signature_header.len() != 2 {
105         return Err(CoseError::MalformedInput);
106     }
107     let signature_algorithm = get_map_value(
108         protected_signature_header,
109         &CborType::Integer(COSE_HEADER_ALG),
110     )?;
111     let signature_algorithm = match signature_algorithm {
112         CborType::SignedInteger(val) => {
113             match val {
114                 COSE_TYPE_ES256 => SignatureAlgorithm::ES256,
115                 COSE_TYPE_ES384 => SignatureAlgorithm::ES384,
116                 COSE_TYPE_ES512 => SignatureAlgorithm::ES512,
117                 COSE_TYPE_PS256 => SignatureAlgorithm::PS256,
118                 _ => return Err(CoseError::UnexpectedHeaderValue),
119             }
120         }
121         _ => return Err(CoseError::UnexpectedType),
122     };
123 
124     let ee_cert = &get_map_value(
125         protected_signature_header,
126         &CborType::Integer(COSE_HEADER_KID),
127     )?;
128     let ee_cert = unpack!(Bytes, ee_cert).clone();
129 
130     // The unprotected header section is expected to be an empty map.
131     ensure_empty_map(&cose_signature[1])?;
132 
133     // Build signature structure to verify.
134     let signature_bytes = &cose_signature[2];
135     let signature_bytes = unpack!(Bytes, signature_bytes).clone();
136     let sig_structure_bytes = get_sig_struct_bytes(
137         protected_body_head.clone(),
138         protected_signature_header_serialized.clone(),
139         payload,
140     );
141 
142     // Read intermediate certificates from protected_body_head.
143     // Any tampering of the protected header during transport will be detected
144     // because it is input to the signature verification.
145     // Note that a protected header has to be present and hold a kid with an
146     // empty list of intermediate certificates.
147     let protected_body_head_bytes = unpack!(Bytes, protected_body_head);
148     let protected_body_head_map = &match decode(protected_body_head_bytes) {
149         Ok(value) => value,
150         Err(_) => return Err(CoseError::DecodingFailure),
151     };
152     let protected_body_head_map = unpack!(Map, protected_body_head_map);
153     if protected_body_head_map.len() != 1 {
154         return Err(CoseError::MalformedInput);
155     }
156     let intermediate_certs_array =
157         &get_map_value(protected_body_head_map, &CborType::Integer(COSE_HEADER_KID))?;
158     let intermediate_certs = unpack!(Array, intermediate_certs_array);
159     let mut certs: Vec<Vec<u8>> = Vec::new();
160     for cert in intermediate_certs {
161         let cert = unpack!(Bytes, cert);
162         certs.push(cert.clone());
163     }
164 
165     Ok(CoseSignature {
166         signature_type: signature_algorithm,
167         signature: signature_bytes,
168         signer_cert: ee_cert,
169         certs: certs,
170         to_verify: sig_structure_bytes,
171     })
172 }
173 
174 /// Decode COSE signature bytes and return a vector of `CoseSignature`.
175 ///
176 ///```rust,ignore
177 /// COSE_Sign = [
178 ///     Headers,
179 ///     payload : bstr / nil,
180 ///     signatures : [+ COSE_Signature]
181 /// ]
182 ///
183 /// Headers = (
184 ///     protected : empty_or_serialized_map,
185 ///     unprotected : header_map
186 /// )
187 ///```
decode_signature(bytes: &[u8], payload: &[u8]) -> Result<Vec<CoseSignature>, CoseError>188 pub fn decode_signature(bytes: &[u8], payload: &[u8]) -> Result<Vec<CoseSignature>, CoseError> {
189     // This has to be a COSE_Sign object, which is a tagged array.
190     let tagged_cose_sign = match decode(bytes) {
191         Err(_) => return Err(CoseError::DecodingFailure),
192         Ok(value) => value,
193     };
194     let cose_sign_array = match tagged_cose_sign {
195         CborType::Tag(tag, cose_sign) => {
196             if tag != COSE_SIGN_TAG {
197                 return Err(CoseError::UnexpectedTag);
198             }
199             match *cose_sign {
200                 CborType::Array(values) => values,
201                 _ => return Err(CoseError::UnexpectedType),
202             }
203         }
204         _ => return Err(CoseError::UnexpectedType),
205     };
206     if cose_sign_array.len() != 4 {
207         return Err(CoseError::MalformedInput);
208     }
209 
210     // The unprotected header section is expected to be an empty map.
211     ensure_empty_map(&cose_sign_array[1])?;
212 
213     // The payload is expected to be Null (i.e. this is a detached signature).
214     match cose_sign_array[2] {
215         CborType::Null => {}
216         _ => return Err(CoseError::UnexpectedType),
217     };
218 
219     let signatures = &cose_sign_array[3];
220     let signatures = unpack!(Array, signatures);
221 
222     // Decode COSE_Signatures.
223     // There has to be at least one signature to make this a valid COSE signature.
224     if signatures.len() < 1 {
225         return Err(CoseError::MalformedInput);
226     }
227     let mut result = Vec::new();
228     for cose_signature in signatures {
229         // cose_sign_array[0] holds the protected body header.
230         let signature = decode_signature_struct(cose_signature, payload, &cose_sign_array[0])?;
231         result.push(signature);
232     }
233 
234     Ok(result)
235 }
236