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