/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! This module implements functions related to Asset database key. use asset_common::SUCCESS; use asset_crypto_manager::{crypto::Crypto, secret_key::SecretKey}; use asset_definition::{log_throw_error, Accessibility, AuthType, ErrCode, Result}; use asset_file_operator::ce_operator::{is_db_key_cipher_file_exist, read_db_key_cipher, write_db_key_cipher}; use asset_log::logi; use std::sync::Mutex; const TRIVIAL_AAD_FOR_DB_KEY: &str = "trivial_aad_for_db_key"; static GEN_KEY_MUTEX: Mutex<()> = Mutex::new(()); static GET_DB_KEY_MUTEX: Mutex<()> = Mutex::new(()); fn build_db_key_secret_key(user_id: i32) -> Result<SecretKey> { let auth_type = AuthType::None; let access_type = Accessibility::DeviceFirstUnlocked; let require_password_set = false; let alias = "db_key_secret_key".as_bytes().to_vec(); SecretKey::new_with_alias(user_id, auth_type, access_type, require_password_set, alias) } /// Generate secret key if it does not exist. pub fn generate_secret_key_if_needed(secret_key: &SecretKey) -> Result<()> { match secret_key.exists() { Ok(true) => Ok(()), Ok(false) => { let _lock = GEN_KEY_MUTEX.lock().unwrap(); match secret_key.exists() { Ok(true) => Ok(()), Ok(false) => { logi!("[INFO]The key does not exist, generate it."); secret_key.generate() }, Err(ret) => Err(ret), } }, Err(ret) => Err(ret), } } extern "C" { fn GenerateRandom(random: *mut u8, random_len: u32) -> i32; } /// db key obj pub struct DbKey { /// db key pub db_key: Vec<u8>, } impl DbKey { fn decrypt_db_key_cipher(user_id: i32, db_key_cipher: &Vec<u8>) -> Result<DbKey> { let secret_key = build_db_key_secret_key(user_id)?; let aad: Vec<u8> = TRIVIAL_AAD_FOR_DB_KEY.as_bytes().to_vec(); let db_key = Crypto::decrypt(&secret_key, db_key_cipher, &aad)?; Ok(Self { db_key }) } fn generate_db_key() -> Result<DbKey> { const KEY_LEN_IN_BYTES: usize = 32; // aes-256-gcm requires key length 256 bits = 32 bytes. let mut db_key = [0; KEY_LEN_IN_BYTES]; if unsafe { GenerateRandom(db_key.as_mut_ptr(), db_key.len() as u32) } != SUCCESS { return log_throw_error!(ErrCode::CryptoError, "[FATAL]Generate random failed!"); } Ok(Self { db_key: db_key.to_vec() }) } fn encrypt_db_key(&self, user_id: i32) -> Result<Vec<u8>> { let secret_key = build_db_key_secret_key(user_id)?; generate_secret_key_if_needed(&secret_key)?; let aad: Vec<u8> = TRIVIAL_AAD_FOR_DB_KEY.as_bytes().to_vec(); let db_key_cipher = Crypto::encrypt(&secret_key, &self.db_key, &aad)?; Ok(db_key_cipher) } /// Check whether the database key exists. pub fn check_existance(user_id: i32) -> Result<bool> { is_db_key_cipher_file_exist(user_id) } /// Read db key cipher and decrypt if the db key cipher file exists, generate db_key if not. pub fn get_db_key(user_id: i32) -> Result<DbKey> { match is_db_key_cipher_file_exist(user_id) { Ok(true) => { let db_key_cipher = read_db_key_cipher(user_id)?; Self::decrypt_db_key_cipher(user_id, &db_key_cipher) }, Ok(false) => { let _lock = GET_DB_KEY_MUTEX.lock().unwrap(); match is_db_key_cipher_file_exist(user_id) { Ok(true) => { let db_key_cipher = read_db_key_cipher(user_id)?; Self::decrypt_db_key_cipher(user_id, &db_key_cipher) }, Ok(false) => { let db_key = Self::generate_db_key()?; let db_key_cipher = db_key.encrypt_db_key(user_id)?; write_db_key_cipher(user_id, &db_key_cipher)?; Ok(db_key) }, Err(e) => Err(e), } }, Err(e) => Err(e), } } } impl Drop for DbKey { fn drop(&mut self) { self.db_key.fill(0); } }