硬件授权认证
硬件授权认证是基于身份认证原理以及硬件安全密钥实现的安全功能,可以让软件或者第三方合作伙伴对芯片的合法性进行认证
eFuse
根据使用的硬件密钥,需要烧录对应的 eFuse,可设置的 eFuse 信息如下所示:
用途 | 位数 | 地址 | 禁止位 | 禁写 | 禁读 | 归属 | 备注 |
---|---|---|---|---|---|---|---|
DIS RD | 64 | 0~7 | 0~1 | V | CSTM | eFuse读禁止配置区域 | |
DIS WR | 64 | 8~F | 2~3 | eFuse写禁止配置区域 | |||
PSK0 | 64 | 70~77 | 28~29 | V | V | CSTM | 安全,连接到CE,合作伙伴密钥 |
PSK1 | 64 | 78~7F | 30~31 | V | V | CSTM | 安全,连接到CE,合作伙伴密钥 |
PSK2 | 64 | 80~87 | 32~33 | V | V | CSTM | 安全,连接到CE,合作伙伴密钥 |
PSK3 | 64 | 88~8F | 34~35 | V | V | CSTM | 安全,连接到CE,合作伙伴密钥 |
PNK | 64 | B8~BF | 46~47 | V | V | AIC | 安全,连接到CE,型号唯一密钥 |
身份认证原理
RSA 认证流程
如下图所示,采用 RSA 加密认证的方式
- 芯片拥有一个 RSA 私钥:RSA-PRIV
- 软件拥有对应的 RSA 公钥:RSA-PUB
- 软件指定一笔数据:Nonce
- 芯片通过私钥:RSA-PRIV 对 Nonce 进行加密,并返回加密结果给软件
- 软件通过公钥:RSA-PUB 对 加密的 Nonce 进行解密,解密结果和 Nonce 匹配则认证成功
私钥存储
RSA 私钥:RSA-PRIV 一般比较大,芯片的 eFuse 一般无法保存,因此不在 eFuse 直接保存 RSA 私钥,而是保存一个额外的 PSK 而把 RSA 私钥通过 PSK 加密后直接发布了
具体步骤:
- 通过 AES/DES 加密的方式,将 RSA 私钥加密
- 使用时,通过 PSK 将 RSA 私钥解密到安全 SRAM,软件不可读写
软件授权原理
即授权软件只能在指定的硬件的运行,软件厂商可以通过 PSK(Partner Secret Key) 机制实现安全的授权认证。
具体步骤:
- 软件厂商将自己的密钥烧录到 PSK 区域,并且设置为软件不可读写
- 软件厂商生成一对 RSA 密钥,并且使用 PSK 将 RSA 私钥(RSA-PRIV)加密,生成加密的 RSA 私钥(RSA-PRIV-e)
- 将加密后的 RSA 私钥(RSA-PRIV-e),以及对应的 RSA 公钥(RSA-PUB) 集成到软件当中
- 需要进行授权检查时,软件设置 CE 使用 PSK,将加密的 RSA 私钥(RSA-PRIV-e) 解密到安全 SRAM,生成RSA 私钥(RSA-PRIV)
- 使用安全 SRAM 中的 RSA 私钥(RSA-PRIV)加密一笔数据,返回给认证软件
- 认证软件使用 RSA 公钥(RSA-PUB)解密返回的数据,如果结果正确,说明该芯片是合法授权的芯片
- D13x 共有四组是 PSK 开放给终端厂商使用。
软件授权原理
PSK 管理
用户可以根据实际情况烧录对应的密钥,这里以 PSK0 为例。
- PSK 存储的是客户的用于解密 RSA 私钥 的密码
- PSK 烧录到 eFuse 后就不可以被看到,因此必须妥善保管
- PSK 只能烧录一次,不可更改
烧录 PSK0
在开发板平台命令行执行下列命令,烧录 PSK0 到 eFuse 中。
efuse writestr 0x70 PASSWORD
禁止 PSK0 读写
efuse writehex 0x00 00000030
efuse writehex 0x08 00000030
2
RSA 密钥管理
RSA 算法需要有密钥对(私钥和公钥)
生成密钥
可以在电脑上通过如下命令生成:
openssl genrsa -out rsa_private_key.pem 2048
DER 二进制
DER 是 ASN.1 密钥结构描述的二进制编码实现。
openssl base64 -d -in rsa_public_key.pem -out rsa_public_key.der
openssl base64 -d -in rsa_private_key.pem -out rsa_private_key.der
2
加密私钥
使用 PSK0 加密私钥
./tools/scripts/encrypt_rsa_key.py -d psk0.bin -r rsa_private_key.der
得到加密过的私钥文件 rsa_private_key_encrypted.der。
C 语言支持
使用 xxd -i rsa_private_key_encrypted.der
和 xxd -i rsa_public_key.der
将加密私钥和公钥转成 C 语言数组格式后可以在代码中直接使用
xxd 是 linux 的一个16进制处理命令
接口设计
源码说明
相关模块 | 源码路径 |
---|---|
Hardware authorization | packages/artinchip/aic-authorization |
aic_rsa_priv_enc
函数原型 | int aic_rsa_pub_enc(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts) |
---|---|
功能说明 | 使用私钥进行加密。 |
参数定义 | - int flen 输入数据长度 - from 输入需要被加密的数据 - to 输出加密后的数据 - opts 一些其它参数 |
返回值 | 成功返回加密后数据长度,失败返回-1 |
注意事项 |
aic_rsa_pub_dec
函数原型 | int aic_rsa_pub_enc(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts) |
---|---|
功能说明 | 使用公钥进行加密。 |
参数定义 | - int flen 输入数据长度 - from 输入需要被加密的数据 - to 输出加密后的数据 - opts 一些其它参数 |
返回值 | 成功返回加密后数据长度,失败返回-1 |
注意事项 |
aic_rsa_pub_enc
函数原型 | int aic_rsa_pub_dec(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts) |
---|---|
功能说明 | 使用公钥进行解密。 |
参数定义 | - int flen 输入数据长度 - from 输入需要被加密的数据 - to 输出加密后的数据 - opts 一些其它参数 |
返回值 | 成功返回加密后数据长度,失败返回-1 |
注意事项 |
aic_rsa_priv_dec
函数原型 | int aic_rsa_priv_dec(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts) |
---|---|
功能说明 | 使用经过 保护密钥加密过的私钥 进行加密。 |
参数定义 | - int flen 输入数据长度 - from 输入需要被解密的数据 - to 输出解密后的数据 - opts 一些其它参数 |
返回值 | 成功返回解密后数据长度,失败返回-1 |
注意事项 |
aic_hwp_rsa_priv_enc
函数原型 | int aic_hwp_rsa_priv_enc(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts, char *algo) |
---|---|
功能说明 | 使用经过保护密钥加密过的私钥 进行加密。 |
参数定义 | - flen: 输入数据长度 - from: 输入需要被加密的数据 - to: 输出加密后的数据 - opts: 一些其它参数 - algo: 指定选用烧录在 eFuse 中的保护密钥,支持以下选项: - PNK_PROTECTED_RSA - PSK0_PROTECTED_RSA - PSK1_PROTECTED_RSA - PSK2_PROTECTED_RSA - PSK3_PROTECTED_RSA |
返回值 | 成功返回加密后数据长度,失败返回 -1 |
注意事项 | 需要确保提供的私钥已经通过保护密钥进行加密。 |
aic_hwp_rsa_priv_dec
函数原型 | int aic_hwp_rsa_priv_dec(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts, char *algo) |
---|---|
功能说明 | 使用经过保护密钥加密过的私钥 进行解密。 |
参数定义 | - flen: 输入数据长度 - from: 输入需要被解密的数据 - to: 输出解密后的数据 - opts: 一些其它参数 - algo: 指定选用烧录在 eFuse 中的保护密钥,支持以下选项: - PNK_PROTECTED_RSA - PSK0_PROTECTED_RSA - PSK1_PROTECTED_RSA - PSK2_PROTECTED_RSA - PSK3_PROTECTED_RSA |
返回值 | 成功返回解密后数据长度,失败返回 -1 |
注意事项 |
示例
参数配置
在 Luban-Lite 根目录下执行 scons --menuconfig
,进入 menuconfig 的功能配置界面,按如下选择:
Board options --->
[*] Using Crypto Engine
Local packages options --->
ArtInChip packages options --->
[*] aic-authorization --->
[*] aic authorization test
2
3
4
5
6
密钥更改
用 xxd -i rsa_private_key_encrypted.der
转换私钥 为 C 语言数组格式,并替换 rsa_private_key2048_encrypted_der 用 xxd -i rsa_public_key.der
转换公钥 为 C 语言数组格式 ,并替换 rsa_public_key2048_der 替换文件路径:packages/artinchip/aic-authorization/test/test_aic_hw_authorization.h
授权测试
- 编译镜像并烧录镜像
- 重启后在开发板平台执行
aic_hw_authorization_test
- 显示
App xxx running.
则表示授权认证成功,否则授权认证失败。
示例代码
授权的检查可以在 APP/中间件 启动时进行,或者在运行时随机进行。
测试用例:
packages/artinchip/aic-authorization/test/
部分代码如下:
int app_hw_authorization_check(unsigned char *from, int flen,
unsigned char *esk, int esk_len,
unsigned char *pk, int pk_len, char *algo)
{
struct ak_options opts = { 0 };
uint8_t *inbuf = NULL, *outbuf = NULL;
uint8_t esk_buf[esk_len];
uint8_t pk_buf[pk_len];
size_t pagesize = 2048;
int ret = 0, rlen;
inbuf = aicos_malloc_align(0, pagesize * 2, CACHE_LINE_SIZE);
if (inbuf == NULL) {
printf("Failed to allocate inbuf.\n");
ret = -ENOMEM;
goto out;
}
outbuf = aicos_malloc_align(0, pagesize * 2, CACHE_LINE_SIZE);
if (outbuf == NULL) {
printf("Failed to allocate outbuf.\n");
ret = -ENOMEM;
goto out;
}
// 1. Set RSA key parameters
memcpy(esk_buf, esk, esk_len);
memcpy(pk_buf, pk, pk_len);
opts.esk_buf = esk_buf;
opts.esk_len = esk_len;
opts.pk_buf = pk_buf;
opts.pk_len = pk_len;
// 2. Nonce private key encryption
rlen = aic_hwp_rsa_priv_enc(flen, from, outbuf, &opts, algo);
if (rlen < 0) {
printf("aic_hwp_rsa_priv_enc failed.\n");
goto out;
}
memcpy(inbuf, outbuf, rlen);
memset(outbuf, 0, 2 * pagesize);
// 3. EncNonce public key decryption
rlen = aic_rsa_pub_dec(rlen, inbuf, outbuf, &opts);
if (rlen < 0) {
printf("aic_rsa_pub_dec failed.\n");
goto out;
}
// 4. compare Nonce and DecNonce
if (memcmp(from, outbuf, rlen)) {
hexdump_msg("Expect", (unsigned char *)from, rlen, 1);
hexdump_msg("Got Result", (unsigned char *)outbuf, rlen, 1);
printf("App %s stop.\n", algo);
ret = -1;
} else {
printf("App %s running.\n", algo);
ret = 0;
}
out:
if (inbuf)
aicos_free_align(0, inbuf);
if (outbuf)
aicos_free_align(0, outbuf);
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int aic_hw_authorization_test(int argc, char **argv)
{
int ret = 0;
int esk_len, pk_len;
unsigned char *esk, *pk, nonce[16] = { 0 }, nlen = 16;
char *algo;
esk = rsa_private_key2048_encrypted_der;
esk_len = rsa_private_key2048_encrypted_der_len;
pk = rsa_public_key2048_der;
pk_len = rsa_public_key2048_der_len;
while (1) {
ret = aic_rng_get_bytes(nonce, 16);
if (ret != nlen)
pr_err("aic rng get bytes failed.\n");
algo = PSK0_PROTECTED_RSA;
ret = app_hw_authorization_check(nonce, nlen, esk, esk_len, pk, pk_len, algo);
if (ret < 0) {
printf("Application %s not authorization.\n", algo);
}
aic_mdelay(2 * 1000);
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27