设备认证

在使用微信小程序提供的部分硬件能力时,需要提前将设备在微信进行注册,以便于微信验证设备的真实可信。

例如:微信小程序音视频通话(for 硬件)

1. 设备要求

微信需要硬件能力来对设备身份进行校验。设备厂商需要保证设备满足一定条件。

1.1 安卓设备

设备需要满足下列条件之一:

  • 设备 EMMC/UFS 存储上的 RPMB(Replay Protected Memory Block) 分区未被使用;
  • 设备支持 TEE,并能按照《设备认证 TEE 规范》开发 TA 并提交验收。

此外,设备厂商需要内置一个 RPMB 分区读写及通信的 RPMBD 服务(由微信提供,参考第 3 节),并保证服务能够开机正常启动。

1.2 Linux 设备

设备需要满足下列条件:

  • 设备 EMMC/UFS 存储上的 RPMB(Replay Protected Memory Block) 分区未被使用;

2. 安全策略

对于同一个 modelId,每一台物理设备应分配唯一且不变的 SN。 如果检测到包括但不限于下列情况,可能会导致设备能力被封禁:

  • 多台设备共用同一个 SN;
  • 同一台设备交替使用多个不同的 SN;
  • 使用虚假设备进行设备注册;
  • 其他伪造或滥用设备的行为。

3. 设备认证(安卓)

3.1 部署 RPMBD 服务

设备认证需要使用 EMMC/UFS 存储上的 RPMB 分区来保证设备的身份,需要设备厂商内置一个 RPMB 分区读写及通信的服务,并保证服务能够开机正常启动。

3.1.1 下载服务

请在 此处 下载对应平台、版本的 rpmbd 二进制文件。

注意:ARM 64 位版本(TEE)需要设备商按照规范开发 TEE 对应的 TA 模块,详细规范与流程参考设备认证 TEE 规范。

3.1.2 运行服务

将下载的 rpmbd 二进制(以下假设文件名为 rpmbd,下载后可以重命名)集成至系统里并以服务的方式运行起来。

注意

  • RPMBD 服务不仅用于注册设备过程,后续使用相关硬件能力时,都需要保证 RPMBD 服务一直运行
  • 每一颗 EMMC/UFS 存储芯片的 RPMB KEY 只能被写一次,不能修改。 如果被写入错误的值 (非注册时的 model_id 和 sn),那么这颗芯片就无法继续使用。
  • 高版本的 android 安全性较强,可能还需要配置 SELinux,且只支持在 system 分区启动。可参考 SELinux 参考配置

运行方式为:

rpmbd /dev/mmcblk1rpmb # /dev/mmcblk1rpmb 为rpmb分区路径, 开发者需要根据自己设备的情况具体填写(高通平台不需要指定)

参考如下 rc 的启动方式:

  • /system/etc/init,放到 system 分区启动(建议)

    service rpmbd /system/bin/rpmbd /dev/mmcblk1rpmb
      class main
      user root
      group root system
    
  • /vendor/etc/init,放到 vendor 分区启动(仅 Android < 8 支持)

    service rpmbd /vendor/bin/rpmbd
      class main
      user root
      group root system
    

3.2 注册设备

在完成 RPMBD 服务部署后,需要使用 WMPF 认证设备。

4. 设备认证(Linux)

  • 使用「微信小程序音视频通话 SDK(直连 Linux 设备)」的设备,请使用wx_device_register注册设备。

5. 常见问题

(1) 注册设备报错 emmc write fail00

检查 rpmbd 服务启动参数里的 rpmb 分区路径是否正确。 若路径正确,确认此路径对应的 rpmb 分区在 Android OS 下能否被访问。


(2) 报错 cert fail

应用缓存被清理,或 Android 认为 APK 有变动导致 keystone 中数字证书失效导致。

需要清理 apk 数据缓存再使用相同的 appid、model_id、SN 调用 registerDevice/registerVoipDevice 刷新密钥。


(3) 接口报错 ticket 1 invalid rpmb_buffer

