• Android Full Disk Encryption
  • Settings中加密入口
  • 调用关系
  • 加密实现
  • 参考
    • Vold properties
    • init properties
    • init actions

Android Full Disk Encryption

FDE是android设备全盘加密的简称;主要用于对Android设备userdata分区数据的加密,以实现数据保护的目的

  1. FDE是什么
    • FDE是Full Disk Encrypt的缩写
      • 保护/data 分区数据
      • 参考google的官方介绍(需要访问google网络) encryption
      • 一种保护user data的机制。例如:联系人,图片,视频等等
      • 截止当前,Android支持加密userdata分区的数据以及不可移除sdcard的数据
      • 不支持其他分区的加密
    • FDE如何工作
      • 基于android kernel的dm-crypt feature 实现
      • 128 Advanced Encryption Standard(AES) with cipher-block chaining(CBD)and ESSIV:SHA256
      • Android5.0之后,首次开机会加密
      • 加密之后,将/data挂在到dm的虚拟节点上
  2. 首次启动
    • fstab.qcom 标志
      • encryptable manually encrypt
      • foeceencrypted encrypt in fist boot
    • 使用默认的密码获得master key
      • 默认密码是”default_password”
      • Keymaster(HW/SW) general key blob,存储在footer中
      • default_password_salt(random)–>scrypt–>derived key
      • Derived+keypair–>sign–>intermedia key(ikey)
      • salt,keybob,derived key stored in footer
      • set ikey to TZ
    • Hardware Crypto
      • TZ generates an encrypt key derived from new password,set to crypto enginee
    • Software Crypto
      • Call APU to set key with ikey(security_hw.c)
  3. 非首次开机
    • 如果用户设置了pin/或者密码
      • 弹出提示用于输入密码
    • 使用默认的密码获得master key
      • 从footer中读取salt,keyblob,derived key
      • 从输入或者salt中获得derivedkey,并进行比较
      • Derived+keypair–>sign–>intermedia key(ikey)
      • salt,keybob,derived key stored in footer
      • set ikey to TZ
    • 升级相关
      • 如果使用了软件加密,可以直接软件升级
      • 如果使用了硬件加密,不可以直接软件升级
  4. init.rc
    • on property:vold.decrypt=trigger_default_encryption
      • start defaultcrypto
    • service defaultcrypto /system/bin/vdc –wait cryptfs mountdefaultencrypted
      • disable
      • oneshot
      • #vold will set vold.decrypt to trigger_restart_framework(default encryption) or trigger_restart_min_framework(other encryption)
  5. FDE 启动总结
    • init 进程读取fstab中,并依据fstab中的配置进行挂载
    • 在挂载过程中发现该分区标记了encrypted,使用property触发一个属性
    • vdc服务传递命令给cryptd socket
    • 监听器接收到该socket,然后触发encrypt/decrypt 动作
    • 重要的函数
      • CryptCommandListener::CryptfsCmd::runCommand
      • get_keymaster_hw_fde_password
      • set_hw_device_encryption_key
      • create_crypto_blk_dev
      • update_hw_device_encryption_key

Settings中加密入口

//package/apps/SettingsCryptKeeper.javaCryptKeeperSettings.javaCryptKeeperConfirm.java

调试加密过程显示的UI

//正常显示进度条adb shell pm enable com.android.settings/.CryptKeeperadb shell am start  -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "progress"  -n com.android.settings/.CryptKeeper//提示输入密码adb shell am start  -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "password"  -n com.android.settings/.CryptKeeper//出现错误adb shell am start  -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "error"  -n com.android.settings/.CryptKeeper

调用关系


MountService 启动后,会创建两个Thread 一个用于处理同底层vold的交互的、一个用于处理加密 。启动NativeDaemonConnector Thread 之后,对socket进行监听
调用流程:

NativeDaemonConnector.run() public int encryptStorage(int type, String password) -->mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", …);    --> CryptCommandListener::CryptfsCmd::runCommand(SocketClient*, argc, **argv)        -->cryptfs_enable_default(char *howarg, int allow_reboot)            -->cryptfs_enable_internal(char*,int,char*,int)
Created with Raphaël 2.1.0 encryptStorage encryptStorage mCryptConnector.execute mCryptConnector.execute CryptCommandListener_CryptfsCmd_runCommand CryptCommandListener_CryptfsCmd_runCommand cryptfs cryptfs 加密 socket:CryptCommandListener::CryptfsCmd::runCommand
主要代码解释int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,                                             int argc, char **argv) {} else if (!strcmp(argv[1], "cryptocomplete")) {        int type = getType(argv[3]);        if (type == -1) {        } else if (type == CRYPT_TYPE_DEFAULT) {          rc = cryptfs_enable_default(argv[2],/*allow_reboot*/false);        } else {            rc = cryptfs_enable(argv[2], type, argv[4],                                /*allow_reboot*/false);        }    } else if (!strcmp(argv[1], "changepw")) {}
//比较文件系统的和分区的大小int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,                            int allow_reboot)    /* Get the size of the real block device */    int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);    get_blkdev_size(fd, &nr_sec);        fs_size_sec = get_fs_size(real_blkdev);        if (fs_size_sec > max_fs_size_sec) {            SLOGE("Orig filesystem overlaps crypto footer region.  Cannot encrypt in place.");
/*获得一个随机的密钥对和盐,作为加密的密码。将密钥对和盐存储在data 分区的footer中*/static int create_encrypted_random_key(char *passwd, unsigned char *master_key, unsigned char *salt,crypt_mnt_ftr *crypt_ftr) {    /* Get some random bits for a key */    fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);    read(fd, key_buf, sizeof(key_buf));    read(fd, salt, SALT_LEN);    close(fd);    /* Now encrypt it with the password */    return encrypt_master_key(passwd, salt, key_buf, master_key, crypt_ftr);}
/* Create mapped block device /dev/dm-0 with key_index which retrieved from set_hw_device_encryption_key*/decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev, "userdata");rc = cryptfs_enable_all_volumes(&crypt_ftr, how, crypto_blkdev, real_blkdev, previously_encrypted_upto);cryptfs_enable_all_volumes--->cryptfs_enable_inplace ------> cryptfs_enable_inplace_ext4
//加密进程从真实的设备节点读取数据并将其写入到mapper的设备节点中static int cryptfs_enable_inplace_ext4(char *crypto_blkdev,                                       char *real_blkdev,                                       off64_t size,                                       off64_t *size_already_done,                                       off64_t tot_size,                                       off64_t previously_encrypted_upto){    encrypt_groups(&data);}

加密实现

