Android加密之文件級(jí)加密

Android加密之文件級(jí)加密

前置文章

《Android加密之全盤加密》

《Android系統(tǒng)之System Server大綱》

前言

Android 的安全性問(wèn)題一直備受關(guān)注,Google 在 Android 系統(tǒng)的安全方面也是一直沒(méi)有停止過(guò)更新抽兆,努力做到更加安全的手機(jī)移動(dòng)操作系統(tǒng)具练。

在 Android 的安全性方面罚拟,有很多模塊:

  1. 內(nèi)核安全性
  2. 應(yīng)用安全性
  3. 應(yīng)用簽名
  4. 身份驗(yàn)證
  5. Trusty TEE
  6. SELinux
  7. 加密
    等等

其中湿颅,加密又分全盤加密(Android 4.4 引入,《Android加密之全盤加密》)和文件級(jí)加密(Android 7.0 引入)垮兑,本文將論述加密中的文件級(jí)加密的基本知識(shí)禾进。

什么是文件級(jí)加密

Android 7.0 及更高版本支持文件級(jí)加密 (FBE)。采用文件級(jí)加密時(shí)衍慎,可以使用不同的密鑰對(duì)不同的文件進(jìn)行加密转唉,并且可以對(duì)這些文件進(jìn)行單獨(dú)解密。

全盤加密和文件級(jí)加密的區(qū)別

借助文件級(jí)加密稳捆,Android 7.0 中引入了一項(xiàng)稱為直接啟動(dòng)的新功能赠法。該功能處于啟用狀態(tài)時(shí),已加密設(shè)備在啟動(dòng)后將直接進(jìn)入鎖定屏幕。之前砖织,在使用全盤加密 (FDE) 的已加密設(shè)備上款侵,用戶在訪問(wèn)任何數(shù)據(jù)之前都需要先提供憑據(jù),從而導(dǎo)致手機(jī)無(wú)法執(zhí)行除最基本操作之外的所有其他操作侧纯。例如新锈,鬧鐘無(wú)法運(yùn)行,無(wú)障礙服務(wù)不可用眶熬,手機(jī)無(wú)法接電話妹笆,而只能進(jìn)行基本的緊急撥號(hào)操作。

文件級(jí)加密概述

引入文件級(jí)加密 (FBE) 和新 API 后娜氏,便可以將應(yīng)用設(shè)為加密感知型應(yīng)用拳缠,這樣一來(lái),它們將能夠在受限環(huán)境中運(yùn)行贸弥。這些應(yīng)用將可以在用戶提供憑據(jù)之前運(yùn)行窟坐,同時(shí)系統(tǒng)仍能保護(hù)私密用戶信息。

在啟用了 FBE 的設(shè)備上绵疲,每位用戶均有兩個(gè)可供應(yīng)用使用的存儲(chǔ)位置:

  • 憑據(jù)加密 (CE) 存儲(chǔ)空間:這是默認(rèn)存儲(chǔ)位置哲鸳,只有在用戶解鎖設(shè)備后才可用。
  • 設(shè)備加密 (DE) 存儲(chǔ)空間:在直接啟動(dòng)模式期間以及用戶解鎖設(shè)備后均可用盔憨。

這種區(qū)分能夠使工作資料更加安全徙菠,因?yàn)檫@樣一來(lái),加密不再只基于啟動(dòng)時(shí)密碼般渡,從而能夠同時(shí)保護(hù)多位用戶懒豹。

Direct Boot API 允許加密感知型應(yīng)用訪問(wèn)上述每個(gè)區(qū)域。應(yīng)用生命周期會(huì)發(fā)生一些變化驯用,以便在用戶的 CE 存儲(chǔ)空間因用戶在鎖定屏幕上首次輸入憑據(jù)而解鎖時(shí)脸秽,或者在工作資料提供工作挑戰(zhàn)時(shí),通知應(yīng)用蝴乔。無(wú)論是否實(shí)現(xiàn)了 FBE记餐,運(yùn)行 Android 7.0 的設(shè)備都必須要支持這些新的 API 和生命周期。不過(guò)薇正,如果沒(méi)有 FBE片酝,DE 和 CE 存儲(chǔ)空間將始終處于解鎖狀態(tài)。