当前 rpmbd 与 SDK aar 的版本不兼容,应保持二者使用相同版本。例如:rpmbd 服务使用了 1.3 以下版本,而 SDK 使用了 1.3 或以上的版本。


(4) 注册设备返回 -7,或调用接口报错 failed to get native service 或其他获取 rpmbd 服务失败的错误

  • 确认已部署 rpmbd 服务,且服务正常运行。(可以通过 ps 查看)
  • Android >= 8 版本,请确认 rpmbd 是在 system 分区启动
  • 如果启用了 SELinux,需确认 SELinux 的相关规则已正确配置


(5) 注册设备报错 register: null

高版本 android 不允许在主线程里进行网络请求,需要单独开线程里来调用 SDK 接口


(6) 使用物联网卡时,网络请求一直失败

物联网卡请使用 WMPF 注册设备,或设备认证 SDK >= 1.3.1 版本,并确保 servicewechat.com 域名能够正常访问。


(7) 注册设备报错 9800004,device xxx is not confirmed

绝大多数情况是因为注册设备时使用了 1.3 以下版本的 设备认证 SDK,且同时发起了多次 registerVoipDevice 请求,此时有概率设备端使用的密钥与后台不同步,导致设备再也无法成功注册,且该过程不可逆。

建议开发者使用 WMPF 注册设备,或升级到设备认证 SDK 1.3 及以上版本,使用低版本时请务必保证前一次 registerVoipDevice 返回前不要重复调用。


(8) 注册设备报错 9800004,device xxx not registered

绝大多数情况是当前设备之前使用不同的 modelId/sn 进行了注册。如使用 WMPF 注册设备,可以使用 getMiniProgramDeviceInfo 检查下当前设备内的 sn 和 modelId,和传入的是否一致。


(9) 获取票据 getCallerTicket/getDeviceToken 报错 9800004

一般是因为传入的 mode_id 与最初注册设备时不一致。


(10) 报错 ticket 0 digital-sig check fail

多数是因为当前设备已经在这台设备的另一个 App 中注册过,目前设备验证只能用于单个应用。需要再使用相同的 appid、model_id、SN 重新调用 registerDevice/registerVoipDevice 刷新密钥。

例如,同时混用「使用 WMPF 认证设备」和「设备认证 SDK」,可能会导致 WMPF 和 开发者应用互相抢占密钥,导致这个错误。


(11) 注册设备报错 40234 hmac check fail

可能有以下原因

  • 设备已经使用其它的 model_id/sn 注册过,此次注册传入了不同的 model_id;
  • 设备曾经注册过,且注册设备时使用了 1.3 以下版本的设备认证 SDK,且同时发起了多次 registerVoipDevice 请求,此时有概率设备端使用的密钥与后台不同步,导致设备再也无法成功注册,且该过程不可逆。


(12) 获取 deviceToken 时报错 register info invalid

当从「设备认证 SDK」切换到「使用 WMPF 认证设备」后,需要调用 WMPF registerMiniProgramDevice 重新进行设备注册,若未调用或调用未成功,则在需要获取 deviceToken 的场景会报这个错误。


使用 WMPF 认证设备(安卓)

在系统集成 rpmbd 后,如果设备上安装了 微信小程序硬件框架(WMPF),可以直接使用 WMPF 认证设备。

相比使用设备认证 SDK,使用 WMPF 注册设备有以下优势:

  • 接入成本低:不需要额外引入设备认证 SDK,不占用包大小,接入成本更低。
  • 免维护设备凭证:deviceToken 的获取由框架按需进行,开发者只需要进行设备注册,不需要维护 deviceToken,也不需要手动传递给微信小程序,维护成本更低。

注意

  • 注1:使用 WMPF 时,需先保证 rmpbd 服务正常运行。
  • 注2:使用设备认证 SDK 注册的设备,需要重新使用 WMPF 注册,才能免维护设备凭证。

具体使用可以参考示例代码

1. 版本要求

  • WMPF:本能力需安卓 WMPF >= 1.2.0 版本支持(如果是 2023/08/19 之前下载的 wmpf-cli,需要重新下载更新下)。
  • VOIP 通话插件:需插件 >= 2.3.0 支持。