//判定当前设备是否正在被加密或者曾经加密过程被中断了。if (how == CRYPTO_ENABLE_INPLACE      && get_crypt_ftr_and_key(&crypt_ftr) == 0      && (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS)) {    previously_encrypted_upto = crypt_ftr.encrypted_upto;    crypt_ftr.encrypted_upto = 0;    crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;    //加密的状态是一直不确定的。直到加密完成,然后重启。所以,这里首先将flag设置为CRYPT_INCONSISTENT_STATE。    //待加密完成之后,将CRYPT_INCONSISTENT_STATE标志移除    crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;    put_crypt_ftr_and_key(&crypt_ftr);}//判断当前加密状态,通过ro.crypto.state属性来感知//计算real block device的大小//申请一个wakelock,这里可以根据需求申请partial或者full wake locksnprintf(lockid, sizeof(lockid), "enablecrypto%d", (int) getpid());acquire_wake_lock(PARTIAL_WAKE_LOCK, lockid);/* The init files are setup to stop the class main and late start when * vold sets trigger_shutdown_framework. *///设置vold.decrypt属性的值为trigger_shutdown_framework,触发init进程shutdown class mainproperty_set("vold.decrypt", "trigger_shutdown_framework");SLOGD("Just asked init to shut down class main\n");/* Ask vold to unmount all devices that it manages *///unmount 被管理的所有设备if (vold_unmountAll()) {    SLOGE("Failed to unmount all vold managed devices");}/* Now unmount the /data partition. *///挂在data分区if (wait_and_unmount(DATA_MNT_POINT, false)) {    if (allow_reboot) {        goto error_shutting_down;    } else {        goto error_unencrypted;    }}//为了提升体验,这里所做的一些额外工作/* Do extra work for a better UX when doing the long inplace encryption */if (how == CRYPTO_ENABLE_INPLACE) {    ///data分区已经被卸载了,这里需要将tmpfs挂在到/data并且设置一个系统属性告诉系统,我们要开始加密啦。。    if (fs_mgr_do_tmpfs_mount(DATA_MNT_POINT)) {        goto error_shutting_down;    }else {         SLOGD("Successful: mount tmpfs for '%s'\n", DATA_MNT_POINT);         //..    }    //告诉framework,我们要开始加密啦。framework    property_set("vold.encrypt_progress", "0");//在/data上座一些必要的准备。//设置vold.decryp=trigger_post_fs_data来触发系列动作。    //动作完成之后,会将vold.post_fs_data_done设置为1,这里等待50s    /*     *property_set("vold.post_fs_data_done", "0");     *property_set("vold.decrypt", "trigger_post_fs_data");     *SLOGD("Just triggered post_fs_data\n");    */    if (prep_data_fs()) {        goto error_shutting_down;    }     //等待2s,shutting down framework并不是同步进行。//故,需要等待一段时间。否则在某些设备上,图形相关的服务有有问题。    sleep(2);}/* Start the actual work of making an encrypted filesystem *//* Initialize a crypt_mnt_ftr for the partition *///开始真正的加密前的初始化。首先初始化得到crypt_mnt_ftr.if (previously_encrypted_upto == 0) {    if (cryptfs_init_crypt_mnt_ftr(&crypt_ftr)) {        goto error_shutting_down;    }    if (!strcmp(key_loc, KEY_IN_FOOTER)) {        crypt_ftr.fs_size = nr_sec          - (CRYPT_FOOTER_OFFSET / CRYPT_SECTOR_SIZE);    } else {        crypt_ftr.fs_size = nr_sec;    }    //加密的状态是一直不确定的。直到加密完成,然后重启。所以,这里首先将flag设置为CRYPT_INCONSISTENT_STATE。    //待加密完成之后,将CRYPT_INCONSISTENT_STATE标志移除    crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;    crypt_ftr.crypt_type = crypt_type;    //选择不同的加密方式 "aes-xts"(使用软件加密) "aes-cbc-essiv:sha256"(使用硬件加密)    //设置秘钥,将秘钥存储在分区的尾部。#ifndef CONFIG_HW_DISK_ENCRYPTION    strlcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256", MAX_CRYPTO_TYPE_NAME_LEN);#else    strlcpy((char *)crypt_ftr.crypto_type_name, "aes-xts", MAX_CRYPTO_TYPE_NAME_LEN);    rc = clear_hw_device_encryption_key();    rc = set_hw_device_encryption_key(passwd,#endif    /* Make an encrypted master key */    if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {        SLOGE("Cannot create encrypted master key\n");        goto error_shutting_down;    }    //将key写入到分区结尾    put_crypt_ftr_and_key(&crypt_ftr);}if (how == CRYPTO_ENABLE_INPLACE) {    /* startup service classes main and late_start */    //启动services class main以及late_start    property_set("vold.decrypt", "trigger_restart_min_framework");    SLOGD("Just triggered restart_min_framework\n");    //framework正在重启。稍等就可以看到进度条的显示了。    //进度条显示的含义是:正在加密的进度或者加密映射的文件进度}//创建master key//将‘userdata’映射到加密节点上decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev,                      "userdata");/* If we are continuing, check checksums match *///读取的sha256值,同计算得到的sha256值进行比较rc = 0;if (previously_encrypted_upto) {    __le8 hash_first_block[SHA256_DIGEST_LENGTH];    rc = cryptfs_SHA256_fileblock(crypto_blkdev, hash_first_block);    if (!rc && memcmp(hash_first_block, crypt_ftr.hash_first_block,                      sizeof(hash_first_block)) != 0) {        SLOGE("Checksums do not match - trigger wipe");        rc = -1;    }}if (!rc) {    rc = cryptfs_enable_all_volumes(&crypt_ftr, how,                                    crypto_blkdev, real_blkdev,                                    previously_encrypted_upto);}/* Calculate checksum if we are not finished */if (!rc && how == CRYPTO_ENABLE_INPLACE        && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {    rc = cryptfs_SHA256_fileblock(crypto_blkdev,                                  crypt_ftr.hash_first_block);    if (rc) {        SLOGE("Error calculating checksum for continuing encryption");        rc = -1;    }}/* Undo the dm-crypt mapping whether we succeed or not */delete_crypto_blk_dev("userdata");if (! rc) {//条件判断ok    /* Success */    crypt_ftr.flags &= ~CRYPT_INCONSISTENT_STATE;    if (how == CRYPTO_ENABLE_INPLACE          && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {        SLOGD("Encrypted up to sector %lld - will continue after reboot",              crypt_ftr.encrypted_upto);        crypt_ftr.flags |= CRYPT_ENCRYPTION_IN_PROGRESS;    }    put_crypt_ftr_and_key(&crypt_ftr);#ifdef MTK_EMMC_SUPPORT    {        struct phone_encrypt_state ps;        ps.state = PHONE_ENCRYPTED;        if (misc_set_phone_encrypt_state(&ps, fstab) < 0) {            SLOGE("Failed to set encrypted status to 0x%x in MISC\n", ps.state);        }        else {            SLOGD("Success: Set encrypted status to 0x%x in MISC\n", ps.state);        }    }#endif    //判断ro.crypted.state的值是否为encrypted(代表加密成功)    if (how == CRYPTO_ENABLE_WIPE          || crypt_ftr.encrypted_upto == crypt_ftr.fs_size) {      char value[PROPERTY_VALUE_MAX];      property_get("ro.crypto.state", value, "");      if (!strcmp(value, "")) {        /* default encryption - continue first boot sequence */        property_set("ro.crypto.state", "encrypted");        release_wake_lock(lockid);        cryptfs_check_passwd(DEFAULT_PASSWORD);        property_set("vold.encrypt_progress", "");        cryptfs_restart_internal(1);        return 0;      } else {        sleep(2); /* Give the UI a chance to show 100% progress */        cryptfs_reboot(reboot);      }    } else {        sleep(2); /* Partially encrypted, ensure writes flushed to ssd */        cryptfs_reboot(shutdown);    }} else {//初始条件判断失败。    char value[PROPERTY_VALUE_MAX];    property_get("ro.vold.wipe_on_crypt_fail", value, "0");    if (!strcmp(value, "1")) {        /* wipe data if encryption failed */        SLOGE("encryption failed - rebooting into recovery to wipe data\n");        mkdir("/cache/recovery", 0700);        int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);        if (fd >= 0) {            write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);            write(fd, "--reason=cryptfs_enable_internal\n", strlen("--reason=cryptfs_enable_internal\n") + 1);            close(fd);        } else {            SLOGE("could not open /cache/recovery/command\n");        }        cryptfs_reboot(recovery);    } else {        /* set property to trigger dialog */        property_set("vold.encrypt_progress", "error_partially_encrypted");        release_wake_lock(lockid);    }    return -1;}