啟用文件級(jí)加密

通過(guò)將不帶參數(shù)的 fileencryption 標(biāo)記添加到 userdata 分區(qū)最后一列的 fstab 行中挖腰,可以啟用 FBE雕沿。

直接啟動(dòng)感知型應(yīng)用

為了實(shí)現(xiàn)系統(tǒng)應(yīng)用的快速遷移,新增了兩個(gè)可在應(yīng)用級(jí)別設(shè)置的屬性猴仑。defaultToDeviceProtectedStorage 屬性僅適用于系統(tǒng)應(yīng)用审轮,directBootAware 屬性則適用于所有應(yīng)用。

<application
android:directBootAware="true"
android:defaultToDeviceProtectedStorage="true">

應(yīng)用級(jí)別的 directBootAware 屬性的含義是將相應(yīng)應(yīng)用中的所有組件均標(biāo)記為加密感知型組件。

defaultToDeviceProtectedStorage 屬性用于將默認(rèn)的應(yīng)用存儲(chǔ)位置重定向到 DE 存儲(chǔ)空間(而非 CE 存儲(chǔ)空間)疾渣。使用此標(biāo)記的系統(tǒng)應(yīng)用必須要仔細(xì)審核存儲(chǔ)在默認(rèn)位置的所有數(shù)據(jù)篡诽,并將敏感數(shù)據(jù)的路徑更改為使用 CE 存儲(chǔ)空間。使用此選項(xiàng)的設(shè)備制造商應(yīng)仔細(xì)檢查要存儲(chǔ)的數(shù)據(jù)榴捡,以確保其中不含任何個(gè)人信息杈女。

在這種模式下運(yùn)行時(shí),以下系統(tǒng) API 可在需要時(shí)用于明確管理由 CE 存儲(chǔ)空間支持的 Context(這些 API 與設(shè)備保護(hù)存儲(chǔ)空間適用的同類 API 相對(duì)應(yīng))吊圾。

  • StorageManager.isFileEncryptedNativeOrEmulated()
  • Context.createCredentialProtectedStorageContext()
  • Context.isCredentialProtectedStorage()

DE 存儲(chǔ)空間支持的 Context

  • Context.createDeviceProtectedStorageContext()
  • Context.isDeviceProtectedStorage()

啟用文件級(jí)加密的條件

  • 對(duì) EXT4 加密的內(nèi)核支持(內(nèi)核配置選項(xiàng):EXT4_FS_ENCRYPTION)
  • 基于 1.0 或 2.0 版 HAL 的 Keymaster 支持达椰。不支持 Keymaster 0.3,因?yàn)樗炔惶峁┍匾墓δ芟钇梗膊荒鼙WC為加密密鑰提供充分保護(hù)砰碴。
  • 必須在可信執(zhí)行環(huán)境 (TEE) 中實(shí)現(xiàn) Keymaster/Keystore 和 Gatekeeper,以便為 DE 密鑰提供保護(hù)板丽,從而使未經(jīng)授權(quán)的操作系統(tǒng)(刷到設(shè)備上的定制操作系統(tǒng))無(wú)法直接請(qǐng)求 DE 密鑰。
  • 內(nèi)核加密性能必須要在使用 AES XTS 時(shí)至少達(dá)到 50MB/s趁尼,以確保良好的用戶體驗(yàn)埃碱。
  • 硬件信任根和驗(yàn)證啟動(dòng)需要綁定到 Keymaster 初始化進(jìn)程,以確保未經(jīng)授權(quán)的操作系統(tǒng)無(wú)法獲取設(shè)備加密憑據(jù)酥泞。

加密過(guò)程

密鑰創(chuàng)建

首次創(chuàng)建設(shè)備的 userdata 分區(qū)時(shí)砚殿,會(huì)由 init 腳本應(yīng)用基本結(jié)構(gòu)和政策。這些腳本將觸發(fā)創(chuàng)建首位用戶(用戶 0)的 CE 密鑰和 DE 密鑰芝囤,并定義要使用這些密鑰加密哪些目錄似炎。創(chuàng)建其他用戶和資料時(shí),會(huì)生成必要的其他密鑰并將其存儲(chǔ)在密鑰代碼庫(kù)中悯姊;接下來(lái)會(huì)創(chuàng)建它們的憑據(jù)和設(shè)備存儲(chǔ)位 置羡藐,并且加密政策會(huì)將這些密鑰關(guān)聯(lián)到相應(yīng)目錄。

