two questions on verified boot
Rasmus Villemoes
rasmus.villemoes at prevas.dk
Thu Jan 27 16:41:51 CET 2022
On 27/01/2022 16.06, Simon Glass wrote:
> Hi Rasmus,
>
> On Sun, 21 Nov 2021 at 07:55, Rasmus Villemoes
> <rasmus.villemoes at prevas.dk> wrote:
>>
>> (1) When one wants to get rid of CONFIG_LEGACY_IMAGE_FORMAT, one also
>> has to wrap any boot script in a FIT rather than a uImage. While it's
>> not directly documented anywhere how to do that, it seems that a minimal
>> .its for achieving it is
>>
>> /dts-v1/;
>>
>> / {
>> description = "U-Boot script(s)";
>>
>> images {
>> default = "boot";
>> boot {
>> description = "Bootscript";
>> data = /incbin/("boot.txt");
>> type = "script";
>> compression = "none";
>> hash-1 {
>> algo = "sha256";
>> };
>> };
>> };
>> };
>>
>> [The UX when one doesn't guess that the description string is mandatory
>> is rather sad, but that's a separate story.]
>>
>> Now, I can get that signed if I include a signature-foo node inside the
>> "boot" node, and also add a dummy empty /configurations node (otherwise
>> mkimage refuses to process it). But that will only work if I have added
>> a "required = image" property with the public key; if I want to use the
>> safer/saner "required = conf", how do I make sure any boot script is
>> properly signed?
>>
>> The code in source.c only cares about the images node and calls
>> fit_image_verify(), and there's no concept of "configuration" and
>> combining multiple images when talking about a simple boot script.
>
> By then the configuration should have been checked, though, right?
By what?
At
> least, that is how bootm works.
>
> So it seems that the scripts are being done in a different way.
The thing is, for a script there's really no such thing as a
"configuration", there are not multiple subimages that need to be
stitched together.
>>
>> (2) Assuming for the moment that I would be happy with just using
>> required=image, am I right in that not only does that mean that the
>> combination of kernel/fdt/initramfs is not verified, merely the
>> individual parts, but more importantly (a mix'n'match attack isn't
>> really very likely), _only_ the data property in each node is part of
>> what gets signed, not the other important properties such as load= and
>> entry=? IOW, suppose I have a FIT image with
>
> Yes the 'image' check does not protect against a mix & match attack -
> see doc/uImage.FIT/signature.txt
I don't care about mix'n'match, they are completely unlikely to be
relevant. But...
>>
>> images {
>> kernel {
>> data = blabla;
>> load = 0x1000000;
>> entry = 0x10000000;
>> signature {
>> ... // correct signature of blabla
>> };
>> };
>> };
>>
>> and I know that the boot process uses $loadaddr = 0x40000000. What is to
>> stop me from modifying that FIT image to read
>>
>> images {
>> kernel {
>> data = blabla;
>> load = 0x1000000;
>> entry = 0x400abcde;
>> signature {
>> ... // correct signature of blabla
>> };
>> };
>> };
>> some-other-node {
>> pwned = /incbin/("pwned");
>> };
>>
>> where 0xabcde is chosen to coincide with where the data part of the
>> pwned property lies in the modified FIT? (That pwned property can be put
>> anywhere; I could even just replace the signer-name property inside the
>> signature node with a value of "mkimage\0<padding><my payload>".)
>
> Yes I believe that is right.
... this then means that 'required = "image"' is not just a little less
safe than the "signed configurations" model, it is completely and
utterly broken.
>>
>> In fit_config_process_sig(), there's this elaborate dance with
>> fit_config_get_data()/fdt_find_regions() which, AFAICT, ends up
>> including all the property values (and the FDT_PROP tags and string
>> offsets etc.), and then we call info.crypto->sign() with some
>> appropriate region_count.
>
> Yes
>
>> But in fit_image_process_sig(), we call
>> info.crypto->sign() with nregions==1, and AFAICT, the data being signed
>> is just the value of the "data" property, nothing else.
>
> Yes, this matches the behaviour of the existing hashing properties.
> They only hash on the data itself.
>
> We could expand this to include the properties of the image node, I
> suppose. But do note that you really need 'config' signing to make
> things secure.
That's kind of what I guessed, but to rephrase my question: How do I
sign a boot script and have that verified, when the source.c code only
calls fit_image_verify(), which eventually calls
fit_image_verify_required_sigs() which only cares about the keys that
have 'required = "image"'.
I'd prefer to just have one public key for verifying both the boot
script and the kernel FIT image. I don't think it would work very well
to have two keys (possibly the same one just added under different key
names), one with required=image and one with required=conf, because
AFAICT then the kernel FIT image would have to have signatures on _both_
image and configuation nodes. If I add just one key with required=conf,
the boot script isn't verified at all, and if it says required=image,
well, then I'm vulnerable to the trivial modification of entry=
mentioned above.
Rasmus
More information about the U-Boot
mailing list