# boot 加密介绍

Linux 内核镜像是操作系统的核心部分，它负责管理硬件、执行进程调度、内存管理和其他系统级任务，因此，它的安全性对于操作系统的整体安全性至关重要。

**Kernel 加密的目的**
- 防止未授权访问：加密内核镜像可以防止未经授权的用户查看内核文件，确保系统的机密性和完整性。
- 防止篡改：攻击者可能会注入恶意代码或后门，导致系统被完全控制
- 增强启动链的安全性：提高启动过程的安全性，防止恶意修改启动加载过程

**Kernel 加密造成的影响**
- 性能开销：加密和解密操作需要计算资源，可能会导致启动过程的延迟或系统性能下降。
- 密钥管理：管理加密密钥是一项挑战，尤其是在嵌入式设备上，密钥的泄露可能导致整个系统的安全性受损。
- 兼容性问题：加密的内核镜像可能会在某些硬件或配置下存在兼容性问题，尤其是在支持加密的 bootloader 上。



# boot 加密使用方法
## boot 加密原理
在 FIT (Flattened Image Tree) 镜像中，cipher 参数用于指定与加密相关的选项。FIT 镜像通常用于嵌入式系统中，它支持多种加密方法来保护内核镜像、设备树、根文件系统等文件。通过配置 cipher 参数，FIT 镜像能够在加密和解密过程中指定加密算法、密钥管理等细节。

X5 支持对 Kernel (即 boot 分区) 加密。采用 AES 对称加密方式，默认不开启。
通过使用 FIT 包添加 cipher 参数的形式开启 boot 加密。

cipher 参数包含以下属性
- `algo` : 加密使用的算法，支持多种算法，使用逗号隔开，例如 `aes,sha256,rsa2048`
- `key-name-hint` : 加密使用的密钥文件
- `iv-name-hint` : 加密使用的 IV 文件

X5 BSP 根据使用场景预置了 its 文件。路径 `device/horizon/x5/board_cfg/soc/boot_its`目录。

| its文件   | 是否支持 Recovery | 是否开启 boot 加密 |
|-----------|---------|---------|
|  x5-common.its  | N | N |
|  x5-recovery.its  | Y | N |
|  x5-enc-common.its  | N | Y |
|  x5-enc-recovery.its  | Y | Y |

以 `x5-common.its` 和 `x5-enc-common.its` 为例，二者差异是增加了 cipher 属性如下
```diff
26c26,32
<                       */
---
...(部分省略代码)...
>                       cipher {
>                               algo = "aes128";
>                               key-name-hint = "fit_enc_key";
>                               iv-name-hint = "fit_enc_iv";
>                       };
...(部分省略代码)...
```


由编译脚本 `build/mk_boot.sh` 根据使用场景选择不同的 its 文件，并调用 mkimage 工具制作 FIT 镜像
```shell
function pack_boot_fit()
{
	# ...(部分省略代码)...
	if [ "${HR_RECOVERY_MODE}" = "yes" ];then      # 支持 Recovery
		if [ "${HR_BOOT_ENC}" = "true" ];then      # 开启 boot 加密
			cp -a "${HR_BOARD_CONF_DIR}"/boot_its/x5-enc-recovery.its "${KERNEL_DEPLOY_DIR}/x5.its"
			"${MKIMAGE_PREFIX}"/mkimage -f "${KERNEL_DEPLOY_DIR}"/x5.its \
				"${HR_TARGET_PRODUCT_DIR}"/boot.img -k "${HR_BOARD_CONF_DIR}"/key_files
		else                                       # 关闭 boot 加密
			cp -a "${HR_BOARD_CONF_DIR}"/boot_its/x5-recovery.its "${KERNEL_DEPLOY_DIR}/x5.its"
			"${MKIMAGE_PREFIX}"/mkimage -f "${KERNEL_DEPLOY_DIR}"/x5.its \
				"${HR_TARGET_PRODUCT_DIR}"/boot.img
		fi
	else                                           # 关闭 Recovery
		if [ "${HR_BOOT_ENC}" = "true" ];then      # 开启 boot 加密
			cp -a "${HR_BOARD_CONF_DIR}"/boot_its/x5-enc-common.its "${KERNEL_DEPLOY_DIR}/x5.its"
			"${MKIMAGE_PREFIX}"/mkimage -f "${KERNEL_DEPLOY_DIR}"/x5.its \
				"${HR_TARGET_PRODUCT_DIR}"/boot.img -k "${HR_BOARD_CONF_DIR}"/key_files
		else                                       # 关闭 boot 加密
			cp -a "${HR_BOARD_CONF_DIR}"/boot_its/x5-common.its "${KERNEL_DEPLOY_DIR}/x5.its"
			"${MKIMAGE_PREFIX}"/mkimage -f "${KERNEL_DEPLOY_DIR}"/x5.its \
				"${HR_TARGET_PRODUCT_DIR}"/boot.img
		fi
	fi

	# ...(部分省略代码)...
}
```