DE密鑰

觸發(fā) late-init action

// 開機(jī)執(zhí)行init.cpp悯许,
int main(int argc, char** argv) {
    ......
    // 解析 init.rc file
    Parser& parser = Parser::GetInstance();
    parser.ParseConfig("/init.rc");
    
    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = property_get("ro.bootmode");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        // 觸發(fā) late-init action
        am.QueueEventTrigger("late-init");
    }
    ......
}

這個(gè)方法定義在文件 system/core/init/init.cpp 中仆嗦。

觸發(fā) post-fs-data

on late-init
    .....
    trigger post-fs
    # Now we can mount /data. File encryption requires keymaster to decrypt
    # /data, which in turn can only be loaded when system properties are present
    trigger post-fs-data
    .....

這個(gè) action 定義在文件 system/core/rootdir/init.rc 中。

執(zhí)行 installkey 命令

on post-fs-data
    chown system system /data
    chmod 0771 /data
    # Make sure we have the device encryption key.
    start vold
    #執(zhí)行 installkey 命令
    installkey /data

這個(gè) action 定義在文件 system/core/rootdir/init.rc 中先壕。

命令 installkey 實(shí)質(zhì)執(zhí)行 do_installkey 函數(shù)

BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    static const Map builtin_functions = {
        .....
        {"installkey",              {1,     1,    do_installkey}},
        {"load_persist_props",      {0,     0,    do_load_persist_props}},
        .....
    };
    return builtin_functions;

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中瘩扼。

do_installkey() 函數(shù)定義如下

// 是否是 文件級(jí)加密
static bool is_file_crypto() {
    // 文件級(jí)加密 ro.crypto.type 的值是 file, 全盤加密是 block
    std::string value = property_get("ro.crypto.type");
    return value == "file";
}

