diff options
author | Rasmus Villemoes <ravi@prevas.dk> | 2025-06-10 14:27:48 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2025-06-26 11:48:39 -0600 |
commit | 7d4eacb0e68a7eb471a8dc43a5585b46ec67a333 (patch) | |
tree | 564bedb11d8fa91653fe4a476bc63c1833132d89 /tools/binman/ftest.py | |
parent | ba60a726d9e64f9ef8af01fcf2d9a6239940e799 (diff) |
mkimage: do a rough estimate for the size needed for hashes/signatures
Background:
I have several customers that will be using a certain remote signing
service for signing their images, in order that the private keys are
never exposed outside that company's secure servers. This is done via
a pkcs#11 interface that talks to the remote signing server, and all
of that works quite well.
However, the way this particular signing service works is that one
must upfront create a "signing session", where one indicates which
keys one will use and, importantly, how many times each key will (may)
be used. Then, depending on the keys requested and the customer's
configuration, one or more humans must authorize that signing session
So for example, if official release keys are to be used, maybe two
different people from upper management must authorize, while if
development keys are requested, the developer himself can authorize
the session.
Once authorized, the requester receives a token that must then be used
for signing via one of the keys associated to that session.
I have that integrated in Yocto in a way that when a CI starts a BSP
build, it automatically works out which keys will be needed (e.g. one
for signing U-Boot, another for signing a kernel FIT image) based on
bitbake metadata, requests an appropriate signing session, and the
appropriate people are then notified and can then look at the details
of that CI pipeline and confirm that it is legitimate.
The problem:
The way mkimage does FIT image signing means that the remote server
can be asked to perform a signature an unbounded number of times, or
at least a number of times that cannot be determined upfront. This
means that currently, I need to artificially say that a kernel key
will be used, say, 10 times, even when only a single FIT image with
just one configuration node is created.
Part of the security model is that once the number of signings using a
given key has been depleted, the authorization token becomes useless
even if somehow leaked from the CI - and _if_ it is leaked/compromised
and abused before the CI has gotten around to do its signings, the
build will then fail with a clear indication of the
compromise. Clearly, having to specify a "high enough" expected use
count is counter to that part of the security model, because it will
inevitably leave some allowed uses behind.
While not perfect, we can give a reasonable estimate of an upper bound
on the necessary extra size by simply counting the number of hash and
signature nodes in the FIT image.
As indicated in the comments, one could probably make it even more
precise, and if there would ever be signatures larger than 512 bytes,
probably one would have to do that. But this works well enough in
practice for now, and is in fact an improvement in the normal case:
Currently, starting with size_inc of 0 is guaranteed to fail, so we
always enter the loop at least twice, even when not doing any signing
but merely filling hash values.
Just in case I've missed anything, keep the loop incrementing 1024
bytes at a time, and also, in case the estimate turns out to be over
64K, ensure that we do at least one attempt by changing to a do-while
loop.
With a little debug printf, creating a FIT image with three
configuration nodes previously resulted in
Trying size_inc=0
Trying size_inc=1024
Trying size_inc=2048
Trying size_inc=3072
Succeeded at size_inc=3072
and dumping info from the signing session (where I've artifically
asked for 10 uses of the kernel key) shows
"keyid": "kernel-dev-20250218",
"usagecount": 9,
"maxusagecount": 10
corresponding to 1+2+3+3 signatures requested (so while the loop count
is roughly linear in the number of config nodes, the number of
signings is quadratic).
With this, I instead get
Trying size_inc=3456
Succeeded at size_inc=3456
and the expected
"keyid": "kernel-dev-20250218",
"usagecount": 3,
"maxusagecount": 10
thus allowing me to set maxusagecount correctly.
Update a binman test case accordingly: With the previous behaviour,
mkimage would try size_inc=0 and then size_inc=1024 and then
succeed. With this patch, we first try, and succeed, with 4*128=512
due to the four hash nodes (and no signature nodes) in 161_fit.dts, so
the image ends up 512 bytes smaller.
Signed-off-by: Rasmus Villemoes <ravi@prevas.dk>
Diffstat (limited to 'tools/binman/ftest.py')
-rw-r--r-- | tools/binman/ftest.py | 8 |
1 files changed, 4 insertions, 4 deletions
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 1e570aba287..3d556535e07 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4019,7 +4019,7 @@ class TestFunctional(unittest.TestCase): self.assertEqual({ 'image-pos': 0, 'offset': 0, - 'size': 1890, + 'size': 1378, 'u-boot:image-pos': 0, 'u-boot:offset': 0, @@ -4027,7 +4027,7 @@ class TestFunctional(unittest.TestCase): 'fit:image-pos': 4, 'fit:offset': 4, - 'fit:size': 1840, + 'fit:size': 1328, 'fit/images/kernel:image-pos': 304, 'fit/images/kernel:offset': 300, @@ -4045,8 +4045,8 @@ class TestFunctional(unittest.TestCase): 'fit/images/fdt-1/u-boot-spl-dtb:offset': 0, 'fit/images/fdt-1/u-boot-spl-dtb:size': 6, - 'u-boot-nodtb:image-pos': 1844, - 'u-boot-nodtb:offset': 1844, + 'u-boot-nodtb:image-pos': 1332, + 'u-boot-nodtb:offset': 1332, 'u-boot-nodtb:size': 46, }, props) |