mirror of
https://xff.cz/git/u-boot/
synced 2025-09-01 08:42:12 +02:00
image: Add support for signing of FIT configurations
While signing images is useful, it does not provide complete protection against several types of attack. For example, it it possible to create a FIT with the same signed images, but with the configuration changed such that a different one is selected (mix and match attack). It is also possible to substitute a signed image from an older FIT version into a newer FIT (roll-back attack). Add support for signing of FIT configurations using the libfdt's region support. Please see doc/uImage.FIT/signature.txt for more information. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
45
doc/uImage.FIT/sign-configs.its
Normal file
45
doc/uImage.FIT/sign-configs.its
Normal file
@@ -0,0 +1,45 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
description = "Chrome OS kernel image with one or more FDT blobs";
|
||||
#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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@@ -105,8 +105,27 @@ When the image is signed, the following properties are optional:
|
||||
|
||||
- comment: Additional information about the signer or image
|
||||
|
||||
For config bindings (see Signed Configurations below), the following
|
||||
additional properties are optional:
|
||||
|
||||
Example: See sign-images.its for an example image tree source file.
|
||||
- sign-images: A list of images to sign, each being a property of the conf
|
||||
node that contains then. The default is "kernel,fdt" which means that these
|
||||
two images will be looked up in the config and signed if present.
|
||||
|
||||
For config bindings, these properties are added by the signer:
|
||||
|
||||
- hashed-nodes: A list of nodes which were hashed by the signer. Each is
|
||||
a string - the full path to node. A typical value might be:
|
||||
|
||||
hashed-nodes = "/", "/configurations/conf@1", "/images/kernel@1",
|
||||
"/images/kernel@1/hash@1", "/images/fdt@1",
|
||||
"/images/fdt@1/hash@1";
|
||||
|
||||
- hashed-strings: The start and size of the string region of the FIT that
|
||||
was hashed
|
||||
|
||||
Example: See sign-images.its for an example image tree source file and
|
||||
sign-configs.its for config signing.
|
||||
|
||||
|
||||
Public Key Storage
|
||||
@@ -144,6 +163,153 @@ For RSA the following are mandatory:
|
||||
- rsa,n0-inverse: -1 / modulus[0] mod 2^32
|
||||
|
||||
|
||||
Signed Configurations
|
||||
---------------------
|
||||
While signing images is useful, it does not provide complete protection
|
||||
against several types of attack. For example, it it possible to create a
|
||||
FIT with the same signed images, but with the configuration changed such
|
||||
that a different one is selected (mix and match attack). It is also possible
|
||||
to substitute a signed image from an older FIT version into a newer FIT
|
||||
(roll-back attack).
|
||||
|
||||
As an example, consider this FIT:
|
||||
|
||||
/ {
|
||||
images {
|
||||
kernel@1 {
|
||||
data = <data for kernel1>
|
||||
signature@1 {
|
||||
algo = "sha1,rsa2048";
|
||||
value = <...kernel signature 1...>
|
||||
};
|
||||
};
|
||||
kernel@2 {
|
||||
data = <data for kernel2>
|
||||
signature@1 {
|
||||
algo = "sha1,rsa2048";
|
||||
value = <...kernel signature 2...>
|
||||
};
|
||||
};
|
||||
fdt@1 {
|
||||
data = <data for fdt1>;
|
||||
signature@1 {
|
||||
algo = "sha1,rsa2048";
|
||||
vaue = <...fdt signature 1...>
|
||||
};
|
||||
};
|
||||
fdt@2 {
|
||||
data = <data for fdt2>;
|
||||
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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Since both kernels are signed it is easy for an attacker to add a new
|
||||
configuration 3 with kernel 1 and fdt 2:
|
||||
|
||||
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";
|
||||
};
|
||||
};
|
||||
|
||||
With signed images, nothing protects against this. Whether it gains an
|
||||
advantage for the attacker is debatable, but it is not secure.
|
||||
|
||||
To solved this problem, we support signed configurations. In this case it
|
||||
is the configurations that are signed, not the image. Each image has its
|
||||
own hash, and we include the hash in the configuration signature.
|
||||
|
||||
So the above example is adjusted to look like this:
|
||||
|
||||
/ {
|
||||
images {
|
||||
kernel@1 {
|
||||
data = <data for kernel1>
|
||||
hash@1 {
|
||||
algo = "sha1";
|
||||
value = <...kernel hash 1...>
|
||||
};
|
||||
};
|
||||
kernel@2 {
|
||||
data = <data for kernel2>
|
||||
hash@1 {
|
||||
algo = "sha1";
|
||||
value = <...kernel hash 2...>
|
||||
};
|
||||
};
|
||||
fdt@1 {
|
||||
data = <data for fdt1>;
|
||||
hash@1 {
|
||||
algo = "sha1";
|
||||
value = <...fdt hash 1...>
|
||||
};
|
||||
};
|
||||
fdt@2 {
|
||||
data = <data for fdt2>;
|
||||
hash@1 {
|
||||
algo = "sha1";
|
||||
value = <...fdt hash 2...>
|
||||
};
|
||||
};
|
||||
};
|
||||
configurations {
|
||||
default = "conf@1";
|
||||
conf@1 {
|
||||
kernel = "kernel@1";
|
||||
fdt = "fdt@1";
|
||||
signature@1 {
|
||||
algo = "sha1,rsa2048";
|
||||
value = <...conf 1 signature...>;
|
||||
};
|
||||
};
|
||||
conf@2 {
|
||||
kernel = "kernel@2";
|
||||
fdt = "fdt@2";
|
||||
signature@1 {
|
||||
algo = "sha1,rsa2048";
|
||||
value = <...conf 1 signature...>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
You can see that we have added hashes for all images (since they are no
|
||||
longer signed), and a signature to each configuration. In the above example,
|
||||
mkimage will sign configurations/conf@1, the kernel and fdt that are
|
||||
pointed to by the configuration (/images/kernel@1, /images/kernel@1/hash@1,
|
||||
/images/fdt@1, /images/fdt@1/hash@1) and the root structure of the image
|
||||
(so that it isn't possible to add or remove root nodes). The signature is
|
||||
written into /configurations/conf@1/signature@1/value. It can easily be
|
||||
verified later even if the FIT has been signed with other keys in the
|
||||
meantime.
|
||||
|
||||
|
||||
Verification
|
||||
------------
|
||||
FITs are verified when loaded. After the configuration is selected a list
|
||||
|
Reference in New Issue
Block a user