static int do_installkey(const std::vector<std::string>& args) {
    // 檢查是否是文件級(jí)加密
    if (!is_file_crypto()) {
        return 0;
    }
    // 創(chuàng)建密鑰
    return e4crypt_create_device_key(args[1].c_str(),
                                     do_installkeys_ensure_dir_exists);
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中垃僚。

ro.crypto.type 在函數(shù) do_mount_all() 中設(shè)置

static int do_mount_all(const std::vector<std::string>& args) {
    } else if (ret == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
        if (e4crypt_install_keyring()) {
            return -1;
        }
        property_set("ro.crypto.state", "encrypted");
        //文件級(jí)加密
        property_set("ro.crypto.type", "file");
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中集绰。

回到 do_installkey() 函數(shù),e4crypt_create_device_key() 定義如下

int e4crypt_create_device_key(const char* dir,
                              int ensure_dir_exists(const char*))
{
    init_logging();
    .....
    // 執(zhí)行 vdc谆棺, 傳入命令 enablefilecrypto栽燕, 同時(shí)需要注意參數(shù) cryptfs
    const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto" };
    // 從 init, 到 vdc, 注意參數(shù) argv[]
    int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
    LOG(INFO) << "enablefilecrypto result: " << rc;
    return rc;
}

這個(gè)方法定義在文件 system/extras/ext4_utils/ext4_crypt_init_extensions.cpp 中纫谅。

android_fork_execvp() 實(shí)質(zhì)是調(diào)用函數(shù) android_fork_execvp_ext()

static inline int android_fork_execvp(int argc, char* argv[], int *status,
                                     bool ignore_int_quit, bool logwrap)
{
    // 實(shí)質(zhì)是調(diào)用函數(shù)這個(gè)函數(shù)
    return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL,
                                   NULL, 0);
}

這個(gè)方法定義在文件 system/core/logwrapper/include/logwrap/logwrap.h 中炫贤。

函數(shù) android_fork_execvp_ext() 的實(shí)現(xiàn)如下

int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
        int log_target, bool abbreviated, char *file_path,
        const struct AndroidForkExecvpOption* opts, size_t opts_len) {
    // fork 一個(gè)新的進(jìn)程運(yùn)行 vdc 程序
    pid = fork();
    if (pid < 0) {
        .....
    } else if (pid == 0) {
        .....
        // fork 進(jìn)程成功, 執(zhí)行函數(shù) child()
        child(argc, argv);
    } else {

}

這個(gè)方法定義在文件 system/core/logwrapper/logwrap.c 中付秕。

static void child(int argc, char* argv[]) {
    // create null terminated argv_child array
    char* argv_child[argc + 1];
    memcpy(argv_child, argv, argc * sizeof(char *));
    argv_child[argc] = NULL;
    // 開始運(yùn)行 vdc 程序兰珍,參數(shù) cryptfs, enablefilecrypto
    // 從 init 進(jìn)程询吴,進(jìn)入到 vdc 進(jìn)程
    if (execvp(argv_child[0], argv_child)) {
        FATAL_CHILD("executing %s failed: %s\n", argv_child[0],
                strerror(errno));
    }
}

這個(gè)方法定義在文件 system/core/logwrapper/logwrap.c 中掠河。

int main(int argc, char **argv) {
    // 定義待連接的 socket 標(biāo)識(shí)
    const char* sockname = "vold";
    //在上面的參數(shù)中 argv[1] 等于 cryptfs, 所以 socket name 等于 cryptd
    if (!strcmp(argv[1], "cryptfs")) {
        sockname = "cryptd";
    }
    // 等待連接到 vold
    while ((sock = socket_local_client(sockname,
                                 ANDROID_SOCKET_NAMESPACE_RESERVED,
                                 SOCK_STREAM)) < 0) {
        .....
    }
    if (!strcmp(argv[1], "monitor")) {
        exit(do_monitor(sock, 0));
    } else {
        //argv[1] 等于 cryptfs猛计, 執(zhí)行函數(shù) do_cmd()
        exit(do_cmd(sock, argc, argv));
    }
}

這個(gè)方法定義在文件 system/vold/vdc.cpp 中唠摹。

static int do_cmd(int sock, int argc, char **argv) {
    .....
    // 寫入 socket,注意參數(shù) cmd.c_str()
    if ((write(sock, cmd.c_str(), cmd.length() + 1)) < 0) {
        fprintf(stderr, "Failed to write command: %s\n", strerror(errno));
        return errno;
    }
    return do_monitor(sock, seq);
}

這個(gè)方法定義在文件 system/vold/vdc.cpp 中奉瘤。

socket 寫入數(shù)據(jù)到遠(yuǎn)程后勾拉,執(zhí)行到 vold 進(jìn)程

int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
                                                 int argc, char **argv) {
    if (subcommand == "checkpw") {
        .....
    } 
    ..... 
    //傳入的命令是 enablefilecrypto
    } else if (subcommand == "enablefilecrypto") {
        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
        dumpArgs(argc, argv, -1);
        rc = cryptfs_enable_file();
    }  
    .....                                             
}

這個(gè)方法定義在文件 system/vold/CryptCommandListener.cpp 中。

函數(shù) cryptfs_enable_file() 定義如下

int cryptfs_enable_file()
{
    return e4crypt_initialize_global_de();
}

這個(gè)函數(shù)定義在文件 system/vold/cryptfs.c 中盗温。

bool e4crypt_initialize_global_de() {
    .....
    // device_key_path = /data/unencrypted/key/
    if (path_exists(device_key_path)) {
        if (!android::vold::retrieveKey(device_key_path,
                kEmptyAuthentication, &device_key)) return false;
    } else {
        LOG(INFO) << "Creating new key";
        // 創(chuàng)建 密鑰
        if (!random_key(&device_key)) return false;
        // 保存密鑰
        if (!store_key(device_key_path, device_key_temp,
                kEmptyAuthentication, device_key)) return false;
    }

    std::string device_key_ref;
    //存儲(chǔ)在密鑰代碼庫(kù)中
    if (!install_key(device_key, &device_key_ref)) {
        LOG(ERROR) << "Failed to install device key";
        return false;
    }
    // 應(yīng)用密鑰
    std::string ref_filename = std::string("/data") + e4crypt_key_ref;
    if (!android::base::WriteStringToFile(device_key_ref, ref_filename)) {
        PLOG(ERROR) << "Cannot save key reference";
        return false;
    }

    s_global_de_initialized = true;
    return true;
}

