Keymaster
概述
工作以来,一直在负责Android系统安全漏洞的跟踪以及修复。最近在处理Android O以上机器的时候遇到了一个坑。当你的升级系统时,如果后一个系统中修改了如:系统版本、系统安全补丁日期等重要信息,系统在未双清升级后,就会处于锁定状态,如下图所示。
遇到这个问题,解决方式只有一个,就是进入recovery双清数据,即可解决。
对于用户来说,他们只需了解该问题如何解决,而对于我们需要了解的是,为什么在Android O以后的机器会出现这种锁检测,搜索了下度娘以及Google,有很多人都遇到这个问题,也有不少人给出了解释,但是好像都没讲到重点,抽空,上谷歌官方看了下相关的文档,总结下这个问题。
Keymaster检测
Android系统如今已经成为全球首屈一指的移动操作系统,拥有庞大的用户量。正式因为如此,谷歌为用户搭建了一个安全的系统架构,使得用户使用Android系统时能够保证其安全性。具体做了哪些安全措施,这里不在赘述,有兴趣的读者可以参阅
谷歌官方安全文档。
Keymaster的发展
谷歌在Android中,做了很多**认证的行为,具体可看**认证。
Android在6.0之前版本已有一个非常简单的由硬件支持的加密服务API(由0.2和0.3版的Keymaster硬件抽象层(HAL)提供)。
Android6.0中,Keystore不仅增加了对称加密基元(AES和HMAC),还增加了针对由硬件支持的**的访问控制系统。
- 一个使用控制方案,用于限制**的使用,并降低因滥用**而损害安全性的风险
- 一个访问控制方案,用于限定只有指定的用户和客户端能够使用相应**,并且只能在规定的时间范围内使用
Android7.0中,Keymaster2增加了对**认证和版本绑定的支持。
Android8.0中,Keymaster3从旧式C结构硬件抽象层(HAL)转换到了从采用新的硬件接口定义语言(HIDL)的定义生成的C++HAL接口。
而在最新的Android 9.0 中,更新包括:
- 更新到 Keymaster 4
- 对嵌入式安全元件的支持
- 对安全**导入的支持
- 对 3DES 加密的支持
- 更改了版本绑定,以便 boot.img 和 system.img 分别设置版本以允许独立更新
Keymaster架构
Android Keystore API和底层Keymaster HAL提供了一套基本的但足以满足需求的加密基元,以便使用访问受控且由硬件支持的**实现相关协议。
Keymaster HAL是由原始设备制造商(OEM)提供的动态加载库,Keystore服务使用它来提供由硬件支持的加密服务。为了确保安全性,HAL实现不会在用户空间(甚至是内核空间)中执行任何敏感操作。敏感操作会被分配给通过某个内核接口连接的安全处理器。 最终的架构如下所示:
检测原理
简单的说,每个系统版本都有对应的Keymaster**,绑定到系统的版本和补丁程序级别,一旦我们更新了内容,它所对应的**也会更新,这样可确保在旧版系统或TEE软件中发现漏洞的攻击者无法将设备回滚到含有漏洞的版本,也无法使用在较新版本中创建的**。此外,在已经升级到更新的版本或补丁程序级别的设备上使用指定版本和补丁程序级别的**时,需要先升级该**才能使用,因为该**的旧版本已失效。如果强制使用,系统在检测时候就会因为该**的旧版本失效弹出那个密码框。
想深入研究原理的话,可以在O平台上查看以下几个代码文件的逻辑。
- system/keymaster/android_keymaster/android_keymaster.cpp
- hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
- frameworks/base/core/java/android/security/keymaster/OperationResult.java
Keymaster源码相关函数
谷歌官方给出了相关函数的地址,这里不在赘述,请查阅Keymaster
加密解决方法
普通用户
也没什么办法了,进入recovery双清吧,数据这时候没手机重要了。
开发者
- 在系统升级时,检测系统重要信息,如安全补丁日期,系统版本号等,如果信息有修改,就需要提醒用户双清升级。
- 修改keymaster逻辑,去掉检测逻辑或者更新**。
//Android O中相关文件以及代码
//system/keymaster/android_keymaster/android_keymaster.cpp
//hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
//frameworks/base/core/java/android/security/keymaster/OperationResult.java
keymaster_error_t CheckVersionInfo(const AuthorizationSet& tee_enforced,
const AuthorizationSet& sw_enforced,
const KeymasterContext& context) {
uint32_t os_version;
uint32_t os_patchlevel;
context.GetSystemVersion(&os_version, &os_patchlevel);
uint32_t key_os_patchlevel;
if (tee_enforced.GetTagValue(TAG_OS_PATCHLEVEL, &key_os_patchlevel) ||
sw_enforced.GetTagValue(TAG_OS_PATCHLEVEL, &key_os_patchlevel)) {
if (key_os_patchlevel < os_patchlevel)
return KM_ERROR_KEY_REQUIRES_UPGRADE;
else if (key_os_patchlevel > os_patchlevel)
return KM_ERROR_INVALID_KEY_BLOB;
}
return KM_ERROR_OK;
}
Return<void> KeymasterDevice::update(uint64_t operationHandle,
const hidl_vec<KeyParameter>& inParams,
const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
// result variables for the wire
uint32_t resultConsumed = 0;
hidl_vec<KeyParameter> resultParams;
hidl_vec<uint8_t> resultBlob;
// result variables the backend understands
size_t consumed = 0;
keymaster_key_param_set_t out_params{nullptr, 0};
keymaster_blob_t out_blob{nullptr, 0};
auto kmInParams = hidlParams2KmParamSet(inParams);
auto kmInput = hidlVec2KmBlob(input);
auto rc = keymaster_device_->update(keymaster_device_, operationHandle, &kmInParams, &kmInput,
&consumed, &out_params, &out_blob);
if (rc == KM_ERROR_OK) {
resultConsumed = consumed;
resultParams = kmParamSet2Hidl(out_params);
resultBlob = kmBlob2hidlVec(out_blob);
}
_hidl_cb(legacy_enum_conversion(rc), resultConsumed, resultParams, resultBlob);
keymaster_free_param_set(&out_params);
if (out_blob.data) free(const_cast<uint8_t*>(out_blob.data));
return Void();
}
第一种方式,是从应用层方面去处理该问题的,相对于来说灵活、简单,不会影响系统问题;而第二种方式则是要违背谷歌的规定,在系统中做一些小聪明,而这是谷歌这些年来不倡导的,谷歌推出的一系列诸如CTS、GTS、STS等认证检测,就是反制产商们对系统乱定制影响系统安全性,所以谨慎点好。