大家好,又见面了,我是你们的朋友全栈君。
Android 的安全性问题一直备受关注,Google 在 Android 系统的安全方面也是一直没有停止过更新,努力做到更加安全的手机移动操作系统。
在 Android 的安全性方面,有很多模块:
其中,加密又分全盘加密(Android 4.4 引入)和文件级加密(Android 7.0 引入),本文将论述加密中的全盘加密的基本知识。全盘加密在 Android 4.4 中引入,在 Android 5.0 中做了比较大的更新。
本文部分片段摘自 Android 官网,融合笔者的个人理解和知识。
全盘加密是使用已加密的密钥对 Android 设备上的所有用户数据进行编码的过程。设备经过加密后,所有由用户创建的数据在写入磁盘之前都会自动加密,并且所有读取操作都会在将数据返回给调用进程之前自动解密数据。
Android 5.0 中又引入了以下新功能:
Android 全盘加密基于在块设备层运行的内核功能 dm-crypt。因此,这种加密方式适用于以块设备的形式呈现给内核的嵌入式多媒体卡 (eMMC) 和类似闪存设备。YAFFS 会直接与原始 NAND 闪存芯片交互,无法进行全盘加密。
全盘加密采用的是 128 位高级加密标准 (AES) 算法(搭配密码块链接 (CBC) 和 ESSIV:SHA256)。对主密钥进行加密时使用的是 128 位 AES 算法,并会调用 OpenSSL 库。对于该密钥,您必须使用 128 位或更多位(可以选择 256 位)。
Android 5.0 版中有以下 4 种加密状态:
首次启动时,设备会创建一个随机生成的 128 位主密钥,然后会使用默认密码和存储的盐对其进行哈希处理。默认密码是“default_password”。不过,设备还会通过 TEE(例如 TrustZone)为生成的哈希签名。TEE 会使用相应签名的哈希来加密主密钥。
您可以在 Android 开放源代码项目 cryptfs.c 文件中找到定义的默认密码。
当用户在设备上设置 PIN 码/通行码或密码时,只有 128 位的密钥会被重新加密并存储起来(也就是说,更改用户 PIN 码/通行码/解锁图案不会导致重新加密用户数据)。请注意,受管理的设备可能受 PIN 码、解锁图案或密码限制。
加密操作由 init 和 vold 管理。 init 负责调用 vold,然后 vold 会设置相关属性以触发 init 中的事件。系统的其他部分也会查看这些属性以执行各项任务,例如报告状态、提示输入密码,或有严重错误发生时提示恢复出厂设置。为了调用 vold 中的加密功能,系统会使用命令行工具 vdc 的 cryptfs 命令:checkpw、restart、enablecrypto、changepw、cryptocomplete、verifypw、setfield、getfield、mountdefaultencrypted、getpwtype、getpw 以及 clearpw。
要加密、解密或清空 /data,/data 不得处于装载状态。但要显示任何界面,框架都必须启动,而框架需要 /data 才能运行。为了解决这一冲突,/data 上会装载一个临时文件系统。通过该文件系统,Android 可以提示输入密码、显示进度或根据需要建议清除数据。不过,该文件系统会带来以下限制:要从临时文件系统切换到实际的 /data 文件系统,系统必须停止临时文件系统中打开了文件的所有进程,并在实际的 /data 文件系统中重启这些进程。为此,所有服务都必须位于以下其中一个组内:core、main 和 late_start。
为了触发这些操作,vold.decrypt 属性会被设为多种字符串。要结束和重启服务,请使用以下 init 命令:
这是 Android 5.0 设备首次启动时的常规流程。
当您启动设有密码的已加密设备时,则会发生该流程。设备的密码可以是 PIN 码、解锁图案或密码。
结合上章节的流程,下面用代码来解析启动未进行默认加密的已加密设备这个流程。
# Android fstab file.
#<src> <mnt_point> <type>
......
/dev/block/platform/soc.0/f9824900.sdhci/by-name/userdata /data ext4 noatime,nosuid,nodev,barrier=1,data=ordered,nomblk_io_submit,noauto_da_alloc,errors=panic wait,check,fileencryption
......
defaults
这个配置定义在 device/lge/bullhead/fstab_fbe.bullhead 文件中。
如上面的代码,在 /data 的末尾加上 fileencryption,便会进行全盘加密。
//设置ro.crypto.state标记,手机已被用户加密
static int do_mount_all(const std::vector<std::string>& args) {
......
if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
ActionManager::GetInstance().QueueEventTrigger("encrypt");
} else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
// 全盘加密,ro.crypto.state = encrypted, ro.crypto.type = block
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
//发送vdc 命令 defaultcrypto
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
......
} else if (ret == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -1;
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
......
return ret;
}
这个方法定义在文件 system/core/init/builtins.cpp 中。
# One shot invocation to deal with encrypted volume.
# do_mount_all 中写入命令 defaultcrypto,执行 vdc 发送命令 mountdefaultencrypted
on defaultcrypto
exec - root -- /system/bin/vdc --wait cryptfs mountdefaultencrypted
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption) or trigger_restart_min_framework (other encryption)
# One shot invocation to encrypt unencrypted volumes
on encrypt
start surfaceflinger
exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption)
这个服务定义在文件 system/vold/vdc.rc 中。
int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
......
} else if (subcommand == "mountdefaultencrypted") {
......
//执行cryptfs_mount_default_encrypted
std::thread(&cryptfs_mount_default_encrypted).detach();
}
......
}
这个方法定义在文件 vold/CryptCommandListener.cpp 中。
int cryptfs_mount_default_encrypted(void)
{
int crypt_type = cryptfs_get_password_type();
......
} else if (crypt_type != CRYPT_TYPE_DEFAULT) {
SLOGD("Password is not default - "
"starting min framework to prompt");
//不是默认加密, 设置 vold.decrypt = trigger_restart_min_framework
property_set("vold.decrypt", "trigger_restart_min_framework");
return 0;
} else if (cryptfs_check_passwd(DEFAULT_PASSWORD) == 0) {
......
这个方法定义在文件 system/vold/cryptfs.c 中。
#属性vold.decrypt==trigger_restart_min_framework 时执行
on property:vold.decrypt=trigger_restart_min_framework
# A/B update verifier that marks a successful boot.
exec - root -- /system/bin/update_verifier trigger_restart_min_framework
class_start main
这个服务定义在服务 system/core/rootdir/init.rc 中。
class_start main 可知重启 main 类别的服务。main 类别的服务包括:
会重启 zygote。
Zygote 启动后,会 fork() system_process 进程,就是运行 SystemServer 代码了。但是 system_process 的运行需要正常的用户空间(/data),所以,需要临时挂载 tmpfs 分区,这个分区是在内存里分配的临时空间。
int CommandListener::StorageCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
......
if (!strcmp(argv[1], "mountall")) {
if (argc != 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
return 0;
}
// 挂载所有设备
fs_mgr_mount_all(fstab);
cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
return 0;
}
这个方法定义在文件 system/vold/CommandListener.cpp 中。
int fs_mgr_mount_all(struct fstab *fstab)
{
......
/* mount(2) returned an error, handle the encryptable/formattable case */
bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
//挂载 tmpfs 临时分区
if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
++error_count;
continue;
}
//全盘加密
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
......
}
这个方法定义在文件 system/core/fs_mgr/fs_mgr.c 中。
private void startBootstrapServices() {
// Only run "core" apps if we're encrypting the device.
//启动min-framework 显示密码输入界面,仅启动 coreApp, 在AndroidManifest.xml中声明。
//此时启动的 APP 在 tmpfs 临时分区,所以,所有app都是原始安装状态,不包含任何用户使用产生的数据。
String cryptState = SystemProperties.get("vold.decrypt");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true;
}
......
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
......
}
这个方法定义在文件frameworks/base/services/java/com/android/server/SystemServer.java 中。
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
// 设置仅解析 core app only,
pp.setOnlyCoreApps(mOnlyCore);
.....
}
这个方法定义在文件 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 中
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0);
// 如果不是 !lite.coreApp, 跳过该 app,即启动时,不会安装该app
if (mOnlyCoreApps && !lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}
这个方法定义在文件 frameworks/base/core/java/android/content/pm/PackageParser.java 中。
安卓中定义为 coreApp 的应用有:
Framework-res.apk 的 manifest 配置文件如下:
这个过程不再阐述。
#重启 main 类别服务
on property:vold.decrypt=trigger_reset_main
class_reset main
这个 setion 定义在文件 system/core/rootdir/init.rc 中。
static int do_mount_all(const std::vector<std::string>& args) {
pid_t pid;
.....
//
if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
// 发送 encrypt 事件到 vdc
ActionManager::GetInstance().QueueEventTrigger("encrypt");
} else if
.....
}
这个方法定义在文件 system/core/init/builtins.cpp 中。
on encrypt
start surfaceflinger
exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption)
这个 setion 定义在文件 system/vold/vdc.rc 中。
on encrypt
start surfaceflinger
#发送命令 enablecrypto 到 vold
exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
# vold will set vold.decrypt to trigger_restart_framework (default
# encryption)
这个 setion 定义在文件 system/vold/vdc.rc 中。
int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
} else if (subcommand == "cryptocomplete") {
if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
dumpArgs(argc, argv, -1);
rc = cryptfs_crypto_complete();
// 命令是 enablecrypto
} else if (subcommand == "enablecrypto") {
.....
// Spawn as thread so init can issue commands back to vold without
// causing deadlock, usually as a result of prep_data_fs.
char* arg2 = argc > 2 ? strdup(argv[2]) : NULL;
char* arg4 = argc > 4 ? strdup(argv[4]) : NULL;
// 执行 do_enablecrypto 方法
std::thread(&do_enablecrypto, arg2, arg4, type, no_ui).detach();
}
这个方法定义在文件 system/vold/CryptCommandListener.cpp 中。
static int do_enablecrypto(char* arg2, char* arg4, int type, bool no_ui) {
int rc;
int tries;
for (tries = 0; tries < 2; ++tries) {
// 不是默认加密,运行方法 cryptfs_enable()
if (type == CRYPT_TYPE_DEFAULT) {
rc = cryptfs_enable_default(arg2, no_ui);
} else {
rc = cryptfs_enable(arg2, type, arg4, no_ui);
}
.....
return -1;
}
这个方法定义在文件 system/vold/CryptCommandListener.cpp 中。
int cryptfs_enable(char *howarg, int type, char *passwd, int no_ui)
{
return cryptfs_enable_internal(howarg, type, passwd, no_ui);
}
这个方法定义在文件 system/vold/cryptfs.c 中。
int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
int no_ui)
{
/* restart the framework. */
/* Create necessary paths on /data */
if (prep_data_fs()) {
goto error_shutting_down;
}
}
这个方法定义在文件 system/vold/cryptfs.c 中。
static int prep_data_fs(void)
{
property_set("vold.post_fs_data_done", "0");
// 设置 vold.decrypt = trigger_post_fs_data,触发 init.rc
property_set("vold.decrypt", "trigger_post_fs_data");
SLOGD("Just triggered post_fs_data\n");
/* Wait a max of 50 seconds, hopefully it takes much less */
for (i=0; i<DATA_PREP_TIMEOUT; i++) {
char p[PROPERTY_VALUE_MAX];
// 等待 init 设置 vold.post_fs_data_done = 1
property_get("vold.post_fs_data_done", p, "0");
if (*p == '1') {
break;
} else {
usleep(50000);
}
}
}
这个方法定义在文件 system/vold/cryptfs.c 中。
on property:vold.decrypt=trigger_post_fs_data
trigger post-fs-data
这个 setion 定义在文件 system/core/rootdir/init.rc 中。
#创建/data 子目录和链接,启动服务
on post-fs-data
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chmod 0771 /data
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
# start debuggerd to make debugging early-boot crashes easier.
start debuggerd
start debuggerd64
#task4597305 added by xiwu.peng to output logcat to uart
start logcat2uart
# Make sure we have the device encryption key.
start vold
installkey /data
# Start bootcharting as soon as possible after the data partition is
# mounted to collect more data.
mkdir /data/bootchart 0755 shell shell
bootchart_init
.....
mkdir /data/system_de 0770 system system
mkdir /data/system_ce 0770 system system
mkdir /data/misc_de 01771 system misc
mkdir /data/misc_ce 01771 system misc
mkdir /data/user 0711 system system
mkdir /data/user_de 0711 system system
symlink /data/data /data/user/0
mkdir /data/media 0770 media_rw media_rw
mkdir /data/media/obb 0770 media_rw media_rw
init_user0
# If there is no fs-post-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won't work.
# Set indication (checked by vold) that we have finished this action
#setprop vold.post_fs_data_done 1
这个 setion 定义在文件 system/core/rootdir/init.rc 中。
static int cryptfs_restart_internal(int restart_main)
{
// init 做完 post-fs-data,继续往下执行代码
if (prep_data_fs()) {
return -1;
}
//init 做完 post-fs-data, vold 将 vold.decrypt 设为 trigger_restart_framework, 触发init
/* startup service classes main and late_start */
property_set("vold.decrypt", "trigger_restart_framework");
}
这个方法定义在文件 system/vold/cryptfs.c 中。
#重启所有服务
on property:vold.decrypt=trigger_restart_framework
# A/B update verifier that marks a successful boot.
exec - root -- /system/bin/update_verifier trigger_restart_framework
class_start main
class_start late_start
vold.decrypt = trigger_restart_framework, framework 就可以正常启动了。
vold 和 init 之间通过设置属性进行通信。下面列出了可用的加密属性。
属性 说明
vold.decrypt trigger_encryption 以无密码方式加密存储卷。
vold.decrypt trigger_default_encryption 检查存储卷是否采用了无密码加密。如果是,则解密并装载存储卷;如果不是,则将 vold.decrypt 设为 trigger_restart_min_framework。
vold.decrypt trigger_reset_main 由 vold 设置,用于关闭提示输入磁盘密码的界面。
vold.decrypt trigger_post_fs_data 由 vold 设置,用于准备具有必要目录等内容的 /data。
vold.decrypt trigger_restart_framework 由 vold 设置,用于启动实际框架和所有服务。
vold.decrypt trigger_shutdown_framework 由 vold 设置,用于关闭整个框架以开始加密。
vold.decrypt trigger_restart_min_framework 由 vold 设置,用于启动加密进度条界面或提示输入密码,具体取决于 ro.crypto.state 的值。
vold.encrypt_progress 框架启动时,如果设置了此属性,则会进入进度条界面模式。
vold.encrypt_progress 0 to 100 进度条界面中应按照设置显示百分比值。
vold.encrypt_progress error_partially_encrypted 进度条界面中应显示一条消息,告诉用户加密失败,并且界面中应为用户提供一个用于将设备恢复出厂设置的按钮。
vold.encrypt_progress error_reboot_failed 进度条界面中应显示一条消息,告诉用户加密已完成,并且界面中应为用户提供一个用于重新启动设备的按钮。此错误不应发生。
vold.encrypt_progress error_not_encrypted 进度条界面中应显示一条消息,告诉用户发生错误,没有已加密的数据或数据已丢失,并且界面中应为用户提供一个用于重新启动系统的按钮。
vold.encrypt_progress error_shutting_down 进度条界面未运行,因此不清楚谁将响应此错误。在任何情况下,都不应发生此错误。
vold.post_fs_data_done 0 由 vold 在将 vold.decrypt 设为 trigger_post_fs_data 的前一刻设置。
vold.post_fs_data_done 1 由 init.rc 或 init.rc 在完成 post-fs-data 任务之后立即设置。
属性 说明
ro.crypto.fs_crypto_blkdev 由 vold 命令 checkpw 设置,供 vold 命令 restart 以后使用。
ro.crypto.state unencrypted 由 init 设置,用于说明相应系统正在未加密的 /data ro.crypto.state encrypted 中运行。由 init 设置,用于说明相应系统正在已加密的 /data 中运行。
ro.crypto.fs_type
ro.crypto.fs_real_blkdev
ro.crypto.fs_mnt_point
ro.crypto.fs_options
ro.crypto.fs_flags
这 5 个属性由 init 在尝试装载 /data(包含从 init.rc 传入的参数)时设置。vold 会使用这些属性来设置加密映射。
ro.crypto.tmpfs_options 由 init.rc 设置,包含 init 在装载 tmpfs /data 文件系统时应使用的选项。
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.
Android 全盘加密分析到此为止。
参考 https://source.android.com/security/encryption/full-disk
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/145227.html原文链接:https://javaforall.cn