Android Full Disk EncryptionSettings中加密入口调用关系加密实现参考
Vold propertiesinit propertiesinit actions
Android Full Disk Encryption
FDE是android设备全盘加密的简称;主要用于对Android设备userdata分区数据的加密,以实现数据保护的目的
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:SHA256Android5.0之后,首次开机会加密加密之后,将/data挂在到dm的虚拟节点上首次启动
fstab.qcom 标志
encryptable manually encryptfoeceencrypted encrypt in fist boot使用默认的密码获得master key
默认密码是”default_password”Keymaster(HW/SW) general key blob,存储在footer中default_password_salt(random)–>scrypt–>derived keyDerived+keypair–>sign–>intermedia key(ikey)salt,keybob,derived key stored in footerset ikey to TZHardware Crypto
TZ generates an encrypt key derived from new password,set to crypto engineeSoftware Crypto
Call APU to set key with ikey(security_hw.c)非首次开机
如果用户设置了pin/或者密码
弹出提示用于输入密码使用默认的密码获得master key
从footer中读取salt,keyblob,derived key从输入或者salt中获得derivedkey,并进行比较Derived+keypair–>sign–>intermedia key(ikey)salt,keybob,derived key stored in footerset ikey to TZ升级相关
如果使用了软件加密,可以直接软件升级如果使用了硬件加密,不可以直接软件升级init.rc
on property:vold.decrypt=trigger_default_encryption
start defaultcryptoservice defaultcrypto /system/bin/vdc –wait cryptfs mountdefaultencrypted
disableoneshot#vold will set vold.decrypt to trigger_restart_framework(default encryption) or trigger_restart_min_framework(other encryption)FDE 启动总结
init 进程读取fstab中,并依据fstab中的配置进行挂载在挂载过程中发现该分区标记了encrypted,使用property触发一个属性vdc服务传递命令给cryptd socket监听器接收到该socket,然后触发encrypt/decrypt 动作重要的函数
CryptCommandListener::CryptfsCmd::runCommandget_keymaster_hw_fde_passwordset_hw_device_encryption_keycreate_crypto_blk_devupdate_hw_device_encryption_key
Settings中加密入口
//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)
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],
false);
}
else {
rc = cryptfs_enable(argv[
2],
type, argv[4],
false);
}
}
else if (!strcmp(argv[
1],
"changepw")) {
}
int cryptfs_enable_internal(
char *howarg,
int crypt_type,
char *passwd,
int allow_reboot)
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.");
static int create_encrypted_random_key(
char *passwd,
unsigned char *master_key,
unsigned char *salt,crypt_mnt_ftr *crypt_ftr) {
fd = open(
"/dev/urandom", O_RDONLY|O_CLOEXEC);
read(fd, key_buf,
sizeof(key_buf));
read(fd, salt, SALT_LEN);
close(fd);
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
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;
crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
put_crypt_ftr_and_key(&crypt_ftr);
}
snprintf(lockid,
sizeof(lockid),
"enablecrypto%d", (
int) getpid());
acquire_wake_lock(PARTIAL_WAKE_LOCK, lockid);
property_set(
"vold.decrypt",
"trigger_shutdown_framework");
SLOGD(
"Just asked init to shut down class main\n");
if (vold_unmountAll()) {
SLOGE(
"Failed to unmount all vold managed devices");
}
if (wait_and_unmount(DATA_MNT_POINT,
false)) {
if (allow_reboot) {
goto error_shutting_down;
}
else {
goto error_unencrypted;
}
}
if (how == CRYPTO_ENABLE_INPLACE) {
if (fs_mgr_do_tmpfs_mount(DATA_MNT_POINT)) {
goto error_shutting_down;
}
else {
SLOGD(
"Successful: mount tmpfs for '%s'\n", DATA_MNT_POINT);
}
property_set(
"vold.encrypt_progress",
"0");
if (prep_data_fs()) {
goto error_shutting_down;
}
sleep(
2);
}
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;
}
crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
crypt_ftr.crypt_type = crypt_type;
#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
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;
}
put_crypt_ftr_and_key(&crypt_ftr);
}
if (how == CRYPTO_ENABLE_INPLACE) {
property_set(
"vold.decrypt",
"trigger_restart_min_framework");
SLOGD(
"Just triggered restart_min_framework\n");
}
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 =
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);
}
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;
}
}
delete_crypto_blk_dev(
"userdata");
if (! rc) {
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
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,
"")) {
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);
cryptfs_reboot(reboot);
}
}
else {
sleep(
2);
cryptfs_reboot(shutdown);
}
}
else {
char value[PROPERTY_VALUE_MAX];
property_get(
"ro.vold.wipe_on_crypt_fail",
value,
"0");
if (!strcmp(
value,
"1")) {
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 {
property_set(
"vold.encrypt_progress",
"error_partially_encrypted");
release_wake_lock(lockid);
}
return -
1;
}
参考
Vold properties
PropertyDescription
vold.decrypt=trigger_encryptionEncrypt the drive with no password.vold.decrypt=trigger_default_encryptionCheck 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_mainSet by vold to shutdown the UI asking for the disk password.vold.decrypt=trigger_post_fs_dataSet by vold to prep /data with necessary directories, et al.vold.decrypt=trigger_restart_frameworkSet by vold to start the real framework and all services.vold.decrypt=trigger_shutdown_frameworkSet by vold to shutdown the full framework to start encryption.vold.decrypt=trigger_restart_min_frameworkSet by vold to start the progress bar UI for encryption or prompt for password, depending on the value of ro.crypto.state.vold.encrypt_progressWhen the framework starts up, if this property is set, enter the progress bar UI mode.vold.encrypt_progress0 to 100 The progress bar UI should display the percentage value set.vold.encrypt_progress=error_partially_encryptedThe 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_failedThe 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_encryptedThe 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_downThe 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_done0 Set by vold just before setting vold.decrypt to trigger_post_fs_data.vold.post_fs_data_done1 Set by init.rc or init.rc just after finishing the task post-fs-data.
init properties
PropertyDescription
ro.crypto.fs_crypto_blkdevSet by the vold command checkpw for later use by the vold command restart.ro.crypto.state unencryptedSet 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_typero.crypto.fs_real_blkdevro.crypto.fs_mnt_pointro.crypto.fs_optionsro.crypto.fs_flagsThese 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_optionsSet 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