# \[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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FR5pC2UQLqzjodYCZX41J%2Fimage.png?alt=media&#x26;token=4ae2d34e-5452-4be7-92b5-aec739758de0" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2Fdgo6kGCA2z60aZVZgOZd%2Fimage.png?alt=media&#x26;token=6f87cd5d-84bc-400a-bd67-ea13e9949e8f" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2F13H0YTsHT4R7wpEi5FiY%2Fimage.png?alt=media&#x26;token=7b755599-fbe9-4a37-b299-da5c2775297b" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FtnAiCmNBVMrF63UStDPR%2Fimage.png?alt=media&#x26;token=c99def7e-5960-4036-a04f-6ee2ddae173b" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FOG8NS1jQcXeUEh6oo5IF%2Fimage.png?alt=media&#x26;token=7aaa4729-fe9f-42ee-ba91-7bc6b4a4835b" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FHr0JdSACniwo2c28UnRV%2Fimage.png?alt=media&#x26;token=82c13218-1483-40dd-8285-26504d66e484" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2F2AyRQn0FIlqsOwch28pn%2Fimage.png?alt=media&#x26;token=ef43b51b-4181-42a3-af15-3432773bd09d" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FTU26vCRZS02DQDfUa9AH%2Fimage.png?alt=media&#x26;token=511620ba-3621-4876-8ff7-cf0bac7d4e61" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FhWKUlMpcyxljHJilUxI2%2Fimage.png?alt=media&#x26;token=32b0c240-bfe2-4692-b96c-0c85d956cb1d" 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)总线，可以加载Key到 *key update register* 中。这个设计主要是为了boot阶段使用了rolling key，对不同块加密的时候要频繁的换key。就是通过这个寄存器实现。

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

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

<figure><img src="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2Fge1PprxzY8Qq0Vb6az0U%2Fimage.png?alt=media&#x26;token=faca6376-6ac8-43fc-a6c3-d8ae6cb8174c" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2Fh1kUL52CAXN1MtYCnGpx%2Fimage.png?alt=media&#x26;token=d77c7cb9-0c6f-4347-ac8b-423afd4f3a32" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FvEENXJjAuCMlIRIBUr3l%2Fimage.png?alt=media&#x26;token=a607ce26-c539-447f-96f1-ecdf2fa2e021" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FtURPoMcHat9w0QOqA0PU%2Fimage.png?alt=media&#x26;token=a6190be1-3468-4102-90b1-c371102c7722" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FPUTY4vQ7YaxllglalOuo%2Fimage.png?alt=media&#x26;token=0f5abc69-4dcc-409e-8b0a-7d3ead9deba4" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2Falyr8SRbolhN56YwaURl%2Fimage.png?alt=media&#x26;token=d4fbfb30-de91-4257-a7d6-187c79b7e5ed" alt=""><figcaption></figcaption></figure>

选择bsp包：

<figure><img src="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FS922bVDQ0hQLB1tWqjgp%2Fimage.png?alt=media&#x26;token=3c90de98-9178-4aa6-a604-6d1277fa3db0" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FYiDbkrTYi9NKdXWdxghr%2Fimage.png?alt=media&#x26;token=f67c6f8e-1b4c-45b4-8743-efa49b9bee2f" alt=""><figcaption></figcaption></figure>

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

<figure><img src="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FWBXg1WigYzNKJ8fDJWQ9%2Fimage.png?alt=media&#x26;token=b945edfb-9b2c-4f8a-8f3a-bf6e89ce2899" alt=""><figcaption></figcaption></figure>

源码进行编译：

<figure><img src="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2F8UahMxklyF9MTkcwHNur%2Fimage.png?alt=media&#x26;token=2882fd4a-077b-43e0-bf7d-eb6ba953182f" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FPNMtHUxkgtdg74grBkeJ%2Fimage.png?alt=media&#x26;token=b085d665-b7ce-4198-b0aa-a759c03cea5a" 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="https://1204947731-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtqiX1ZbXhRorHX3bwk1r%2Fuploads%2FVi3iPs0ybsXPez3OVthC%2Fimage.png?alt=media&#x26;token=4be18fde-0604-4e8f-ad3c-1b3e1b69eaa8" 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/)
