1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
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//! This lib is used by updater to parse and get sig from hash signe data file:
17
18mod hsd;
19mod evp;
20mod ffi;
21mod img_hash_check;
22mod macros;
23
24use core::{ffi::{c_char, CStr}, mem::ManuallyDrop, ptr};
25use hsd::HashSignedData;
26use img_hash_check::ImgHashData;
27use img_hash_check::ReadLeBytes;
28
29/// load hash signed data from buffer, then you can verify them by VerifyHashBySignedData
30///
31/// # Safety
32///
33/// signed_data must contain a valid nul terminator at the end
34#[no_mangle]
35pub unsafe extern fn LoadHashSignedData(signed_data: *const c_char)
36    -> *const HashSignedData
37{
38    if signed_data.is_null() {
39        updaterlog!(ERROR, "signed data is null");
40        return ptr::null();
41    }
42
43    let signed_data_str: &CStr = unsafe { CStr::from_ptr(signed_data) };
44    let hsd = signed_data_str.to_str();
45    if hsd.is_err() {
46        updaterlog!(ERROR, "hash signed data str format is invalid {:?}", signed_data_str);
47        return ptr::null();
48    }
49
50    match HashSignedData::try_from(hsd.unwrap()) {
51        Ok(hsd) => {
52            updaterlog!(INFO, "hash signed data parse successful!");
53            Box::into_raw(Box::new(hsd))
54        },
55        Err(err) => {
56            updaterlog!(ERROR, "hash signed data parse failed, err is {}", err);
57            ptr::null()
58        }
59    }
60}
61
62/// Get signature of file from hash signed data
63///
64/// # Safety
65///
66/// file_name should be a valid utf8 str, ended with a nul terminator
67#[no_mangle]
68pub unsafe extern fn GetSigFromHashData(signed_data: *const HashSignedData,
69    out: *mut u8, out_len: usize, file_name: *const c_char) -> usize
70{
71    if out.is_null() || file_name.is_null() || signed_data.is_null() {
72        updaterlog!(ERROR, "input invalid, null status hash:{} file_name:{} signed_data:{}",
73            out.is_null(), file_name.is_null(), signed_data.is_null());
74        return 0;
75    }
76    let signed_data = ManuallyDrop::new(unsafe { &*signed_data });
77    let file_name_c_str: &CStr = unsafe { CStr::from_ptr(file_name) };
78    let file_name = match file_name_c_str.to_str() {
79        Ok(file_name) => file_name,
80        Err(_) => {
81            updaterlog!(ERROR, "filename is invalid utf8 str");
82            return 0;
83        }
84    };
85    let sig = match signed_data.get_sig_for_file(file_name) {
86        Ok(sig) => sig,
87        Err(err) => {
88            unsafe { ffi::ERR_print_errors_cb(ffi::err_print_cb, ptr::null_mut()); }
89            updaterlog!(ERROR, "get sig for file {} failed, err is {}", file_name, err);
90            return 0;
91        }
92    };
93    if sig.len() > out_len {
94        updaterlog!(ERROR, "out is too small to hold signature");
95        return 0;
96    }
97    unsafe { ptr::copy_nonoverlapping(sig.as_ptr(), out, sig.len()); }
98    // hash is owned by a vector in c++, it's memory is allocated in c++, so need to forget it in rust
99    updaterlog!(INFO, "get sig succeed for {}", file_name);
100    sig.len()
101}
102
103/// release hash signed data when you no longer need it
104///
105/// # Safety
106///
107/// HashSignedData should be a return value of LoadHashSignedData
108#[no_mangle]
109pub unsafe extern fn ReleaseHashSignedData(signed_data: *const HashSignedData)
110{
111    if signed_data.is_null() {
112        updaterlog!(ERROR, "signed data is null");
113        return;
114    }
115    unsafe { drop(Box::from_raw(signed_data as *mut HashSignedData)); }
116    updaterlog!(INFO, "release hash signed data");
117}
118
119/// load hash signed data from buffer, then you can verify them by check_data_hash
120///
121/// # Safety
122///
123/// hash_data must contain a valid nul terminator at the end
124#[no_mangle]
125pub unsafe extern fn LoadImgHashData(hash_data: *const u8, len: usize)
126    -> *const ImgHashData<u32>
127{
128    if hash_data.is_null() {
129        updaterlog!(ERROR, "hash data is null");
130        return ptr::null();
131    }
132
133    let hash_data_vec: Vec<u8> = unsafe {Vec::from_raw_parts(hash_data as *mut u8, len, len)};
134    match ImgHashData::load_img_hash_data(&hash_data_vec[..]) {
135        Ok(hash_data) => {
136            std::mem::forget(hash_data_vec);
137            updaterlog!(INFO, "hash data parse successful!");
138            Box::into_raw(Box::new(hash_data))
139        },
140        Err(err) => {
141            std::mem::forget(hash_data_vec);
142            updaterlog!(ERROR, "hash data parse failed, err is {}", err);
143            ptr::null()
144        }
145    }
146}
147
148/// load hash signed data from buffer, then you can verify them by check_data_hash
149///
150/// # Safety
151///
152/// hash_data must contain a valid nul terminator at the end
153#[no_mangle]
154pub unsafe extern fn LoadImgHashDataNew(hash_data: *const u8, len: usize)
155    -> *const ImgHashData<u64>
156{
157    if hash_data.is_null() {
158        updaterlog!(ERROR, "hash data is null");
159        return ptr::null();
160    }
161
162    let hash_data_vec: Vec<u8> = unsafe {Vec::from_raw_parts(hash_data as *mut u8, len, len)};
163    match ImgHashData::load_img_hash_data(&hash_data_vec[..]) {
164        Ok(hash_data) => {
165            std::mem::forget(hash_data_vec);
166            updaterlog!(INFO, "hash data parse successful!");
167            Box::into_raw(Box::new(hash_data))
168        },
169        Err(err) => {
170            std::mem::forget(hash_data_vec);
171            updaterlog!(ERROR, "hash data parse failed, err is {}", err);
172            ptr::null()
173        }
174    }
175}
176
177/// check hash data from buffer
178///
179/// # Safety
180///
181/// signed_data must contain a valid nul terminator at the end
182// #[no_mangle]
183pub unsafe extern fn check_data_hash_template<T>(img_hash_data: *const ImgHashData<T>,
184    img_name: *const c_char, start: T, end: T, hash_value: *const u8,  len: usize) -> bool
185    where T: ReadLeBytes + std::hash::Hash + std::cmp::Eq + std::fmt::Display + std::default::Default
186{
187    if img_hash_data.is_null() || img_name.is_null() || hash_value.is_null() {
188        updaterlog!(ERROR, "input invalid, null status img_hash_data:{} img_name:{} hash_value:{}",
189        img_hash_data.is_null(), img_name.is_null(), hash_value.is_null());
190        return false;
191    }
192
193    let hash_data = ManuallyDrop::new( unsafe { &*img_hash_data });
194    let img_name_c_str: &CStr = unsafe { CStr::from_ptr(img_name) };
195    let img_name = match img_name_c_str.to_str() {
196        Ok(img_name) => img_name.to_owned(),
197        Err(_) => {
198            updaterlog!(ERROR, "img_name is invalid utf8 str");
199            return false;
200        }
201    };
202
203    let hash_value_vec: Vec<u8> = unsafe {Vec::from_raw_parts(hash_value as *mut u8, len, len)};
204    updaterlog!(INFO, "check_data_hash, img_name: {}, start: {}, hash_value_vec: {:?}", img_name, start, hash_value_vec);
205    let is_valid = hash_data.check_img_hash(img_name, start, end, &hash_value_vec[..]);
206    std::mem::forget(hash_value_vec);
207    is_valid
208}
209
210/// check hash data from buffer
211///
212/// # Safety
213///
214/// signed_data must contain a valid nul terminator at the end
215#[no_mangle]
216pub unsafe extern fn check_data_hash(img_hash_data: *const ImgHashData<u32>,
217    img_name: *const c_char, start: u32, end: u32, hash_value: *const u8,  len: usize) -> bool
218{
219    check_data_hash_template(img_hash_data, img_name, start, end, hash_value, len)
220}
221
222/// check hash data from buffer
223///
224/// # Safety
225///
226/// signed_data must contain a valid nul terminator at the end
227#[no_mangle]
228pub unsafe extern fn CheckDataHashNew(img_hash_data: *const ImgHashData<u64>,
229    img_name: *const c_char, start: u64, end: u64, hash_value: *const u8,  len: usize) -> bool
230{
231    check_data_hash_template(img_hash_data, img_name, start, end, hash_value, len)
232}
233
234/// release hash signed data when you no longer need it
235///
236/// # Safety
237///
238/// HashSignedData should be a return value of LoadHashSignedData
239#[no_mangle]
240pub unsafe extern fn ReleaseImgHashData(hash_data: *const ImgHashData<u32>)
241{
242    if hash_data.is_null() {
243        updaterlog!(ERROR, "image hash data is null");
244        return;
245    }
246    unsafe { drop(Box::from_raw(hash_data as *mut ImgHashData<u32>)); }
247    updaterlog!(INFO, "release image hash data");
248}
249
250/// release hash signed data when you no longer need it
251///
252/// # Safety
253///
254/// HashSignedData should be a return value of LoadHashSignedData
255#[no_mangle]
256pub unsafe extern fn ReleaseImgHashDataNew(hash_data: *const ImgHashData<u64>)
257{
258    if hash_data.is_null() {
259        updaterlog!(ERROR, "image hash data is null");
260        return;
261    }
262    unsafe { drop(Box::from_raw(hash_data as *mut ImgHashData<u64>)); }
263    updaterlog!(INFO, "release image hash data");
264}