DE密鑰創(chuàng)建過(guò)程就分析到這里藕赞。

CE密鑰

同樣在 init.rc 的 post-fs-data action 中

on post-fs-data
    .....
    installkey /data
    .....
    執(zhí)行 init_user0 命令
    init_user0
    .....

這個(gè) action 定義在文件 system/core/rootdir/init.rc 中。

init_user0 實(shí)質(zhì)是執(zhí)行函數(shù)

BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    static const Map builtin_functions = {
    .....
    {"ifup",                    {1,     1,    do_ifup}},
    //執(zhí)行 do_init_user0() 函數(shù)
    {"init_user0",              {0,     0,    do_init_user0}},
    .....
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中卖局。

函數(shù) do_init_user0() 定義如下

static int do_init_user0(const std::vector<std::string>& args) {
    //直接調(diào)用了函數(shù) e4crypt_do_init_user0()
    return e4crypt_do_init_user0();
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中斧蜕。

函數(shù) e4crypt_do_init_user0() 定義如下

int e4crypt_do_init_user0()
{
    init_logging();
    //執(zhí)行 vdc , 參數(shù) cryptfs 和 init_user0砚偶, 和 DE 的創(chuàng)建過(guò)程類似
    const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "init_user0" };
    // fork vdc 進(jìn)程批销,并運(yùn)行 vdc 程序
    int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
    LOG(INFO) << "init_user0 result: " << rc;
    return rc;
}

這個(gè)方法定義在文件 system/extras/ext4_utils/ext4_crypt_init_extensions.cpp 中。

函數(shù) android_fork_execvp() 運(yùn)行 vdc 后染坯,vdc 并沒(méi)有做什么具體的操作均芽,只是把相應(yīng)的參數(shù)繼續(xù)傳遞給 vold,和 DE 的密鑰創(chuàng)建過(guò)程一樣酒请,參數(shù) "cryptfs" 和 參數(shù) "init_user0" 決定會(huì)執(zhí)行到 vold 的如下代碼

int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
                                                 int argc, char **argv) {    
    .....
    } else if (subcommand == "init_user0") {
        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
        //執(zhí)行函數(shù) e4crypt_init_user0()
        return sendGenericOkFailOnBool(cli, e4crypt_init_user0());
    .....
}

這個(gè)方法定義在文件 system/vold/CryptCommandListener.cpp 中骡技。

函數(shù) e4crypt_init_user0() 定義如下

bool e4crypt_init_user0() {
    LOG(DEBUG) << "e4crypt_init_user0";
    if (e4crypt_is_native()) {
        // user_key_dir 等于 data/misc/vold/user_keys
        if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
        if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
        if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
        if (!path_exists(get_de_key_path(0))) {
            //創(chuàng)建和安裝 CD keys, user 為 0羞反, 即開機(jī)默認(rèn)的 user
            if (!create_and_install_user_keys(0, false)) return false;
        }
        // TODO: switch to loading only DE_0 here once framework makes
        // explicit calls to install DE keys for secondary users
        if (!load_all_de_keys()) return false;
    }
    // We can only safely prepare DE storage here, since CE keys are probably
    // entangled with user credentials.  The framework will always prepare CE
    // storage once CE keys are installed.
    if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) {
        LOG(ERROR) << "Failed to prepare user 0 storage";
        return false;
    }

    // If this is a non-FBE device that recently left an emulated mode,
    // restore user data directories to known-good state.
    if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
        e4crypt_unlock_user_key(0, 0, "!", "!");
    }

    return true;
}

這個(gè)方法定義在文件 system/vold/Ext4Crypt.cpp 中布朦。

函數(shù) create_and_install_user_keys() 定義如下

