]>
Commit | Line | Data |
---|---|---|
3e569a6b SG |
1 | U-Boot FIT Signature Verification |
2 | ================================= | |
3 | ||
4 | Introduction | |
5 | ------------ | |
6 | FIT supports hashing of images so that these hashes can be checked on | |
7 | loading. This protects against corruption of the image. However it does not | |
8 | prevent the substitution of one image for another. | |
9 | ||
10 | The signature feature allows the hash to be signed with a private key such | |
11 | that it can be verified using a public key later. Provided that the private | |
12 | key is kept secret and the public key is stored in a non-volatile place, | |
13 | any image can be verified in this way. | |
14 | ||
15 | See verified-boot.txt for more general information on verified boot. | |
16 | ||
17 | ||
18 | Concepts | |
19 | -------- | |
20 | Some familiarity with public key cryptography is assumed in this section. | |
21 | ||
22 | The procedure for signing is as follows: | |
23 | ||
24 | - hash an image in the FIT | |
25 | - sign the hash with a private key to produce a signature | |
26 | - store the resulting signature in the FIT | |
27 | ||
28 | The procedure for verification is: | |
29 | ||
30 | - read the FIT | |
31 | - obtain the public key | |
32 | - extract the signature from the FIT | |
33 | - hash the image from the FIT | |
34 | - verify (with the public key) that the extracted signature matches the | |
35 | hash | |
36 | ||
37 | The signing is generally performed by mkimage, as part of making a firmware | |
38 | image for the device. The verification is normally done in U-Boot on the | |
39 | device. | |
40 | ||
41 | ||
42 | Algorithms | |
43 | ---------- | |
44 | In principle any suitable algorithm can be used to sign and verify a hash. | |
45 | At present only one class of algorithms is supported: SHA1 hashing with RSA. | |
46 | This works by hashing the image to produce a 20-byte hash. | |
47 | ||
48 | While it is acceptable to bring in large cryptographic libraries such as | |
49 | openssl on the host side (e.g. mkimage), it is not desirable for U-Boot. | |
50 | For the run-time verification side, it is important to keep code and data | |
51 | size as small as possible. | |
52 | ||
53 | For this reason the RSA image verification uses pre-processed public keys | |
54 | which can be used with a very small amount of code - just some extraction | |
55 | of data from the FDT and exponentiation mod n. Code size impact is a little | |
56 | under 5KB on Tegra Seaboard, for example. | |
57 | ||
58 | It is relatively straightforward to add new algorithms if required. If | |
59 | another RSA variant is needed, then it can be added to the table in | |
60 | image-sig.c. If another algorithm is needed (such as DSA) then it can be | |
61 | placed alongside rsa.c, and its functions added to the table in image-sig.c | |
62 | also. | |
63 | ||
64 | ||
65 | Creating an RSA key and certificate | |
66 | ----------------------------------- | |
67 | To create a new public key, size 2048 bits: | |
68 | ||
69 | $ openssl genrsa -F4 -out keys/dev.key 2048 | |
70 | ||
71 | To create a certificate for this: | |
72 | ||
73 | $ openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt | |
74 | ||
75 | If you like you can look at the public key also: | |
76 | ||
77 | $ openssl rsa -in keys/dev.key -pubout | |
78 | ||
79 | ||
80 | Device Tree Bindings | |
81 | -------------------- | |
82 | The following properties are required in the FIT's signature node(s) to | |
83 | allow thes signer to operate. These should be added to the .its file. | |
84 | Signature nodes sit at the same level as hash nodes and are called | |
85 | signature@1, signature@2, etc. | |
86 | ||
87 | - algo: Algorithm name (e.g. "sha1,rs2048") | |
88 | ||
89 | - key-name-hint: Name of key to use for signing. The keys will normally be in | |
90 | a single directory (parameter -k to mkimage). For a given key <name>, its | |
91 | private key is stored in <name>.key and the certificate is stored in | |
92 | <name>.crt. | |
93 | ||
94 | When the image is signed, the following properties are added (mandatory): | |
95 | ||
96 | - value: The signature data (e.g. 256 bytes for 2048-bit RSA) | |
97 | ||
98 | When the image is signed, the following properties are optional: | |
99 | ||
100 | - timestamp: Time when image was signed (standard Unix time_t format) | |
101 | ||
102 | - signer-name: Name of the signer (e.g. "mkimage") | |
103 | ||
104 | - signer-version: Version string of the signer (e.g. "2013.01") | |
105 | ||
106 | - comment: Additional information about the signer or image | |
107 | ||
4d098529 SG |
108 | For config bindings (see Signed Configurations below), the following |
109 | additional properties are optional: | |
3e569a6b | 110 | |
4d098529 SG |
111 | - sign-images: A list of images to sign, each being a property of the conf |
112 | node that contains then. The default is "kernel,fdt" which means that these | |
113 | two images will be looked up in the config and signed if present. | |
114 | ||
115 | For config bindings, these properties are added by the signer: | |
116 | ||
117 | - hashed-nodes: A list of nodes which were hashed by the signer. Each is | |
118 | a string - the full path to node. A typical value might be: | |
119 | ||
120 | hashed-nodes = "/", "/configurations/conf@1", "/images/kernel@1", | |
121 | "/images/kernel@1/hash@1", "/images/fdt@1", | |
122 | "/images/fdt@1/hash@1"; | |
123 | ||
124 | - hashed-strings: The start and size of the string region of the FIT that | |
125 | was hashed | |
126 | ||
127 | Example: See sign-images.its for an example image tree source file and | |
128 | sign-configs.its for config signing. | |
3e569a6b SG |
129 | |
130 | ||
131 | Public Key Storage | |
132 | ------------------ | |
133 | In order to verify an image that has been signed with a public key we need to | |
134 | have a trusted public key. This cannot be stored in the signed image, since | |
135 | it would be easy to alter. For this implementation we choose to store the | |
136 | public key in U-Boot's control FDT (using CONFIG_OF_CONTROL). | |
137 | ||
138 | Public keys should be stored as sub-nodes in a /signature node. Required | |
139 | properties are: | |
140 | ||
141 | - algo: Algorithm name (e.g. "sha1,rs2048") | |
142 | ||
143 | Optional properties are: | |
144 | ||
145 | - key-name-hint: Name of key used for signing. This is only a hint since it | |
146 | is possible for the name to be changed. Verification can proceed by checking | |
147 | all available signing keys until one matches. | |
148 | ||
149 | - required: If present this indicates that the key must be verified for the | |
150 | image / configuration to be considered valid. Only required keys are | |
151 | normally verified by the FIT image booting algorithm. Valid values are | |
152 | "image" to force verification of all images, and "conf" to force verfication | |
153 | of the selected configuration (which then relies on hashes in the images to | |
154 | verify those). | |
155 | ||
156 | Each signing algorithm has its own additional properties. | |
157 | ||
158 | For RSA the following are mandatory: | |
159 | ||
160 | - rsa,num-bits: Number of key bits (e.g. 2048) | |
161 | - rsa,modulus: Modulus (N) as a big-endian multi-word integer | |
162 | - rsa,r-squared: (2^num-bits)^2 as a big-endian multi-word integer | |
163 | - rsa,n0-inverse: -1 / modulus[0] mod 2^32 | |
164 | ||
165 | ||
4d098529 SG |
166 | Signed Configurations |
167 | --------------------- | |
168 | While signing images is useful, it does not provide complete protection | |
169 | against several types of attack. For example, it it possible to create a | |
170 | FIT with the same signed images, but with the configuration changed such | |
171 | that a different one is selected (mix and match attack). It is also possible | |
172 | to substitute a signed image from an older FIT version into a newer FIT | |
173 | (roll-back attack). | |
174 | ||
175 | As an example, consider this FIT: | |
176 | ||
177 | / { | |
178 | images { | |
179 | kernel@1 { | |
180 | data = <data for kernel1> | |
181 | signature@1 { | |
182 | algo = "sha1,rsa2048"; | |
183 | value = <...kernel signature 1...> | |
184 | }; | |
185 | }; | |
186 | kernel@2 { | |
187 | data = <data for kernel2> | |
188 | signature@1 { | |
189 | algo = "sha1,rsa2048"; | |
190 | value = <...kernel signature 2...> | |
191 | }; | |
192 | }; | |
193 | fdt@1 { | |
194 | data = <data for fdt1>; | |
195 | signature@1 { | |
196 | algo = "sha1,rsa2048"; | |
197 | vaue = <...fdt signature 1...> | |
198 | }; | |
199 | }; | |
200 | fdt@2 { | |
201 | data = <data for fdt2>; | |
202 | signature@1 { | |
203 | algo = "sha1,rsa2048"; | |
204 | vaue = <...fdt signature 2...> | |
205 | }; | |
206 | }; | |
207 | }; | |
208 | configurations { | |
209 | default = "conf@1"; | |
210 | conf@1 { | |
211 | kernel = "kernel@1"; | |
212 | fdt = "fdt@1"; | |
213 | }; | |
214 | conf@1 { | |
215 | kernel = "kernel@2"; | |
216 | fdt = "fdt@2"; | |
217 | }; | |
218 | }; | |
219 | }; | |
220 | ||
221 | Since both kernels are signed it is easy for an attacker to add a new | |
222 | configuration 3 with kernel 1 and fdt 2: | |
223 | ||
224 | configurations { | |
225 | default = "conf@1"; | |
226 | conf@1 { | |
227 | kernel = "kernel@1"; | |
228 | fdt = "fdt@1"; | |
229 | }; | |
230 | conf@1 { | |
231 | kernel = "kernel@2"; | |
232 | fdt = "fdt@2"; | |
233 | }; | |
234 | conf@3 { | |
235 | kernel = "kernel@1"; | |
236 | fdt = "fdt@2"; | |
237 | }; | |
238 | }; | |
239 | ||
240 | With signed images, nothing protects against this. Whether it gains an | |
241 | advantage for the attacker is debatable, but it is not secure. | |
242 | ||
243 | To solved this problem, we support signed configurations. In this case it | |
244 | is the configurations that are signed, not the image. Each image has its | |
245 | own hash, and we include the hash in the configuration signature. | |
246 | ||
247 | So the above example is adjusted to look like this: | |
248 | ||
249 | / { | |
250 | images { | |
251 | kernel@1 { | |
252 | data = <data for kernel1> | |
253 | hash@1 { | |
254 | algo = "sha1"; | |
255 | value = <...kernel hash 1...> | |
256 | }; | |
257 | }; | |
258 | kernel@2 { | |
259 | data = <data for kernel2> | |
260 | hash@1 { | |
261 | algo = "sha1"; | |
262 | value = <...kernel hash 2...> | |
263 | }; | |
264 | }; | |
265 | fdt@1 { | |
266 | data = <data for fdt1>; | |
267 | hash@1 { | |
268 | algo = "sha1"; | |
269 | value = <...fdt hash 1...> | |
270 | }; | |
271 | }; | |
272 | fdt@2 { | |
273 | data = <data for fdt2>; | |
274 | hash@1 { | |
275 | algo = "sha1"; | |
276 | value = <...fdt hash 2...> | |
277 | }; | |
278 | }; | |
279 | }; | |
280 | configurations { | |
281 | default = "conf@1"; | |
282 | conf@1 { | |
283 | kernel = "kernel@1"; | |
284 | fdt = "fdt@1"; | |
285 | signature@1 { | |
286 | algo = "sha1,rsa2048"; | |
287 | value = <...conf 1 signature...>; | |
288 | }; | |
289 | }; | |
290 | conf@2 { | |
291 | kernel = "kernel@2"; | |
292 | fdt = "fdt@2"; | |
293 | signature@1 { | |
294 | algo = "sha1,rsa2048"; | |
295 | value = <...conf 1 signature...>; | |
296 | }; | |
297 | }; | |
298 | }; | |
299 | }; | |
300 | ||
301 | ||
302 | You can see that we have added hashes for all images (since they are no | |
303 | longer signed), and a signature to each configuration. In the above example, | |
304 | mkimage will sign configurations/conf@1, the kernel and fdt that are | |
305 | pointed to by the configuration (/images/kernel@1, /images/kernel@1/hash@1, | |
306 | /images/fdt@1, /images/fdt@1/hash@1) and the root structure of the image | |
307 | (so that it isn't possible to add or remove root nodes). The signature is | |
308 | written into /configurations/conf@1/signature@1/value. It can easily be | |
309 | verified later even if the FIT has been signed with other keys in the | |
310 | meantime. | |
311 | ||
312 | ||
3e569a6b SG |
313 | Verification |
314 | ------------ | |
315 | FITs are verified when loaded. After the configuration is selected a list | |
316 | of required images is produced. If there are 'required' public keys, then | |
317 | each image must be verified against those keys. This means that every image | |
318 | that might be used by the target needs to be signed with 'required' keys. | |
319 | ||
320 | This happens automatically as part of a bootm command when FITs are used. | |
321 | ||
322 | ||
323 | Enabling FIT Verification | |
324 | ------------------------- | |
325 | In addition to the options to enable FIT itself, the following CONFIGs must | |
326 | be enabled: | |
327 | ||
328 | CONFIG_FIT_SIGNATURE - enable signing and verfication in FITs | |
329 | CONFIG_RSA - enable RSA algorithm for signing | |
330 | ||
21d29f7f HS |
331 | WARNING: When relying on signed FIT images with required signature check |
332 | the legacy image format is default disabled by not defining | |
333 | CONFIG_IMAGE_FORMAT_LEGACY | |
3e569a6b SG |
334 | |
335 | Testing | |
336 | ------- | |
337 | An easy way to test signing and verfication is to use the test script | |
338 | provided in test/vboot/vboot_test.sh. This uses sandbox (a special version | |
339 | of U-Boot which runs under Linux) to show the operation of a 'bootm' | |
340 | command loading and verifying images. | |
341 | ||
342 | A sample run is show below: | |
343 | ||
344 | $ make O=sandbox sandbox_config | |
345 | $ make O=sandbox | |
346 | $ O=sandbox ./test/vboot/vboot_test.sh | |
347 | Simple Verified Boot Test | |
348 | ========================= | |
349 | ||
350 | Please see doc/uImage.FIT/verified-boot.txt for more information | |
351 | ||
646257d1 | 352 | /home/hs/ids/u-boot/sandbox/tools/mkimage -D -I dts -O dtb -p 2000 |
3e569a6b | 353 | Build keys |
646257d1 | 354 | do sha1 test |
3e569a6b SG |
355 | Build FIT with signed images |
356 | Test Verified Boot Run: unsigned signatures:: OK | |
357 | Sign images | |
358 | Test Verified Boot Run: signed images: OK | |
359 | Build FIT with signed configuration | |
360 | Test Verified Boot Run: unsigned config: OK | |
361 | Sign images | |
362 | Test Verified Boot Run: signed config: OK | |
29a23f9d | 363 | check signed config on the host |
ce1400f6 | 364 | Signature check OK |
29a23f9d HS |
365 | OK |
366 | Test Verified Boot Run: signed config: OK | |
646257d1 HS |
367 | Test Verified Boot Run: signed config with bad hash: OK |
368 | do sha256 test | |
369 | Build FIT with signed images | |
370 | Test Verified Boot Run: unsigned signatures:: OK | |
371 | Sign images | |
372 | Test Verified Boot Run: signed images: OK | |
373 | Build FIT with signed configuration | |
374 | Test Verified Boot Run: unsigned config: OK | |
375 | Sign images | |
376 | Test Verified Boot Run: signed config: OK | |
29a23f9d | 377 | check signed config on the host |
ce1400f6 | 378 | Signature check OK |
29a23f9d HS |
379 | OK |
380 | Test Verified Boot Run: signed config: OK | |
646257d1 | 381 | Test Verified Boot Run: signed config with bad hash: OK |
3e569a6b SG |
382 | |
383 | Test passed | |
384 | ||
ce1400f6 | 385 | |
3e569a6b SG |
386 | Future Work |
387 | ----------- | |
388 | - Roll-back protection using a TPM is done using the tpm command. This can | |
389 | be scripted, but we might consider a default way of doing this, built into | |
390 | bootm. | |
391 | ||
392 | ||
393 | Possible Future Work | |
394 | -------------------- | |
395 | - Add support for other RSA/SHA variants, such as rsa4096,sha512. | |
396 | - Other algorithms besides RSA | |
397 | - More sandbox tests for failure modes | |
398 | - Passwords for keys/certificates | |
399 | - Perhaps implement OAEP | |
400 | - Enhance bootm to permit scripted signature verification (so that a script | |
401 | can verify an image but not actually boot it) | |
402 | ||
403 | ||
404 | Simon Glass | |
405 | [email protected] | |
406 | 1-1-13 |