2. 注册设备

使用 registerMiniProgramDevice 进行设备注册。使用 getMiniProgramDeviceInfo 进行注册信息查询。

3. 设备凭证预拉取

当使用 WMPF 注册设备后,框架会按需自行获取设备凭证,无需开发者介入。为了优化设备凭证的获取耗时,开发者可以在可能用到设备凭证前,调用prefetchDeviceToken接口提前进行预拉取,在有效期内(目前 1 小时)框架可以直接从缓存获得。

例如,在发起音视频通话时,框架会获取 deviceToken。建议开发者在用户发起通话的前置页面(例如:联系人页面等)进行设备凭证预拉取。

4. 框架获取设备凭证的场景

目前框架会在下列时机获取设备凭证,建议提前进行预拉取:

  • 微信小程序音视频通话,使用 VOIP 通话插件,调用 initByCaller 发起通话时。

5. 从设备认证 SDK 切换到使用 WMPF 注册设备

如果之前使用设备认证 SDK,想要切换成 WMPF 方式注册,可以注意以下事项:

  1. 同一台设备不要混用设备认证 SDK 和 WMPF 注册设备
  2. APP 中可以删除 voipsdk-1.x-release.aar 和 safeguard-release.aar。
  3. 对于之前注册过的设备,需要重新调用一次 registerMiniProgramDevice 接口以刷新设备密钥。
  4. 开发者不再需要获取和传入 deviceToken/callerTicket,使用插件时不能传 voipToken 参数。
  5. 如果之前开发者做了提前获取 deviceToken/callerTicket 的逻辑,可以替换为提前调用 prefetchDeviceToken

设备认证 SDK(安卓)

注意:License 计费不再支持使用设备认证 SDK 认证的设备,请尽快切换到使用 WMPF 认证设备,无需维护 deviceToken,接入更便捷。

在系统集成 rpmbd 后,开发者需要在 Launcher 应用中接入设备认证 SDK,SDK 主要提供以下能力:

  • 注册设备 registerDevice:将 model_id 和 SN 与设备绑定。一旦成功后 model_id 和 SN 不可修改
  • 获取设备凭证 getDeviceToken:进行设备认证并从微信后台获取凭证,设备端发起通话时传给 VOIP 通话插件的 initByCaller 接口的 voipToken 参数。

1. 下载 SDK

请在 此处 下载 SDK 的 aar 文件。

建议使用 1.3 及以上版本(物联网卡应使用 1.3.1 及以上版本)。低版本不支持并发调用 registerVoipDevice,请务必注意在前一次调用返回前不要重复调用。

  • v1.5.0 及以上版本需集成 voipsdk-x.x-release.aarsafeguard-release.aar 两个 aar 文件
  • v1.3.1 及以下版本只需集成 voipsdk-x.x-release.aar

注意:使用设备认证 SDK 前,需先保证 rmpbd 服务正常运行。

2. 接口文档(v1.5.0及以上版本)

2.1 注册设备 registerDevice

将 model_id 和 SN 与设备绑定,一旦成功后 appid、model_id、SN 均不能更换。

int registerDevice(String appid, String model_id, String sn, String sn_ticket) throws Exception