static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
    std::string de_key, ce_key;
    //創(chuàng)建 DE 密鑰
    if (!random_key(&de_key)) return false;
    //創(chuàng)建 CE 密鑰
    if (!random_key(&ce_key)) return false;
    .....
    std::string de_raw_ref;
    // 存儲(chǔ) DE 密鑰到密鑰代碼庫(kù)
    if (!install_key(de_key, &de_raw_ref)) return false;
    s_de_key_raw_refs[user_id] = de_raw_ref;
    std::string ce_raw_ref;
    // 存儲(chǔ) CE 密鑰到密鑰代碼庫(kù)
    if (!install_key(ce_key, &ce_raw_ref)) return false;
    s_ce_keys[user_id] = ce_key;
    s_ce_key_raw_refs[user_id] = ce_raw_ref;
    LOG(DEBUG) << "Created keys for user " << user_id;
    return true;
}

這個(gè)方法定義在文件 system/vold/Ext4Crypt.cpp 中。

再看看密鑰的真正生成過(guò)程 random_key()

static bool random_key(std::string* key) {
    // 讀取隨機(jī)密鑰
    if (android::vold::ReadRandomBytes(EXT4_AES_256_XTS_KEY_SIZE, *key) != 0) {
        // TODO status_t plays badly with PLOG, fix it.
        LOG(ERROR) << "Random read failed";
        return false;
    }
    return true;
}

這個(gè)方法定義在文件 system/vold/Ext4Crypt.cpp 中昼窗。

ReadRandomBytes() 定義如下

status_t ReadRandomBytes(size_t bytes, std::string& out) {
    out.clear();
    //打開 linux 的隨機(jī)數(shù)文件
    int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
    if (fd == -1) {
        return -errno;
    }

    char buf[BUFSIZ];
    size_t n;
    //讀取一個(gè)隨機(jī)數(shù)是趴,作為密鑰
    while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
        out.append(buf, n);
        bytes -= n;
    }
    close(fd);

    if (bytes == 0) {
        return OK;
    } else {
        return -EIO;
    }
}

這個(gè)方法定義在文件 system/vold/Utils.cpp 中。

使用創(chuàng)建的密鑰加密

在解析 init.rc 文件時(shí)澄惊,會(huì)執(zhí)行命令 mkdir唆途, 如

mkdir /data/system_de 0770 system system
on post-fs-data
    mkdir /data/system_ce 0770 system system

    mkdir /data/misc_de 01771 system misc
    mkdir /data/misc_ce 01771 system misc
    //用戶數(shù)據(jù)路徑
    mkdir /data/user 0711 system system
    // 用戶 DE 空間
    mkdir /data/user_de 0711 system system
    // /data/data 連接到目錄 /data/user/0
    // /data/user 和 /data/data 都是 CE 空間
    symlink /data/data /data/user/0

這個(gè) action 定義在文件 system/core/rootdir/init.rc 中富雅。

命令 mkdir 實(shí)質(zhì)執(zhí)行的的是函數(shù) do_mkdir()

BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    static const Map builtin_functions = {
    .....
    {"mkdir",                   {1,     4,    do_mkdir}},
    .....
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中。

函數(shù) do_mkdir() 的實(shí)現(xiàn)如下

static int do_mkdir(const std::vector<std::string>& args) {
    .....
    // 創(chuàng)建目錄
    ret = make_dir(args[1].c_str(), mode);
    .....
    if (e4crypt_is_native()) {
        // 加密目錄
        if (e4crypt_set_directory_policy(args[1].c_str())) {
            wipe_data_via_recovery(std::string() + "set_policy_failed:" + args[1]);
            return -1;
        }
    }
    return 0;
}

這個(gè)方法定義在文件 system/core/init/builtins.cpp 中肛搬。

函數(shù) e4crypt_set_directory_policy() 的實(shí)現(xiàn)如下

int e4crypt_set_directory_policy(const char* dir)
{
    // 只加密 /data 目錄以及子目錄
    if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
        return 0;
    }

    // 不需要加密的目錄在這里設(shè)置没佑,但是,它們的子目錄是會(huì)被加密的
    std::vector<std::string> directories_to_exclude = {
        "lost+found",
        "system_ce", "system_de",
        "misc_ce", "misc_de",
        "media",
        "data", "user", "user_de",
    };
    std::string prefix = "/data/";
    for (auto d: directories_to_exclude) {
        if ((prefix + d) == dir) {
            KLOG_INFO(TAG, "Not setting policy on %s\n", dir);
            return 0;
        }
    }
    // 密鑰引用
    std::string ref_filename = std::string("/data") + e4crypt_key_ref;
    std::string policy;
    if (!android::base::ReadFileToString(ref_filename, &policy)) {
        KLOG_ERROR(TAG, "Unable to read system policy to set on %s\n", dir);
        return -1;
    }
    KLOG_INFO(TAG, "Setting policy on %s\n", dir);
    // 加密目錄
    int result = e4crypt_policy_ensure(dir, policy.c_str(), policy.size());
    if (result) {
        KLOG_ERROR(TAG, "Setting %02x%02x%02x%02x policy on %s failed!\n",
                   policy[0], policy[1], policy[2], policy[3], dir);
        return -1;
    }

    return 0;
}

