[PATCH v6 11/15] tools: binman: pre-load: add support of ecdsa

Raymond Mao raymondmaoca at gmail.com
Mon May 25 17:35:26 CEST 2026


Hi Philippe,

On Mon, May 25, 2026 at 9:52 AM Philippe Reynes
<philippe.reynes at softathome.com> wrote:
>
> Right now, binman can only create pre-load header
> using rsa. We add the support of ecdsa.
>
> Reviewed-by: Simon Glass <sjg at chromium.org>
> Signed-off-by: Philippe Reynes <philippe.reynes at softathome.com>
> ---
> 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 <raymondmaoca at gmail.com>


> 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
>


More information about the U-Boot mailing list