Khorina · 2022年07月11日

Android Gatekeeper流程深度解剖

1、gatekeeper是什么?

在android中,gatekeeper是密码锁或图案锁的一种服务. 主要支持的两个方法是:enroll(密码的录入)、verify(密码的验证).

调用流程:locksetting APP ----> IGatekeeperserivce ----> Hardware Gatekeeper HAL ----> Vendor Gatekeeper HAL ----> Gatekeeper TA

enroll录入密码时,locksetting APP将密码数据传送到TEE的gatekeeper TA, 在TA中先计算signature,计算方法为:HMAC(密码数据,hmackey)=signature,
然后再去填充password_handle结构体,最后再将password_handle返回给android,locksetting
APP中再将password_handle保存到文件中.
verify验证密码时,locksetting
APP将保存在文件中的password_handle和输入的密码数据一同传进TEE的gatekeeper
TA,在TA中先计算signature,计算方法为:HMAC(密码数据,hmackey)=signature.
然后再拿这个signature和password_handle中的signature相比较,如果一样,则返回authToken给android.
在android的IGatekeeperserivce中,将authToken发送给keystore存储内存中.
同时返回给locksetting APP结果failed或ok

2、gatekeeper的软件框图

image.png
(代码结构图)
image.png

3、enroll和verify的调用流程

image.png

4、重要的结构体

(1)、password_handle

在enroll的时候gatekeeper TA负责填充password_handle结构体,返回给android的locksetting保存到文件中.

(system/gatekeeper/include/gatekeeper/password_handle.h)
struct __attribute__ ((__packed__)) password_handle_t {
       uint8_t version;
       secure_id_t user_id;
       uint64_t flags;
      salt_t salt;
      uint8_t signature[32];
       bool hardware_backed;
};

gatekeeper TA是怎样填充password_handle结构体的?

enrolled_password_handle->version = handle_version
enrolled_password_handle->salt = salt
enrolled_password_handle->user_id = user_id
enrolled_password_handle->flags = flags
enrolled_password_handle->hardware_backed = gkbase->IsHardwareBacked()
enrolled_password_handle->signature
  • handle_version:在tee中写死的2
  • salt:每次enroll时,在tee中GetRandom随机生成
  • user_id:第一次enroll时,在tee中GetRandom随机生成.其实就是SID
  • flags : throttle flag,写死1. 就是是否开启,失败密码次数计数功能.
  • hardware_backed:为1
  • signature: 对密码进行hmac hash运算得到. 即 HMAC(data,key) = signature

(2)、authToken

在verify通过时候gatekeeper TA填充authToken,返回给android的IGatekeeperService程序,再发送给keystore保存到内存中. (authToken的详细介绍可以参考这篇文章)

(hardware/libhardware/include/hardware/hw_auth_token.h)
typedef struct __attribute__((__packed__)) {
    uint8_t version;  // Current version is 0
    uint64_t challenge;
    uint64_t user_id;             // secure user ID, not Android user ID
    uint64_t authenticator_id;    // secure authenticator ID
    uint32_t authenticator_type;  // hw_authenticator_type_t, in network order
    uint64_t timestamp;           // in network order
    uint8_t hmac[32];
} hw_auth_token_t;

typedef enum {
    HW_AUTH_NONE = 0,
    HW_AUTH_PASSWORD = 1 << 0,
    HW_AUTH_FINGERPRINT = 1 << 1,
    // Additional entries should be powers of 2.
    HW_AUTH_ANY = UINT32_MAX,
} hw_authenticator_type_t;
  • 质询 : challenge
  • 用户SID :user_id
  • 身份验证程序 ID (ASID) : authenticator_id, 身份验证程序类型 : authenticator_type,00-gatekeeper,01-指纹

5、技术的细节

(1)、failure_record :记录失败信息

verify失败后,会将failure_counter和当前的timestamp同时记录下来,secure_user_id用于索引.

struct __attribute__((packed)) failure_record_t {
    uint64_t secure_user_id;
    uint64_t last_checked_timestamp;
    uint32_t failure_counter;
};

(2)、throttle : failed_counter和retry_time的规则

在verify的失败的时候,需要将失败的次数记录下来,通常的做法是将这个failed_count保存到RPMB中。
在verify成功的时候,再去清除这个数据.
另外,在verify失败的时候,还会根据failed_count值来计算retry_timeout值,retry_timeout最终返回给android侧,对应的也就是输错一次密码后,还需再等待多数秒才能进行下一次的输入. retry_timeout的计算规则是:
它的计算方式:
(a)、failure_counter为0-4次时,retry_timeout = 0
(b)、failure_counter为5次时,retry_timeout = 30s
©、failure_counter为6-9次时,retry_timeout = 0
(d)、failure_counter为10-29次时,retry_timeout = 30
(e)、failure_counter大于等于30次时,retry_timeout变得更大了,有个指数增长的过程