## 开启 boot 加密
boot 加密默认不开启，开启方法: 在板级配置文件中添加 `export HR_BOOT_ENC="true"`

用户根据使用配置文件修改，以 `device/horizon/x5/board_x5_evb_debug_config.mk` 为例。

```diff
diff --git a/board_x5_evb_debug_config.mk b/board_x5_evb_debug_config.mk
index 3981858..3443367 100644
--- a/board_x5_evb_debug_config.mk
+++ b/board_x5_evb_debug_config.mk
@@ -8,7 +8,7 @@ export HR_TARGET_MODE="debug"

 # 板级名称，对应每一个新的硬件型号
 export HR_BOARD_TYPE="soc"
+export HR_BOOT_ENC="true"
 # 编译out目录
```
**注意: 修改配置文件后需 `./bd.sh lunch` 选择对应配置文件，使配置选项生效**

# boot 密钥存储
## boot 加密密钥存储
boot 密钥的安全存储是防止恶意攻击的首要步骤。
X5 BSP boot 加密使用的 key 和 iv 文件路径 `device/horizon/x5/board_cfg/soc/key_files/fit_enc_key.bin` 和 `device/horizon/x5/board_cfg/soc/key_files/fit_enc_iv.bin` 。

用户可根据实际使用替换为自己使用的 key。

**注意：key 和 iv 文件内容以 ASCII 字符形式保存**

## uboot 获取密钥
在嵌入式系统启动流程中，Uboot 需要在启动过程中对内核镜像进行解密。而解密操作需要密钥。为了保证内核镜像的安全性，密钥通常不应硬编码在 U-Boot 或内核源代码中。相反，密钥应保存在 U-Boot 启动环境中，以便在启动时读取。这个过程涉及密钥的存储、检索、解密操作的执行等多个方面。

X5 Uboot 获取密钥有以下两种方式
- [方式1: 通过 dts 获取](#uboot_getkey_1)
- [方式2: 通过 RPMB 获取](#uboot_getkey_2)


<span id="uboot_getkey_1"/> </span>

### 方式1: 通过 dts 获取
在一些嵌入式平台上，U-Boot 使用设备树来描述硬件配置和资源，设备树（Device Tree）本质上是一个数据结构，U-Boot 使用它来识别硬件组件并进行初始化。也可以利用设备树来存储用于解密内核的密钥信息。

#### 使用方法

X5 Uboot 设备树文件路径 `uboot/arch/arm/dts/x5.dtsi`，在根节点上添加 cipher 节点，并修改启动 key 和 iv 属性即可，如下:

```C
cipher {
        key-aes128-fit_enc_key-fit_enc_iv {
                key = "2222222222222222";
                iv =  "1111111111111111";
        };
};
```

**注意: 设备树通常是未加密的，这可能会导致密钥暴露**

<span id="uboot_getkey_2"/> </span>

### 方式2: 通过 RPMB 获取
相比较方式1 从 dts 获取密钥，更安全的做法是将密钥存储在硬件安全模块中，对于 eMMC 存储介质，可使用 RPMB 存储密钥。

#### RPMB 介绍
eMMC 中有一个特殊的存储区域，称为 RPMB（Replay Protected Memory Block），这是一个加密保护的区域，用于存储敏感数据。其主要特点是数据保护和防重放攻击，通过加密和签名来确保存储数据的完整性和安全性。

RPMB 的特性
- 防篡改：RPMB 区域的数据被加密存储，且无法被普通的读写操作直接访问。任何对 RPMB 区域的读取和写入操作都需要经过密钥认证。
- 密钥保护：RPMB 使用一个密钥（通常由硬件生成并存储在设备内）来加密存储的数据。这个密钥是受保护的，无法直接读取。
- 防重放攻击：RPMB 提供了防止重放攻击的机制，确保每个写操作都具有唯一性，避免了数据被重复提交的风险。
- 独立区域：RPMB 是 eMMC 存储卡的一个独立区域，通常与其他存储区域（如用户数据存储区）隔离开来。

#### 使用方法
要开启 U-boot 通过 RPMB 读取 key 和 iv，需要开启 U-boot 选项 `CONFIG_DROBOT_BOOT_KEY_IN_RPMB`

**注意**

key 对应保存在 RPMB 中的名字要与 its 中的一致，即 `fit_enc_key`，iv 对应的名字是`fit_enc_iv`，写入的 key 和 iv 以 ASCII 形式表示

RPMB 的使用请参考[secure storage](../secure_storage/secure_storage.html)
