Edit online

硬件授权认证

4 Dec 2024
Read time: 9 minute(s)

硬件授权认证是一种基于身份认证原理以及硬件安全密钥实现的安全功能,可以让软件或者第三方合作伙伴对芯片的合法性进行认证。

根据使用的硬件密钥,需要烧录对应的 eFuse,可设置的 eFuse 信息如下所示:
1. 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-PRIV

  • 软件拥有对应的 RSA 公钥:RSA-PUB

  • 软件指定一笔数据:Nonce

  • 芯片通过私钥:RSA-PRIV 对 Nonce 进行加密,并返回加密结果给软件

  • 软件通过公钥:RSA-PUB 对 加密的 Nonce 进行解密,解密结果和 Nonce 匹配则认证成功


identification

RSA 私钥存储
RSA 私钥:RSA-PRIV 较大,通常不直接保存在 芯片的 eFuse 中,而是通过额外的 PSK(Protection Secure Key)进行加密后保存。 eFuse 中仅保存 PSK ,而 RSA 私钥则通过 PSK 加密后直接发布。具体步骤为:
  • 通过 AES/DES 加密的方式,将 RSA 私钥加密。

  • 使用时,通过 PSK 将 RSA 私钥解密到安全 SRAM,软件不可读写

软件授权认证

芯片身份认证可在软件授权认证中应用,特别是在需要确保软件仅能运行在特定芯片或硬件平台上时。通过芯片身份认证,软件厂商可以确保其软件和算法只在合法、授权的硬件上运行,从而保护知识产权并防止未经授权的使用。

在实际应用中,设备可能会集成了不同厂商的软件和算法。软件厂商会有相关知识产权保护、软件授权上的需求,确保能够限定自身的软件只能运行在指定芯片型号上。

通过 PSK (Partner Secret Key) 机制,可以实现芯片身份认证在软件授权认证中的应用,具体步骤如下:

  1. 设备厂商将一个 eFuse PSK 区域分配给合作伙伴。

  2. 软件厂商将自己的密钥烧录到 PSK 区域,并且设置为软件不可读写。

  3. 软件厂商生成 RSA 密钥对,并且使用 PSK 将 RSA 私钥 (RSA-PRIV) 加密,生成加密的 RSA 私钥 (RSA-PRIV-e)。

  4. 将加密后的 RSA 私钥 (RSA-PRIV-e) 以及对应的 RSA 公钥集成到软件中。

  5. 需要进行授权检查时,软件设置 CE 使用 PSK,将加密的 RSA 私钥解密到安全 SRAM。

  6. 认证软件使用安全 SRAM 中的 RSA 私钥对一段随机数 (Nonce) 进行加密,生成加密数据 (EncNonce) 返回给认证软件。

  7. 认证软件使用对应的 RSA 公钥 (RSA-PUB) 对 EncNonce 进行解密,还原出原始的 Nonce 数据。比较解密后的 Nonce 与原始 Nonce 是否一致,以验证软件的合法性。

    如果结果正确,说明该芯片是合法授权的芯片。


sw_certification

注:
  • D13x 共有四组 PSK 开放给终端厂商使用。

  • D211共有五组保护密钥,一组是 PNK,出厂烧录。另外四组是 PSK,由终端厂商自行烧录。

烧写保护密钥

用户可以根据实际情况烧录对应的密钥,以 PSK0 为例。
  1. 在开发板平台命令行执行下列命令,烧录 PSK0 到 eFuse 中。
    efuse writestr 0x70 PASSWORD
  2. 禁止 PSK0 读写。
    efuse writehex 0x00 00000030
    efuse writehex 0x08 00000030
    
注:
  • PSK 存储用于解密 RSA 私钥的密码。

  • PSK 烧录到 eFuse 后就不可以被看到,因此必须妥善保管。

  • PSK 只能烧录一次,不可更改。

生成 RSA 密钥

RSA 算法需要有密钥对(私钥和公钥),详细的密钥生成流程如下:

  1. 在主机端执行以下命令生成 RSA 私钥和公钥:
    openssl genrsa -out rsa_private_key.pem 2048

    结果:生成一对公钥和私钥,保存在 rsa_private_key.pem 文件中。

  2. 执行下列命令从密钥对中提取公钥:
    openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

    在实际使用时,通常私钥保密存储,公钥需要发送给其他相关方,因此需要提取公钥。

  3. 执行以下命令将生成的公钥和私钥转换为 DER 二进制。
    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
    

    DER 是 ASN.1 密钥结构描述的二进制编码实现。

  4. 使用 PSK0 加密私钥。
    ./tools/scripts/encrypt_rsa_key.py -h -d psk0.bin -r rsa_private_key.der
    

    通过上述命令,得到加密过的私钥文件 rsa_private_key_encrypted.der

  5. 使用 xxd -i rsa_private_key_encrypted.derxxd -i rsa_public_key.der 命令,将加密私钥和公钥转成 C 语言数组格式,方便在代码中直接使用。

    xxd 是 Linux 的一个 16 进制处理命令。

完成上述所有操作后,编译镜像并直接使用 AiBurn 工具进行烧录,重启后在开发板平台执行 aic_hw_authorization_test 即可进行测试,当显示 App xxx running. 则表示授权认证成功,否则授权认证失败。

源码说明

相关模块 源码路径
Hardware authorization
packages/artinchip/aic-authorization/

接口设计

2. aic_rsa_priv_enc
函数原型 int aic_rsa_priv_enc(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts)
功能说明 使用私钥进行加密。
参数定义
int flen
输入数据长度
from
输入需要被加密的数据
to
输出加密后的数据
opts
一些其它参数
返回值
成功返回加密后数据长度,失败返回-1
注意事项 -
3. aic_rsa_pub_dec
函数原型 int aic_rsa_pub_dec(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts)
功能说明 使用公钥进行解密。
参数定义
int flen
输入数据长度
from
输入需要被解密的数据
to
输出解密后的数据
opts
一些其它参数
返回值
成功返回解密后数据长度,失败返回-1
注意事项 -
4. aic_rsa_pub_enc
函数原型 int aic_rsa_pub_enc(int flen, unsigned char *from, unsigned char *to, struct ak_options *opts)
功能说明 使用公钥进行加密。
参数定义
int flen
输入数据长度
from
输入需要被加密的数据
to
输出加密后的数据
opts
一些其它参数
返回值
成功返回加密后数据长度,失败返回-1
注意事项 -
5. 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
注意事项 -
6. 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
注意事项 -
7. 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
密钥更改

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;
}
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;
}