(根据failed_counter计算timeout的源码)
uint32_t GateKeeper::ComputeRetryTimeout(const failure_record_t *record) {
    static const int failure_timeout_ms = 30000;
    if (record->failure_counter == 0) return 0;

    if (record->failure_counter > 0 && record->failure_counter <= 10) {
        if (record->failure_counter % 5 == 0) {
            return failure_timeout_ms;
        }  else {
            return 0;
        }
    } else if (record->failure_counter < 30) {
        return failure_timeout_ms;
    } else if (record->failure_counter < 140) {
        return failure_timeout_ms << ((record->failure_counter - 30) / 10);
    }

    return DAY_IN_MS;
}

由于Gatekeeper TA闭源,我们这里就贴下google的软实现(写得不是很好哦)
这种做法意味着,每次verify成功,都会对failed\_count操作两次,如果failed\_count是保存在RPMB或某个固定分区中,那么频繁的verify显然容易对这块分区或RPMB造成损坏.
在项目设计中,我们还是建议尽量减少RPMB的读写次数
image.png

(3)、timestamp

timestamp是从开机到现在的时间,单位为毫秒. 在TA中是uint64_t timestamp = GetMillisecondsSinceBoot()获取的.

timestamp的功能有两个:

a、在gatekeeper TA中的简单合法校验

在verify中,比对两个signature之前,会先检查RPMB中存储的的timestamp、根据RPMB中的failure_counter计算而来的retry_time. 然后进行简单的逻辑判断

if (timeout > 0) {
    // we have a pending timeout
    if (timestamp < last_checked + timeout && timestamp > last_checked) {
        // attempt before timeout expired, return remaining time
        response->SetRetryTimeout(timeout - (timestamp - last_checked));
        return true;
    } else if (timestamp <= last_checked) {
        // device was rebooted or timer reset, don't count as new failure but
        // reset timeout
        record->last_checked_timestamp = timestamp;
        if (!WriteFailureRecord(uid, record, secure)) {
            response->error = ERROR_UNKNOWN;
            return true;
        }
        response->SetRetryTimeout(timeout);
        return true;
    }
}

b、 在android中,会对timestamp进行检查

verify成功后,会将此时的timestamp填充到authToken结构体,返回给android。 android在使用该authToken时,会对timestamp进行检查.

6、关键函数的介绍

LockSetting

(1)、writeCredentialHash //将enroll_handle保存到文件

如果是password,保存到passwordFilename, patterFilename写入空
如果是patter,保存到patterFilename, passwordFilename写入空

frameworks/base/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
public void writeCredentialHash(CredentialHash hash, int userId) {
    byte[] patternHash = null;
    byte[] passwordHash = null;

    if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
        passwordHash = hash.hash;
    } else if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
        patternHash = hash.hash;
    }
    writeFile(getLockPasswordFilename(userId), passwordHash);
    writeFile(getLockPatternFilename(userId), patternHash);
}

passwordFilename和patterFilename的文件名字分别是:“gatekeeper.password.key”、“gatekeeper.pattern.key”

frameworks/base/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
private static final String SYSTEM_DIRECTORY = "/system/";
private static final String LOCK_PATTERN_FILE = "gatekeeper.pattern.key";
private static final String BASE_ZERO_LOCK_PATTERN_FILE = "gatekeeper.gesture.key";
private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key";
private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key";
private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";

private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";

Vendor Gatekeeper Hal

(1)、enroll

(函数原型)
int (*enroll)(const struct gatekeeper_device *dev, uint32_t uid,
        const uint8_t *current_password_handle, uint32_t current_password_handle_length,
        const uint8_t *current_password, uint32_t current_password_length,
        const uint8_t *desired_password, uint32_t desired_password_length,
        uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
  • current_password_handle : 输入参数,原来的包含signature的handle结构体,第一次录入密码时为空, 修改密码时使用
  • current_password : 输入参数,原来的密码数据,第一次录入密码时为空, 修改密码时使用
  • desired_password : 输入参数,录入密码数据(修改后的密码数据)
  • enrolled_password_handle : 输出参数,

返回包含signature的handle结构体,交由android保存到文件中.
(2)、verify

(函数原型)
int (*verify)(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
         const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
         const uint8_t *provided_password, uint32_t provided_password_length,
         uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
  • enrolled_password_handle : 输入参数,
    android中传来的从文件中读取的包含signature的handle结构体
  • provided_password : 输入参数, 需要验证的密码数据
  • auth_token : 输出参数 , 在gatekeeper TA
    verify成功后会填充authToken结构体返回给android,如果verify失败,则authToken为NULL
作者:代码改变世界ctw
文章来源:CSDN

推荐阅读

更多物联网安全,PSA等技术干货请关注平台安全架构(PSA)专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入PSA技术交流群,请备注研究方向。
推荐阅读
关注数
4569
内容数
186
Arm发布的PSA旨在为物联网安全提供一套全面的安全指导方针,使从芯片制造商到设备开发商等价值链中的每位成员都能成功实现安全运行。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息