1 /*
2 * Copyright (c) 2025 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 module provides interfaces for upgrade clone apps.
17 //! Databases are isolated based on users and protected by locks.
18
19 use std::{ffi::CString, collections::HashSet};
20 use std::os::raw::c_char;
21
22 use asset_common::{CallingInfo, OwnerType, SUCCESS};
23 use asset_crypto_manager::secret_key::SecretKey;
24 use asset_definition::{log_throw_error, Accessibility, AuthType, ErrCode, Extension, Result, Value};
25 use asset_plugin::asset_plugin::AssetPlugin;
26 use asset_sdk::plugin_interface::{
27 EventType, ExtDbMap, PARAM_NAME_AAD, PARAM_NAME_ACCESSIBILITY, PARAM_NAME_APP_INDEX, PARAM_NAME_CIPHER,
28 PARAM_NAME_DECRYPT_KEY_ALIAS, PARAM_NAME_ENCRYPT_KEY_ALIAS, PARAM_NAME_USER_ID,
29 };
30 use asset_db_operator::{
31 database::Database, database_file_upgrade::{
32 get_file_content, get_upgrade_list, get_upgrade_version, update_upgrade_list,
33 OriginVersion, UpgradeData, is_hap_special
34 },
35 types::{column, DbMap},
36 };
37 use asset_db_key_operator::generate_secret_key_if_needed;
38
39 use crate::operations::common;
40
41 extern "C" {
GetCloneAppIndexes(userId: i32, appIndexes: *mut i32, indexSize: *mut u32, appName: *const c_char) -> i3242 fn GetCloneAppIndexes(userId: i32, appIndexes: *mut i32, indexSize: *mut u32, appName: *const c_char) -> i32;
IsHapInAllowList(userId: i32, appName: *const c_char, isHapInList: &mut bool) -> i3243 fn IsHapInAllowList(userId: i32, appName: *const c_char, isHapInList: &mut bool) -> i32;
44 }
45
46 const DEFAULT_VALUE: i32 = 0;
47 const DEFAULT_SIZE: usize = 5;
48 const INIT_INDEX: usize = 1;
49
50 struct UnwrapInfo<'a> {
51 data: &'a mut DbMap,
52 calling_info: &'a CallingInfo,
53 calling_info_new: &'a CallingInfo,
54 suffix: &'a [u8],
55 new_owner: &'a Vec<u8>,
56 }
57
58 /// Upgrade the data of clone apps.
upgrade_clone_app_data(user_id: i32) -> Result<()>59 pub fn upgrade_clone_app_data(user_id: i32) -> Result<()> {
60 let upgrade_data = get_file_content(user_id)?;
61 upgrade(user_id, upgrade_data)
62 }
63
64 /// To upgrade a clone app.
upgrade_single_clone_app_data(user_id: i32, hap_info: String) -> Result<()>65 pub fn upgrade_single_clone_app_data(user_id: i32, hap_info: String) -> Result<()> {
66 if user_id == 0 {
67 return Ok(());
68 }
69 let parts: Vec<_> = hap_info.split('_').collect();
70 if parts.len() < INIT_INDEX + 1 {
71 return log_throw_error!(ErrCode::InvalidArgument, "Hap info too short.");
72 }
73 match parts.last().unwrap().parse::<i32>() {
74 Ok(num) => {
75 if num == 0 {
76 return Ok(());
77 }
78 },
79 Err(_) => return log_throw_error!(ErrCode::InvalidArgument, "Upgrade clone app failed."),
80 }
81 let version = get_upgrade_version(user_id)?;
82 if version == OriginVersion::V2 {
83 return Ok(());
84 }
85 upgrade_single(user_id, version, parts[INIT_INDEX..parts.len() - 1].join("_"));
86 Ok(())
87 }
88
upgrade(user_id: i32, upgrade_data: UpgradeData) -> Result<()>89 fn upgrade(user_id: i32, upgrade_data: UpgradeData) -> Result<()> {
90 for info in upgrade_data.upgrade_list {
91 let version = match upgrade_data.version {
92 version if version == OriginVersion::V1 as u32 => OriginVersion::V1,
93 version if version == OriginVersion::V2 as u32 => OriginVersion::V2,
94 version if version == OriginVersion::V3 as u32 => OriginVersion::V3,
95 _ => OriginVersion::V2,
96 };
97 upgrade_single(user_id, version, info.to_owned());
98 }
99 Ok(())
100 }
101
is_hap_in_upgrade_list(user_id: i32, info: &str) -> bool102 fn is_hap_in_upgrade_list(user_id: i32, info: &str) -> bool {
103 let list = match get_upgrade_list(user_id) {
104 Ok(list) => list,
105 Err(_) => return false,
106 };
107 list.contains(&info.to_owned())
108 }
109
upgrade_single(user_id: i32, version: OriginVersion, info: String)110 fn upgrade_single(user_id: i32, version: OriginVersion, info: String) {
111 if !is_hap_in_upgrade_list(user_id, &info) {
112 return;
113 }
114 let _ = upgrade_execute(user_id, version.clone(), &info);
115 }
116
upgrade_execute(user_id: i32, version: OriginVersion, info: &str) -> Result<()>117 fn upgrade_execute(user_id: i32, version: OriginVersion, info: &str) -> Result<()> {
118 if is_hap_special(info) || (version == OriginVersion::V3 && !(is_hap_in_allowlist(user_id, info)?)) {
119 return update_upgrade_list(user_id, &info.to_owned());
120 }
121 let indexes = get_clone_app_indexes(user_id, info)?;
122 if indexes.is_empty() {
123 return update_upgrade_list(user_id, &info.to_owned());
124 }
125 clone_data_from_app_to_clone_app(user_id, info, &indexes)?;
126 update_upgrade_list(user_id, &info.to_owned())
127 }
128
get_clone_app_indexes(user_id: i32, app_name: &str) -> Result<Vec<i32>>129 fn get_clone_app_indexes(user_id: i32, app_name: &str) -> Result<Vec<i32>> {
130 let mut indexes: Vec<i32> = vec![DEFAULT_VALUE; DEFAULT_SIZE];
131 let app_name_cstr = match CString::new(app_name) {
132 Ok(app_name_cstr) => app_name_cstr,
133 Err(_) => return log_throw_error!(ErrCode::OutOfMemory, "Create CString failed."),
134 };
135 let mut index_size = DEFAULT_SIZE as u32;
136 let ret = unsafe { GetCloneAppIndexes(user_id, indexes.as_mut_ptr(), &mut index_size, app_name_cstr.as_ptr())};
137 if ret != SUCCESS {
138 return log_throw_error!(ErrCode::try_from(ret as u32)?, "Get clone app indexes failed.");
139 }
140 indexes.truncate(index_size as usize);
141 Ok(indexes)
142 }
143
fmt_de_db_name(app_name: &str, app_index: i32) -> String144 fn fmt_de_db_name(app_name: &str, app_index: i32) -> String {
145 format!("Hap_{}_{}", app_name, app_index)
146 }
147
clone_data_from_app_to_clone_app(user_id: i32, app_name: &str, app_indexes: &[i32]) -> Result<()>148 fn clone_data_from_app_to_clone_app(user_id: i32, app_name: &str, app_indexes: &[i32]) -> Result<()> {
149 let main_name = fmt_de_db_name(app_name, 0);
150 let mut db_main = Database::build_with_file_name(user_id, &main_name, false)?;
151 let mut datas: Vec<DbMap> = db_main.query_datas(&vec![], &DbMap::new(), None, false)?;
152 if datas.is_empty() {
153 return Ok(());
154 }
155 for index in app_indexes {
156 clone_single_app(user_id, app_name, *index, &mut datas)?;
157 }
158 Ok(())
159 }
160
is_hap_in_allowlist(user_id: i32, info: &str) -> Result<bool>161 fn is_hap_in_allowlist(user_id: i32, info: &str) -> Result<bool> {
162 let app_name = CString::new(info).unwrap();
163 let mut is_in_list: bool = false;
164 let ret = unsafe { IsHapInAllowList(user_id, app_name.as_ptr(), &mut is_in_list) };
165 if ret != SUCCESS {
166 return log_throw_error!(ErrCode::try_from(ret as u32)?, "Check hap in allowlist failed.");
167 }
168 Ok(is_in_list)
169 }
170
clone_single_app(user_id: i32, app_name: &str, app_index: i32, datas: &mut Vec<DbMap>) -> Result<()>171 fn clone_single_app(user_id: i32, app_name: &str, app_index: i32, datas: &mut Vec<DbMap>) -> Result<()> {
172 let clone_name = fmt_de_db_name(app_name, app_index);
173 let mut db_clone = Database::build_with_file_name(user_id, &clone_name, false)?;
174 let db_map = db_clone.query_data_without_lock(&vec![], &DbMap::new(), None, true)?;
175 let mut alias_set = HashSet::new();
176 for data in db_map {
177 alias_set.insert(data.get_bytes_attr(&column::ALIAS)?.clone());
178 }
179 let mut need_rollback = false;
180 let owner_info = datas.first().unwrap().get_bytes_attr(&column::OWNER)?;
181 let owner_type = datas.first().unwrap().get_enum_attr::<OwnerType>(&column::OWNER_TYPE)?;
182 let calling_info = CallingInfo::new(user_id, owner_type, owner_info.clone(), None);
183 let index = match owner_info.iter().rev().position(|&x| x == b'_') {
184 Some(index) => index,
185 _ => return log_throw_error!(ErrCode::InvalidArgument, "Owner info is incorrect."),
186 };
187 if index >= owner_info.len() - 1 {
188 return log_throw_error!(ErrCode::InvalidArgument, "Owner info is too short.");
189 }
190 let mut new_owner = owner_info[..(owner_info.len() - index)].to_vec();
191 let app_index_str = app_index.to_string();
192 let suffix = app_index_str.as_bytes();
193 new_owner.extend_from_slice(suffix.clone());
194 let calling_info_new = CallingInfo::new(user_id, owner_type, new_owner.clone(), None);
195 db_clone.exec("begin transaction")?;
196 for data in datas {
197 if alias_set.contains(data.get_bytes_attr(&column::ALIAS)?) {
198 continue;
199 }
200 let unwrap_info = UnwrapInfo{
201 data, calling_info: &calling_info, calling_info_new: &calling_info_new, suffix, new_owner: &new_owner
202 };
203 if unwrap_and_insert(user_id, unwrap_info, &mut db_clone).is_err() {
204 need_rollback = true;
205 break;
206 }
207 }
208 if need_rollback {
209 db_clone.exec("rollback")?;
210 return log_throw_error!(ErrCode::DatabaseError, "Upgrade clone app data failed.");
211 }
212 db_clone.exec("commit")
213 }
214
unwrap_and_insert(user_id: i32, unwrap_info: UnwrapInfo, db_clone: &mut Database) -> Result<()>215 fn unwrap_and_insert(user_id: i32, unwrap_info: UnwrapInfo, db_clone: &mut Database) -> Result<()> {
216 let auth_type = unwrap_info.data.get_enum_attr::<AuthType>(&column::AUTH_TYPE)?;
217 let accessibility = unwrap_info.data.get_enum_attr::<Accessibility>(&column::ACCESSIBILITY)?;
218 let required_password_set = unwrap_info.data.get_bool_attr(&column::REQUIRE_PASSWORD_SET)?;
219 let secret_key = SecretKey::new_without_alias(unwrap_info.calling_info, auth_type, accessibility, required_password_set)?;
220 let new_secret_key =
221 SecretKey::new_without_alias(unwrap_info.calling_info_new, auth_type, accessibility, required_password_set)?;
222 let _ = generate_secret_key_if_needed(&new_secret_key);
223 let _ = generate_secret_key_if_needed(&secret_key);
224 let secret = unwrap_info.data.get_bytes_attr(&column::SECRET)?;
225 let mut params = ExtDbMap::new();
226 params.insert(PARAM_NAME_DECRYPT_KEY_ALIAS, Value::Bytes(secret_key.alias().to_vec()));
227 params.insert(PARAM_NAME_ENCRYPT_KEY_ALIAS, Value::Bytes(new_secret_key.alias().to_vec()));
228 params.insert(PARAM_NAME_ACCESSIBILITY, Value::Number(accessibility as u32));
229 params.insert(PARAM_NAME_CIPHER, Value::Bytes(secret.clone()));
230 params.insert(PARAM_NAME_AAD, Value::Bytes(common::build_aad(unwrap_info.data)?));
231 params.insert(PARAM_NAME_APP_INDEX, Value::Bytes(unwrap_info.suffix.to_vec()));
232 params.insert(PARAM_NAME_USER_ID, Value::Number(user_id as u32));
233
234 let load = AssetPlugin::get_instance().load_plugin()?;
235 match load.process_event(EventType::WrapData, &mut params) {
236 Ok(()) => {
237 let cipher = params.get_bytes_attr(&PARAM_NAME_CIPHER)?;
238 unwrap_info.data.insert(column::SECRET, Value::Bytes(cipher.to_vec()));
239 unwrap_info.data.insert(column::OWNER, Value::Bytes(unwrap_info.new_owner.to_vec()));
240 if db_clone.insert_datas(unwrap_info.data).is_err() {
241 return log_throw_error!(ErrCode::CryptoError, "Unwrap the clone app data failed.");
242 }
243 },
244 Err(_) => {
245 return log_throw_error!(ErrCode::CryptoError, "Unwrap the clone app data failed.");
246 },
247 };
248 Ok(())
249 }