這個(gè)方法定義在文件 system/extras/ext4_utils/ext4_crypt_init_extensions.cpp 中温赔。

函數(shù) e4crypt_policy_ensure() 定義如下

int e4crypt_policy_ensure(const char *directory, const char *policy, size_t policy_length) {
    bool is_empty;
    if (!is_dir_empty(directory, &is_empty)) return -1;
    if (is_empty) {
        // 應(yīng)用加密政策
        if (!e4crypt_policy_set(directory, policy, policy_length)) return -1;
    } else {
        if (!e4crypt_policy_check(directory, policy, policy_length)) return -1;
    }
    return 0;
}

這個(gè)方法定義在文件 system/extras/ext4_utils/ext4_crypt.cpp 中蛤奢。

函數(shù) e4crypt_policy_set() 定義如下

static bool e4crypt_policy_set(const char *directory, const char *policy, size_t policy_length) {
    int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
    ......
    ext4_encryption_policy eep;
    eep.version = 0;
    // 設(shè)置加密類型 AES 256
    eep.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
    eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
    eep.flags = 0;
    memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
    // 用命令 EXT4_IOC_SET_ENCRYPTION_POLICY 控制 IO
    if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
        PLOG(ERROR) << "Failed to set encryption policy for " << directory;
        close(fd);
        return false;
    }
    close(fd);

    char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
    policy_to_hex(policy, policy_hex);
    LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
    return true;
}

這個(gè)方法定義在文件 system/extras/ext4_utils/ext4_crypt.cpp 中。

加密過(guò)程就分析到這里陶贼。

直接啟動(dòng)

應(yīng)用了文件級(jí)加密的設(shè)備啤贩,可以以直接啟動(dòng)的方式啟動(dòng)。此時(shí)拜秧,設(shè)備可以加載并使用沒(méi)有通過(guò)文件級(jí)加密的目錄痹屹,如 /data/user_de/0/。那么枉氮,直接啟動(dòng)的 APP 的數(shù)據(jù)保存在這個(gè)目錄下志衍。

在上文中,我們知道需要在直接啟動(dòng)就可以立馬使用的的 APP聊替,需要在應(yīng)用的 manifest 的 application 標(biāo)簽聲明 android:directBootAware="true" 屬性足画。對(duì)于系統(tǒng)的應(yīng)用,聲明 android:defaultToDeviceProtectedStorage="true" 可以把應(yīng)用的默認(rèn)存儲(chǔ)空間設(shè)置為 /data/user_de/佃牛。

因此,在用戶沒(méi)有輸入憑據(jù)解密 CE 空間之前医舆,系統(tǒng)只是加載 DE 下的應(yīng)用俘侠。

