簡介
FIT 格式支持存儲鏡像的hash值,并且在加載鏡像時會校驗hash值。這可以保護(hù)鏡像免受破壞,但是,它并不能保護(hù)鏡像不被替換。
而如果對hash值使用私鑰簽名,在加載鏡像時使用公鑰驗簽則可以保護(hù)鏡像不被替換。因此,公鑰必須保存在一個絕對安全的地方。
接下來的內(nèi)容要求大家了解一些密碼學(xué)的內(nèi)容,之前也介紹過一些,可以看這篇文章
secure boot簽名的大致流程:
- 計算鏡像的hash值
- 利用私鑰對hash值簽名
- 簽名結(jié)果存在FIT Image 中。
secure boot驗簽的大致流程:
- 讀取FIT Image
- 獲得pubkey
- 從FIT Image 提取簽名
- 計算鏡像的hash
- 使用公鑰驗簽獲得hash值,與計算得到的hash值進(jìn)行對比
簽名是由mkimage工具完成的,驗簽由uboot完成。
簽名算法
原則上講,任何合適的算法都可以用來簽名和驗簽。在uboot中,目前只支持一類算法:SHA&RSA。
RSA 算法使用提前準(zhǔn)備好的公鑰就可以完成驗簽,驗簽相關(guān)的代碼量也很少。在驗簽時,RSA只是在FDT中提取必要的數(shù)據(jù)進(jìn)行校驗。
當(dāng)然也可以在uboot中添加合適的算法,如果有其他簽名算法(如DSA),可以直接替換rsa.c
,并在image-sig.c
中添加對應(yīng)算法即可。
創(chuàng)建RSA key和證書
openssl 創(chuàng)建一副2048的密鑰對:
$opensslgenpkey-algorithmRSA-outkeys/dev.key-pkeyoptrsa_keygen_bits:2048-pkeyoptrsa_keygen_pubexp:65537
創(chuàng)建包含pubkey的證書:
$opensslreq-batch-new-x509-keykeys/dev.key-outkeys/dev.crt
查看pubkey的值:
$opensslrsa-inkeys/dev.key-pubout
綁定設(shè)備樹
在FIT Image的簽名節(jié)點中需要添加以下 屬性,簽名節(jié)點與哈希節(jié)點處于同一級別,被稱為signature@1, signature@2
等。
-
algo: 算法名稱
-
key-name-hint:用來簽名的key。密鑰對必須存放在單獨(dú)的文件夾(mkimage 使用-k 參數(shù)指定),私鑰被命名為
,證書命名為.key
。.crt
鏡像被簽名后,以下這些屬性都會被自動強(qiáng)制添加:
- value: 簽名后的值(RSA-2048 占256 bytes)
以下這些屬性是可選的:
-
timestamp:簽名的時間
-
signer-name:簽名者的名字(例如mkimage)
-
signer-version:簽名的版本(例如"2013.01")
-
comment:簽名者或者鏡像的額外信息
-
sign-images:簽名鏡像的列表
-
hashed-nodes:簽名者簽名的節(jié)點列表,一般是包含節(jié)點完整路徑的字符串。例如:
hashed-nodes="/","/configurations/conf@1","/images/kernel@1",
"/images/kernel@1/hash@1","/images/fdt@1",
"/images/fdt@1/hash@1";
以下是一個待簽名鏡像的its配置。
/dts-v1/;
/{
description="ChromeOSkernelimagewithoneormoreFDTblobs";
#address-cells=<1>;
images{
kernel@1{
data=/incbin/("test-kernel.bin");
type="kernel_noload";
arch="sandbox";
os="linux";
compression="none";
load=<0x4>;
entry=<0x8>;
kernel-version=<1>;
signature@1{
algo="sha1,rsa2048";
key-name-hint="dev";
};
};
fdt@1{
description="snow";
data=/incbin/("sandbox-kernel.dtb");
type="flat_dt";
arch="sandbox";
compression="none";
fdt-version=<1>;
signature@1{
algo="sha1,rsa2048";
key-name-hint="dev";
};
};
};
configurations{
default="conf@1";
conf@1{
kernel="kernel@1";
fdt="fdt@1";
};
};
};
以下是配置項簽名后的its文件。
/dts-v1/;
/{
description="ChromeOSkernelimagewithoneormoreFDTblobs";
#address-cells=<1>;
images{
kernel@1{
data=/incbin/("test-kernel.bin");
type="kernel_noload";
arch="sandbox";
os="linux";
compression="lzo";
load=<0x4>;
entry=<0x8>;
kernel-version=<1>;
hash@1{
algo="sha1";
};
};
fdt@1{
description="snow";
data=/incbin/("sandbox-kernel.dtb");
type="flat_dt";
arch="sandbox";
compression="none";
fdt-version=<1>;
hash@1{
algo="sha1";
};
};
};
configurations{
default="conf@1";
conf@1{
kernel="kernel@1";
fdt="fdt@1";
signature@1{
algo="sha1,rsa2048";
key-name-hint="dev";
sign-images="fdt","kernel";
};
};
};
};
pubkey的存儲
為了校驗簽名后的鏡像,必須把pubkey存放在可信賴的位置。將pubkey存在鏡像中是不安全的,很容易被破解。一般我們將其存放在uboot的FDT中(CONFIG_OF_CONTROL)。
pubkey應(yīng)該作為一個子節(jié)點存放在/signature
節(jié)點中。節(jié)點中要加上以下特性:
-
algo:算法名稱
-
key-name-hint: 簽名使用的key的名稱
-
required: 校驗?zāi)撑渲盟褂玫墓€
除此之外,每個算法都有一些必要的特性。RSA算法中,以下特性必須被添加:
-
rsa,num-bits:key的位數(shù)
-
rsa,modulus:N,多字節(jié)的整數(shù)
-
rsa,exponent:E,64位的無符號整數(shù)
-
rsa,r-squared:
(2^num-bits)^2
-
rsa,n0-inverse:
-1 / modulus[0] mod 2^32
下面看一個例子,以下是一個uboot.dtb存放RSA的例子。RSA key被mkimage打包在u-boot.dtb和u-boot-spl.dtb中,然后它們再被打包進(jìn)u-boot.bin和u-boot-spl.bin。
ubuntu:~/uboot-nextdev$fdtdumpu-boot.dtb|less
/dts-v1/;
....
/{
#address-cells=<0x00000001>;
#size-cells=<0x00000001>;
compatible="rockchip,rv1126-evb","rockchip,rv1126";
model="RockchipRV1126EvaluationBoard";
//signature節(jié)點由mkimage工具自動插入生成,節(jié)點里保存了RSA-SHA算法類型、RSA核心因子參
//數(shù)等信息。
signature{
key-dev{
required="conf";
algo="sha256,rsa2048";
rsa,np=<0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x1327f633?0x00000003?0x00000003?0x00000003?0xc7aead6a?0xb4c79f40?0xa82bdf76?0xfb2f8387?0xa1e06dce?0xd451a706?0xc7f865e3?0x3e2d7ca8?0x6a71762e?0x125f1828?0x36ab1a41?0xb7e9e852?0x7bd0011a?0x7279e0b8?0xf37e189c?0x8cf00963?0x00000100?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000377?0x00000004?0x00000004?0x00000004?0x00000002?0x00000003?0x69616c40?0x00000003?0x6d634066?0x00000010?0x66633630?0x73797363>;
rsa,c=<0x00000000>;
rsa,r-squared=<0x00000000>;
rsa,modulus=<0xc25ae693?0xc359f2a4?0xa866c89d?0xb7b1994f?0xf9f9f690?0x518d54a7?0xda0b83e8?0x06606e12?0x6ad1cbf9?0x92438edd?0x81e039c0?0x5d7322cc?0x124cdc80?0xa0c3288a?0x9265c3ae?0x6ac47a4b?0x00000003?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000008?0x00000003?0x00000003?0x00000003?0x00000002?0x73657300?0x2f736572?0x00000000?0x2f64776d?0x00000003?0x6d634066?0x00000001?0x30303000?0x726f636b?0x67726600?0x00000008?0x00000003?0x00000004?0x00000001?0x30303000?0x726f636b?0x706d7567?0x00000003?0x00001000?0x00000003?0x00000002?0x6e616765?0x30000000?0x726f636b?0x706d7500?0x00000008>;
rsa,exponent-BN=<0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000000?0x00000003?0x00010001?0xe95771c5?0x00000800?0x64657600?0x616c6961?0x0000002c?0x30303030?0x00000034?0x30303000?0x2f64776d?0x00000002?0x65303030?0x0000001b?0x3132362d?0x00000003?0x00020000?0x00000003?0x00000002?0x65303230?0x0000001b?0x3132362d?0x6e000000?0xfe020000?0x00000042?0x0000006d?0x722d6d61?0x65303030?0x0000001b?0x3132362d?0x00000003?0x00001000?0x00000002?0x6e74726f?0x30000000?0x726f636b?0x706d7563?0x0000003e?0x00000004?0x00000004?0x00000004?0x00000000?0x00000050?0x636c6f63?0x40666634?0x00000014?0x2c727631?0x00000008>;
rsa,exponent=<0x00000000?0x00000368>;
rsa,n0-inverse=<0xe95771c5>;
rsa,num-bits=<0x00000800>;
key-name-hint="dev";
};
};
簽名方案
上一節(jié)內(nèi)容提到過,在secure boot中一般使用RSA簽名方案。
要完成對鏡像的簽名,就必須使用私鑰。而私鑰一般是存在服務(wù)器上的,在本地PC上只存公鑰。要想完成對鏡像的簽名,就必須把所有鏡像上傳到服務(wù)器重新打包。這種方案上傳的文件太多,比較繁瑣。下面我們介紹一種常用的簽名方案。
在PC上,存放一把公鑰和臨時私鑰,公鑰是打包進(jìn)dtb中的,安全啟動時使用。臨時私鑰是為了生成簽名數(shù)據(jù)。
在本地打包時,使用臨時私鑰對非安全鏡像簽名,將簽名數(shù)據(jù)上傳到服務(wù)器使用真正的私鑰進(jìn)行二次簽名。將二次簽名的數(shù)據(jù)和非安全鏡像打包在一起,就得到了安全鏡像。安全啟動時,從dtb中拿出公鑰對安全鏡像進(jìn)行校驗即可。
這樣既可以保證私鑰的安全,又避免了上傳所有鏡像簽名的繁瑣。
簽名鏡像+簽名配置
在secure boot中,除了對各個獨(dú)立鏡像簽名外,還要對FIT Image中的配置項進(jìn)行簽名。
有些情況下,已經(jīng)簽名的鏡像也有可能遭到破壞。例如,也可以使用相同的簽名鏡像創(chuàng)建一個FIT image,但是,其配置已經(jīng)被改變,從而可以選擇不同的鏡像去加載(混合式匹配攻擊)。也有可能拿舊版本的FIT Image去替換新的FIT image(回滾式攻擊)。
下面舉個例子。
/{
images{
kernel@1{
data=forkernel1>
signature@1{
algo="sha1,rsa2048";
#kernelimage鏡像的哈希值,由mkiamge工具自動生成
value=<...kernel?signature?1...>
};
};
kernel@2{
data=forkernel2>
signature@1{
algo="sha1,rsa2048";
value=<...kernel?signature?2...>
};
};
fdt@1{
data=forfdt1>;
signature@1{
algo="sha1,rsa2048";
vaue=<...fdt?signature?1...>
};
};
fdt@2{
data=forfdt2>;
signature@1{
algo="sha1,rsa2048";
vaue=<...fdt?signature?2...>
};
};
};
configurations{
default="conf@1";
conf@1{
kernel="kernel@1";
fdt="fdt@1";
};
conf@1{
kernel="kernel@2";
fdt="fdt@2";
};
};
};
兩個kernel image 都已經(jīng)被簽名了,但是,攻擊者可以很容易的將kernel1 和fdt2 作為configuration 3去加載。
configurations{
default="conf@1";
conf@1{
kernel="kernel@1";
fdt="fdt@1";
};
conf@1{
kernel="kernel@2";
fdt="fdt@2";
};
conf@3{
kernel="kernel@1";
fdt="fdt@2";
};
};
攻擊者可以拿到簽名的鏡像,并且鏡像是正確的。這種組合式攻擊會給設(shè)備帶來很大風(fēng)險。
因此,為了解決這個問題,除了給鏡像簽名外,我們可以把配置選項也簽名,每個鏡像都有自己的簽名,在給配置選項簽名時,把鏡像的hash值也包含進(jìn)去。具體例子如下:
/{
images{
kernel@1{
data=forkernel1>
hash@1{
algo="sha1";
value=<...kernel?hash1...>
};
};
kernel@2{
data=forkernel2>
hash@1{
algo="sha1";
value=<...kernel?hash2...>
};
};
fdt@1{
data=forfdt1>;
hash@1{
algo="sha1";
value=<...fdt?hash1...>
};
};
fdt@2{
data=forfdt2>;
hash@1{
algo="sha1";
value=<...fdt?hash2...>
};
};
};
configurations{
default="conf@1";
conf@1{
kernel="kernel@1";
fdt="fdt@1";
signature@1{
algo="sha1,rsa2048";
#對配置項簽名,由mkimage工具自動生成
value=<...conf?1?signature...>;
};
};
conf@2{
kernel="kernel@2";
fdt="fdt@2";
signature@1{
algo="sha1,rsa2048";
value=<...conf?1?signature...>;
};
};
};
};
如上所示,除了給所有鏡像添加了hash值,還為每個配置添加了簽名。mkimage將會對configurations/conf@1
簽名(/images/kernel@1, /images/kernel@1/hash@1,/images/fdt@1, /images/fdt@1/hash@1)
。簽名會被寫入 /configurations/conf@1/signature@1/value
。
驗簽
FIT image 在加載時會驗簽。如果'required' 指定了驗簽的公鑰,則會使用這把公鑰校驗該配置對應(yīng)的所有鏡像。
為了支持FIT格式,以下配置項必須被選上。
CONFIG_FIT_SIGNATURE :使能FIT image的簽名和驗簽
CONFIG_RSA :使能RSA簽名算法
默認(rèn)情況下,使能FIT Image的簽名和驗簽后,CONFIG_IMAGE_FORMAT_LEGACY會被禁用。即FIT uboot image的只能引導(dǎo)FIT kernel Image。
如果需要引導(dǎo)legacy kernel image,需要手動添加CONFIG_IMAGE_FORMAT_LEGACY 定義。
測試
為了校驗簽名和驗簽是否正確,可以使用測試腳本test/vboot/vboot_test.sh
。下面以sandbox為例子來說明bootm的啟動和對鏡像的驗簽。
$makeO=sandboxsandbox_config
$makeO=sandbox
$O=sandbox./test/vboot/vboot_test.sh
/home/hs/ids/u-boot/sandbox/tools/mkimage-D-Idts-Odtb-p2000
Buildkeys
dosha1test
BuildFITwithsignedimages
TestVerifiedBootRun:unsignedsignatures::OK
Signimages
TestVerifiedBootRun:signedimages:OK
BuildFITwithsignedconfiguration
TestVerifiedBootRun:unsignedconfig:OK
Signimages
TestVerifiedBootRun:signedconfig:OK
checksignedconfigonthehost
SignaturecheckOK
OK
TestVerifiedBootRun:signedconfig:OK
TestVerifiedBootRun:signedconfigwithbadhash:OK
dosha256test
BuildFITwithsignedimages
TestVerifiedBootRun:unsignedsignatures::OK
Signimages
TestVerifiedBootRun:signedimages:OK
BuildFITwithsignedconfiguration
TestVerifiedBootRun:unsignedconfig:OK
Signimages
TestVerifiedBootRun:signedconfig:OK
checksignedconfigonthehost
SignaturecheckOK
OK
TestVerifiedBootRun:signedconfig:OK
TestVerifiedBootRun:signedconfigwithbadhash:OK
Testpassed
完整校驗流程
OTP校驗loader
那么,這種鏡像校驗方式有個很重要的問題,公鑰存在哪里才是安全的呢?
一般SOC中會有一個叫OTP或EFUSE的區(qū)域,這部分區(qū)域比較特殊,只可以寫入一次,寫入后就再也不可以修改了。把公鑰存儲在OTP中,就可以很好地保證其不能被修改。
OTP的存儲空間很小,一般只有幾KB,因此并不適合直接存放RSA公鑰。一般都是將RSA公鑰的hash val 存放在OTP中。像sha256的hash值僅為256 bits,而RSA 公鑰本身一般存放在鏡像中。
在使用公鑰之前,只需要使用OTP中的公鑰hash值驗證鏡像附帶公鑰的完整性,即可確定公鑰是否合法。
RSA公鑰需要一般使用芯片廠家的工具寫入loader。安全啟動時,bootrom首先從loader固件頭中獲取RSA公鑰并校驗合法性;然后再使用該公鑰校驗SPL的固件簽名。
spl校驗uboot
SPL把RSA公鑰保存在u-boot-spl.dtb中,u-boot-spl.dtb會被打包進(jìn)u-boot-spl.bin文件(最后打包進(jìn)loader);安全啟動時SPL從自己的dtb文件中拿出RSA公鑰對uboot.img進(jìn)行安全校驗。
uboot校驗kernel
U-Boot把RSA公鑰保存在u-boot.dtb中,u-boot.dtb會被打包進(jìn)u-boot.bin文件(最后打包為uboot.img);安全啟動時U-Boot從自己的dtb文件中拿RSA公鑰對boot.img進(jìn)行校驗。
總結(jié)
從bootrom到kernel為止的安全啟動,統(tǒng)一使用一把RSA公鑰完成安全校驗,并且當(dāng)前這級的RSA Key已經(jīng)作為自身固件的一部分,由前一級loader完成了安全校驗,從而保證了Key的安全。
審核編輯 :李倩en
-
代碼
+關(guān)注
關(guān)注
30文章
4779瀏覽量
68521 -
Hash
+關(guān)注
關(guān)注
0文章
32瀏覽量
13195 -
Uboot
+關(guān)注
關(guān)注
4文章
125瀏覽量
28214
原文標(biāo)題:secure boot(三)secure boot的簽名和驗簽方案
文章出處:【微信號:嵌入式與Linux那些事,微信公眾號:嵌入式與Linux那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論