Android——Keymaster安全检测

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),还增加了针对由硬件支持的密钥的访问控制系统。

  1. 一个使用控制方案,用于限制密钥的使用,并降低因滥用密钥而损害安全性的风险
  2. 一个访问控制方案,用于限定只有指定的用户和客户端能够使用相应密钥,并且只能在规定的时间范围内使用

   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双清吧,数据这时候没手机重要了。

开发者

  1. 在系统升级时,检测系统重要信息,如安全补丁日期,系统版本号等,如果信息有修改,就需要提醒用户双清升级。
  2. 修改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等认证检测,就是反制产商们对系统乱定制影响系统安全性,所以谨慎点好。

参考文献

谷歌Source

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值