# \[ZYNQ] Encrypt external files based on file system using PUF key

如果使用Zynq® UltraScale+™设备，大部分数据是存储在设备的外面的(NVM)，如果想增强数据的保密性，则需要启动Secure Storage来保护数据。保护数据的算法使用ZYNQ内置的GCM-AES硬件引擎，那么同样，对于GCM-AES来说确保Key的可信是最重要和最根本的事情。eFUSE/OTP保存着Secure Boot和Secure Storage使用的加密的Key，而且内置的PUF（physically unclonable function）对Key的保密性进行了增强。Key的写入eFUSE/OTP和配置PUF的**过程叫做Provisioning**，也只有在Provisioning之后，Secure Storage和Secure Boot才能使用RSA和GCM-AES保密与认证功能。

**本文参考**：

* (AN) Secure Storage Application Note \[^1]
* (Provisioning) - Programming BBRAM and eFUSEs Application Note \[^2]
* (TRM) Zynq UltraScale+ Device Technical Reference Manual (UG1085) - Security \[^3]

## 1. Secure Storage High-Level Design <a href="#id-1.-secure-storage-high-level-design" id="id-1.-secure-storage-high-level-design"></a>

### 1.1 一般性的安全存储业务模式（非ZYNQ） <a href="#id-1.1-yi-ban-xing-de-an-quan-cun-chu-ye-wu-mo-shi-fei-zynq" id="id-1.1-yi-ban-xing-de-an-quan-cun-chu-ye-wu-mo-shi-fei-zynq"></a>

本节借助文献\[^4]的根文件系统保护和应用程序数据保护来阐述以下对于Secure Storage的业务需求。

