1*4882a593SmuzhiyunVerified Boot on the Beaglebone Black 2*4882a593Smuzhiyun===================================== 3*4882a593Smuzhiyun 4*4882a593SmuzhiyunIntroduction 5*4882a593Smuzhiyun------------ 6*4882a593Smuzhiyun 7*4882a593SmuzhiyunBefore reading this, please read verified-boot.txt and signature.txt. These 8*4882a593Smuzhiyuninstructions are for mainline U-Boot from v2014.07 onwards. 9*4882a593Smuzhiyun 10*4882a593SmuzhiyunThere is quite a bit of documentation in this directory describing how 11*4882a593Smuzhiyunverified boot works in U-Boot. There is also a test which runs through the 12*4882a593Smuzhiyunentire process of signing an image and running U-Boot (sandbox) to check it. 13*4882a593SmuzhiyunHowever, it might be useful to also have an example on a real board. 14*4882a593Smuzhiyun 15*4882a593SmuzhiyunBeaglebone Black is a fairly common board so seems to be a reasonable choice 16*4882a593Smuzhiyunfor an example of how to enable verified boot using U-Boot. 17*4882a593Smuzhiyun 18*4882a593SmuzhiyunFirst a note that may to help avoid confusion. U-Boot and Linux both use 19*4882a593Smuzhiyundevice tree. They may use the same device tree source, but it is seldom useful 20*4882a593Smuzhiyunfor them to use the exact same binary from the same place. More typically, 21*4882a593SmuzhiyunU-Boot has its device tree packaged wtih it, and the kernel's device tree is 22*4882a593Smuzhiyunpackaged with the kernel. In particular this is important with verified boot, 23*4882a593Smuzhiyunsince U-Boot's device tree must be immutable. If it can be changed then the 24*4882a593Smuzhiyunpublic keys can be changed and verified boot is useless. An attacker can 25*4882a593Smuzhiyunsimply generate a new key and put his public key into U-Boot so that 26*4882a593Smuzhiyuneverything verifies. On the other hand the kernel's device tree typically 27*4882a593Smuzhiyunchanges when the kernel changes, so it is useful to package an updated device 28*4882a593Smuzhiyuntree with the kernel binary. U-Boot supports the latter with its flexible FIT 29*4882a593Smuzhiyunformat (Flat Image Tree). 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun 32*4882a593SmuzhiyunOverview 33*4882a593Smuzhiyun-------- 34*4882a593Smuzhiyun 35*4882a593SmuzhiyunThe steps are roughly as follows: 36*4882a593Smuzhiyun 37*4882a593Smuzhiyun1. Build U-Boot for the board, with the verified boot options enabled. 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun2. Obtain a suitable Linux kernel 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun3. Create a Image Tree Source file (ITS) file describing how you want the 42*4882a593Smuzhiyunkernel to be packaged, compressed and signed. 43*4882a593Smuzhiyun 44*4882a593Smuzhiyun4. Create a key pair 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun5. Sign the kernel 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun6. Put the public key into U-Boot's image 49*4882a593Smuzhiyun 50*4882a593Smuzhiyun7. Put U-Boot and the kernel onto the board 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun8. Try it 53*4882a593Smuzhiyun 54*4882a593Smuzhiyun 55*4882a593SmuzhiyunStep 1: Build U-Boot 56*4882a593Smuzhiyun-------------------- 57*4882a593Smuzhiyun 58*4882a593Smuzhiyuna. Set up the environment variable to point to your toolchain. You will need 59*4882a593Smuzhiyunthis for U-Boot and also for the kernel if you build it. For example if you 60*4882a593Smuzhiyuninstalled a Linaro version manually it might be something like: 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun export CROSS_COMPILE=/opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux/bin/arm-linux-gnueabihf- 63*4882a593Smuzhiyun 64*4882a593Smuzhiyunor if you just installed gcc-arm-linux-gnueabi then it might be 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun export CROSS_COMPILE=arm-linux-gnueabi- 67*4882a593Smuzhiyun 68*4882a593Smuzhiyunb. Configure and build U-Boot with verified boot enabled: 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun export ARCH=arm 71*4882a593Smuzhiyun export UBOOT=/path/to/u-boot 72*4882a593Smuzhiyun cd $UBOOT 73*4882a593Smuzhiyun # You can add -j10 if you have 10 CPUs to make it faster 74*4882a593Smuzhiyun make O=b/am335x_boneblack_vboot am335x_boneblack_vboot_config all 75*4882a593Smuzhiyun export UOUT=$UBOOT/b/am335x_boneblack_vboot 76*4882a593Smuzhiyun 77*4882a593Smuzhiyunc. You will now have a U-Boot image: 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun file b/am335x_boneblack_vboot/u-boot-dtb.img 80*4882a593Smuzhiyunb/am335x_boneblack_vboot/u-boot-dtb.img: u-boot legacy uImage, U-Boot 2014.07-rc2-00065-g2f69f8, Firmware/ARM, Firmware Image (Not compressed), 395375 bytes, Sat May 31 16:19:04 2014, Load Address: 0x80800000, Entry Point: 0x00000000, Header CRC: 0x0ABD6ACA, Data CRC: 0x36DEF7E4 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun 83*4882a593SmuzhiyunStep 2: Build Linux 84*4882a593Smuzhiyun-------------------- 85*4882a593Smuzhiyun 86*4882a593Smuzhiyuna. Find the kernel image ('Image') and device tree (.dtb) file you plan to 87*4882a593Smuzhiyunuse. In our case it is am335x-boneblack.dtb and it is built with the kernel. 88*4882a593SmuzhiyunAt the time of writing an SD Boot image can be obtained from here: 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun http://www.elinux.org/Beagleboard:Updating_The_Software#Image_For_Booting_From_microSD 91*4882a593Smuzhiyun 92*4882a593SmuzhiyunYou can write this to an SD card and then mount it to extract the kernel and 93*4882a593Smuzhiyundevice tree files. 94*4882a593Smuzhiyun 95*4882a593SmuzhiyunYou can also build a kernel. Instructions for this are are here: 96*4882a593Smuzhiyun 97*4882a593Smuzhiyun http://elinux.org/Building_BBB_Kernel 98*4882a593Smuzhiyun 99*4882a593Smuzhiyunor you can use your favourite search engine. Following these instructions 100*4882a593Smuzhiyunproduces a kernel Image and device tree files. For the record the steps were: 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun export KERNEL=/path/to/kernel 103*4882a593Smuzhiyun cd $KERNEL 104*4882a593Smuzhiyun git clone git://github.com/beagleboard/kernel.git . 105*4882a593Smuzhiyun git checkout v3.14 106*4882a593Smuzhiyun ./patch.sh 107*4882a593Smuzhiyun cp configs/beaglebone kernel/arch/arm/configs/beaglebone_defconfig 108*4882a593Smuzhiyun cd kernel 109*4882a593Smuzhiyun make beaglebone_defconfig 110*4882a593Smuzhiyun make uImage dtbs # -j10 if you have 10 CPUs 111*4882a593Smuzhiyun export OKERNEL=$KERNEL/kernel/arch/arm/boot 112*4882a593Smuzhiyun 113*4882a593Smuzhiyunc. You now have the 'Image' and 'am335x-boneblack.dtb' files needed to boot. 114*4882a593Smuzhiyun 115*4882a593Smuzhiyun 116*4882a593SmuzhiyunStep 3: Create the ITS 117*4882a593Smuzhiyun---------------------- 118*4882a593Smuzhiyun 119*4882a593SmuzhiyunSet up a directory for your work. 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun export WORK=/path/to/dir 122*4882a593Smuzhiyun cd $WORK 123*4882a593Smuzhiyun 124*4882a593SmuzhiyunPut this into a file in that directory called sign.its: 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun/dts-v1/; 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun/ { 129*4882a593Smuzhiyun description = "Beaglebone black"; 130*4882a593Smuzhiyun #address-cells = <1>; 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun images { 133*4882a593Smuzhiyun kernel@1 { 134*4882a593Smuzhiyun data = /incbin/("Image.lzo"); 135*4882a593Smuzhiyun type = "kernel"; 136*4882a593Smuzhiyun arch = "arm"; 137*4882a593Smuzhiyun os = "linux"; 138*4882a593Smuzhiyun compression = "lzo"; 139*4882a593Smuzhiyun load = <0x80008000>; 140*4882a593Smuzhiyun entry = <0x80008000>; 141*4882a593Smuzhiyun hash@1 { 142*4882a593Smuzhiyun algo = "sha1"; 143*4882a593Smuzhiyun }; 144*4882a593Smuzhiyun }; 145*4882a593Smuzhiyun fdt@1 { 146*4882a593Smuzhiyun description = "beaglebone-black"; 147*4882a593Smuzhiyun data = /incbin/("am335x-boneblack.dtb"); 148*4882a593Smuzhiyun type = "flat_dt"; 149*4882a593Smuzhiyun arch = "arm"; 150*4882a593Smuzhiyun compression = "none"; 151*4882a593Smuzhiyun hash@1 { 152*4882a593Smuzhiyun algo = "sha1"; 153*4882a593Smuzhiyun }; 154*4882a593Smuzhiyun }; 155*4882a593Smuzhiyun }; 156*4882a593Smuzhiyun configurations { 157*4882a593Smuzhiyun default = "conf@1"; 158*4882a593Smuzhiyun conf@1 { 159*4882a593Smuzhiyun kernel = "kernel@1"; 160*4882a593Smuzhiyun fdt = "fdt@1"; 161*4882a593Smuzhiyun signature@1 { 162*4882a593Smuzhiyun algo = "sha1,rsa2048"; 163*4882a593Smuzhiyun key-name-hint = "dev"; 164*4882a593Smuzhiyun sign-images = "fdt", "kernel"; 165*4882a593Smuzhiyun }; 166*4882a593Smuzhiyun }; 167*4882a593Smuzhiyun }; 168*4882a593Smuzhiyun}; 169*4882a593Smuzhiyun 170*4882a593Smuzhiyun 171*4882a593SmuzhiyunThe explanation for this is all in the documentation you have already read. 172*4882a593SmuzhiyunBut briefly it packages a kernel and device tree, and provides a single 173*4882a593Smuzhiyunconfiguration to be signed with a key named 'dev'. The kernel is compressed 174*4882a593Smuzhiyunwith LZO to make it smaller. 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun 177*4882a593SmuzhiyunStep 4: Create a key pair 178*4882a593Smuzhiyun------------------------- 179*4882a593Smuzhiyun 180*4882a593SmuzhiyunSee signature.txt for details on this step. 181*4882a593Smuzhiyun 182*4882a593Smuzhiyun cd $WORK 183*4882a593Smuzhiyun mkdir keys 184*4882a593Smuzhiyun openssl genrsa -F4 -out keys/dev.key 2048 185*4882a593Smuzhiyun openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt 186*4882a593Smuzhiyun 187*4882a593SmuzhiyunNote: keys/dev.key contains your private key and is very secret. If anyone 188*4882a593Smuzhiyungets access to that file they can sign kernels with it. Keep it secure. 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun 191*4882a593SmuzhiyunStep 5: Sign the kernel 192*4882a593Smuzhiyun----------------------- 193*4882a593Smuzhiyun 194*4882a593SmuzhiyunWe need to use mkimage (which was built when you built U-Boot) to package the 195*4882a593SmuzhiyunLinux kernel into a FIT (Flat Image Tree, a flexible file format that U-Boot 196*4882a593Smuzhiyuncan load) using the ITS file you just created. 197*4882a593Smuzhiyun 198*4882a593SmuzhiyunAt the same time we must put the public key into U-Boot device tree, with the 199*4882a593Smuzhiyun'required' property, which tells U-Boot that this key must be verified for the 200*4882a593Smuzhiyunimage to be valid. You will make this key available to U-Boot for booting in 201*4882a593Smuzhiyunstep 6. 202*4882a593Smuzhiyun 203*4882a593Smuzhiyun ln -s $OKERNEL/dts/am335x-boneblack.dtb 204*4882a593Smuzhiyun ln -s $OKERNEL/Image 205*4882a593Smuzhiyun ln -s $UOUT/u-boot-dtb.img 206*4882a593Smuzhiyun cp $UOUT/arch/arm/dts/am335x-boneblack.dtb am335x-boneblack-pubkey.dtb 207*4882a593Smuzhiyun lzop Image 208*4882a593Smuzhiyun $UOUT/tools/mkimage -f sign.its -K am335x-boneblack-pubkey.dtb -k keys -r image.fit 209*4882a593Smuzhiyun 210*4882a593SmuzhiyunYou should see something like this: 211*4882a593Smuzhiyun 212*4882a593SmuzhiyunFIT description: Beaglebone black 213*4882a593SmuzhiyunCreated: Sun Jun 1 12:50:30 2014 214*4882a593Smuzhiyun Image 0 (kernel@1) 215*4882a593Smuzhiyun Description: unavailable 216*4882a593Smuzhiyun Created: Sun Jun 1 12:50:30 2014 217*4882a593Smuzhiyun Type: Kernel Image 218*4882a593Smuzhiyun Compression: lzo compressed 219*4882a593Smuzhiyun Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB 220*4882a593Smuzhiyun Architecture: ARM 221*4882a593Smuzhiyun OS: Linux 222*4882a593Smuzhiyun Load Address: 0x80008000 223*4882a593Smuzhiyun Entry Point: 0x80008000 224*4882a593Smuzhiyun Hash algo: sha1 225*4882a593Smuzhiyun Hash value: c94364646427e10f423837e559898ef02c97b988 226*4882a593Smuzhiyun Image 1 (fdt@1) 227*4882a593Smuzhiyun Description: beaglebone-black 228*4882a593Smuzhiyun Created: Sun Jun 1 12:50:30 2014 229*4882a593Smuzhiyun Type: Flat Device Tree 230*4882a593Smuzhiyun Compression: uncompressed 231*4882a593Smuzhiyun Data Size: 31547 Bytes = 30.81 kB = 0.03 MB 232*4882a593Smuzhiyun Architecture: ARM 233*4882a593Smuzhiyun Hash algo: sha1 234*4882a593Smuzhiyun Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d 235*4882a593Smuzhiyun Default Configuration: 'conf@1' 236*4882a593Smuzhiyun Configuration 0 (conf@1) 237*4882a593Smuzhiyun Description: unavailable 238*4882a593Smuzhiyun Kernel: kernel@1 239*4882a593Smuzhiyun FDT: fdt@1 240*4882a593Smuzhiyun 241*4882a593Smuzhiyun 242*4882a593SmuzhiyunNow am335x-boneblack-pubkey.dtb contains the public key and image.fit contains 243*4882a593Smuzhiyunthe signed kernel. Jump to step 6 if you like, or continue reading to increase 244*4882a593Smuzhiyunyour understanding. 245*4882a593Smuzhiyun 246*4882a593SmuzhiyunYou can also run fit_check_sign to check it: 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb 249*4882a593Smuzhiyun 250*4882a593Smuzhiyunwhich results in: 251*4882a593Smuzhiyun 252*4882a593SmuzhiyunVerifying Hash Integrity ... sha1,rsa2048:dev+ 253*4882a593Smuzhiyun## Loading kernel from FIT Image at 7fc6ee469000 ... 254*4882a593Smuzhiyun Using 'conf@1' configuration 255*4882a593Smuzhiyun Verifying Hash Integrity ... 256*4882a593Smuzhiyunsha1,rsa2048:dev+ 257*4882a593SmuzhiyunOK 258*4882a593Smuzhiyun 259*4882a593Smuzhiyun Trying 'kernel@1' kernel subimage 260*4882a593Smuzhiyun Description: unavailable 261*4882a593Smuzhiyun Created: Sun Jun 1 12:50:30 2014 262*4882a593Smuzhiyun Type: Kernel Image 263*4882a593Smuzhiyun Compression: lzo compressed 264*4882a593Smuzhiyun Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB 265*4882a593Smuzhiyun Architecture: ARM 266*4882a593Smuzhiyun OS: Linux 267*4882a593Smuzhiyun Load Address: 0x80008000 268*4882a593Smuzhiyun Entry Point: 0x80008000 269*4882a593Smuzhiyun Hash algo: sha1 270*4882a593Smuzhiyun Hash value: c94364646427e10f423837e559898ef02c97b988 271*4882a593Smuzhiyun Verifying Hash Integrity ... 272*4882a593Smuzhiyunsha1+ 273*4882a593SmuzhiyunOK 274*4882a593Smuzhiyun 275*4882a593SmuzhiyunUnimplemented compression type 4 276*4882a593Smuzhiyun## Loading fdt from FIT Image at 7fc6ee469000 ... 277*4882a593Smuzhiyun Using 'conf@1' configuration 278*4882a593Smuzhiyun Trying 'fdt@1' fdt subimage 279*4882a593Smuzhiyun Description: beaglebone-black 280*4882a593Smuzhiyun Created: Sun Jun 1 12:50:30 2014 281*4882a593Smuzhiyun Type: Flat Device Tree 282*4882a593Smuzhiyun Compression: uncompressed 283*4882a593Smuzhiyun Data Size: 31547 Bytes = 30.81 kB = 0.03 MB 284*4882a593Smuzhiyun Architecture: ARM 285*4882a593Smuzhiyun Hash algo: sha1 286*4882a593Smuzhiyun Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d 287*4882a593Smuzhiyun Verifying Hash Integrity ... 288*4882a593Smuzhiyunsha1+ 289*4882a593SmuzhiyunOK 290*4882a593Smuzhiyun 291*4882a593Smuzhiyun Loading Flat Device Tree ... OK 292*4882a593Smuzhiyun 293*4882a593Smuzhiyun## Loading ramdisk from FIT Image at 7fc6ee469000 ... 294*4882a593Smuzhiyun Using 'conf@1' configuration 295*4882a593SmuzhiyunCould not find subimage node 296*4882a593Smuzhiyun 297*4882a593SmuzhiyunSignature check OK 298*4882a593Smuzhiyun 299*4882a593Smuzhiyun 300*4882a593SmuzhiyunAt the top, you see "sha1,rsa2048:dev+". This means that it checked an RSA key 301*4882a593Smuzhiyunof size 2048 bits using SHA1 as the hash algorithm. The key name checked was 302*4882a593Smuzhiyun'dev' and the '+' means that it verified. If it showed '-' that would be bad. 303*4882a593Smuzhiyun 304*4882a593SmuzhiyunOnce the configuration is verified it is then possible to rely on the hashes 305*4882a593Smuzhiyunin each image referenced by that configuration. So fit_check_sign goes on to 306*4882a593Smuzhiyunload each of the images. We have a kernel and an FDT but no ramkdisk. In each 307*4882a593Smuzhiyuncase fit_check_sign checks the hash and prints sha1+ meaning that the SHA1 308*4882a593Smuzhiyunhash verified. This means that none of the images has been tampered with. 309*4882a593Smuzhiyun 310*4882a593SmuzhiyunThere is a test in test/vboot which uses U-Boot's sandbox build to verify that 311*4882a593Smuzhiyunthe above flow works. 312*4882a593Smuzhiyun 313*4882a593SmuzhiyunBut it is fun to do this by hand, so you can load image.fit into a hex editor 314*4882a593Smuzhiyunlike ghex, and change a byte in the kernel: 315*4882a593Smuzhiyun 316*4882a593Smuzhiyun $UOUT/tools/fit_info -f image.fit -n /images/kernel@1 -p data 317*4882a593SmuzhiyunNAME: kernel@1 318*4882a593SmuzhiyunLEN: 7790938 319*4882a593SmuzhiyunOFF: 168 320*4882a593Smuzhiyun 321*4882a593SmuzhiyunThis tells us that the kernel starts at byte offset 168 (decimal) in image.fit 322*4882a593Smuzhiyunand extends for about 7MB. Try changing a byte at 0x2000 (say) and run 323*4882a593Smuzhiyunfit_check_sign again. You should see something like: 324*4882a593Smuzhiyun 325*4882a593SmuzhiyunVerifying Hash Integrity ... sha1,rsa2048:dev+ 326*4882a593Smuzhiyun## Loading kernel from FIT Image at 7f5a39571000 ... 327*4882a593Smuzhiyun Using 'conf@1' configuration 328*4882a593Smuzhiyun Verifying Hash Integrity ... 329*4882a593Smuzhiyunsha1,rsa2048:dev+ 330*4882a593SmuzhiyunOK 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun Trying 'kernel@1' kernel subimage 333*4882a593Smuzhiyun Description: unavailable 334*4882a593Smuzhiyun Created: Sun Jun 1 13:09:21 2014 335*4882a593Smuzhiyun Type: Kernel Image 336*4882a593Smuzhiyun Compression: lzo compressed 337*4882a593Smuzhiyun Data Size: 7790938 Bytes = 7608.34 kB = 7.43 MB 338*4882a593Smuzhiyun Architecture: ARM 339*4882a593Smuzhiyun OS: Linux 340*4882a593Smuzhiyun Load Address: 0x80008000 341*4882a593Smuzhiyun Entry Point: 0x80008000 342*4882a593Smuzhiyun Hash algo: sha1 343*4882a593Smuzhiyun Hash value: c94364646427e10f423837e559898ef02c97b988 344*4882a593Smuzhiyun Verifying Hash Integrity ... 345*4882a593Smuzhiyunsha1 error 346*4882a593SmuzhiyunBad hash value for 'hash@1' hash node in 'kernel@1' image node 347*4882a593SmuzhiyunBad Data Hash 348*4882a593Smuzhiyun 349*4882a593Smuzhiyun## Loading fdt from FIT Image at 7f5a39571000 ... 350*4882a593Smuzhiyun Using 'conf@1' configuration 351*4882a593Smuzhiyun Trying 'fdt@1' fdt subimage 352*4882a593Smuzhiyun Description: beaglebone-black 353*4882a593Smuzhiyun Created: Sun Jun 1 13:09:21 2014 354*4882a593Smuzhiyun Type: Flat Device Tree 355*4882a593Smuzhiyun Compression: uncompressed 356*4882a593Smuzhiyun Data Size: 31547 Bytes = 30.81 kB = 0.03 MB 357*4882a593Smuzhiyun Architecture: ARM 358*4882a593Smuzhiyun Hash algo: sha1 359*4882a593Smuzhiyun Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d 360*4882a593Smuzhiyun Verifying Hash Integrity ... 361*4882a593Smuzhiyunsha1+ 362*4882a593SmuzhiyunOK 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun Loading Flat Device Tree ... OK 365*4882a593Smuzhiyun 366*4882a593Smuzhiyun## Loading ramdisk from FIT Image at 7f5a39571000 ... 367*4882a593Smuzhiyun Using 'conf@1' configuration 368*4882a593SmuzhiyunCould not find subimage node 369*4882a593Smuzhiyun 370*4882a593SmuzhiyunSignature check Bad (error 1) 371*4882a593Smuzhiyun 372*4882a593Smuzhiyun 373*4882a593SmuzhiyunIt has detected the change in the kernel. 374*4882a593Smuzhiyun 375*4882a593SmuzhiyunYou can also be sneaky and try to switch images, using the libfdt utilities 376*4882a593Smuzhiyunthat come with dtc (package name is device-tree-compiler but you will need a 377*4882a593Smuzhiyunrecent version like 1.4: 378*4882a593Smuzhiyun 379*4882a593Smuzhiyun dtc -v 380*4882a593SmuzhiyunVersion: DTC 1.4.0 381*4882a593Smuzhiyun 382*4882a593SmuzhiyunFirst we can check which nodes are actually hashed by the configuration: 383*4882a593Smuzhiyun 384*4882a593Smuzhiyun fdtget -l image.fit / 385*4882a593Smuzhiyunimages 386*4882a593Smuzhiyunconfigurations 387*4882a593Smuzhiyun 388*4882a593Smuzhiyun fdtget -l image.fit /configurations 389*4882a593Smuzhiyunconf@1 390*4882a593Smuzhiyunfdtget -l image.fit /configurations/conf@1 391*4882a593Smuzhiyunsignature@1 392*4882a593Smuzhiyun 393*4882a593Smuzhiyun fdtget -p image.fit /configurations/conf@1/signature@1 394*4882a593Smuzhiyunhashed-strings 395*4882a593Smuzhiyunhashed-nodes 396*4882a593Smuzhiyuntimestamp 397*4882a593Smuzhiyunsigner-version 398*4882a593Smuzhiyunsigner-name 399*4882a593Smuzhiyunvalue 400*4882a593Smuzhiyunalgo 401*4882a593Smuzhiyunkey-name-hint 402*4882a593Smuzhiyunsign-images 403*4882a593Smuzhiyun 404*4882a593Smuzhiyun fdtget image.fit /configurations/conf@1/signature@1 hashed-nodes 405*4882a593Smuzhiyun/ /configurations/conf@1 /images/fdt@1 /images/fdt@1/hash@1 /images/kernel@1 /images/kernel@1/hash@1 406*4882a593Smuzhiyun 407*4882a593SmuzhiyunThis gives us a bit of a look into the signature that mkimage added. Note you 408*4882a593Smuzhiyuncan also use fdtdump to list the entire device tree. 409*4882a593Smuzhiyun 410*4882a593SmuzhiyunSay we want to change the kernel that this configuration uses 411*4882a593Smuzhiyun(/images/kernel@1). We could just put a new kernel in the image, but we will 412*4882a593Smuzhiyunneed to change the hash to match. Let's simulate that by changing a byte of 413*4882a593Smuzhiyunthe hash: 414*4882a593Smuzhiyun 415*4882a593Smuzhiyun fdtget -tx image.fit /images/kernel@1/hash@1 value 416*4882a593Smuzhiyunc9436464 6427e10f 423837e5 59898ef0 2c97b988 417*4882a593Smuzhiyun fdtput -tx image.fit /images/kernel@1/hash@1 value c9436464 6427e10f 423837e5 59898ef0 2c97b981 418*4882a593Smuzhiyun 419*4882a593SmuzhiyunNow check it again: 420*4882a593Smuzhiyun 421*4882a593Smuzhiyun $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb 422*4882a593SmuzhiyunVerifying Hash Integrity ... sha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13 423*4882a593Smuzhiyunrsa_verify_with_keynode: RSA failed to verify: -13 424*4882a593Smuzhiyun- 425*4882a593SmuzhiyunFailed to verify required signature 'key-dev' 426*4882a593SmuzhiyunSignature check Bad (error 1) 427*4882a593Smuzhiyun 428*4882a593SmuzhiyunThis time we don't even get as far as checking the images, since the 429*4882a593Smuzhiyunconfiguration signature doesn't match. We can't change any hashes without the 430*4882a593Smuzhiyunsignature check noticing. The configuration is essentially locked. U-Boot has 431*4882a593Smuzhiyuna public key for which it requires a match, and will not permit the use of any 432*4882a593Smuzhiyunconfiguration that does not match that public key. The only way the 433*4882a593Smuzhiyunconfiguration will match is if it was signed by the matching private key. 434*4882a593Smuzhiyun 435*4882a593SmuzhiyunIt would also be possible to add a new signature node that does match your new 436*4882a593Smuzhiyunconfiguration. But that won't work since you are not allowed to change the 437*4882a593Smuzhiyunconfiguration in any way. Try it with a fresh (valid) image if you like by 438*4882a593Smuzhiyunrunning the mkimage link again. Then: 439*4882a593Smuzhiyun 440*4882a593Smuzhiyun fdtput -p image.fit /configurations/conf@1/signature@2 value fred 441*4882a593Smuzhiyun $UOUT/tools/fit_check_sign -f image.fit -k am335x-boneblack-pubkey.dtb 442*4882a593SmuzhiyunVerifying Hash Integrity ... - 443*4882a593Smuzhiyunsha1,rsa2048:devrsa_verify_with_keynode: RSA failed to verify: -13 444*4882a593Smuzhiyunrsa_verify_with_keynode: RSA failed to verify: -13 445*4882a593Smuzhiyun- 446*4882a593SmuzhiyunFailed to verify required signature 'key-dev' 447*4882a593SmuzhiyunSignature check Bad (error 1) 448*4882a593Smuzhiyun 449*4882a593Smuzhiyun 450*4882a593SmuzhiyunOf course it would be possible to add an entirely new configuration and boot 451*4882a593Smuzhiyunwith that, but it still needs to be signed, so it won't help. 452*4882a593Smuzhiyun 453*4882a593Smuzhiyun 454*4882a593Smuzhiyun6. Put the public key into U-Boot's image 455*4882a593Smuzhiyun----------------------------------------- 456*4882a593Smuzhiyun 457*4882a593SmuzhiyunHaving confirmed that the signature is doing its job, let's try it out in 458*4882a593SmuzhiyunU-Boot on the board. U-Boot needs access to the public key corresponding to 459*4882a593Smuzhiyunthe private key that you signed with so that it can verify any kernels that 460*4882a593Smuzhiyunyou sign. 461*4882a593Smuzhiyun 462*4882a593Smuzhiyun cd $UBOOT 463*4882a593Smuzhiyun make O=b/am335x_boneblack_vboot EXT_DTB=${WORK}/am335x-boneblack-pubkey.dtb 464*4882a593Smuzhiyun 465*4882a593SmuzhiyunHere we are overrriding the normal device tree file with our one, which 466*4882a593Smuzhiyuncontains the public key. 467*4882a593Smuzhiyun 468*4882a593SmuzhiyunNow you have a special U-Boot image with the public key. It can verify can 469*4882a593Smuzhiyunkernel that you sign with the private key as in step 5. 470*4882a593Smuzhiyun 471*4882a593SmuzhiyunIf you like you can take a look at the public key information that mkimage 472*4882a593Smuzhiyunadded to U-Boot's device tree: 473*4882a593Smuzhiyun 474*4882a593Smuzhiyun fdtget -p am335x-boneblack-pubkey.dtb /signature/key-dev 475*4882a593Smuzhiyunrequired 476*4882a593Smuzhiyunalgo 477*4882a593Smuzhiyunrsa,r-squared 478*4882a593Smuzhiyunrsa,modulus 479*4882a593Smuzhiyunrsa,n0-inverse 480*4882a593Smuzhiyunrsa,num-bits 481*4882a593Smuzhiyunkey-name-hint 482*4882a593Smuzhiyun 483*4882a593SmuzhiyunThis has information about the key and some pre-processed values which U-Boot 484*4882a593Smuzhiyuncan use to verify against it. These values are obtained from the public key 485*4882a593Smuzhiyuncertificate by mkimage, but require quite a bit of code to generate. To save 486*4882a593Smuzhiyuncode space in U-Boot, the information is extracted and written in raw form for 487*4882a593SmuzhiyunU-Boot to easily use. The same mechanism is used in Google's Chrome OS. 488*4882a593Smuzhiyun 489*4882a593SmuzhiyunNotice the 'required' property. This marks the key as required - U-Boot will 490*4882a593Smuzhiyunnot boot any image that does not verify against this key. 491*4882a593Smuzhiyun 492*4882a593Smuzhiyun 493*4882a593Smuzhiyun7. Put U-Boot and the kernel onto the board 494*4882a593Smuzhiyun------------------------------------------- 495*4882a593Smuzhiyun 496*4882a593SmuzhiyunThe method here varies depending on how you are booting. For this example we 497*4882a593Smuzhiyunare booting from an micro-SD card with two partitions, one for U-Boot and one 498*4882a593Smuzhiyunfor Linux. Put it into your machine and write U-Boot and the kernel to it. 499*4882a593SmuzhiyunHere the card is /dev/sde: 500*4882a593Smuzhiyun 501*4882a593Smuzhiyun cd $WORK 502*4882a593Smuzhiyun export UDEV=/dev/sde1 # Change thes two lines to the correct device 503*4882a593Smuzhiyun export KDEV=/dev/sde2 504*4882a593Smuzhiyun sudo mount $UDEV /mnt/tmp && sudo cp $UOUT/u-boot-dtb.img /mnt/tmp/u-boot.img && sleep 1 && sudo umount $UDEV 505*4882a593Smuzhiyun sudo mount $KDEV /mnt/tmp && sudo cp $WORK/image.fit /mnt/tmp/boot/image.fit && sleep 1 && sudo umount $KDEV 506*4882a593Smuzhiyun 507*4882a593Smuzhiyun 508*4882a593Smuzhiyun8. Try it 509*4882a593Smuzhiyun--------- 510*4882a593Smuzhiyun 511*4882a593SmuzhiyunBoot the board using the commands below: 512*4882a593Smuzhiyun 513*4882a593Smuzhiyun setenv bootargs console=ttyO0,115200n8 quiet root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait 514*4882a593Smuzhiyun ext2load mmc 0:2 82000000 /boot/image.fit 515*4882a593Smuzhiyun bootm 82000000 516*4882a593Smuzhiyun 517*4882a593SmuzhiyunYou should then see something like this: 518*4882a593Smuzhiyun 519*4882a593SmuzhiyunU-Boot# setenv bootargs console=ttyO0,115200n8 quiet root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait 520*4882a593SmuzhiyunU-Boot# ext2load mmc 0:2 82000000 /boot/image.fit 521*4882a593Smuzhiyun7824930 bytes read in 589 ms (12.7 MiB/s) 522*4882a593SmuzhiyunU-Boot# bootm 82000000 523*4882a593Smuzhiyun## Loading kernel from FIT Image at 82000000 ... 524*4882a593Smuzhiyun Using 'conf@1' configuration 525*4882a593Smuzhiyun Verifying Hash Integrity ... sha1,rsa2048:dev+ OK 526*4882a593Smuzhiyun Trying 'kernel@1' kernel subimage 527*4882a593Smuzhiyun Description: unavailable 528*4882a593Smuzhiyun Created: 2014-06-01 19:32:54 UTC 529*4882a593Smuzhiyun Type: Kernel Image 530*4882a593Smuzhiyun Compression: lzo compressed 531*4882a593Smuzhiyun Data Start: 0x820000a8 532*4882a593Smuzhiyun Data Size: 7790938 Bytes = 7.4 MiB 533*4882a593Smuzhiyun Architecture: ARM 534*4882a593Smuzhiyun OS: Linux 535*4882a593Smuzhiyun Load Address: 0x80008000 536*4882a593Smuzhiyun Entry Point: 0x80008000 537*4882a593Smuzhiyun Hash algo: sha1 538*4882a593Smuzhiyun Hash value: c94364646427e10f423837e559898ef02c97b988 539*4882a593Smuzhiyun Verifying Hash Integrity ... sha1+ OK 540*4882a593Smuzhiyun## Loading fdt from FIT Image at 82000000 ... 541*4882a593Smuzhiyun Using 'conf@1' configuration 542*4882a593Smuzhiyun Trying 'fdt@1' fdt subimage 543*4882a593Smuzhiyun Description: beaglebone-black 544*4882a593Smuzhiyun Created: 2014-06-01 19:32:54 UTC 545*4882a593Smuzhiyun Type: Flat Device Tree 546*4882a593Smuzhiyun Compression: uncompressed 547*4882a593Smuzhiyun Data Start: 0x8276e2ec 548*4882a593Smuzhiyun Data Size: 31547 Bytes = 30.8 KiB 549*4882a593Smuzhiyun Architecture: ARM 550*4882a593Smuzhiyun Hash algo: sha1 551*4882a593Smuzhiyun Hash value: cb09202f889d824f23b8e4404b781be5ad38a68d 552*4882a593Smuzhiyun Verifying Hash Integrity ... sha1+ OK 553*4882a593Smuzhiyun Booting using the fdt blob at 0x8276e2ec 554*4882a593Smuzhiyun Uncompressing Kernel Image ... OK 555*4882a593Smuzhiyun Loading Device Tree to 8fff5000, end 8ffffb3a ... OK 556*4882a593Smuzhiyun 557*4882a593SmuzhiyunStarting kernel ... 558*4882a593Smuzhiyun 559*4882a593Smuzhiyun[ 0.582377] omap_init_mbox: hwmod doesn't have valid attrs 560*4882a593Smuzhiyun[ 2.589651] musb-hdrc musb-hdrc.0.auto: Failed to request rx1. 561*4882a593Smuzhiyun[ 2.595830] musb-hdrc musb-hdrc.0.auto: musb_init_controller failed with status -517 562*4882a593Smuzhiyun[ 2.606470] musb-hdrc musb-hdrc.1.auto: Failed to request rx1. 563*4882a593Smuzhiyun[ 2.612723] musb-hdrc musb-hdrc.1.auto: musb_init_controller failed with status -517 564*4882a593Smuzhiyun[ 2.940808] drivers/rtc/hctosys.c: unable to open rtc device (rtc0) 565*4882a593Smuzhiyun[ 7.248889] libphy: PHY 4a101000.mdio:01 not found 566*4882a593Smuzhiyun[ 7.253995] net eth0: phy 4a101000.mdio:01 not found on slave 1 567*4882a593Smuzhiyunsystemd-fsck[83]: Angstrom: clean, 50607/218160 files, 306348/872448 blocks 568*4882a593Smuzhiyun 569*4882a593Smuzhiyun.---O---. 570*4882a593Smuzhiyun| | .-. o o 571*4882a593Smuzhiyun| | |-----.-----.-----.| | .----..-----.-----. 572*4882a593Smuzhiyun| | | __ | ---'| '--.| .-'| | | 573*4882a593Smuzhiyun| | | | | |--- || --'| | | ' | | | | 574*4882a593Smuzhiyun'---'---'--'--'--. |-----''----''--' '-----'-'-'-' 575*4882a593Smuzhiyun -' | 576*4882a593Smuzhiyun '---' 577*4882a593Smuzhiyun 578*4882a593SmuzhiyunThe Angstrom Distribution beaglebone ttyO0 579*4882a593Smuzhiyun 580*4882a593SmuzhiyunAngstrom v2012.12 - Kernel 3.14.1+ 581*4882a593Smuzhiyun 582*4882a593Smuzhiyunbeaglebone login: 583*4882a593Smuzhiyun 584*4882a593SmuzhiyunAt this point your kernel has been verified and you can be sure that it is one 585*4882a593Smuzhiyunthat you signed. As an exercise, try changing image.fit as in step 5 and see 586*4882a593Smuzhiyunwhat happens. 587*4882a593Smuzhiyun 588*4882a593Smuzhiyun 589*4882a593SmuzhiyunFurther Improvements 590*4882a593Smuzhiyun-------------------- 591*4882a593Smuzhiyun 592*4882a593SmuzhiyunSeveral of the steps here can be easily automated. In particular it would be 593*4882a593Smuzhiyuncapital if signing and packaging a kernel were easy, perhaps a simple make 594*4882a593Smuzhiyuntarget in the kernel. 595*4882a593Smuzhiyun 596*4882a593SmuzhiyunSome mention of how to use multiple .dtb files in a FIT might be useful. 597*4882a593Smuzhiyun 598*4882a593SmuzhiyunU-Boot's verified boot mechanism has not had a robust and independent security 599*4882a593Smuzhiyunreview. Such a review should look at the implementation and its resistance to 600*4882a593Smuzhiyunattacks. 601*4882a593Smuzhiyun 602*4882a593SmuzhiyunPerhaps the verified boot feature could could be integrated into the Amstrom 603*4882a593Smuzhiyundistribution. 604*4882a593Smuzhiyun 605*4882a593Smuzhiyun 606*4882a593SmuzhiyunSimon Glass 607*4882a593Smuzhiyunsjg@chromium.org 608*4882a593Smuzhiyun2-June-14 609