1. 指纹框架UML
2. Fingerprint
2.1 Fingerprint数据流图
-
FingerprintManager API. 直接与应用程序交互的API,属于当前APP进程.
每个应用程序都可以获取FingerprintManager.
FingerprintManager主要是应用程序与FingerprintService的交互封装
FingerprintService. 一个运行在SystemServer进程中的单例service,主要负责与fingerprintd通讯.
fingerprintd (Fingerprint daemon). A C/C++ implementation of the binder interface from FingerprintService. The fingerprintd daemon operates in its own process and wraps the Fingerprint HAL vendor-specific library.
Fingerprint HAL vendor-specific library. A hardware vendor's implementation of the Fingerprint HAL. The vendor-specific library communicates with the device-specific hardware.
Keystore API and Keymaster. These components provide hardware-backed cryptography for secure key storage in a Trusted Execution Environment (TEE).
2.2 HAL层的初始化工作
2.2.1 HAL初始化
每个Hal层库文件有一个入口,即HAL_MODULE_INFO_SYM,上层在调用hal层库文件时会在/system/lib/hw/下面寻找对应库文件,找到对应库文件后便从入口HAL_MODULE_INFO_SYM调用Hal层里面的open, init, write, read等接口,Hal层再通过这个接口去读写设备节点。
static struct hw_module_methods_t fingerprint_module_methods = {
.open = fingerprint_open,
};
Fingerprintd 调用hw_get_module函数获取了一个fingerprint_module_t类型的数据结构。 这个就是在fingerprint.default.so中,由指纹芯片厂商填充实现的。
static int fingerprint_open(const hw_module_t* module, const char __unused *id,
hw_device_t** device) {
if (fpc_init() < 0) {
ALOGE("Could not init FPC device");
return -EINVAL;
}
fingerprint_device_t *dev = malloc(sizeof(fingerprint_device_t));
memset(dev, 0, sizeof(fingerprint_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = FINGERPRINT_MODULE_API_VERSION_2_0;
dev->common.module = (struct hw_module_t*) module;
dev->common.close = fingerprint_close;
dev->pre_enroll = fingerprint_pre_enroll;
dev->enroll = fingerprint_enroll;
dev->get_authenticator_id = fingerprint_get_auth_id;
dev->cancel = fingerprint_cancel;
dev->remove = fingerprint_remove;
dev->set_active_group = fingerprint_set_active_group;
dev->enumerate = fingerprint_enumerate;
dev->authenticate = fingerprint_authenticate;
dev->set_notify = set_notify_callback;
dev->notify = NULL;
*device = (hw_device_t*) dev;
return 0;
}
fingerprint_open就是填充实现Android 在fingerprint.h定义fingerprint_device_t需要实现的这些接口。然后赋给指针device。上层,也就是fingerprintd,就能用这个device来操作hal层的指纹模块了。
fpc_init() 应该是初始化指纹驱动,并与Trusted Execution Environment (TEE)建立安全链接,TEE提供的一个安全的硬件运行环境。指纹就是运行在这样一个硬件安全环境下的程序。它保证了指纹敏感数据的安全性。
- HAL必须通过TEE来与指纹建立通讯,一般是采用SPI通讯。指纹采集的生物图像必须在TEE中完成,不能传输到TEE外
- 指纹的采集、登记和认可都必须在TEE中指纹数据也必须是加密(比如采用对称加密AES等加密手段),并且不能拷贝
2.2.2 HAL主要接口
HAL主要接口函数都会在/hardware/libhardware/include/hardware/fingerprint.h 中,主要的接口主要是enroll和authenticate,以及相应的回调函数
其他的函数还有以下:
- enroll. Switches the HAL state machine to start the collection and storage of a fingerprint template. As soon as enrollment is complete, or after a timeout, the HAL state machine is returned to the idle state.
- pre_enroll. Generates a unique token to indicate the start of a fingerprint enrollment. Provides a token to the enroll function to ensure there was prior authentication, e.g. using a password. The token is wrapped and, for example, HMAC'd, once the device credential is confirmed, to prevent tampering. The token must be checked during enrollment to verify that the token is still valid.
- get_authenticator_id. Returns a token associated with the current fingerprint set.
- cancel. Cancels any pending enroll or authenticate operations. The HAL state machine is returned to the idle state.
- enumerate. Synchronous call for enumerating all known fingerprint templates.
- remove. Deletes a fingerprint template.
- set_active_group. Restricts a HAL operation to a set of fingerprints that belong to a specified group (identified by a group identifier, or GID).
- authenticate. Authenticates a fingerprint-related operation (identified by an operation ID).
- set_notify. Registers a user function that will get notifications from the HAL. If the HAL state machine is in a busy state, the function is blocked until the HAL leaves the busy state.
Enroll流程
2.3.1 preEnroll点击设置中的添加指纹,setting就会调到fingerprintd的preEnroll接口。
- preEnroll会在指纹设备中生成并保存一个64位的随机数。这个随机数有两个用途:返回给上层,用于填充enroll中的authenticated token challenge.
- 指纹设备会用它对下次enroll做初步校验,保证enroll没有被第三方篡改。
2.3.2 enroll
- 1 、对enroll接口的定义
int (*enroll)(struct fingerprint_device *dev, const hw_auth_token_t *hat,uint32_t gid, uint32_t timeout_sec);
- hw_auth_token_t:保证此次enroll的合法性,我们具体看一下android怎么定义此结构。
- AuthToken version :此token的版本号
- Challenge:就是前面调用preEnroll的到的64位随机数,防止此次enroll被第三方假冒
- User SID : 安全性id,不是android user id
- Athenticator ID: 用于标明不同的认证权限
- Authenticator Type:0x00表示Gatekeeper,0x01表示Fingerprint
- Timestamp:最近一次开机时间戳
- AuthToken HMAC key: 用一个特殊的key和SHA-256算法去计算前面一堆参数后,得到的一个 hmac值,保证前面参数的合法性和安全性。
- gid:说明是哪个用户注册指纹(anroid支持多用户),在FingerprintService中通过UserManager拿到的。
- timeout_sec:超时设置。
- 2、上层调用enroll接口,一直将上述参数传递到指纹设备中。指纹设备拿到参数之后会先检查参数是否合法。
-
3、验证完参数合法之后,指纹设备会将指纹IC切换到一种等待手指按下采图的工作模式。此时一旦手指按下,会进入中断处理函数,该函数主要的工作流程如下图:
- 4、注册成功后会将获取到相应的type,gid,fingerid, samples_ remaining回传给fingerprintd,samples_ remaining是录入指纹剩余次数。
void *enroll_thread_loop() {
fingerprint_msg_t msg;
msg.type = FINGERPRINT_TEMPLATE_ENROLLING;
msg.data.enroll.finger.fid = print_id;
msg.data.enroll.finger.gid = fpc_gid;
msg.data.enroll.samples_remaining = 0;
msg.data.enroll.msg = 0;
callback(&msg);
}
最终会在FingerprintService保存一份Fingerprint,Fingerprint包含以下数据,主要用于FingerprintService判断是否有指纹template存在。
2.3.3 postEnroll
postEnroll主要工作是更新一下指纹设备中保存的Challenge。
2.4 Authenticate流程
当按下power键锁屏时,APP会调用FingerprintManager api,进入到authenticate流程。和enroll一样,框架层工作工作很少,基本就是一个简单的接口调用。其中CryptoObject是加密对象,这是由于验证结果有可能被第三方软件篡改,这个加密对象会随着验证结果返回并验证。
void authenticate (FingerprintManager.CryptoObject crypto,CancellationSignal cancel,int flags,FingerprintManager.AuthenticationCallback callback,Handler handler)
parameter | |
---|---|
crypto | FingerprintManager.CryptoObject: object associated with the call or null if none required. |
cancel | CancellationSignal: an object that can be used to cancel authentication |
flags | int: optional flags; should be 0 |
callback | FingerprintManager.AuthenticationCallback: an object to receive authentication events |
handler | Handler: an optional handler to handle callback events |
主要工作还是在hal层以下完成。重点看hal层之下的部分。authenticate一直调用到指纹服务进程fingerprintd。在有fingerprintd根据加载的fingerprint module调用hal层的接口。我们看看HAl怎样定义authenticate接口的,在fingerprint.h中
int (*authenticate)(struct fingerprint_device *dev, uint64_t operation_id, uint32_t gid);
- operation_id:64位的session id标识本次识别流程。
- gid:用户id,android是多用户系统,每个用户可以录入多个指纹(一般是5个)TEE也会根据gid来加载不同的用户模板。
2.4.1 加载用户模板、切换采图模式
- 调用authenticate接口之后,会在TEE中保存好operation_id,这个operation_id 会被用来做authenticate token的challenge保存到keystore service中去指纹支付会用到。然后TEE会根据gid从安全存储区加载之前该用户注册好的模板。
- 通过spi发送指令到指纹IC,让其切换到采图模式,最后等待手指按压。
2.4.2 采图与识别
之后的主要工作流程如下图
- 手指按压IC后,会报一个中断给主控
- 主控驱动层接收到中断脚的脉冲信号,通过netlink或者信号量等跨进程通信方式将这个中断信息上报给hal层
- hal层收到中断,根据当前状态机的工作模式,开始执行识别工作
- 识别工作包括,从IC读取一帧指纹图像,然后将图像预处理为算法需要的格式
- 算法模块接收到预处理后的图像,开始匹配模板库
- 生成识别结果,向上用binder回调给fingerprintService,向下将本次识别的finger id同步给其他安全应用,譬如支付宝,微信。
-
更新模板库
识别成功后,会构造一个message,具体包括type,gid,fingerid和auth_token。
void *enroll_thread_loop() {
fingerprint_msg_t msg;
msg.type = FINGERPRINT_AUTHENTICATED;
msg.data.authenticated.finger.gid = fpc_gid;
msg.data.authenticated.finger.fid = print_id;
msg.data.authenticated.hat = hat;
callback(&msg);
}
其中auth_token会在fingerprintd中同步到keystore service中。
void FingerprintDaemonProxy::hal_notify_callback(const fingerprint_msg_t *msg) {
switch (msg->type) {
case FINGERPRINT_AUTHENTICATED:
if (msg->data.authenticated.finger.fid != 0) {
const uint8_t* hat = reinterpret_cast(&msg->data.authenticated.hat);
instance->notifyKeystore(hat, sizeof(msg->data.authenticated.hat));
}
callback->onAuthenticated(device,
msg->data.authenticated.finger.fid,
msg->data.authenticated.finger.gid);
break;
}
}
void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) {
if (auth_token != NULL && auth_token_length > 0) {
// TODO: cache service?
sp < IServiceManager > sm = defaultServiceManager();
sp < IBinder > binder = sm->getService(String16("android.security.keystore"));
sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder);
if (service != NULL) {
status_t ret = service->addAuthToken(auth_token, auth_token_length);
}
}
}
2.4.3 AuthToken数据流
安全框架
指纹安全框架一般有两种方案,一种是Fingerprint without TrustZone 和Fingerprint with TrustZone。在root情况下without TrustZone是非常危险的,所有的数据都可以轻松获取到。但是在有了TrustZone的情况下,hacker在获取了root以后依然无法读取TrustZone中的指纹信息。如果想要获取指纹信息,理论上还需要破解TrustZone才行。TrustZone是arm支持的一种安全运行环境空间,有自己运行操作系统。
Fingerprint可能存在的安全漏洞
1)Replacing fingerprintd
替换fngerprintd这个Daemon,因为指纹识别成功与否是在APP向fingerprintd注册的回调来获取结果的,只要能替换fngerprintd,将其中指纹识别成功与否,修改成无论什么时候验证都返回success即可2)Modied IPC
由于FingerprintService与fngerprintd是通过binder获取验证结果的,同时在FingerprintService中判断是否验证成功是靠获取到的fid(finger Id)是否为0(为0则验证失败)来判断的,修改binder传输过程中的fid即可达到无论什么时候都返回success的目的。3)Replaying authentication tokens
可以提前截取之前验证通过的auth_token,然后用此auth_token来作为身份验证的凭据。
参考
安卓指纹验证官方指南
Keystore
指纹验证APP Demo