谈谈QNX安全之防篡改
1. Secure Boot
安全启动想必大家肯定很熟悉,它是通过对启动过程的每个阶段进行加密验证,确保运行系统完整性的一种安全机制。 如图 1 所示, QNX 的安全启动可以保证由 BootRom 、 PBL/SBL 、 IPL 和 IFS ,最后到可选的文件系统的完整性。在启动过程中,每一个后续的固件和软件都是由其前身进行加密验证的。
图
1 QNX
安全启动流程
QNX
可以支持两套安全启动方案,如图
1
右侧所示:
方案
1
:将
MRK
(
Master
Root
Key
)作为信任根,其公钥存储在
OTP
或
EEPROM
中,由
OTP
或
EEPROM
保证
MRK
不可篡改,由
MRK
的私钥完成对
CVK
(
Code
Verification
Key
)公钥的签名,由
CVK
的私钥完成
Bootloader
等后续代码的签名。启动时,
BootRom
使用
MRK
的公钥验证
CVK
公钥,验证成功后,由
CVK
公钥验证下面代码的签名。这样做可以将代码签名密钥
CVK
和信任根
MRK
解绑。由于
CVK
由
MRK
保证其真实性,
CVK
的公钥不需要存储在有任何保护的存储器中。这在不安全的制造环境中非常有用,因为即使
CVK
被泄露,我们也可以将其进行替换,而
MRK
则由芯片厂家保管。
方案
2
:不使用
MRK
,将
CVK
作为信任根,其公钥存储在
OTP
或
EEPROM
中。有时候为了节省存储空间,也可以存储
CVK
的公钥哈希。由于没有
MRK
,
BootRom
直接使用
CVK
完成代码的验签。此时代码签名密钥和信任根都跟
CVK
关联,一旦
CVK
的私钥被泄露,基本无解,优点在于
ECU
的设备制造商可完全掌握代码签名。
2.
QTD
Secure Boot 中最后一项是可选的完整性保护的文件系统, QTD 便是其中一种。 QTD ( QNX Trusted Disk ),是 QNX 针对数据存储的安全机制,通过结合哈希树和 PKI 密钥签名,为二进制数据、关键系统配置等提供完整性和真实性保护。 它 基于 Merkle 树 ,如图 2 所示, 在构建 QTD 映像时,元数据哈希树是从源文件系统映像的块 一层一层构建的,直至 RootHash 。
图 2 QTD 哈希树
与 AVB 中的 DM - Verity 类似,如图 3 所示。 RootHash 会进行签名保护,并纳入到安全启动链中。只要保证哈希树的 RootHash 未被篡改,便可以保证整个哈希树的完整性。这样可以带来一个好处,在安全启动的时候不用验证整个文件系统,只需要验证哈希树即可。当需要实际访问具体的某个存储块时,再通过对比哈希树中对应的哈希值即可发现存储块是否被更改。 QTD 的 驱动程序位于原始块设备和受支持的上层文件系统层之间(例如, fs-qnx6.so 支持的 power-safe 文件系统)。在读取访问时,使用哈希树 Metadata 来验证数据的完整性。如果验证失败,则会返回错误。使用 QTD 肯定会有一定的性能开销,好在 QTD 可以通过预先缓存内部哈希计算来减少实际访问时哈希运算的数量。
图 3 安全启动中的文件系统保护
需要注意的是,不同 QNX 版本支持的摘要算法和签名算法不同,当前 QNX 710 支持的密码学算法如表 1 所示。
表
1
QTD
密码学算法
架构
|
摘要算法
|
签名算法
|
|
128 bits
|
256 bits
|
||
32-bit
|
sha256
blake2s256
blake3
|
sha512
blake2b512
|
RSA
-
SHA
256
ECDSA
-
SHA
256
EDDSA-
25519
ECDSA
-448
|
64-bit
|
sha512256
blake2b256
blake3
|
sha512
blake2b512
|
QTD
使用由
QNX
加密库
qcrypto
提供的加密算法,详细可参考
“
QNX Cryptography Library
”
。
当前
支持
ECC
和
RSA
签名,其中
私钥必须是
PKCS#8
格式,公钥必须是
X.509
格式
,
所有密钥都必须使用
PEM
编码。对于
RSA
,最多支持
16 KiB (kibibits)
的密钥长度。
QTD
的挂载由
fs-qtd.so
提供
支持,
devb-*
驱动程序会在挂载
QTD
设备时加载它
。以下命令构建
QTD
映像。
-P
选项精确指定
2 MB
的镜像
:
mkqfs qtd -s -vv -B
4096
-H sha256 -S ecdsa-sha256 -K
ec_test_private_key.pem
-P 2097152 -o
qtd.img qnx6.img
以下命令通过全盘验证挂载
QTD
镜像:
mount -t qtd -o key=/proc/boot/ec_test_public_key.pem,stats,verbose,verify /dev/hd1 /dev/qtd-1
3.
Power
-
Safe文件系统
Power-Safe文件系统是一个可靠的磁盘文件系统,可以承受电源故障而不丢失或损坏数据。它
支持
file-based
加密,
密钥类型如表
2
所示,
可以对
Power-Safe
文件系统的全部或部分进行加密,以保护其内容。其中文件和文件夹被聚合到加密域中。每个域都有一个唯一的加密密钥
,称之为域密钥,用于加密唯一的文件密钥。访问文件需要一些外部参与者使用主密钥解锁域密钥,向文件系统驱动程序提供域密钥,允许它解密文件密钥并授予对加密文件内容的访问权限。
表
2
Power-Safe
文件系统密钥种类
密钥类型
|
描述
|
文件密钥
|
私有,在文件创建时随机产生的(如果文件被分配到一个域)。保存关于文件的一些信息,以确保文件和其密钥之间的完整性。用于加密文件数据,并由域密钥加密。密钥由文件系统管理,对用户是隐藏的。
|
域密钥
|
私有,在创建域的时候随机产生。用于加密属于其域的所有文件密钥,并由域的主密钥进行加密。密钥由文件系统管理,对用户是隐藏的。
|
主密钥
|
可以选择公开,因为它是由第三方提供和管理的(不是文件系统)。用于加密域的密钥,在域的创建和随后的解锁请求中需要。
|
具体使用的加密算法如表
3
所示。
表
3
Power
-
Safe
文件系统加密类型
域加密类型
|
常量
|
描述
|
密钥长度
|
0
|
FS_CRYPTO_TYPE_NONE
|
不加密
|
-
|
1
|
FS_CRYPTO_TYPE_XTS
|
ASE256-XTS
,两个密钥是随机生成的
|
512bit
|
2
|
FS_CRYPTO_TYPE_CBC
|
AES256-CBC
|
256bit
|
3-99
|
-
|
预留
|
-
|
Power-Safe
文件系统
中
一个域可以包含任意数量的文件或目录,但一个文件或目录最多只能属于一个域。如果给一个目录指定了一个域,那么随后在该目录中创建的所有文件都会被加密并继承该域,但已经在其中的任何文件或目录不会加密。允许你创建多个加密域,你可以根据需要锁定或解锁这些域。在操作过程中,分配给一个域的文件被加密,只有当相关的域被解锁时,文件的内容才能被使用。当一个域被解锁时,该域下的所有文件和目录
--
无论它们在卷中的位置如何
--
也被解锁,因此可以被访问(根据基本文件权限)。当一个域被锁定时,对属于该域的文件内容的任何访问都被拒绝。锁定和解锁操作适用于整个域,而不是特定的文件或目录。
那么如何生成一个
Power
-
Safe
文件系统呢?以下命令生成一个小于
2MB
的
Power-Safe
文件系统镜像
(QNX)
:
mkxfs -t qnx6fsimg qnx6.build qnx6.img
可以使用以下命令挂载
Power-Safe
文件系统镜像
:
mount -t qnx6 /dev/qtd-1 /fs
需要注意的是
Power-Safe
文件系统
可能
包含在
QTD
分区中,因此
有时候需要
先挂载
QTD
分区
。
小结
安全启动和 QTD 可以保证从 BootRom 到文件系统时软件的完整性,基于 Power-Safe 的文件加密可以保证存储在该文件系统中文件的机密性 。 这些都是 QNX 提供的安全特性,实际应用中可以基于芯片的安全能力,结合 QNX 的安全属性,搭建更加完善的防御体系。