参考

Vold properties


Property Description
vold.decrypt=trigger_encryption Encrypt the drive with no password.
vold.decrypt=trigger_default_encryption Check the drive to see if it is encrypted with no password. If it is,decrypt and mount it, else setvold.decrypt to trigger_restart_min_framework.
vold.decrypt=trigger_reset_main Set by vold to shutdown the UI asking for the disk password.
vold.decrypt=trigger_post_fs_data Set by vold to prep /data with necessary directories, et al.
vold.decrypt=trigger_restart_framework Set by vold to start the real framework and all services.
vold.decrypt=trigger_shutdown_framework Set by vold to shutdown the full framework to start encryption.
vold.decrypt=trigger_restart_min_framework Set by vold to start the progress bar UI for encryption or prompt for password, depending on the value of ro.crypto.state.
vold.encrypt_progress When the framework starts up, if this property is set, enter the progress bar UI mode.
vold.encrypt_progress 0 to 100 The progress bar UI should display the percentage value set.
vold.encrypt_progress=error_partially_encrypted The progress bar UI should display a message that the encryption failed, and give the user an option to factory reset the device.
vold.encrypt_progress=error_reboot_failed The progress bar UI should display a message saying encryption completed, and give the user a button to reboot the device. This error is not expected to happen.
vold.encrypt_progress=error_not_encrypted The progress bar UI should display a message saying an error occurred, no data was encrypted or lost, and give the user a button to reboot the system.
vold.encrypt_progress=error_shutting_down The progress bar UI is not running, so it is unclear who will respond to this error. And it should never happen anyway.
vold.post_fs_data_done 0 Set by vold just before setting vold.decrypt to trigger_post_fs_data.
vold.post_fs_data_done 1 Set by init.rc or init.rc just after finishing the task post-fs-data.

