Hi Philippe, On Mon, May 25, 2026 at 9:52 AM Philippe Reynes <[email protected]> wrote: > > Right now, binman can only create pre-load header > using rsa. We add the support of ecdsa. > > Reviewed-by: Simon Glass <[email protected]> > Signed-off-by: Philippe Reynes <[email protected]> > --- > v3: > - initial version > v4: > - merge patch 11 that was adding test for ecdsa pre-load > - add key size check > - use exc instead of simply e > - rename dts filaneme > - add a test to check key size > v5: > - compute ecdsa521 sig instead of using hardcoded value 132 > - fix english: don't -> doesn't > - avoid line too long > v6: > - no change > > tools/binman/etype/pre_load.py | 78 ++++++++++++++++--- > tools/binman/ftest.py | 50 ++++++++++++ > tools/binman/test/ecdsa521.pem | 7 ++ > tools/binman/test/security/pre_load_ecdsa.dts | 22 ++++++ > .../security/pre_load_ecdsa_invalid_algo.dts | 22 ++++++ > .../security/pre_load_ecdsa_invalid_key.dts | 22 ++++++ > .../security/pre_load_ecdsa_invalid_sha.dts | 22 ++++++ > 7 files changed, 214 insertions(+), 9 deletions(-) > create mode 100644 tools/binman/test/ecdsa521.pem > create mode 100644 tools/binman/test/security/pre_load_ecdsa.dts > create mode 100644 tools/binman/test/security/pre_load_ecdsa_invalid_algo.dts > create mode 100644 tools/binman/test/security/pre_load_ecdsa_invalid_key.dts > create mode 100644 tools/binman/test/security/pre_load_ecdsa_invalid_sha.dts >
Looks good to me. Thanks! Reviewed-by: Raymond Mao <[email protected]> > diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py > index 0d953cb258e..7890c1c62a8 100644 > --- a/tools/binman/etype/pre_load.py > +++ b/tools/binman/etype/pre_load.py > @@ -16,8 +16,10 @@ from binman.entry import EntryArg > > from Cryptodome.Hash import SHA256, SHA384, SHA512 > from Cryptodome.PublicKey import RSA > +from Cryptodome.PublicKey import ECC > from Cryptodome.Signature import pkcs1_15 > from Cryptodome.Signature import pss > +from Cryptodome.Signature import DSS > > PRE_LOAD_MAGIC = b'UBSH' > > @@ -27,6 +29,12 @@ RSAS = { > 'rsa4096': 4096 / 8 > } > > +ECDSAS = { > + 'ecdsa256': 256 / 8 * 2, > + 'ecdsa384': 384 / 8 * 2, > + 'ecdsa521': (521 + 7) / 8 * 2 > +} > + > SHAS = { > 'sha256': SHA256, > 'sha384': SHA384, > @@ -86,24 +94,17 @@ class Entry_pre_load(Entry_collection): > if self.key_path is None: > self.key_path = '' > > - def _CreateHeader(self): > - """Create a pre load header""" > - hash_name, sign_name = self.algo_name.split(',') > - padding_name = self.padding_name > - key_name = os.path.join(self.key_path, self.key_name) > - > + def _CreateHeaderRsa(self, hash_name, sign_name, padding_name, key_name): > # Check hash and signature name/type > if hash_name not in SHAS: > self.Raise(hash_name + " is not supported") > - if sign_name not in RSAS: > - self.Raise(sign_name + " is not supported") > > # Read the key > key = RSA.import_key(tools.read_file(key_name)) > > # Check if the key has the expected size > if key.size_in_bytes() != RSAS[sign_name]: > - self.Raise("The key " + self.key_name + " don't have the > expected size") > + self.Raise("The key " + self.key_name + " doesn't have the > expected size") > > # Compute the hash > hash_image = SHAS[hash_name].new() > @@ -151,6 +152,65 @@ class Entry_pre_load(Entry_collection): > > return data + pad > > + def _CreateHeaderEcdsa(self, hash_name, sign_name, key_name): > + # Check hash and signature name/type > + if hash_name not in SHAS: > + self.Raise(hash_name + " is not supported") > + > + # Read the key > + key = ECC.import_key(tools.read_file(key_name)) > + > + # Check if the key has the expected size > + if key.pointQ.size_in_bytes() * 2 != ECDSAS[sign_name]: > + self.Raise("The key " + self.key_name + " doesn't have the > expected size") > + > + # Compute the hash > + hash_image = SHAS[hash_name].new() > + hash_image.update(self.image) > + > + # Compute the signature > + signer = DSS.new(key, 'fips-186-3') > + sig = signer.sign(hash_image) > + > + hash_sig = SHA256.new() > + hash_sig.update(sig) > + > + version = self.version > + header_size = self.header_size > + image_size = len(self.image) > + ofs_img_sig = 64 + len(sig) > + flags = 0 > + reserved0 = 0 > + reserved1 = 0 > + > + first_header = struct.pack('>4sIIIIIII32s', PRE_LOAD_MAGIC, > + version, header_size, image_size, > + ofs_img_sig, flags, reserved0, > + reserved1, hash_sig.digest()) > + > + hash_first_header = SHAS[hash_name].new() > + hash_first_header.update(first_header) > + sig_first_header = signer.sign(hash_first_header) > + > + data = first_header + sig_first_header + sig > + pad = bytearray(self.header_size - len(data)) > + > + return data + pad > + > + def _CreateHeader(self): > + """Create a pre load header""" > + hash_name, sign_name = self.algo_name.split(',') > + padding_name = self.padding_name > + key_name = os.path.join(self.key_path, self.key_name) > + > + if sign_name in RSAS: > + return self._CreateHeaderRsa(hash_name, sign_name, padding_name, > key_name) > + > + if sign_name in ECDSAS: > + return self._CreateHeaderEcdsa(hash_name, sign_name, key_name) > + > + self.Raise(sign_name + " is not supported") > + > def ObtainContents(self): > """Create a placeholder for the header""" > self.SetContents(tools.get_bytes(0, self.header_size)) > diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py > index 9a3811c1732..5c88bec9b46 100644 > --- a/tools/binman/ftest.py > +++ b/tools/binman/ftest.py > @@ -5910,12 +5910,62 @@ fdt fdtmap Extract the > devicetree blob from the fdtmap > > image_fname = tools.get_output_filename('image.bin') > is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key")) > + self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)]) > + self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)]) > + self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + > len(PRE_LOAD_HDR_SIZE)]) > + self.assertEqual(is_signed, True) > > + def testPreLoadEcdsa(self): > + """Test an image with a pre-load header using ecdsa key""" > + entry_args = { > + 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), > + } > + data = self._DoReadFileDtb( > + 'security/pre_load_ecdsa.dts', entry_args=entry_args, > + extra_indirs=[os.path.join(self._binman_dir, 'test')])[0] > + > + image_fname = tools.get_output_filename('image.bin') > + is_signed = self._CheckPreload(image_fname, > + self.TestFile('ecdsa521.pem'), > + 'sha256,ecdsa521') > self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)]) > self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)]) > self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + > len(PRE_LOAD_HDR_SIZE)]) > self.assertEqual(is_signed, True) > > + def testPreLoadEcdsaInvalidSha(self): > + """Test an image with a pre-load ecdsa header with an invalid hash""" > + entry_args = { > + 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), > + } > + with self.assertRaises(ValueError) as exc: > + self._DoReadFileDtb('security/pre_load_ecdsa_invalid_sha.dts', > + entry_args=entry_args) > + self.assertIn("Node '/binman/pre-load': sha2560 is not supported", > + str(exc.exception)) > + > + def testPreLoadEcdsaInvalidAlgo(self): > + """Test an image with a pre-load header with an invalid algo""" > + entry_args = { > + 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), > + } > + with self.assertRaises(ValueError) as exc: > + data = > self._DoReadFileDtb('security/pre_load_ecdsa_invalid_algo.dts', > + entry_args=entry_args) > + self.assertIn("Node '/binman/pre-load': ecdsa5210 is not supported", > + str(exc.exception)) > + > + def testPreLoadEcdsaInvalidKey(self): > + """Test an image with a pre-load header with an invalid key size""" > + entry_args = { > + 'pre-load-key-path': os.path.join(self._binman_dir, 'test'), > + } > + with self.assertRaises(ValueError) as exc: > + data = > self._DoReadFileDtb('security/pre_load_ecdsa_invalid_key.dts', > + entry_args=entry_args) > + self.assertIn("Node '/binman/pre-load': The key ecdsa521.pem doesn't > have the expected size", > + str(exc.exception)) > + > def _CheckSafeUniqueNames(self, *images): > """Check all entries of given images for unsafe unique names""" > for image in images: > diff --git a/tools/binman/test/ecdsa521.pem b/tools/binman/test/ecdsa521.pem > new file mode 100644 > index 00000000000..ac1904d3955 > --- /dev/null > +++ b/tools/binman/test/ecdsa521.pem > @@ -0,0 +1,7 @@ > +-----BEGIN EC PRIVATE KEY----- > +MIHcAgEBBEIBM+CNnraGci2/mw1wPq44l2HccHnoBbdP3DiU6zqsBOq8IR8uegz2 > +FLzWsjxcW7hwROCdEm6tW99wqsyPE25RZ3egBwYFK4EEACOhgYkDgYYABABu5bWV > +aQ4EgnXFjojX9df3gBEBipphEEFAoG87GuoWBdlimFC8UEEXiKNU37w0wlJn4bG0 > +8uOKwDqBk3uF+DrmZwB45lCSKkjdRWsJeDt+iEuFe2O/mbXoL4p5D8MM2OsDV5GT > +srUbxhXq+T/i5lV7XXm2+tT/7zU8ZQce6WRufbd9KQ== > +-----END EC PRIVATE KEY----- > diff --git a/tools/binman/test/security/pre_load_ecdsa.dts > b/tools/binman/test/security/pre_load_ecdsa.dts > new file mode 100644 > index 00000000000..247b85aad4c > --- /dev/null > +++ b/tools/binman/test/security/pre_load_ecdsa.dts > @@ -0,0 +1,22 @@ > +// SPDX-License-Identifier: GPL-2.0+ > + > +/dts-v1/; > + > +/ { > + #address-cells = <1>; > + #size-cells = <1>; > + > + binman { > + pre-load { > + content = <&image>; > + algo-name = "sha256,ecdsa521"; > + key-name = "ecdsa521.pem"; > + header-size = <4096>; > + version = <0x11223344>; > + }; > + > + image: blob-ext { > + filename = "refcode.bin"; > + }; > + }; > +}; > diff --git a/tools/binman/test/security/pre_load_ecdsa_invalid_algo.dts > b/tools/binman/test/security/pre_load_ecdsa_invalid_algo.dts > new file mode 100644 > index 00000000000..be71edbbdcd > --- /dev/null > +++ b/tools/binman/test/security/pre_load_ecdsa_invalid_algo.dts > @@ -0,0 +1,22 @@ > +// SPDX-License-Identifier: GPL-2.0+ > + > +/dts-v1/; > + > +/ { > + #address-cells = <1>; > + #size-cells = <1>; > + > + binman { > + pre-load { > + content = <&image>; > + algo-name = "sha256,ecdsa5210"; > + key-name = "ecdsa521.pem"; > + header-size = <4096>; > + version = <0x11223344>; > + }; > + > + image: blob-ext { > + filename = "refcode.bin"; > + }; > + }; > +}; > diff --git a/tools/binman/test/security/pre_load_ecdsa_invalid_key.dts > b/tools/binman/test/security/pre_load_ecdsa_invalid_key.dts > new file mode 100644 > index 00000000000..15d71cf0324 > --- /dev/null > +++ b/tools/binman/test/security/pre_load_ecdsa_invalid_key.dts > @@ -0,0 +1,22 @@ > +// SPDX-License-Identifier: GPL-2.0+ > + > +/dts-v1/; > + > +/ { > + #address-cells = <1>; > + #size-cells = <1>; > + > + binman { > + pre-load { > + content = <&image>; > + algo-name = "sha256,ecdsa384"; > + key-name = "ecdsa521.pem"; > + header-size = <4096>; > + version = <0x11223344>; > + }; > + > + image: blob-ext { > + filename = "refcode.bin"; > + }; > + }; > +}; > diff --git a/tools/binman/test/security/pre_load_ecdsa_invalid_sha.dts > b/tools/binman/test/security/pre_load_ecdsa_invalid_sha.dts > new file mode 100644 > index 00000000000..1017707375e > --- /dev/null > +++ b/tools/binman/test/security/pre_load_ecdsa_invalid_sha.dts > @@ -0,0 +1,22 @@ > +// SPDX-License-Identifier: GPL-2.0+ > + > +/dts-v1/; > + > +/ { > + #address-cells = <1>; > + #size-cells = <1>; > + > + binman { > + pre-load { > + content = <&image>; > + algo-name = "sha2560,ecdsa521"; > + key-name = "ecdsa521.pem"; > + header-size = <4096>; > + version = <0x11223344>; > + }; > + > + image: blob-ext { > + filename = "refcode.bin"; > + }; > + }; > +}; > -- > 2.43.0 >