在 AMS ready 時(shí),如下(讀者不了解這個(gè)過(guò)程的以看考文章《 Android系統(tǒng)之System Server大綱》

public void systemReady(final Runnable goingCallback) {
    .....
    synchronized (this) {
        // Only start up encryption-aware persistent apps; once user is
        // unlocked we'll come back around and start unaware apps
        //啟動(dòng) persistent app蔬将,注意參數(shù) PackageManager.MATCH_DIRECT_BOOT_AWARE
        startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
    }
    .....

這個(gè)方法定義在文件 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 中爷速。

方法 startPersistentApps() 的實(shí)現(xiàn)如下

private void startPersistentApps(int matchFlags) {
    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;

    synchronized (this) {
        try {
            //獲取所有 direct boot 的 app
            final List<ApplicationInfo> apps = AppGlobals.getPackageManager()
                    .getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
            for (ApplicationInfo app : apps) {
                if (!"android".equals(app.packageName) && validNewProc(app.packageName, UserHandle.getUserId(app.uid))) {//modified by yongfeng.zhang for task 3682193 on 2016-12-28
                    // 加入啟動(dòng)隊(duì)列
                    addAppLocked(app, false, null /* ABI override */);
                }
            }
        } catch (RemoteException ex) {
        }
    }
}

這個(gè)方法定義在文件 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 中。

方法 addAppLocked() 定義如下

final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
        String abiOverride) {
    .....
    if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
        mPersistentStartingProcesses.add(app);
        // 啟動(dòng) APP
        startProcessLocked(app, "added application", app.processName, abiOverride,
                null /* entryPoint */, null /* entryPointArgs */);
    }
    return app;
}

這個(gè)方法定義在文件 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 中霞怀。

在 PMS 啟動(dòng)時(shí)惫东,掃描安裝 APP 是,會(huì)過(guò)濾不是直接啟動(dòng)的 APP

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
        final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
        throws PackageManagerException {
    // Apply policy
    if ((policyFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        //直接啟動(dòng)的 APP
        if (pkg.applicationInfo.isDirectBootAware()) {
            // we're direct boot aware; set for all components
            for (PackageParser.Service s : pkg.services) {
                s.info.encryptionAware = s.info.directBootAware = true;
            }
            for (PackageParser.Provider p : pkg.providers) {
                p.info.encryptionAware = p.info.directBootAware = true;
            }
            for (PackageParser.Activity a : pkg.activities) {
                a.info.encryptionAware = a.info.directBootAware = true;
            }
            for (PackageParser.Activity r : pkg.receivers) {
                r.info.encryptionAware = r.info.directBootAware = true;
            }
        }
    }

}

這個(gè)方法定義在文件 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 中毙石。

總結(jié)

文件級(jí)加密廉沮,比較全盤加密具有一些優(yōu)點(diǎn),可以讓沒(méi)有輸入憑證的設(shè)備可以使用更多的功能徐矩。文件級(jí)加密分 CE 空間和 DE 空間滞时,CE 空間需要憑證加密方可使用,DE 空間則是設(shè)備啟動(dòng)后即可使用滤灯。應(yīng)用如果需要區(qū)分 CE 和 DE 空間坪稽,需要?jiǎng)?chuàng)建不同的上下文環(huán)境 Context曼玩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市窒百,隨后出現(xiàn)的幾起案子黍判,更是在濱河造成了極大的恐慌,老刑警劉巖篙梢,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顷帖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡庭猩,警方通過(guò)查閱死者的電腦和手機(jī)窟她,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蔼水,“玉大人震糖,你說(shuō)我怎么就攤上這事∨恳福” “怎么了吊说?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)优炬。 經(jīng)常有香客問(wèn)我颁井,道長(zhǎng),這世上最難降的妖魔是什么蠢护? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任雅宾,我火速辦了婚禮,結(jié)果婚禮上葵硕,老公的妹妹穿的比我還像新娘眉抬。我一直安慰自己,他們只是感情好懈凹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布蜀变。 她就那樣靜靜地躺著,像睡著了一般介评。 火紅的嫁衣襯著肌膚如雪库北。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天们陆,我揣著相機(jī)與錄音寒瓦,去河邊找鬼。 笑死坪仇,一個(gè)胖子當(dāng)著我的面吹牛孵构,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烟很,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼颈墅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蜡镶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起恤筛,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤官还,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后毒坛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體望伦,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年煎殷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屯伞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豪直,死狀恐怖劣摇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情弓乙,我是刑警寧澤末融,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站暇韧,受9級(jí)特大地震影響勾习,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜懈玻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一巧婶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涂乌,春花似錦粹舵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)巴席。三九已至历涝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漾唉,已是汗流浹背荧库。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赵刑,地道東北人分衫。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像般此,于是被迫代替她去往敵國(guó)和親蚪战。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牵现,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容