Android FDE 加密过程

    xiaoxiao2021-04-01  67

    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],/*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; }

    参考

    Vold properties


    PropertyDescriptionvold.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

    PropertyDescriptionro.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

    转载请注明原文地址: https://ju.6miu.com/read-665651.html

    最新回复(0)