init properties

Property Description
ro.crypto.fs_crypto_blkdev Set by the vold command checkpw for later use by the vold command restart.
ro.crypto.state unencrypted Set by init to say this system is running with an unencrypted /data ro.crypto.state encrypted. Set by init to say this system is running with an encrypted /data.
ro.crypto.fs_type
ro.crypto.fs_real_blkdev
ro.crypto.fs_mnt_point
ro.crypto.fs_options
ro.crypto.fs_flags These five properties are set by init when it tries to mount /data with parameters passed in from init.rc. vold uses these to setup the crypto mapping.
ro.crypto.tmpfs_options Set by init.rc with the options init should use when mounting the tmpfs /data filesystem

init actions

on post-fs-data
on nonencrypted
on property:vold.decrypt=trigger_reset_main
on property:vold.decrypt=trigger_post_fs_data
on property:vold.decrypt=trigger_restart_min_framework
on property:vold.decrypt=trigger_restart_framework
on property:vold.decrypt=trigger_shutdown_framework
on property:vold.decrypt=trigger_encryption
on property:vold.decrypt=trigger_default_encryption

更多相关文章

  1. Java/android下哈希sha1和MD5的实现
  2. webview开发-适配多分辨率的Android设备
  3. Android(安卓)实现蓝牙客户端与服务器端通信
  4. android内核字符驱动设备实战之----------设备驱动程序篇
  5. Google 发布 Android(安卓)@ Home,让你用 Android(安卓)设备控制
  6. android事件处理
  7. Android(安卓)手机bluetooth 名字的设置
  8. js判断Android和Ios
  9. Android加密算法:AES、Base64加密算法

随机推荐

  1. 性能怪兽的诞生日记:Kindle Fire HDX 8.9
  2. 自定义Drawable实现灵动的红鲤鱼动画(上篇
  3. Android(安卓)ConstraintLayout百分比布
  4. Android中实现「类方法指令抽取方式」加
  5. 从HTML5统治世界的说法来看Native APP 和
  6. android带返回按钮的自定义标题栏布局文
  7. 【幻灯片分享】如何创建更加灵活的App |
  8. 【玖哥乱弹】Android初学路上会遇到的瓶
  9. android uses-feature导致设备在Android(
  10. android中常见的内存泄漏和解决办法