aboutsummaryrefslogtreecommitdiff
path: root/tools/binman/etype/pre_load.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/binman/etype/pre_load.py')
-rw-r--r--tools/binman/etype/pre_load.py162
1 files changed, 162 insertions, 0 deletions
diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
new file mode 100644
index 0000000..245ee75
--- /dev/null
+++ b/tools/binman/etype/pre_load.py
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2022 Softathome
+# Written by Philippe Reynes <philippe.reynes@softathome.com>
+#
+# Entry-type for the global header
+#
+
+import os
+import struct
+from dtoc import fdt_util
+from patman import tools
+
+from binman.entry import Entry
+from binman.etype.collection import Entry_collection
+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_collection):
+ """Pre load image header
+
+ Properties / Entry arguments:
+ - pre-load-key-path: Path of the directory that store key (provided by the environment variable PRE_LOAD_KEY_PATH)
+ - content: List of phandles to entries to sign
+ - 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 creates a pre-load header that contains 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 {
+ content = <&image>;
+ algo-name = "sha256,rsa2048";
+ padding-name = "pss";
+ key-name = "private.pem";
+ header-size = <4096>;
+ version = <1>;
+ };
+
+ image: blob-ext {
+ filename = "sandbox.itb";
+ };
+ };
+ };
+ """
+
+ def __init__(self, section, etype, node):
+ super().__init__(section, etype, node)
+ 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 ReadNode(self):
+ super().ReadNode()
+ self.key_path, = self.GetEntryArgsOrProps([EntryArg('pre-load-key-path', str)])
+ 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)
+
+ # 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
+ 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]:
+ 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
+ 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:
+ self.Raise(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(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 = padding.new(key, **padding_args).sign(hash_first_header)
+
+ data = first_header + sig_first_header + sig
+ pad = bytearray(self.header_size - len(data))
+
+ return data + pad
+
+ def ObtainContents(self):
+ """Obtain a placeholder for the header contents"""
+ # wait that the image is available
+ self.image = self.GetContents(False)
+ if self.image is None:
+ return False
+ self.SetContents(self._CreateHeader())
+ return True
+
+ def ProcessContents(self):
+ data = self._CreateHeader()
+ return self.ProcessContentsUpdate(data)