FDE是android设备全盘加密的简称;主要用于对Android设备userdata分区数据的加密,以实现数据保护的目的
//package/apps/Settings
CryptKeeper.java
CryptKeeperSettings.java
CryptKeeperConfirm.java
调试加密过程显示的UI
//正常显示进度条
adb shell pm enable com.android.settings/.CryptKeeper
adb 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)
主要代码解释
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 lock
snprintf(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 main
property_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;
}
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. |
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 |
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