blob: 9896df63871f940127dad949c3eefa1ec529677b [file] [log] [blame]
David Brown187dd882017-07-11 11:15:23 -06001//! TLV Support
2//!
3//! mcuboot images are followed immediately by a list of TLV items that contain integrity
4//! information about the image. Their generation is made a little complicated because the size of
5//! the TLV block is in the image header, which is included in the hash. Since some signatures can
6//! vary in size, we just make them the largest size possible.
7//!
8//! Because of this header, we have to make two passes. The first pass will compute the size of
9//! the TLV, and the second pass will build the data for the TLV.
10
David Brown7e701d82017-07-11 13:24:25 -060011use pem;
Fabio Utzig1e48b912018-09-18 09:04:18 -030012use base64;
Fabio Utzig05ab0142018-07-10 09:15:28 -030013use ring::{digest, rand};
14use ring::signature::{
15 RsaKeyPair,
16 RSA_PSS_SHA256,
17 EcdsaKeyPair,
18 ECDSA_P256_SHA256_ASN1_SIGNING,
19};
David Brown7e701d82017-07-11 13:24:25 -060020use untrusted;
Fabio Utzig80fde2f2017-12-05 09:25:31 -020021use mcuboot_sys::c;
David Brown187dd882017-07-11 11:15:23 -060022
David Brown187dd882017-07-11 11:15:23 -060023#[repr(u8)]
24#[derive(Copy, Clone, PartialEq, Eq)]
25#[allow(dead_code)] // TODO: For now
26pub enum TlvKinds {
David Brown43cda332017-09-01 09:53:23 -060027 KEYHASH = 0x01,
David Brown27648b82017-08-31 10:40:29 -060028 SHA256 = 0x10,
29 RSA2048 = 0x20,
30 ECDSA224 = 0x21,
31 ECDSA256 = 0x22,
Fabio Utzig39297432019-05-08 18:51:10 -030032 RSA3072 = 0x23,
Fabio Utzig1e48b912018-09-18 09:04:18 -030033 ENCRSA2048 = 0x30,
34 ENCKW128 = 0x31,
35}
36
37#[allow(dead_code, non_camel_case_types)]
38pub enum TlvFlags {
39 PIC = 0x01,
40 NON_BOOTABLE = 0x02,
41 ENCRYPTED = 0x04,
42 RAM_LOAD = 0x20,
David Brown187dd882017-07-11 11:15:23 -060043}
44
David Brown43643dd2019-01-11 15:43:28 -070045/// A generator for manifests. The format of the manifest can be either a
46/// traditional "TLV" or a SUIT-style manifest.
47pub trait ManifestGen {
David Brownac46e262019-01-11 15:46:18 -070048 /// Retrieve the header magic value for this manifest type.
49 fn get_magic(&self) -> u32;
50
David Brown43643dd2019-01-11 15:43:28 -070051 /// Retrieve the flags value for this particular manifest type.
52 fn get_flags(&self) -> u32;
53
54 /// Add a sequence of bytes to the payload that the manifest is
55 /// protecting.
56 fn add_bytes(&mut self, bytes: &[u8]);
57
58 /// Construct the manifest for this payload.
59 fn make_tlv(self: Box<Self>) -> Vec<u8>;
60}
61
David Brown187dd882017-07-11 11:15:23 -060062pub struct TlvGen {
David Brown43cda332017-09-01 09:53:23 -060063 flags: u32,
David Brown187dd882017-07-11 11:15:23 -060064 kinds: Vec<TlvKinds>,
65 size: u16,
David Brown4243ab02017-07-11 12:24:23 -060066 payload: Vec<u8>,
David Brown187dd882017-07-11 11:15:23 -060067}
68
Fabio Utzig1e48b912018-09-18 09:04:18 -030069pub const AES_SEC_KEY: &[u8; 16] = b"0123456789ABCDEF";
70
David Brown187dd882017-07-11 11:15:23 -060071impl TlvGen {
72 /// Construct a new tlv generator that will only contain a hash of the data.
David Brown7e701d82017-07-11 13:24:25 -060073 #[allow(dead_code)]
David Brown187dd882017-07-11 11:15:23 -060074 pub fn new_hash_only() -> TlvGen {
75 TlvGen {
David Brown43cda332017-09-01 09:53:23 -060076 flags: 0,
David Brown187dd882017-07-11 11:15:23 -060077 kinds: vec![TlvKinds::SHA256],
78 size: 4 + 32,
David Brown4243ab02017-07-11 12:24:23 -060079 payload: vec![],
David Brown187dd882017-07-11 11:15:23 -060080 }
81 }
82
David Brown7e701d82017-07-11 13:24:25 -060083 #[allow(dead_code)]
84 pub fn new_rsa_pss() -> TlvGen {
85 TlvGen {
David Brown43cda332017-09-01 09:53:23 -060086 flags: 0,
Fabio Utzig754438d2018-12-14 06:39:58 -020087 kinds: vec![TlvKinds::SHA256, TlvKinds::RSA2048],
88 size: 4 + 32 + 4 + 32 + 4 + 256,
David Brown7e701d82017-07-11 13:24:25 -060089 payload: vec![],
90 }
91 }
92
Fabio Utzig80fde2f2017-12-05 09:25:31 -020093 #[allow(dead_code)]
Fabio Utzig39297432019-05-08 18:51:10 -030094 pub fn new_rsa3072_pss() -> TlvGen {
95 TlvGen {
96 flags: 0,
97 kinds: vec![TlvKinds::SHA256, TlvKinds::RSA3072],
98 size: 4 + 32 + 4 + 32 + 4 + 384,
99 payload: vec![],
100 }
101 }
102
103 #[allow(dead_code)]
Fabio Utzig80fde2f2017-12-05 09:25:31 -0200104 pub fn new_ecdsa() -> TlvGen {
105 TlvGen {
106 flags: 0,
Fabio Utzig754438d2018-12-14 06:39:58 -0200107 kinds: vec![TlvKinds::SHA256, TlvKinds::ECDSA256],
108 size: 4 + 32 + 4 + 32 + 4 + 72,
Fabio Utzig80fde2f2017-12-05 09:25:31 -0200109 payload: vec![],
110 }
111 }
112
Fabio Utzig1e48b912018-09-18 09:04:18 -0300113 #[allow(dead_code)]
114 pub fn new_enc_rsa() -> TlvGen {
115 TlvGen {
116 flags: TlvFlags::ENCRYPTED as u32,
117 kinds: vec![TlvKinds::SHA256, TlvKinds::ENCRSA2048],
118 size: 4 + 32 + 4 + 256,
119 payload: vec![],
120 }
121 }
122
123 #[allow(dead_code)]
Fabio Utzig754438d2018-12-14 06:39:58 -0200124 pub fn new_sig_enc_rsa() -> TlvGen {
125 TlvGen {
126 flags: TlvFlags::ENCRYPTED as u32,
127 kinds: vec![TlvKinds::SHA256, TlvKinds::RSA2048, TlvKinds::ENCRSA2048],
128 size: 4 + 32 + 4 + 32 + 4 + 256 + 4 + 256,
129 payload: vec![],
130 }
131 }
132
133 #[allow(dead_code)]
Fabio Utzig1e48b912018-09-18 09:04:18 -0300134 pub fn new_enc_kw() -> TlvGen {
135 TlvGen {
136 flags: TlvFlags::ENCRYPTED as u32,
137 kinds: vec![TlvKinds::SHA256, TlvKinds::ENCKW128],
138 size: 4 + 32 + 4 + 24,
139 payload: vec![],
140 }
141 }
142
Fabio Utzig251ef1d2018-12-18 17:20:19 -0200143 #[allow(dead_code)]
144 pub fn new_rsa_kw() -> TlvGen {
145 TlvGen {
146 flags: TlvFlags::ENCRYPTED as u32,
147 kinds: vec![TlvKinds::SHA256, TlvKinds::RSA2048, TlvKinds::ENCKW128],
148 size: 4 + 32 + 4 + 32 + 4 + 256 + 4 + 24,
149 payload: vec![],
150 }
151 }
152
Fabio Utzigb4d20c82018-12-27 16:08:39 -0200153 #[allow(dead_code)]
154 pub fn new_ecdsa_kw() -> TlvGen {
155 TlvGen {
156 flags: TlvFlags::ENCRYPTED as u32,
157 kinds: vec![TlvKinds::SHA256, TlvKinds::ECDSA256, TlvKinds::ENCKW128],
158 size: 4 + 32 + 4 + 32 + 4 + 72 + 4 + 24,
159 payload: vec![],
160 }
161 }
162
David Brown187dd882017-07-11 11:15:23 -0600163 /// Retrieve the size that the TLV will occupy. This can be called at any time.
164 pub fn get_size(&self) -> u16 {
David Brownf5b33d82017-09-01 10:58:27 -0600165 4 + self.size
David Brown187dd882017-07-11 11:15:23 -0600166 }
David Brown43643dd2019-01-11 15:43:28 -0700167}
168
169impl ManifestGen for TlvGen {
David Brownac46e262019-01-11 15:46:18 -0700170 fn get_magic(&self) -> u32 {
171 0x96f3b83d
172 }
173
David Brown43643dd2019-01-11 15:43:28 -0700174 /// Retrieve the header flags for this configuration. This can be called at any time.
175 fn get_flags(&self) -> u32 {
176 self.flags
177 }
David Brown187dd882017-07-11 11:15:23 -0600178
179 /// Add bytes to the covered hash.
David Brown43643dd2019-01-11 15:43:28 -0700180 fn add_bytes(&mut self, bytes: &[u8]) {
David Brown4243ab02017-07-11 12:24:23 -0600181 self.payload.extend_from_slice(bytes);
David Brown187dd882017-07-11 11:15:23 -0600182 }
183
184 /// Compute the TLV given the specified block of data.
David Brown43643dd2019-01-11 15:43:28 -0700185 fn make_tlv(self: Box<Self>) -> Vec<u8> {
David Brown187dd882017-07-11 11:15:23 -0600186 let mut result: Vec<u8> = vec![];
187
David Brownf5b33d82017-09-01 10:58:27 -0600188 let size = self.get_size();
189 result.push(0x07);
190 result.push(0x69);
191 result.push((size & 0xFF) as u8);
192 result.push(((size >> 8) & 0xFF) as u8);
193
David Brown187dd882017-07-11 11:15:23 -0600194 if self.kinds.contains(&TlvKinds::SHA256) {
David Brown4243ab02017-07-11 12:24:23 -0600195 let hash = digest::digest(&digest::SHA256, &self.payload);
David Brown8054ce22017-07-11 12:12:09 -0600196 let hash = hash.as_ref();
David Brown187dd882017-07-11 11:15:23 -0600197
David Brown8054ce22017-07-11 12:12:09 -0600198 assert!(hash.len() == 32);
David Brown187dd882017-07-11 11:15:23 -0600199 result.push(TlvKinds::SHA256 as u8);
200 result.push(0);
201 result.push(32);
202 result.push(0);
David Brown8054ce22017-07-11 12:12:09 -0600203 result.extend_from_slice(hash);
David Brown187dd882017-07-11 11:15:23 -0600204 }
205
Fabio Utzig39297432019-05-08 18:51:10 -0300206 if self.kinds.contains(&TlvKinds::RSA2048) ||
207 self.kinds.contains(&TlvKinds::RSA3072) {
208
209 let is_rsa2048 = self.kinds.contains(&TlvKinds::RSA2048);
210
David Brown43cda332017-09-01 09:53:23 -0600211 // Output the hash of the public key.
Fabio Utzig39297432019-05-08 18:51:10 -0300212 let hash = if is_rsa2048 {
213 digest::digest(&digest::SHA256, RSA_PUB_KEY)
214 } else {
215 digest::digest(&digest::SHA256, RSA3072_PUB_KEY)
216 };
David Brown43cda332017-09-01 09:53:23 -0600217 let hash = hash.as_ref();
218
219 assert!(hash.len() == 32);
220 result.push(TlvKinds::KEYHASH as u8);
221 result.push(0);
222 result.push(32);
223 result.push(0);
224 result.extend_from_slice(hash);
225
David Brown7e701d82017-07-11 13:24:25 -0600226 // For now assume PSS.
Fabio Utzig39297432019-05-08 18:51:10 -0300227 let key_bytes = if is_rsa2048 {
228 pem::parse(include_bytes!("../../root-rsa-2048.pem").as_ref()).unwrap()
229 } else {
230 pem::parse(include_bytes!("../../root-rsa-3072.pem").as_ref()).unwrap()
231 };
David Brown7e701d82017-07-11 13:24:25 -0600232 assert_eq!(key_bytes.tag, "RSA PRIVATE KEY");
233 let key_bytes = untrusted::Input::from(&key_bytes.contents);
Fabio Utzig05ab0142018-07-10 09:15:28 -0300234 let key_pair = RsaKeyPair::from_der(key_bytes).unwrap();
David Brown7e701d82017-07-11 13:24:25 -0600235 let rng = rand::SystemRandom::new();
Fabio Utzig05ab0142018-07-10 09:15:28 -0300236 let mut signature = vec![0; key_pair.public_modulus_len()];
Fabio Utzig39297432019-05-08 18:51:10 -0300237 if is_rsa2048 {
238 assert_eq!(signature.len(), 256);
239 } else {
240 assert_eq!(signature.len(), 384);
241 }
Fabio Utzig05ab0142018-07-10 09:15:28 -0300242 key_pair.sign(&RSA_PSS_SHA256, &rng, &self.payload, &mut signature).unwrap();
David Brown7e701d82017-07-11 13:24:25 -0600243
Fabio Utzig39297432019-05-08 18:51:10 -0300244 if is_rsa2048 {
245 result.push(TlvKinds::RSA2048 as u8);
246 } else {
247 result.push(TlvKinds::RSA3072 as u8);
248 }
David Brown7e701d82017-07-11 13:24:25 -0600249 result.push(0);
250 result.push((signature.len() & 0xFF) as u8);
251 result.push(((signature.len() >> 8) & 0xFF) as u8);
252 result.extend_from_slice(&signature);
253 }
254
Fabio Utzig80fde2f2017-12-05 09:25:31 -0200255 if self.kinds.contains(&TlvKinds::ECDSA256) {
256 let keyhash = digest::digest(&digest::SHA256, ECDSA256_PUB_KEY);
257 let keyhash = keyhash.as_ref();
258
259 assert!(keyhash.len() == 32);
260 result.push(TlvKinds::KEYHASH as u8);
261 result.push(0);
262 result.push(32);
263 result.push(0);
264 result.extend_from_slice(keyhash);
265
Fabio Utzig05ab0142018-07-10 09:15:28 -0300266 let key_bytes = pem::parse(include_bytes!("../../root-ec-p256-pkcs8.pem").as_ref()).unwrap();
267 assert_eq!(key_bytes.tag, "PRIVATE KEY");
Fabio Utzig80fde2f2017-12-05 09:25:31 -0200268
Fabio Utzig05ab0142018-07-10 09:15:28 -0300269 let key_bytes = untrusted::Input::from(&key_bytes.contents);
270 let key_pair = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING,
271 key_bytes).unwrap();
272 let rng = rand::SystemRandom::new();
273 let payload = untrusted::Input::from(&self.payload);
274 let signature = key_pair.sign(&rng, payload).unwrap();
Fabio Utzig80fde2f2017-12-05 09:25:31 -0200275
276 result.push(TlvKinds::ECDSA256 as u8);
277 result.push(0);
Fabio Utzig05ab0142018-07-10 09:15:28 -0300278
279 // signature must be padded...
280 let mut signature = signature.as_ref().to_vec();
281 while signature.len() < 72 {
282 signature.push(0);
283 signature[1] += 1;
284 }
285
286 result.push((signature.len() & 0xFF) as u8);
287 result.push(((signature.len() >> 8) & 0xFF) as u8);
288 result.extend_from_slice(signature.as_ref());
Fabio Utzig80fde2f2017-12-05 09:25:31 -0200289 }
290
Fabio Utzig1e48b912018-09-18 09:04:18 -0300291 if self.kinds.contains(&TlvKinds::ENCRSA2048) {
292 let key_bytes = pem::parse(include_bytes!("../../enc-rsa2048-pub.pem")
293 .as_ref()).unwrap();
294 assert_eq!(key_bytes.tag, "PUBLIC KEY");
295
296 let encbuf = match c::rsa_oaep_encrypt(&key_bytes.contents, AES_SEC_KEY) {
297 Ok(v) => v,
298 Err(_) => panic!("Failed to encrypt secret key"),
299 };
300
301 assert!(encbuf.len() == 256);
302 result.push(TlvKinds::ENCRSA2048 as u8);
303 result.push(0);
304 result.push(0);
305 result.push(1);
306 result.extend_from_slice(&encbuf);
307 }
308
309 if self.kinds.contains(&TlvKinds::ENCKW128) {
310 let key_bytes = base64::decode(
311 include_str!("../../enc-aes128kw.b64").trim()).unwrap();
312
313 let encbuf = match c::kw_encrypt(&key_bytes, AES_SEC_KEY) {
314 Ok(v) => v,
315 Err(_) => panic!("Failed to encrypt secret key"),
316 };
317
318 assert!(encbuf.len() == 24);
319 result.push(TlvKinds::ENCKW128 as u8);
320 result.push(0);
321 result.push(24);
322 result.push(0);
323 result.extend_from_slice(&encbuf);
324 }
325
David Brown187dd882017-07-11 11:15:23 -0600326 result
327 }
328}
David Brown43cda332017-09-01 09:53:23 -0600329
330include!("rsa_pub_key-rs.txt");
Fabio Utzig39297432019-05-08 18:51:10 -0300331include!("rsa3072_pub_key-rs.txt");
Fabio Utzig80fde2f2017-12-05 09:25:31 -0200332include!("ecdsa_pub_key-rs.txt");