* 全盘级别加密：
  * 加密： [dm-crypt](https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt)
  * 认证： [dm-verity](https://source.android.com/docs/security/features/verifiedboot/dm-verity)
  * 完整性： [dm-integrity](https://kernel.googlesource.com/pub/scm/linux/kernel/git/kasatkin/linux-digsig/+/dm-integrity/Documentation/device-mapper/dm-integrity.txt)
* 目录级别：
  * 加密：[ext4](https://wiki.archlinux.org/index.php/ext4#Using_file-based_encryption) [ubifs](https://lwn.net/Articles/707900/) [eCryptfs](https://wiki.archlinux.org/title/ECryptfs)
  * 认证：[IMA/EVM](https://sourceforge.net/p/linux-ima/wiki/Home/)

在台式机或手机上，用于加密文件系统的密钥来自交互式输入的用户密码。物联网和嵌入式设备通常没有这样丰富的操作流程。因此，需要在设备上存储和保护密钥。以下是一些保护机制：

* **使用 SoC 机制加密密钥，ARM的使用方法**
* 将密钥存储在提供安全存储的外部加密或安全芯片（例如：[ATECC508A](https://www.microchip.com/en-us/product/atecc508a)）中
* 使用外部可信平台模块 (TPM) 芯片
* 远程证明

我们只来阐述第一种使用 SoC 机制加密密钥，ARM的使用方法。很多外部安全芯片如TPM容易受到I2C[总线攻击](https://github.com/nccgroup/TPMGenie)。如果可能的话最好利用主处理器的安全存储功能。**安全存储的最核心的业务逻辑就是：如何保护好密钥**。

我们来看一下加密过程：

* 使用加密的方法保护文件系统；
* 需要加密上述加密的密钥；

在NXP [I.MX](http://I.MX)上面，每个设备都有一个不同的主密钥（通过PUF，PUF是根据每个设备的物理工艺不同，可以实现每个设备的黑密钥差异的硬件设备），该密钥被SoC内部的加解密引擎访问。因此，**就可以编写安全应用程序，例如Linux内核安全驱动程序或者HSM系统，读取该唯一密钥，在HSM程序内部对密钥进行加密**，输出一个key blob用于解密方解密。

Note，CAAM driver：<https://mcuxpresso.nxp.com/api_doc/dev/721/group__caam__driver.html>

<figure><img src="/files/8rLR5aPztYPHiFT5zID2" alt="" width="563"><figcaption></figcaption></figure>

如上图所示：

* 先对文件系统进行加密，使用的密钥就是文件加密的密钥key0；
* 再对key0进行加密（调用HSM或者Linux内核安全程序或者自己编写安全服务程序，需要保证解密方也能被调用）
* 解密过程就是先call安全系统HSM或者Linux内核安全程序或者安全应用解密密钥，拿到密钥之后解密文件系统。

这个就是一般的文件系统的加密模型。

### 1.2 ZYNQ的Secure Storage设计 <a href="#id-1.2zynq-de-securestorage-she-ji" id="id-1.2zynq-de-securestorage-she-ji"></a>

在上述的加密模型中，需要引入HSM或者Linux内核安全服务程序用来对文件系统加密的密钥进行加解密。在ZYNQ中可以使用PUF硬件来满足加密的需求。PUF硬件根据物理工艺不同，可以对写入eFUSE的密钥进行加扰和解扰（也算是一种加密手段），因此使实际存储在eFUSE上的每个设备的black key都是不一样的。

我们可以对一批设备假定1万台，管理员生成一个红密钥作为文件系统加密的密钥，管理员需要为每台设备Provisioning这个红密钥到eFUSE上，并启动PUF功能。这一万台设备的实际存储的黑密钥都是不一样的。这就能够抵御侧信道攻击，即便是有人用功率分析，分析出eFUSE的密钥，拿到的也是黑密钥，没有任何的实际用处。

PUF可以理解为一个函数，这个函数输入红密钥、IV、ID等信息，输出一个AES-GCM加密的key blob结果，包含了tag、密文等。当我们要使用密钥解密文件系统的时候，则需要先把key blob输入进去，此时，ZYNQ读取到key blob则对输入的tag和密文解密，解密之后就可以拿到真实的红密钥，也会对tag进行验证，tag验证通过，红密钥才可信，解密者拿红密钥对文件系统进行解密。**ZYNQ只是对密钥加密和解密做了封装，而文件系统的加密解密需要自己的应用程序来做**。

#### 1.2.1 加密解密High-Level角度过程 <a href="#id-1.2.1-jia-mi-jie-mi-highlevel-jiao-du-guo-cheng" id="id-1.2.1-jia-mi-jie-mi-highlevel-jiao-du-guo-cheng"></a>

**Alice的加密**

我们来看看Alice加密过程，Alice产生一个Red Key，用于真实文件系统加密，Alice把Red Key注入FPGA中，得到了密文的Key Blob。与此同时，Alice使用Red Key对文件加密，得到加密了的文件系统。

<figure><img src="/files/YjZrWxaKequt0VpiZgZE" alt=""><figcaption></figcaption></figure>

**Bob的解密**

FPGA内部提供了AES-256-GCM加速引擎来完成加密及认证的AEAD操作。Bob拿到Key Blob之后喂给FPGA，FPGA会输出Red Key和一个GCM的AUTH结果（tag），如果AUTH结果（tag对比）通过，那么Bob可以使用红密钥对加密的文件进行解密，最后拿到解密后的文件。

<figure><img src="/files/GCEAlVCGyDWXJ9uLYTzL" alt=""><figcaption></figcaption></figure>

FPGA在这里充当的角色是对Red Key进行加解密验证。

#### 1.2.2 FPGA SoC内部过程 <a href="#id-1.2.2fpgasoc-nei-bu-guo-cheng" id="id-1.2.2fpgasoc-nei-bu-guo-cheng"></a>

如图所示，为Alice加密和Bob解密在External Memory以及FPGA SoC内部的加解密全过程：

<figure><img src="/files/gZCfWANqwoybn6hKPjYK" alt=""><figcaption></figcaption></figure>

在FPGA SoC内部，主要是利用PUF的物理特性，产生一个PUF Key，用做内部数据加密的密钥。FPGA内部依旧使用AES-GCM-256来完成加密操作。Alice和Bob可以在External Memory协商数据加密的方式。

在FPGA内部，生成的黑色密钥是被写入到eFUSE中的，正因为每一个PUF的物理工艺不同导致的即便是数据一致，也会让每个设备驻留在eFUSE内部的数据不一致。这就达到了防止DPA的目的。

**加密过程**

如图，注，**这里的New Data指的就是Alice的Red Key**。

<figure><img src="/files/WLpm8tK7ipKKXJprn6DN" alt=""><figcaption></figcaption></figure>

1. Alice把明文Red Key喂给FPGA；
2. FPGA产生一个PUF使用的Key（PUF物理工艺差异产生的数，不同芯片不一致，同一个芯片就是一致的），用于把Red Key加密成为一个Black Key，并写入eFUSE;
3. FPGA输出Black Key和TAG值，到外部内存；
4. FPGA读回Black Key和TAG值，组成Key Blob；
5. FPGA对Black Key进行解密，解密得到一个TAG1的值；
6. FPGA对比TAG1和TAG是否一致，以此检验是否加密成功；若不一致，进入“惩罚”流程，比如烧写FUSE的User Data，告知解密失败过。

**1.2.3 解密过程**

解密的过程：

<figure><img src="/files/ub5hDFQMiYOSXMNe3Frf" alt=""><figcaption></figcaption></figure>

1. Bob把Key Blob输入到FPGA内部；
2. FPGA读取到Key Blob分离出black key和TAG；
3. FPGA重新让PUF产生一个Key（PUF物理工艺差异产生的数，同一个设备，这个值必然一致）；
4. FPGA使用PUF产生的Key解密black key成为red key，并输出TAG1；
5. FPGA校验TAG1和Key Blob输入的TAG是否一致，若一致则解密成功并输出Red Key。

#### 1.2.2 FPGA的安全角度考量 <a href="#id-1.2.2fpga-de-an-quan-jiao-du-kao-liang" id="id-1.2.2fpga-de-an-quan-jiao-du-kao-liang"></a>

在ZYNQ内部，有很多安全关键因素在设计中被考虑到，我们应该熟悉其背后的设计逻辑，明白他们的设计意图。一种是对于防重放攻击的抵御，一种是防DPA，除此之外还有对black KEY 考量[FIPS legal KEK](https://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/fips140-2/FIPS1402IG.pdf)的因素。（PUF key的来源是因为每个设备工艺不一致生成的一串数据，这串数据是不符合标准的。生成key的需要真随机数或者DRBG才能达到生成key的质量。如果想要black key达到质量，我们则需要让加密前的数据达到质量，这样加密后的数据就达到了key的质量要求。）

**防重放攻击**

防重放攻击的设计体现在，Alice对于Red Key的输入不光是有 Red Key和IV这些成分，除此之外还有ID放入到OTP的256bits长度的User Data里面。Alice产生Key之后，可以增加ID，在解密的时候，除了要验证解密的信息的GCM的TAG之外，还要对比解析出来的ID信息，和OTP/eFUSE上的ID进行对比。这样很可能就可以有效的抵御重放攻击。

id在bootgen的时候通过以下方法指定：

<figure><img src="/files/IVXOlc5I9jLcXHE2QNFT" alt=""><figcaption></figcaption></figure>

**功率分析攻击（DPA）**

PUF Key是直接加载到SoC内部的加密引擎中的。为了防止eFUSE中的Key被分析出来，ZYNQ给了两个建议：

* 尽量让User Data短。PUF Key在SoC中不是对所有的数据加密，而是加密第一部分，因此存在没有加密的部分。如果User Data过长，就难保后面的数据的保密性。入侵者Mallory很可能使用DPA功率分析分析出后面User Data的数据；
* 如果User Data不得不做的很长。ZYNQ推荐使用Rolling Key/OP Key的方式，进行加密；

**Operational Key (OP Key)**

实际上在boot中也不希望使用私密的key频繁加密，以下是提供了OP Key方法来帮忙减少使用私密的key：

<figure><img src="/files/hlL3FqYDVlJZ5LtBRNtS" alt=""><figcaption></figcaption></figure>

bootgen在生成image的时候，会把op key放到image header里面，还包含了IV这类的信息。当需要进行解密的时候，使用私密的key对第一个块进行解密，从解密的信息中读取OP key，然后利用该key解下一个块，以此类推。这样私密的Key只使用了一次。

Note， **DPA功率分析，可以通过频谱分析出eFUSE上的数据**。

**Keys Rolling**

以下为Key Rolling的过程：

<figure><img src="/files/TSsSuxFN72yzGvirIspV" alt=""><figcaption></figcaption></figure>

**FIPS Legay的key审查**

使用SoC PUF产生的加密数据，也就是Block Key，如果作为Key的话，处于密码学安全边界之外，换句话说，此密钥不符合FIPS-legal KEK标准。因此我们需要在产生Red Key阶段就产生一个符合FIPS标准的Key，这样加密出来的Key就会处于密码学安全边界内。

## 2. Secure Storage Low-Level Design <a href="#id-2.-secure-storage-low-level-design" id="id-2.-secure-storage-low-level-design"></a>

### 2.1 eFUSE/OTP Provisioning <a href="#id-2.1-efuse-otp-provisioning" id="id-2.1-efuse-otp-provisioning"></a>

eFUSE array包含一个256的块，在这个块里面提供GCM-AES-256的key给加解密引擎。eFUSE的key可以存储为明文形式（red key），混淆模式（gray key），或者是加密模式（black key）。

eFUSE的写入过程叫做Provisioning。通过PJTAG（on MIO）使RPU或者APU处理器写寄存器的方式完成Provisioning。 [Ref, Programming BBRAM and eFUSEs Application Note (XAPP1319)](https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/kdP~nr5__We0rfXUfmQbXQ?section=XREF_13077_20_Programming).

在一些老的ZYNQ设计中是支持读回操作来验证写入的eFUSE数据是否正确。在一些eFUSE的SoC设计中，有驱动接口是支持读出eFUSE上面值的。以前我们在做Provisioning工具的时候，都是先利用写接口写入eFUSE数据，然后再通过读接口读回数据，对比写入和读出的数据来判断是否真的写成功eFUSE。但对于现阶段的ZYNQ来说具备很高的风险性，所以这个途径已经被**关闭读回**了。取而代之的是，写入eFUSE之后同时会返回CRC32，可以通过对比返回的CRC来确定值是否正确。

从安全角度来看，无法确定“一次性”的功能给eFUSE带来多少的增益。但是要注意，确保电压稳定，否则可能导致eFUSE写入失败。在烧录eFUSE的时候，一定要确保电压、温度这些条件稳定。否则可能使eFUSE烧写发生错误。写eFUSE的时候不要用SPA对密钥进行分析，SPA是通过接入不同电压，静态分析eFUSE的功率变化。所以在烧录eFUSE时候为了保持稳定电压，xilinx建议不要这个时候做SPA分析。

最后，请注意通过[APB](https://developer.arm.com/documentation/ihi0024/c/Introduction/About-the-APB-protocol#:~:text=The%20Advanced%20Peripheral%20Bus%20\(APB,consumption%20and%20reduced%20interface%20complexity.)总线，可以加载Key到 *key update register* 中。这个设计主要是为了boot阶段使用了rolling key，对不同块加密的时候要频繁的换key。就是通过这个寄存器实现。

#### HRoTS <a href="#hrots" id="hrots"></a>

ZYNQ的HRoTS基于RSA-4096认证，而且需要基于两类的public key：

<figure><img src="/files/44wlKn7WtB5EXuPj8tfk" alt=""><figcaption></figcaption></figure>

Primay的2个公钥，一个需要存储在外部的内存中，一个需要把其hash值存入eFUSE；因此，Primay的pk hash是一个根凭证。

<figure><img src="/files/zXeZPtIOPNDXaLsgwFCZ" alt=""><figcaption></figcaption></figure>

最小需要做Provisioning的信息如上。

### 2.2 eFUSE layout <a href="#id-2.2-efuse-layout" id="id-2.2-efuse-layout"></a>

在FPGA系统中，逻辑和处理单元是分开的两部分：

* PS: 处理系统 （Processing System) ：就是与FPGA无关的ARM的SoC的部分。
* PL: 可编程逻辑 (Progarmmable Logic)： 就是FPGA部分。

因此，存在两类eFUSE，一个是256-PS的eFUSEs，还有是128-PL的eFUSEs。 PL的eFUSEs顾名思义，是给可编程逻辑 (Progarmmable Logic)使用的，我们用不到。我们更关心处理系统 （Processing System) 的侧的eFUSE，也就是存储密钥之类的eFUSE。

参考：<https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/2Ubsx6RiXaJnAO2rAuCVYA?section=XREF_67790_Zynq_UltraScale>

| Size | Name                                                                                                                                                                                                                                       | Description                                                                                                                                                                                                                                                                                                           | MACRO define                                         |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| 32   | USER\_{0:7}                                                                                                                                                                                                                                | 256 user defined eFUSEs:**Note:** In the **input.h**file (see text), write data in the XSK\_EFUSEPS\_USER{0:7}\_FUSES macro and execute the write by setting the XSK\_EFUSEPS\_USER{0:7}\_FUSE macro = True.                                                                                                          | USER{0:7}\_FUSE                                      |
| 1    | USER\_WRLK                                                                                                                                                                                                                                 | 8 user-defined eFUSE locks.USER\_WRLK columns:0: Locks USER\_0,1: Locks USER\_1,...7: Locks USER\_7,**Note:** Each eFUSE permanently locks the entire corresponding user-defined USER\_{0:7} eFUSE row so it cannot be changed.                                                                                       | USER\_WRLK\_{0:7}                                    |
| 1    | LBIST\_EN                                                                                                                                                                                                                                  | Enables logic BIST to run during boot.                                                                                                                                                                                                                                                                                | LBIST\_EN                                            |
| 3    | LPD\_SC                                                                                                                                                                                                                                    | Enables zeroization of registers in low power domain (LBD) during boot.**Note:** Any of the eFUSE programmed will perform zeroization. Xilinx recommends programming all of them.                                                                                                                                     | LPD\_SC\_EN                                          |
| 3    | FPD\_SC                                                                                                                                                                                                                                    | Enables zeroization of registers in full power domain (FBD) during boot.**Note:** MGTs must be powered to perform zeroization of the FPD.**Note:** Any of the eFUSE programmed will perform zeroization. Xilinx recommends programming all of them.                                                                   | FPD\_SC\_EN                                          |
| 3    | PBR\_BOOT\_ERROR                                                                                                                                                                                                                           | When programmed, boot is halted on any PMU error.                                                                                                                                                                                                                                                                     | PBR\_BOOT\_ERR                                       |
| 32   | CHASH                                                                                                                                                                                                                                      | PUF helper data（参考：2.2.1 PUF Helper Data）                                                                                                                                                                                                                                                                             | N/A - handled by PUF registration software directly. |
| 24   | AUX                                                                                                                                                                                                                                        | PUF helper data: ECC vector                                                                                                                                                                                                                                                                                           | N/A - handled by PUF registration software directly. |
| 1    | SYN\_INVLD                                                                                                                                                                                                                                 | Invalidates PUF helper data stored in eFUSEs.                                                                                                                                                                                                                                                                         | XSK\_PUF\_SYN\_INVALID                               |
| 1    | SYN\_LOCK                                                                                                                                                                                                                                  | Locks PUF helper data from future programming.                                                                                                                                                                                                                                                                        | XSK\_PUF\_SYN\_WRLK                                  |
| 1    | REG\_DIS                                                                                                                                                                                                                                   | Disables PUF registration.                                                                                                                                                                                                                                                                                            | XSK\_PUF\_REGISTER\_DISABLE                          |
| 1    | AES\_RD                                                                                                                                                                                                                                    | Disables the AES key CRC integrity check for eFUSE key storage.                                                                                                                                                                                                                                                       | AES\_RD\_LOCK                                        |
| 1    | AES\_WR                                                                                                                                                                                                                                    | Locks AES key from future programming.                                                                                                                                                                                                                                                                                | AES\_WR\_LOCK                                        |
| 1    | ENC\_ONLY[^(1)^](https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/2Ubsx6RiXaJnAO2rAuCVYA?section=XREF_29338_1_IMPORTANT)[^(2)^](https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/2Ubsx6RiXaJnAO2rAuCVYA?section=XREF_36827_2_When_the_ENC) | When programmed, all partitions are required to be encrypted. Xilinx recommends using this only if security is required and the hardware root of trust (RSA\_EN) is not used.                                                                                                                                         | ENC\_ONLY                                            |
| 1    | BBRAM\_DIS                                                                                                                                                                                                                                 | Disables the use of the AES key stored in BBRAM.                                                                                                                                                                                                                                                                      | BBRAM\_DISABLE                                       |
| 1    | ERR\_DIS                                                                                                                                                                                                                                   | Prohibits error messages from being read via JTAG (ERROR\_STATUS register).**Note:** The error is still readable from inside the device.                                                                                                                                                                              | ERR\_DISABLE                                         |
| 1    | JTAG\_DIS[^(1)^](https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/2Ubsx6RiXaJnAO2rAuCVYA?section=XREF_29338_1_IMPORTANT)                                                                                                                   | Disables JTAG. IDCODE and BYPASS are the only allowed commands.                                                                                                                                                                                                                                                       | JTAG\_DISABLE                                        |
| 1    | DFT\_DIS[^(1)^](https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/2Ubsx6RiXaJnAO2rAuCVYA?section=XREF_29338_1_IMPORTANT)                                                                                                                    | Disables design for test (DFT) boot mode.                                                                                                                                                                                                                                                                             | DFT\_DISABLE                                         |
| 3    | PROG\_GATE                                                                                                                                                                                                                                 | When programmed, these fuses prohibit the PROG\_GATE feature from being engaged. If any of these are programmed, the PL is always reset when the PS is reset.**Note:** Only one eFUSE needs to be programed to prohibit the PROG\_GATE feature from being engaged. Xilinx recommends programming all three.           | PROG\_GATE\_DISABLE                                  |
| 1    | SEC\_LK                                                                                                                                                                                                                                    | When programmed, the device does not enable BSCAN capability while in secure lockdown.                                                                                                                                                                                                                                | SECURE\_LOCK                                         |
| 15   | RSA\_EN[^(1)^](https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/2Ubsx6RiXaJnAO2rAuCVYA?section=XREF_29338_1_IMPORTANT)[^(2)^](https://docs.xilinx.com/r/dqE2tE0k~iMhpEDoQwXIKg/2Ubsx6RiXaJnAO2rAuCVYA?section=XREF_36827_2_When_the_ENC)   | When any one of the eFUSEs is programmed, every boot must be authenticated using RSA. Xilinx recommends programming all 15 eFUSEs.                                                                                                                                                                                    | RSA\_ENABLE                                          |
| 1    | PPK0\_WR                                                                                                                                                                                                                                   | Primary public key write lock. When programmed, this prohibits future programming of PPK0.                                                                                                                                                                                                                            | PPK0\_WR\_LOCK                                       |
| 2    | PPK0\_INVLD                                                                                                                                                                                                                                | When either of the eFUSEs are programmed, PPK0 is revocated. Xilinx recommends programming both eFUSEs when revocating PPK0.                                                                                                                                                                                          | PPK0\_INVLD                                          |
| 1    | PPK1 WR                                                                                                                                                                                                                                    | Primary public key write lock. When programmed this prohibits future programming of PPK1.                                                                                                                                                                                                                             | PPK1\_WR\_LOCK                                       |
| 2    | PPK1\_INVLD                                                                                                                                                                                                                                | When either of the eFUSEs are programmed, PPK1 is revocated. Xilinx recommends programming both eFUSEs when revocating PPK1.                                                                                                                                                                                          | PPK1\_INVLD                                          |
| 32   | SPK\_ID                                                                                                                                                                                                                                    | Secondary public key ID.**Note:** Write the SPK ID bits into the XSK\_EFUSEPS\_SPK\_ID eFUSE array and set XSK\_EFUSEPS\_SPKID = True.                                                                                                                                                                                | SPK\_ID                                              |
| 256  | AES                                                                                                                                                                                                                                        | User AES key**Note:** Write data in the XSK\_EFUSEPS\_AES\_KEY macro and execute the write by setting the XSK\_EFUSEPS\_WRITE\_AES\_KEYmacro = True.                                                                                                                                                                  | AES\_KEY                                             |
| 384  | PPK0                                                                                                                                                                                                                                       | User primary public key0 HASH**Note:** Write data in the XSK\_EFUSEPS\_PPK0\_HASH macro. To program 256 bits, use the LSBs and set XSK\_EFUSEPS\_PPK0\_IS\_SHA3 = False. To program 384 bits, set XSK\_EFUSEPS\_PPK0\_IS\_SHA3 = True. Execute the write by setting the XSK\_EFUSEPS\_WRITE\_PPK0\_HASH macro = True. | PPK0\_HASH                                           |
| 384  | PPK1                                                                                                                                                                                                                                       | User primary public key1 HASH**Note:** Write data in the XSK\_EFUSEPS\_PPK1\_HASH macro. To program 256 bits, use the LSBs and set XSK\_EFUSEPS\_PPK1\_IS\_SHA3 = False. To program 384 bits, set XSK\_EFUSEPS\_PPK1\_IS\_SHA3 = True. Execute the write by setting the XSK\_EFUSEPS\_WRITE\_PPK1\_HASH macro = True. | PPK1\_HASH                                           |
| N/A  | PUF\_HD                                                                                                                                                                                                                                    | Syndrome of PUF HD. These eFUSEs are programmed using Xilinx provided software, Xilskey                                                                                                                                                                                                                               | N/A - handled by PUF registration software directly. |

这个表，很关键，eFUSE会影响到ZYNQ处理器的工作状态、安全信息，配置错了之后，Xilinx已经声明不接受要求eFUSE复原的返厂件。在操作这些位的时候，尤其是Provisioning的时候，会影响其他非安全的测试。

> **IMPORTANT**: THESE INSTRUCTIONS MODIFY THE EFUSES ON THE ZCU102 DEVELOPMENT BOARD AND MAY LIMIT FUTURE USE OF THE DEVELOPMENT BOARD FOR NON-SECURE TESTING AND DEBUGGING!

### 2.2 PUF <a href="#id-2.2-puf" id="id-2.2-puf"></a>

#### 2.2.1 PUF Helper Data <a href="#id-2.2.1-puf-helper-data" id="id-2.2.1-puf-helper-data"></a>

PUF使用大约4Kb的辅助数据来帮助PUF在正确的寿命、规定的工作温度和电压范围内重新创建原始KEK值。辅助数据由Syndrome值、Aux值和Chash值组成（请参见表：PUF辅助数据）。助手数据可以存储在eFUSE或引导映像中。

| PUF Helper Data Field | Size (Bits) | Description                                                                                                                                                                                                                                                |
| --------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Syndrome              | 4060        | 鉴于环形振荡器在温度、电压和时间上的微小变化，这些比特有助于PUF恢复正确的PUF特征                                                                                                                                                                                                                |
| Aux                   | 24          | 这是一个汉明码，允许PUF对PUF签名执行某种程度的纠错。                                                                                                                                                                                                                              |
| Chash                 | 32          | <p>•如果CHASH未被编写，则只要使用（bh\_auth或rsa\_en），就可以使用boot-header black key。</p><p>•如果对CHASH进行了编写，则只要（使用bh\_auth或rsa\_en）且eFUSE的Syndrome数据未失效，就可以使用eFUSE的black key。</p><p>•如果对CHASH进行了编写，则只要使用（bh\_auth或rsa\_en）且efuse的Syndrome数据无效，就可以使用boot-header black key。</p> |

helper data 简单的说是帮助生成PUF key时候所需要的材料（Syndrome，Aux）。如图所示，生成PUF key及调用AES引擎的过程。PUF原数据复原的算法，可以参考：\[Helper Data Algorithms for PUF-Based Key Generation: Overview and Analysis]\(<https://lirias.kuleuven.be/retrieve/334181>) （有点专业和复杂。。） 特征值（Syndrome）和Aux（汉明距）是算法需要的输入。汉明距是实现比特纠错码的一种手段，可以参考，文献：\[ <https://blog.csdn.net/weixin_45783996/article/details/116203267>]。

<figure><img src="/files/u4OWrvrkZDlUT2zGhHmC" alt=""><figcaption></figcaption></figure>

生成puf helper data的过程参考：

{% embed url="<https://xilinx.github.io/Embedded-Design-Tutorials/docs/2021.1/build/html/docs/Introduction/ZynqMPSoC-EDT/9-secure-boot.html#puf-registration-in-boot-header-mode>" %}

#### 2.2.2 PUF operations <a href="#id-2.2.2-puf-operations" id="id-2.2.2-puf-operations"></a>

只有CSU可以访问PUF，所以PUF的初始化包括黑密钥的产生，必然是在CSU阶段完成。PUF暴露出以下接口供CSU操作：

| Command         | Description                     |
| --------------- | ------------------------------- |
| Registration    | 产生一个新的KEK并且关联helper data        |
| Re-registration | 产生一个新的KEK并且关联**新的**helper data  |
| Reuse           | 使用已经存在的KEK进行加解密，并且关联helper data |

当一个设备上电的时候，CSU bootROM会检测已经校验过的boot header，确认以下信息：

* 是否使用了PUF；
* 加密的密钥存储在哪里？（eFUSE 或者 boot image）
* helper data存储在哪里？（eFUSE 或者 boot image）

接着CSU初始化PUF，并且加载helper data，最后产生KEK，`这个过程叫做regeneration`。一旦KEK产生成功，CSU bootROM使用它来解密剩下boot image需要的key。

#### 2.2.3 PUF Control eFUSEs <a href="#id-2.2.3-puf-control-efuses" id="id-2.2.3-puf-control-efuses"></a>

eFUSE也给PUF留了一些功能接口：

| Command      | Description               |
| ------------ | ------------------------- |
| REG\_DIS     | 屏蔽PUF的注册                  |
| SYN\_INVALID | 使存在eFUSE上的helper data无效   |
| SYN\_LOCK    | 屏蔽修改eFUSE上的helper data的功能 |

Xilinx也做了PUF的强度分析，包含加密，安全强度KEK，过温过压的测试的数据报告，需要联系FAE或者销3. Example售才可以拿到这个报告。

### 3.1 Provisioning <a href="#id-3.1-provisioning" id="id-3.1-provisioning"></a>

Provisioning是所有安全机制的基础。其目的是把根凭证写入eFUSE或者其他存储密钥的敏感介质。包含RSA的根密钥，也包含GCM的Key。

在ZYNQ中我们需要Provisioning的内容：

* PPK HASH （主引导Primary Public Key Hash）
* GCM-AES Key （用于image解密）
* PSK ID (写入key的id信息，作为PUF解密时候比对，参考'防重放攻击'一节)

在调试阶段为了不伤害eFUSE。对于PPK HASH，zynq提供了bh\_boot模式，即可以绕过eFUSE的PPK HASH检测，直接使用AC中的hash值。

同样，为了不伤害eFUSE。对于GCM-AES key，我们可以不使用eFUSE，而把Key烧录到BBRAM中。

**在调试阶段，推荐配置为**：

* **ppk hash**，对于验证，使用bh\_boot（不需要包含到Provisioning过程中）
* **gcm aes key**，对于验证，使用bbram （需要包含到Provisioning过程中）
* id
* **ID**，对于验证，在bootgen阶段选择`spk_id = 0`，不需要包含到Provisioning中。

**在产品阶段，必须配置为**： ppk hash 和 gcm aes key都需要在eFUSE中。

我们可以把完整的Provisioning过程总结为：

* 手动产生两对RSA密钥，产生gcm-aes-key；
* 使能PUF eFUSE的配置；
* 使用写入寄存器的方法写入eFUSE。

**可以通过JTAG烧录eFUSE（这种方法数据操作互动型，具备一定的危险性，eFUSE烧录不可撤销），因此建议使用配置寄存器的方法烧录eFUSE**。

#### 3.1.1 Gen Key <a href="#id-3.1.1-gen-key" id="id-3.1.1-gen-key"></a>

密钥生成主要是需要以下材料：

* AES Key Generation
  * 输出nky文件（包含key和iv）
* RSA Asymmetric Key Generation
  * 输出1：psk0.pem
  * 输出2：ssk0.pem
* Generate SHA3 of Public RSA Asymmetric Key
  * 输出是：sha3.txt

#### 3.1.2 PUF eFUSE config <a href="#id-3.1.2-puf-efuse-config" id="id-3.1.2-puf-efuse-config"></a>

PUF eFUSE的配置需要使用Vitis建立AP的工程，使用ZYNQ上面的AP来完成PUF的配置。非常重要的提示：**这一步会修改eFUSE上面的内容**！

需要配置的项目是：

* XSK\_PUF\_INFO\_ON\_UART
* XSK\_PUF\_PROGRAM\_EFUSE
* XSK\_PUF\_PROGRAM\_SECUREBITS
* XSK\_PUF\_SYN\_WRLK
* XSK\_PUF\_AES\_KEY
* XSK\_PUF\_IV

`XSK_PUF_AES_KEY`是用户指定的，而且这个`XSK_PUF_IV`和AES Key Generation中的IV不相关。这个IV是用于PUF KEK的red key加密。

#### 3.1.3 RSA eFUSE config <a href="#id-3.1.3-rsa-efuse-config" id="id-3.1.3-rsa-efuse-config"></a>

这一步是配置RSA相关的信息到eFUSE上面，非常重要的提示：**这一步会修改eFUSE上面的内容**！

* XSK\_EFUSEPS\_RSA\_ENABLE
* XSK\_EFUSEPS\_PPK0\_WR\_LOCK
* XSK\_EFUSEPS\_WRITE\_PPK0\_HASH
* XSK\_EFUSEPS\_PPK0\_HASH

#### 3.1.4 RSA Key Revocation Support <a href="#id-3.1.4-rsa-key-revocation-support" id="id-3.1.4-rsa-key-revocation-support"></a>

RSA密钥提供了撤销一个分区的**secondary**密钥（SPK）的能力，而无需撤销所有分区的密钥。这是通过使用新的BIF参数`spk_select`利用`USER_FUSE0`到`USER_FUSE7` 位域实现（如果这些位域没有用于表示其他信息，只用于表示密钥的id，最多可以撤销256个SPK，如图所示）。

<figure><img src="/files/RtbFDDDXqB6LA2eNNQ5f" alt=""><figcaption></figcaption></figure>

下图表示ZYNQ使用SPK\_ID进行SPK revocation的过程。

以下是使用辅助密钥创建经过身份验证的映像的步骤：

* 使用bootgen生成RSA密钥对。
* 我们在步骤1中生成了一个辅助密钥（SSK）。如果需要更多的SSK，请重复步骤1以创建SSK密钥。
* 使用bootgen和下面的bif文件模板生成经过验证的引导映像。下面的模板假设bootloader和u-boot使用\[sskfile]标记提供的密钥进行了验证，并且该密钥根据eFUSE中存储的SPK\_ID进行了验证；PMU FW和ATF images使用sskfile提供的密钥进行验证，并根据存储在USER\_eFUSE中的SPK\_ID bitmap 验证该密钥。(确保在生成映像时使用命令行参数–efuseppkbits\<path\_to\_sha\_txt\_file>命令bootgen生成PPK哈希。)
* 使能RSA认证通过设定 “RSA\_EN” 在eFUSE上. 参考 [Programming BBRAM and eFUSEs Application Note (XAPP1319)](https://www.xilinx.com/support/documentation/application_notes/xapp1319-zynq-usp-prog-nvm.pdf)
* 写入在第三步创建的PPK的SHA-3 hash 到eFUSE的PPK0 hash位域。

确保在生成image时使用命令行参数–efuseppkbits\<path\_to\_sha\_txt\_file>命令bootgen生成PPK hash。

**模板示例**：\
image header和FSBL使用不同的SSK进行身份验证（分别为ssk1.pem和ssk2.pem），用以下bif文件：

<figure><img src="/files/gPtJHDSTo5hiBTKNpu1V" alt="" width="563"><figcaption></figcaption></figure>

```
the_ROM_image: {
[auth_params]ppk_select = 0
[pskfile]psk.pem
[sskfile]ssk1.pem
[bootloader, authentication = rsa, spk_select = spk-efuse, spk_id = x00000001, sskfile = ssk2.pem]zynqmp_fsbl.elf
[destination_cpu =a53-0, authentication = rsa, spk_select = user-efuse,spk_id = 0x1, sskfile = ssk3.pem]Application1.elf
[destination_cpu =a53-0, authentication = rsa, spk_select = spk-efuse, spk_id = 0x00000001, sskfile = ssk4.pem]Application2.elf
}
```

相同的SSK将作用于image header和FSBL（ssk2.pem）：

```
the_ROM_image: {
[auth_params]ppk_select = 0 [pskfile]psk.pem
[bootloader, authentication = rsa, spk_select = spk-efuse, spk_id = 0x00000001, sskfile = ssk2.pem]zynqmp_fsbl.elf
[destination_cpu =a53-0, authentication = rsa, spk_select = user-efuse, spk_id = 1, sskfile = ssk3.pem]Application1.elf
[destination_cpu =a53-0, authentication = rsa, spk_select = spk-efuse, spk_id = 0x00000001, sskfile = ssk4.pem]Application2.elf
}

```

注意：

* `spk_select = spk-efuse` 表示 指定的分区将会使用`spk_id`eFUSE位域。&#x20;
* `spk_select = user-efuse` 指示 指定的分区将会使用user eFUSE位域，而CSU ROM总是使用`spk_id`eFUSE位域。

### 3.2 PUF Enc/Dec demo <a href="#id-3.2-puf-enc-dec-demo" id="id-3.2-puf-enc-dec-demo"></a>

完成上面PUF的注册，我们假定eFUSE和PUF的配置已经OK了，现在我们需要编写APU(Application Processor Unit)的固件（bare-mental程序），来使用PUF的加密和解密功能。

该固件是在xilinx的vitis ide上完成的，vitis已经集成了ZYNQ所用的bsp驱动包，并提供了相应的操作key、加密解密、访问寄存器、控制外设等接口。

打开vitis ide创建工程：

<figure><img src="/files/B7j34FNiqF7OiH10ciu9" alt=""><figcaption></figcaption></figure>

选择bsp包：

<figure><img src="/files/tFQtcrhvWIMmGwHxycpd" alt=""><figcaption></figcaption></figure>

选择processor为APU (Application Processor Unit)：CortexA53\_0：

<figure><img src="/files/NcwSBMT7Rfg0dybvMmfW" alt=""><figcaption></figcaption></figure>

导入bare-mental的源码（下面就是源码核心）：

<figure><img src="/files/yljSzNWNuZczmd2TU6yX" alt=""><figcaption></figcaption></figure>

源码进行编译：

<figure><img src="/files/R1IBIrHcfvRcqeshHgrQ" alt=""><figcaption></figcaption></figure>

最后生成`BOOT.BIN`文件，将其复制到SD卡的boot分区。启动即可运行。

#### 3.2.1 加密 <a href="#id-3.2.1-jia-mi" id="id-3.2.1-jia-mi"></a>

<figure><img src="/files/UdJR6iRFd6stW9ZWFhxf" alt=""><figcaption></figcaption></figure>

对于一个PUF加密过程的程序：

```c
void puf_encrypt(u8 *Iv, u8 *Dst, u8 *Src, u32 Size) {

	XCsuDma_Config *Config;
	XCsuDma_Configure ConfigurValues = {0};

    /* Configure PUF configuration 0 and configure the shutter. */
	XilSKey_WriteReg(XSK_ZYNQMP_CSU_BASEADDR, XSK_ZYNQMP_CSU_PUF_CFG0,
                    XSK_ZYNQMP_CSU_PUF_CFG0_DEFAULT);
	XilSKey_WriteReg(XSK_ZYNQMP_CSU_BASEADDR, XSK_ZYNQMP_CSU_PUF_SHUT,
                    XSK_ZYNQMP_CSU_PUF_SHUT_DEFAULT);


	// Spin up the PUF and connect the key to the AES engine
	XilSKey_WriteReg(XSK_ZYNQMP_CSU_BASEADDR, XSK_ZYNQMP_CSU_PUF_CMD,
                    XSK_ZYNQMP_PUF_REGENERATION);

    /* Wait for the PUF regeneration to complete. */
	usleep(PUF_REGEN_TIME_US);

	/* Initialize & configure the DMA */
	Config = XCsuDma_LookupConfig(XSK_CSUDMA_DEVICE_ID);
	XCsuDma_CfgInitialize(&CsuDma, Config, Config->BaseAddress);

	/* Initialize AES engine */
	XSecure_AesInitialize(&AesInstance, &CsuDma, XSK_PUF_DEVICE_KEY, (u32 *) Iv, NULL);

	/* Set the data endianess for IV */
	XCsuDma_GetConfig(&CsuDma, XCSUDMA_SRC_CHANNEL,
				&ConfigurValues);
	ConfigurValues.EndianType = 1U;
	XCsuDma_SetConfig(&CsuDma, XCSUDMA_SRC_CHANNEL,
					&ConfigurValues);

	/* Enable CSU DMA Dst channel for byte swapping.*/
	XCsuDma_GetConfig(&CsuDma, XCSUDMA_DST_CHANNEL,
			&ConfigurValues);
	ConfigurValues.EndianType = 1U;
	XCsuDma_SetConfig(&CsuDma, XCSUDMA_DST_CHANNEL,
			&ConfigurValues);

	/* Request to encrypt the AES key using PUF Key	 */
	XSecure_AesEncryptData(&AesInstance, (u8 *) Dst, (u8 *) Src, Size);

   /* Clear the PUF key. */
	XilSKey_WriteReg(XSK_ZYNQMP_CSU_BASEADDR, XSK_ZYNQMP_CSU_PUF_CMD,
                    XSK_ZYNQMP_PUF_RESET);
}
```

#### 3.2.2 解密 <a href="#id-3.2.2-jie-mi" id="id-3.2.2-jie-mi"></a>

<figure><img src="/files/0vO1zP6N428mtCYqX7pY" alt=""><figcaption></figcaption></figure>

## Ref <a href="#ref" id="ref"></a>

1. [External Secure Storage Using the PUF Application Note](https://docs.xilinx.com/r/en-US/xapp1333-external-storage-puf/External-Secure-Storage-Using-the-PUF-Application-Note)
2. [Programming BBRAM and eFUSEs Application Note (XAPP1319)](https://docs.xilinx.com/v/u/en-US/xapp1319-zynq-usp-prog-nvm)
3. [Zynq UltraScale+ Device Technical Reference Manual (UG1085) - Security](https://docs.xilinx.com/r/en-US/ug1085-zynq-ultrascale-trm/Introduction?tocId=A~Ce_pZ6I0b4P1VxSk8qcg)
4. [secure-boot-encrypted-data-storage](https://www.timesys.com/security/secure-boot-encrypted-data-storage/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://carloss-organization-4.gitbook.io/tech/ecus/zynq_documents/zynq-encrypt-external-files-based-on-file-system-using-puf-key.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