注意事项(调用前必读

  • 注册设备成功后,会将 SN 固化至 EMMC/RPMB 分区中,标定此设备的唯一身份。SN 和 model_id 一经写入后续即不可更改
  • 此处使用的 SN,必须经过 WMPF 的 addDevice 接口作为 deviceId 注册,并与 WMPF 设备激活 使用的 deviceId 一致。 否则后续无法正常发起通话。
  • 注册成功后会在本 APK 的存储里存放数字证书,如果 APK 有变动(Android 系统认为应用变更了),则证书失效(会报错cert fail),可以清理 APK 的数据再以相同的 appid、model_id、SN调用接口重新申请即可。
  • 注册过程会有网络请求,时长根据网络情况会有所不同。高版本 android 不允许在主线程里进行网络请求,可以加处理或在线程里来调用 sdk。

参数说明

参数 类型 说明
appid String 微信小程序的 appid
model_id String 设备接入时从「微信小程序管理后台」申请获得的 model_id
sn String 设备序列号。厂商自己生成,长度不能超过 128 字节。字符只接受数字,大小写字母,下划线(*)和连字符(-)。
此处使用的 sn 必须与 WMPF 激活设备使用的 deviceId 一致
sn_ticket String 通过获取设备票据接口获得

返回值

其他异常说明请参考设备验证常见问题

名称 描述
OK 0 成功
ERR_ARGS -1 参数错误
ERR_IO -2 通用 IO 错误
ERR_KEY_IO -3 KEY 不匹配
ERR_RESPONSE -4 网络请求无回复
ERR_PEM -5 权限错误
ERR_INVALID_KEY -6 KEY 不可用
ERR_SERVICE -7 rpmbd 服务没运行
ERR_EMMC_UFS_CONFUSED -8 EMMC/UFS 不匹配。里面已经存在 SN
ERR_EMMC_UFS_IO -9 EMMC/UFS IO 错误
ERR_REG_NOPEM -10 密钥不存在

2.2 获取设备凭证 getDeviceToken

进行设备认证,并从微信后台获取设备凭证。设备发起通话时需要将这一凭证传给 VOIP 通话插件的 initByCaller 接口的 voipToken 参数。

String getDeviceToken(String appid, String model_id) throws Exception

如果设备是使用 v1.5 及以上版本 SDK 进行注册设备的,可以使用无参数的版本。

String getDeviceToken() throws Exception

参数说明

参数 类型 说明
appid String 微信小程序的 appid
model_id String 设备接入时从「微信小程序管理后台」申请获得的 model_id

注意事项

  • 接口耗时与网络有关,正常会在 1 秒左右。高版本 android 不允许在主线程里做,可以加处理或在线程里来调用 sdk。
  • ticket 有一个小时的有效期,一个小时内可被多次通话复用。建议开发者在用户发起通话前,提前调用 getCallerTicket 并缓存,避免在发起通话时再进行获取,以缩短发起通话时的用户等待时长。

2.3 获取设备 SN getDeviceSn(仅调试用)

获取 registerDevice 接口写入的 SN。

String getDeviceSn()

2.4 获取设备 modelId getDeviceModelid(仅调试用)

获取使用 registerDevice 接口写入的 modelId。仅在设备是使用 v1.5 及以上版本 SDK 进行注册设备时有效。

String getDeviceModelid()

3. 接口文档(v1.3.1 及以下版本)

3.1 初始化 init

SDK 初始化,其它接口在调用之前需要保证 init 成功。

boolean init()

3.2 注册设备 registerVoipDevice

参考 2.1 registerDevice

  • 1.3 以下版本 SDK,此接口严禁并发执行,务必在逻辑中保证一次 registerVoipDevice 返回后才能再次调用。

3.3 获取拨打方票据 getCallerTicket

参考 2.2 getDeviceToken

3.4 获取设备 SN GetDeviceSn(仅调试用)

同 2.3 getDeviceSn

设备认证 TEE 规范

1. 背景

设备身份是微信 VOIP 业务能够正常运行的基础,开发者在接入 VOIP 业务时,会通过如下两个流程来进行身份的确定:

  1. 设备注册。通过 SN + modelId 两个维度来标定这台设备。
  2. 拿票据。在进行 VOIP 通话前,需要拿到这台设备所对应的票据。

对于没有 TEE 的机器,我们要求设备系统里能够操作 EMMC 存储的 RPMB 区域,并且需要将 RPMB 的归属权给到 VOIP 业务,VOIP 在设备注册时会将与设备对应的唯一密钥写入存储的 KEY 区域,用来做身份校验。

而对于有 TEE 的机器,我们信赖 TEE 的结果,但需要 TEE 里按照规范完成 TA 的开发。

2. TA 开发

不管是 optee,还是 trusty 或 qsee 等,TEE 的使用流程通常如下:

打开 TEE 环境 > 开启一个会话 > 发送命令 > 获取信息 > 结束会话 > 关闭 TEE 环境

这里我们需要定义如下标准:

  1. 会话名称。
  2. 命令的功能。
  3. 命令的交互数据定义。
  4. TA 里的运算逻辑

设备开发者需要根据规范进行如下开发:

  1. TA 开发,需要开发者或 tee 提供商按照规范开发 TA。
  2. HAL 开发,HAL 用于与 TA 的交互,微信的系统服务基于此 HAL。
  3. 服务集成,将微信发布的 rpmbd_tee 以系统服务方式运行起来。

TA 在逻辑上将存储分为两个区域。需要注意的是,这些数据最终应都存在于 EMMC 或 UFS 的 rpmb 分区,或其它 REE 访问不到的安全器件区域。

  1. 密钥区:32个字节。TA 代码逻辑里需要将此区域实现成只能写一次,类似于硬件上的 OTP (One Time Programmable) 区域。
  2. 数据区:单位为 Block,每个 Block 有 256 个字节,最小需要 32 个,开发者可根据实际情况来确定大小,若越界则返回相应错误码即可。Block 的地址从 0 开始。

2.1 会话名称

TA 的名称统一为 ta_devauth

2.2 命令定义

定义三个命令,分别是读数据、写数据、写密钥。

#define TA_DEVAUTH_CMD_READ   0x10
#define TA_DEVAUTH_CMD_WRITE  0x11
#define TA_DEVAUTH_CMD_PROKEY 0x12

详细说明:

命令 TA_DEVAUTH_CMD_READ 0x10
功能 读 1 个 Block 的数据
参数 输入:Block 地址
输入/输出:Block 数据 BUFFER,284字节,见 2.3 数据定义。
输出:签名 BUFFER,=32字节
返回 0:成功读取,并返回 284 字节数据和 32 字节签名
-1:参数错误。
-2:地址越界。
-3:密钥区还没被写。
-5:其它错误。
特别说明 若密钥区还没被写(例如裸数据全是0x00,或没有TEE文件系统里的文件?),这种情况认为是一台全新的未激活设备,需要读取错误返回 -3。
命令 TA_DEVAUTH_CMD_WRITE 0x11
功能 写 1 个 Block 的数据
参数 输入:Block 地址
输入:Block 数据 BUFFER,=284字节,见 2.3 数据定义。
输入:对应的签名 BUFFER,=32字节
返回 0:写入成功。
-1:参数错误。
-2:地址越界。
-3:密钥区还没被写。
-4:签名错误。
-5:其它错误。
特别说明 若密钥区还没被写(例如裸数据全是0x00,或没有TEE文件系统里的文件?),这种情况认为是一台全新的未激活设备,需要返回 -3。
此功能在写数据前,需要计算数据对应的签名,并且与参数传入的签名比对,若比对不成功返回 -4
命令 TA_DEVAUTH_CMD_PROKEY 0x12
功能 写密钥
参数 密钥 BUFFER,=32字节
返回 0:写入成功。
-1:参数错误。
-3:密钥区已有数据。
-5:其它错误。
特别说明 若密钥区已有数据(例如裸数据不为0x00,或有文件?),则需要返回 -3。

2.3 数据定义

与 TA 交互时传入传出 Buffer 的大小为 284 字节,它的定义如下:

struct ta_data {
    uint8_t  data[256];    // 此 256 字节是 TA 写入到1个Block的内容
    uint8_t  nonce[16];    // 一般为随机字节,传入与传出的一定要一致。
    uint32_t reserve1;
    uint16_t reserve2;
    uint16_t reserve3;
    uint16_t reserve4;
    uint16_t reserve5;
};

// sizeof(struct ta_data) = 284

签名 Buffer 大小为 32 字节


CA 与 TA 交互消息定义:

struct ta_message {
    uint32_t        cmd;        // 命令号
    uint32_t        block;      // 读写地址
    struct ta_data  data;       // 284 字节数据
    uint8_t         key[32];    // 32 字节 Key
    uint8_t         hmac[32];   // 32 字节 hmac
    int             ret;        // 返回值
};

CA 侧参考伪代码:

static int tee_send_cmd_req(struct ta_message* ta_msg) {
    int rc = 0;

    if (ca_handle == 0) {
        printf("not connected\n");
        return -EINVAL;
    }

    if (tee_send_msg(ca_handle, ta_msg) < 0) {
        return -1;
    }

    if (tee_resp_msg(ca_handle, ta_msg) < 0) {
        return -1;
    }

    return 0;
}

2.4 运算逻辑

数据签名的算法为 HMAC_SHA256。

读数据时,TA 的流程:

  1. 若密钥区没数据,返回 -3。
  2. 读地址参数对应 Block 的 256 字节数据。
  3. 将读到的 256 字节与输入参数里的 16 字节 noce 以及其它 reserve,一共有 284 个字节。
  4. 利用密钥区的 32 字节密钥,对 284 字节进行签名,并返回 284 字节以及签名。

写数据时,TA 的流程:

  1. 若密钥区没数据,返回 -3。
  2. 得到输入参数里的 284 字节,得到输入参数中的签名1。
  3. 利用密钥区的 32 字节密钥,对 284 字节进行签名,得到签名2。
  4. 比较签名1与签名2,如果相等则将 284 字节中的 256 字节写入对应的 Block,如果不等则返回错误 -4。

写密钥时,TA 的流程:

  1. 若密钥区已有数据,返回 -3。
  2. 将 32 字节的密钥写入密钥区。

一个签名的数据示例,开发者可以此为基准来验证自己的 hmac_sha256:

char *key = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH";
uint8_t buffer[284] = {0};

int main(int argc, char **argv) {
    uint8_t hmac[32] = {0};
    memset(buffer, 0x55, 284);

    hmac_sha256(key, 32,
      buffer, 284,
      hmac, sizeof(hmac)
    );

    for (int i=0; i < 32; i++) {
        printf("%02x ", hmac[i]);
    }
    return 0;
}
// 以上代码输出:
// 61 16 67 22 a0 93 66 74 bb 75 f8 87 0e 5e d4 59 2c d6 99 c0 14 a6 93 70 bd ff ea 3e 8e 84 52 4e 

hmac_sha256 为标准算法,一般 tee 里已有此类算法,如果没有,可参考开源实现

2.5 注意事项

  1. TEE 里的密钥区和数据区需保证具有 “永久存储” 特性,并且不应该被 REE 以任何方式直接访问,不会随用户的刷机、升级或其它常规行为而丢失。
  2. 密钥区域需要实现为 OTP 特性,即仅一次写入。
  3. 密钥区域无数据时,需要按规范返回错误码。
  4. 厂商需要将 CA 测试例程和代码给到微信,微信进行验收测试。

3. HAL 开发

按照 HAL 规范完成 HAL 的开发,HAL 里使用 CA 代码与 TA 进行交互,完成 TEE 的使用。

HAL 路径:android/hardware/interface/devauth

3.1 HAL 规范

types.hal

package android.hardware.devauth@1.0;

enum TA_CMD : uint32_t {
    TA_DEVAUTH_CMD_READ = 0x10,
    TA_DEVAUTH_CMD_WRITE = 0x11,
    TA_DEVAUTH_CMD_PROKEY = 0x12,
};

struct ta_data {
    uint8_t[256] data; // 此 256 字节是 TA 写入到1个Block的内容
    uint8_t[16]  nonce; // 一般为随机字节,传入与传出的一定要一致。
    uint32_t reserve1;
    uint16_t reserve2;
    uint16_t reserve3;
    uint16_t reserve4;
    uint16_t reserve5;
};

struct ta_message {
    uint32_t        cmd;        // 命令号
    uint32_t        block;      // 读写地址
    ta_data         data;       // 284 字节数据
    uint8_t[32]     key;        // 32 字节 Key
    HMacBuffer      hmac;       // 32 字节 hmac
    int8_t          ret;        // 返回值
};

typedef uint8_t[32] HMacBuffer;
typedef uint8_t[32] ProKeyBuffer;

typedef ta_data ta_data_t;
typedef ta_message ta_message_t;

IDevauth.hal

package android.hardware.devauth@1.0;

interface IDevauth {

    /**
     * 读 Block 数据
     *
     * @param addr:     Block 地址
     * @param data:     输入的 struct ta_data
     * @return retval:  返回值,返回值说明请见规范
     * @return data:    返回的 struct ta_data,284 字节。
     * @return hmac:    返回的签名, 32 字节。
     *
     */
    read_block(uint16_t addr, ta_data data) generates (int8_t retval, vec<uint8_t> data, vec<uint8_t> hmac); 

    /**
     * 写 Block 数据
     *
     * @param addr:     Block 地址
     * @param data:     输入输出数据,对应规范里的 struct ta_data,284 字节。
     * @param hmac:     HAL 写数据时用的签名, 32 字节
     *
     * @return retval:  返回值说明请见规范
     */
    write_block(uint16_t addr, ta_data data, HMacBuffer hmac) generates (int8_t retval); 

    /**
     * 写 Key
     *
     * @param key:      32 字节key
     *
     * @return retval:  返回值说明请见规范
     */
    program_key(ProKeyBuffer key) generates(int8_t retval);
};

3.2 开发参考

hardware/interfaces/devauth/1.0/ 下放置 IDevauth.haltypes.hal,内容如上。

然后使用如下方式来生成代码:

LOC=hardware/interfaces/devauth/1.0/default
PACKAGE=android.hardware.devauth@1.0
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport ${PACKAGE}
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport ${PACKAGE}
./hardware/interfaces/update-makefiles.sh

此时 hal 目录应该如下:

root~/android> tree hardware/interfaces/devauth/
hardware/interfaces/devauth/
└── 1.0
    ├── Android.bp
    ├── default
    │   ├── Android.bp
    │   ├── Devauth.cpp
    │   └── Devauth.h
    ├── IDevauth.hal
    └── types.hal

2 directories, 6 files

再按照 HAL 接口定义,在相应的接口函数里完成 CA 代码的开发即可。

4. 验收

4.1 提交资料

  • ✓ 芯片平台,存储类型。例:MTK81xx、EMMC 64GB
  • ✓ REE 操作系统详细信息。例:Android 7 64位
  • ✓ TEE 系统详细信息。例:基于 optee 的自研 tee,提供商为 xxx
  • ✓ TEE 里数据存储位置。例:EMMC 里的 RPMB 分区
  • ✓ TEE 侧的 TA 代码。例:ta_devauth 模块代码
  • ✓ REE 侧的 CA 代码。例:测试用例代码包及相应 TEE 功能 so。
  • ✓ REE 侧的 HAL 代码。例:hardware/interface/devauth 下的代码。
  • ✓ 能 adb root 的样机。

4.2 测试用例

开发者完成 TEE 的 TA 开发后,应该进行测试用例开发,以验证 TA 的功能与逻辑。 前置条件:ta_deauth 所管理的区域无任何数据,再按顺序进行以下测试项。

  1. 读数据测试,预期返回 -3
  2. 写数据测试,预期返回 -3
  3. 写密钥测试,预期返回 0
  4. 写密钥测试,预期返回 -3
  5. 读数据测试,预期返回 0,且数据全是 0x00 且有签名。
  6. 用正确的 HMAC 写数据测试,预期返回 0
  7. 用错误的 HMAC 写数据测试,预期返回 -4
  8. 读数据测试,预期返回 0,并且返回正确的数据和签名。

可在官方测试用例代码的基础上,加上自己的 CA 实现,以快速验证 TA。

开发者完成 HAL 开发后,可用测试用例进行测试,

也可以直接下载已编译好的 tee_hal_test 进行测试,测试方法如下

  1. tee_hal_test a 进行一次全新的测试,需要一个 ta_devauth 所管理的区域无任何数据。
  2. tee_hal_test 不带任何参数,可在 1. 后运行。

4.3 集成

4.2 中的测试用例通过后,下载 rpmbd_tee 并集成到系统中以服务方式运行起来即可,参考:

service rpmbd_tee /system/bin/rpmbd_tee
  class main
  user root
  group root system