• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }