[PATCH v3 10/15] tools: binman: pre-load: add support of ecdsa
Philippe Reynes
philippe.reynes at softathome.com
Tue Mar 31 12:00:42 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
tools/binman/etype/pre_load.py | 78 ++++++++++++++++++++++++++++++----
tools/binman/ftest.py | 31 ++++++++++++++
2 files changed, 101 insertions(+), 8 deletions(-)
diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
index 00f1a896767..2a2636403d3 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,67 @@ class Entry_pre_load(Entry_collection):
return data + pad
+ def _CreateHeaderEcdsa(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")
+
+ # Read the key
+ key = ECC.import_key(tools.read_file(key_name))
+
+ # Check if the key has the expected size
+ # if key.size_in_bytes() != 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)
+
+ # print("len = %d\n", len(sig))
+
+ 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, padding_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 a53e37f31b3..bdb46fbf24f 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -5894,6 +5894,37 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
data = self._DoReadFileDtb('236_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(
+ '351_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 e:
+ self._DoReadFileDtb('352_pre_load_ecdsa_invalid_sha.dts',
+ entry_args=entry_args)
+
+ def testPreLoadEcdsaInvalidAlgo(self):
+ """Test an image with a pre-load header with an invalid algo"""
+ with self.assertRaises(ValueError) as e:
+ data = self._DoReadFile('353_pre_load_ecdsa_invalid_algo.dts')
+
def _CheckSafeUniqueNames(self, *images):
"""Check all entries of given images for unsafe unique names"""
for image in images:
--
2.43.0
More information about the U-Boot
mailing list