1 // Copyright (c) 2017-2021 Linaro LTD
2 // Copyright (c) 2017-2019 JUUL Labs
3 // Copyright (c) 2019-2023 Arm Limited
4 //
5 // SPDX-License-Identifier: Apache-2.0
6 
7 //! Interface wrappers to C API entering to the bootloader
8 
9 use crate::area::AreaDesc;
10 use simflash::SimMultiFlash;
11 use crate::api;
12 
13 #[allow(unused)]
14 use std::sync::Once;
15 
16 use std::borrow::Borrow;
17 
18 /// The result of an invocation of `boot_go`.  This is intentionally opaque so that we can provide
19 /// accessors for everything we need from this.
20 #[derive(Debug)]
21 pub enum BootGoResult {
22     /// This run was stopped by the flash simulation mechanism.
23     Stopped,
24     /// The bootloader ran to completion with the following data.
25     Normal {
26         result: i32,
27         asserts: u8,
28 
29         resp: api::BootRsp,
30     },
31 }
32 
33 impl BootGoResult {
34     /// Was this run interrupted.
interrupted(&self) -> bool35     pub fn interrupted(&self) -> bool {
36         matches!(self, BootGoResult::Stopped)
37     }
38 
39     /// Was this boot run successful (returned 0)
success(&self) -> bool40     pub fn success(&self) -> bool {
41         matches!(self, BootGoResult::Normal { result: 0, .. })
42     }
43 
44     /// Success, but also no asserts.
success_no_asserts(&self) -> bool45     pub fn success_no_asserts(&self) -> bool {
46         matches!(self, BootGoResult::Normal {
47             result: 0,
48             asserts: 0,
49             ..
50         })
51     }
52 
53     /// Get the asserts count.  An interrupted run will be considered to have no asserts.
asserts(&self) -> u854     pub fn asserts(&self) -> u8 {
55         match self {
56             BootGoResult::Normal { asserts, .. } => *asserts,
57             _ => 0,
58         }
59     }
60 
61     /// Retrieve the 'resp' field that is filled in.
resp(&self) -> Option<&api::BootRsp>62     pub fn resp(&self) -> Option<&api::BootRsp> {
63         match self {
64             BootGoResult::Normal { resp, .. } => Some(resp),
65             _ => None,
66         }
67     }
68 }
69 
70 /// Invoke the bootloader on this flash device.
boot_go(multiflash: &mut SimMultiFlash, areadesc: &AreaDesc, counter: Option<&mut i32>, image_index: Option<i32>, catch_asserts: bool) -> BootGoResult71 pub fn boot_go(multiflash: &mut SimMultiFlash, areadesc: &AreaDesc,
72                counter: Option<&mut i32>, image_index: Option<i32>,
73                catch_asserts: bool) -> BootGoResult {
74     init_crypto();
75 
76     for (&dev_id, flash) in multiflash.iter_mut() {
77         api::set_flash(dev_id, flash);
78     }
79     let mut sim_ctx = api::CSimContext {
80         flash_counter: match counter {
81             None => 0,
82             Some(ref c) => **c as libc::c_int
83         },
84         c_catch_asserts: if catch_asserts { 1 } else { 0 },
85         .. Default::default()
86     };
87     let mut rsp = api::BootRsp {
88         br_hdr: std::ptr::null(),
89         flash_dev_id: 0,
90         image_off: 0,
91     };
92     let result: i32 = unsafe {
93         let adesc = areadesc.get_c();
94         match image_index {
95             None => raw::invoke_boot_go(&mut sim_ctx as *mut _,
96                                         adesc.borrow() as *const _,
97                                         &mut rsp as *mut _, -1) as i32,
98             Some(i) => raw::invoke_boot_go(&mut sim_ctx as *mut _,
99                                            adesc.borrow() as *const _,
100                                            &mut rsp as *mut _,
101                                            i as i32) as i32
102         }
103     };
104     let asserts = sim_ctx.c_asserts;
105     if let Some(c) = counter {
106         *c = sim_ctx.flash_counter;
107     }
108     for &dev_id in multiflash.keys() {
109         api::clear_flash(dev_id);
110     }
111     if result == -0x13579 {
112         BootGoResult::Stopped
113     } else {
114         BootGoResult::Normal { result, asserts, resp: rsp }
115     }
116 }
117 
boot_trailer_sz(align: u32) -> u32118 pub fn boot_trailer_sz(align: u32) -> u32 {
119     unsafe { raw::boot_trailer_sz(align) }
120 }
121 
boot_status_sz(align: u32) -> u32122 pub fn boot_status_sz(align: u32) -> u32 {
123     unsafe { raw::boot_status_sz(align) }
124 }
125 
boot_magic_sz() -> usize126 pub fn boot_magic_sz() -> usize {
127     unsafe { raw::boot_magic_sz() as usize }
128 }
129 
boot_max_align() -> usize130 pub fn boot_max_align() -> usize {
131     unsafe { raw::boot_max_align() as usize }
132 }
133 
rsa_oaep_encrypt(pubkey: &[u8], seckey: &[u8]) -> Result<[u8; 256], &'static str>134 pub fn rsa_oaep_encrypt(pubkey: &[u8], seckey: &[u8]) -> Result<[u8; 256], &'static str> {
135     unsafe {
136         let mut encbuf: [u8; 256] = [0; 256];
137         if raw::rsa_oaep_encrypt_(pubkey.as_ptr(), pubkey.len() as u32,
138                                   seckey.as_ptr(), seckey.len() as u32,
139                                   encbuf.as_mut_ptr()) == 0 {
140             return Ok(encbuf);
141         }
142         Err("Failed to encrypt buffer")
143     }
144 }
145 
kw_encrypt(kek: &[u8], seckey: &[u8], keylen: u32) -> Result<Vec<u8>, &'static str>146 pub fn kw_encrypt(kek: &[u8], seckey: &[u8], keylen: u32) -> Result<Vec<u8>, &'static str> {
147     unsafe {
148         let mut encbuf = vec![0u8; 24];
149         if keylen == 32 {
150             encbuf = vec![0u8; 40];
151         }
152         if raw::kw_encrypt_(kek.as_ptr(), seckey.as_ptr(), encbuf.as_mut_ptr()) == 0 {
153             return Ok(encbuf);
154         }
155         Err("Failed to encrypt buffer")
156     }
157 }
158 
set_security_counter(image_index: u32, security_counter_value: u32)159 pub fn set_security_counter(image_index: u32, security_counter_value: u32) {
160     api::sim_set_nv_counter_for_image(image_index, security_counter_value);
161 }
162 
get_security_counter(image_index: u32) -> u32163 pub fn get_security_counter(image_index: u32) -> u32 {
164     let mut counter_val: u32 = 0;
165     api::sim_get_nv_counter_for_image(image_index, &mut counter_val as *mut u32);
166     return counter_val;
167 }
168 
169 mod raw {
170     use crate::area::CAreaDesc;
171     use crate::api::{BootRsp, CSimContext};
172 
173     extern "C" {
174         // This generates a warning about `CAreaDesc` not being foreign safe.  There doesn't appear to
175         // be any way to get rid of this warning.  See https://github.com/rust-lang/rust/issues/34798
176         // for information and tracking.
invoke_boot_go(sim_ctx: *mut CSimContext, areadesc: *const CAreaDesc, rsp: *mut BootRsp, image_index: libc::c_int) -> libc::c_int177         pub fn invoke_boot_go(sim_ctx: *mut CSimContext, areadesc: *const CAreaDesc,
178             rsp: *mut BootRsp, image_index: libc::c_int) -> libc::c_int;
179 
boot_trailer_sz(min_write_sz: u32) -> u32180         pub fn boot_trailer_sz(min_write_sz: u32) -> u32;
boot_status_sz(min_write_sz: u32) -> u32181         pub fn boot_status_sz(min_write_sz: u32) -> u32;
182 
boot_magic_sz() -> u32183         pub fn boot_magic_sz() -> u32;
boot_max_align() -> u32184         pub fn boot_max_align() -> u32;
185 
rsa_oaep_encrypt_(pubkey: *const u8, pubkey_len: libc::c_uint, seckey: *const u8, seckey_len: libc::c_uint, encbuf: *mut u8) -> libc::c_int186         pub fn rsa_oaep_encrypt_(pubkey: *const u8, pubkey_len: libc::c_uint,
187                                  seckey: *const u8, seckey_len: libc::c_uint,
188                                  encbuf: *mut u8) -> libc::c_int;
189 
kw_encrypt_(kek: *const u8, seckey: *const u8, encbuf: *mut u8) -> libc::c_int190         pub fn kw_encrypt_(kek: *const u8, seckey: *const u8,
191                            encbuf: *mut u8) -> libc::c_int;
192 
193         #[allow(unused)]
psa_crypto_init() -> u32194         pub fn psa_crypto_init() -> u32;
195 
196         #[allow(unused)]
mbedtls_test_enable_insecure_external_rng()197         pub fn mbedtls_test_enable_insecure_external_rng();
198     }
199 }
200 
201 #[allow(unused)]
202 static PSA_INIT_SYNC: Once = Once::new();
203 
204 #[allow(unused)]
205 static MBEDTLS_EXTERNAL_RNG_ENABLE_SYNC: Once = Once::new();
206 
207 #[cfg(feature = "psa-crypto-api")]
init_crypto()208 fn init_crypto() {
209     PSA_INIT_SYNC.call_once(|| {
210         assert_eq!(unsafe { raw::psa_crypto_init() }, 0);
211     });
212 
213     /* The PSA APIs require properly initialisation of the entropy subsystem
214      * The configuration adds the option MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG when the
215      * psa-crypto-api feature is enabled. As a result the tests use the implementation
216      * of the test external rng that needs to be initialised before being able to use it
217      */
218     MBEDTLS_EXTERNAL_RNG_ENABLE_SYNC.call_once(|| {
219         unsafe { raw::mbedtls_test_enable_insecure_external_rng() }
220     });
221 }
222 
223 #[cfg(not(feature = "psa-crypto-api"))]
init_crypto()224 fn init_crypto() {
225    // When the feature is not enabled, the init is just empty
226 }
227