[PATCH v4 10/14] tools: binman: pre-load: add support of ecdsa
Philippe Reynes
philippe.reynes at softathome.com
Fri Apr 17 15:02:00 CEST 2026
Right now, binman can only create pre-load header
using rsa. We add the support of ecdsa.
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
tools/binman/etype/pre_load.py | 76 +++++++++++++++++--
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, 213 insertions(+), 8 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
diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
index 00f1a896767..057422ab0dc 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': 132
+}
+
SHAS = {
'sha256': SHA256,
'sha384': SHA384,
@@ -86,17 +94,10 @@ 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))
@@ -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 + " don'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):
"""Obtain a placeholder for the header contents"""
# wait that the image is available
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index ca5149ee654..bc0fc05a36d 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -5895,6 +5895,56 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
data = self._DoReadFileDtb('security/pre_load_invalid_key.dts',
entry_args=entry_args)
+ 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("/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 don'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