[PATCH v5 12/16] tools: binman: add support for pre-load header

Philippe Reynes philippe.reynes at softathome.com
Wed Feb 9 19:01:24 CET 2022


Adds the support of the pre-load header with the image signature
to binman.

Signed-off-by: Philippe Reynes <philippe.reynes at softathome.com>
---
 tools/binman/etype/pre_load.py | 156 +++++++++++++++++++++++++++++++++
 1 file changed, 156 insertions(+)
 create mode 100644 tools/binman/etype/pre_load.py

diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
new file mode 100644
index 0000000000..adc25fe844
--- /dev/null
+++ b/tools/binman/etype/pre_load.py
@@ -0,0 +1,156 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2022 Softathome
+# Written by Philippe Reynes <philippe.reynes at softathome.com>
+#
+# Entry-type for the global header
+#
+
+import struct
+from dtoc import fdt_util
+from patman import tools
+
+from binman.entry import Entry
+from binman.etype.blob import Entry_blob
+from binman.entry import EntryArg
+
+from Cryptodome.Hash import SHA256, SHA384, SHA512
+from Cryptodome.PublicKey import RSA
+from Cryptodome.Signature import pkcs1_15
+from Cryptodome.Signature import pss
+
+PRE_LOAD_MAGIC = b'UBSH'
+
+RSAS = {
+    'rsa1024': 1024 / 8,
+    'rsa2048': 2048 / 8,
+    'rsa4096': 4096 / 8
+}
+
+SHAS = {
+    'sha256': SHA256,
+    'sha384': SHA384,
+    'sha512': SHA512
+}
+
+class Entry_pre_load(Entry_blob):
+    """Pre load image header
+
+    Properties / Entry arguments:
+        - key-path: Path of the directory that store key (provided by the environment variable KEY_PATH)
+        - image: Filename of the image
+        - algo-name: Hash and signature algo to use for the signature
+        - padding-name: Name of the padding (pkcs-1.5 or pss)
+        - key-name: Filename of the private key to sign
+        - header-size: Total size of the header
+        - version: Version of the header
+
+    This entry create a pre-load header that contain a global
+    image signature.
+
+    For example, this creates an image with a pre-load header and a binary::
+
+        binman {
+	    image2 {
+		filename = "sandbox.bin";
+
+		pre-load {
+		    image = "sandbox.itb";
+		    algo-name = "sha256,rsa2048";
+		    padding-name = "pss";
+		    key-name = "private.pem";
+		    header-size = <4096>;
+		    version = <1>;
+		};
+
+		blob-ext {
+		    filename = "sandbox.itb";
+		};
+	    };
+        };
+    """
+
+    def __init__(self, section, etype, node):
+        super().__init__(section, etype, node)
+        self.image = fdt_util.GetString(self._node, 'image')
+        self.pathimage = tools.GetInputFilename(self.image,
+                                               self.external and self.section.GetAllowMissing())
+        self.algo_name = fdt_util.GetString(self._node, 'algo-name')
+        self.padding_name = fdt_util.GetString(self._node, 'padding-name')
+        self.key_name = fdt_util.GetString(self._node, 'key-name')
+        self.header_size = fdt_util.GetInt(self._node, 'header-size')
+        self.version = fdt_util.GetInt(self._node, 'version')
+
+    def _CreateHeader(self):
+        """Create a pre load header"""
+        with open(self.pathimage, 'rb') as f:
+            image = f.read()
+        hash_name, sign_name  = self.algo_name.split(',')
+        padding_name=self.padding_name
+        key_path, = self.GetEntryArgsOrProps([EntryArg('key-path', str)])
+        if key_path == "":
+            key_name = self.key_name
+        else:
+            key_name = key_path + "/" + self.key_name
+
+        # Check hash and signature name/type
+        if hash_name not in SHAS:
+            raise ValueError(hash_name + " is not supported")
+        if sign_name not in RSAS:
+            raise ValueError(sign_name + "is not supported")
+
+        # Read the key
+        with open(key_name, 'rb') as pem:
+            key = RSA.import_key(pem.read())
+
+        # Check if the key has the expected size
+        if key.size_in_bytes() != RSAS[sign_name]:
+            raise ValueError("The key " + self.key_name + " don't have the expected size")
+
+
+        # Compute the hash
+        hash_image = SHAS[hash_name].new()
+        with open(self.pathimage, 'rb') as f:
+            image = f.read()
+        hash_image.update(image)
+
+        # Compute the signature
+        if padding_name is None:
+            padding_name = "pkcs-1.5"
+        if padding_name == "pss":
+            salt_len = key.size_in_bytes() - hash_image.digest_size - 2
+            padding = pss
+            padding_args = {'salt_bytes': salt_len}
+        elif padding_name == "pkcs-1.5":
+            padding = pkcs1_15
+            padding_args = {}
+        else:
+            raise ValueError(padding_name + " is not supported")
+
+        sig = padding.new(key, **padding_args).sign(hash_image)
+
+        hash_sig = SHA256.new()
+        hash_sig.update(sig)
+
+        version = self.version
+        header_size = self.header_size
+        image_size = len(image)
+        ofs_img_sig = 64 + len(sig)
+        flags = 0
+        reserved0 = 0
+        reserved1 = 0
+
+        first_header = PRE_LOAD_MAGIC + struct.pack('<IIIIIII', 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 = padding.new(key, **padding_args).sign(hash_first_header)
+
+        data = first_header + sig_first_header + sig
+        padd = bytearray(self.header_size - len(data))
+
+        return data + padd
+
+    def ObtainContents(self):
+        """Obtain a placeholder for the header contents"""
+        self.SetContents(self._CreateHeader())
+        return True
-- 
2.17.1



More information about the U-Boot mailing list