From: Philippe Reynes Date: Mon, 28 Mar 2022 20:57:04 +0000 (+0200) Subject: tools: binman: add support for pre-load header X-Git-Url: http://git.dujemihanovic.xyz/login.html?a=commitdiff_plain;h=b1c5093008aa92687fbbea9d5de26f83fb02faba;p=u-boot.git tools: binman: add support for pre-load header Adds the support of the pre-load header with the image signature to binman. Reviewed-by: Simon Glass Signed-off-by: Philippe Reynes --- diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index be8de5560c..ae4305c99e 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1155,6 +1155,44 @@ placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'. +Entry: pre-load: Pre load image header +-------------------------------------- + +Properties / Entry arguments: + - key-path: Path of the directory that store key (provided by the environment variable 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"; + }; + }; + }; + + + Entry: scp: System Control Processor (SCP) firmware blob -------------------------------------------------------- diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py new file mode 100644 index 0000000000..245ee75525 --- /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 +# +# 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) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 876953f113..4ce181a066 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -91,6 +91,9 @@ SCP_DATA = b'scp' TEST_FDT1_DATA = b'fdt1' TEST_FDT2_DATA = b'test-fdt2' ENV_DATA = b'var1=1\nvar2="2"' +PRE_LOAD_MAGIC = b'UBSH' +PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big') +PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big') # Subdirectory of the input dir to use to put test FDTs TEST_FDT_SUBDIR = 'fdts' @@ -5471,6 +5474,54 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err, "Image '.*' is missing external blobs and is non-functional: .*") + def testPreLoad(self): + """Test an image with a pre-load header""" + entry_args = { + 'pre-load-key-path': '.', + } + data, _, _, _ = self._DoReadFileDtb('225_pre_load.dts', + entry_args=entry_args) + 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)]) + data = self._DoReadFile('225_pre_load.dts') + 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)]) + + def testPreLoadPkcs(self): + """Test an image with a pre-load header with padding pkcs""" + data = self._DoReadFile('226_pre_load_pkcs.dts') + 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)]) + + def testPreLoadPss(self): + """Test an image with a pre-load header with padding pss""" + data = self._DoReadFile('227_pre_load_pss.dts') + 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)]) + + def testPreLoadInvalidPadding(self): + """Test an image with a pre-load header with an invalid padding""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('228_pre_load_invalid_padding.dts') + + def testPreLoadInvalidSha(self): + """Test an image with a pre-load header with an invalid hash""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('229_pre_load_invalid_sha.dts') + + def testPreLoadInvalidAlgo(self): + """Test an image with a pre-load header with an invalid algo""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('230_pre_load_invalid_algo.dts') + + def testPreLoadInvalidKey(self): + """Test an image with a pre-load header with an invalid key""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('231_pre_load_invalid_key.dts') if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/225_dev.key b/tools/binman/test/225_dev.key new file mode 100644 index 0000000000..b36bad2cfb --- /dev/null +++ b/tools/binman/test/225_dev.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDYngNWUvXYRXX/ +WEUI7k164fcpv1srXz+u+5Y3Yhouw3kPs+ffvYyHAPfjF7aUIAgezKk/4o7AvsxE +Rdih3T+0deAd/q/yuqN4Adzt6ImnsO/EqdtYl3Yh+Vck9xWhLd3SAw1++GfSmNMT +gxlcc/z6z+bIh2tJNtPtRSNNHMmvYYOkBmkfwcjbMXD+fe4vBwYjVrIize+l7Yuv +1qN2nFlq56pFi8Lj5vOvFyNhZHRvwcpWdUdkx39beNUfwrGhgewOeWngTcY75n7S +FY45TBR1G2PR90CQvyDinCi9Mm0u5s+1WASQWPblovfD6CPbHQu4GZm+FAs7yUvr +hA7VCyNxAgMBAAECggEAUbq0uaJNfc8faTtNuMPo2d9eGRNI+8FRTt0/3R+Xj2NT +TvhrGUD0P4++96Df012OkshXZ3I8uD6E5ZGQ3emTeqwq5kZM7oE64jGZwO3G2k1o ++cO4reFfwgvItHrBX3HlyrI6KljhG1Vr9mW1cOuWXK+KfMiTUylrpo86dYLSGeg3 +7ZlsOPArr4eof/A0iPryQZX6X5POf7k/e9qRFYsOkoRQO8pBL3J4rIKwBl3uBN3K ++FY40vCkd8JyTo2DNfHeIe1XYA9fG2ahjD2qMsw10TUsRRMd5yhonEcJ7VzGzy8m +MnuMDAr7CwbbLkKi4UfZUl6YDkojqerwLOrxikBqkQKBgQD6sS6asDgwiq5MtstE +4/PxMrVEsCdkrU+jjQN749qIt/41a6lbp0Pr6aUKKKGs0QbcnCtlpp7qmhvymBcW +hlqxk2wokKMChv4WLXjZS3DGcOdMglc81y2F+252bToN8vwUfm6DPp9/GKtejA0a +GP57GeHxoVO7vfDX1F/vZRogRQKBgQDdNCLWOlGWvnKjfgNZHgX+Ou6ZgTSAzy+/ +hRsZPlY5nwO5iD7YkIKvqBdOmfyjlUpHWk2uAcT9pfgzYygvyBRaoQhAYBGkHItt +slaMxnLd+09wWufoCbgJvFn+wVQxBLcA5PXB98ws0Dq8ZYuo6AOuoRivsSO4lblK +MW0guBJXPQKBgQDGjf0ukbH/aGfC5Oi8SJvWhuYhYC/jQo2YKUEAKCjXLnuOThZW +PHXEbUrFcAcVfH0l0B9jJIQrpiHKlAF9Wq6MhQoeWuhxQQAQCrXzzRemZJgd9gIo +cvlgbBNCgyJ/F9vmU3kuRDRJkv1wJhbee7tbPtXA7pkGUttl5pSRZI87zQKBgQC/ +0ZkwCox72xTQP9MpcYai6nnDta5Q0NnIC+Xu4wakmwcA2WweIlqhdnMXnyLcu/YY +n+9iqHgpuMXd0eukW62C1cexA13o4TPrYU36b5BmfKprdPlLVzo3fxTPfNjEVSFY +7jNLC9YLOlrkym3sf53Jzjr5B/RA+d0ewHOwfs6wxQKBgFSyfjx5wtdHK4fO+Z1+ +q3bxouZryM/4CiPCFuw4+aZmRHPmufuNCvfXdF+IH8dM0E9ObwKZAe/aMP/Y+Abx +Wz9Vm4CP6g7k3DU3INEygyjmIQQDKQ9lFdDnsP9ESzrPbaGxZhc4x2lo7qmeW1BR +/RuiAofleFkT4s+EhLrfE/v5 +-----END PRIVATE KEY----- diff --git a/tools/binman/test/225_pre_load.dts b/tools/binman/test/225_pre_load.dts new file mode 100644 index 0000000000..c1ffe1a2ff --- /dev/null +++ b/tools/binman/test/225_pre_load.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,rsa2048"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <0x11223344>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/226_pre_load_pkcs.dts b/tools/binman/test/226_pre_load_pkcs.dts new file mode 100644 index 0000000000..3db0a37f77 --- /dev/null +++ b/tools/binman/test/226_pre_load_pkcs.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <0x11223344>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/227_pre_load_pss.dts b/tools/binman/test/227_pre_load_pss.dts new file mode 100644 index 0000000000..b1b01d5ad5 --- /dev/null +++ b/tools/binman/test/227_pre_load_pss.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <0x11223344>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/228_pre_load_invalid_padding.dts b/tools/binman/test/228_pre_load_invalid_padding.dts new file mode 100644 index 0000000000..84fe289183 --- /dev/null +++ b/tools/binman/test/228_pre_load_invalid_padding.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "padding"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/229_pre_load_invalid_sha.dts b/tools/binman/test/229_pre_load_invalid_sha.dts new file mode 100644 index 0000000000..a2b6725c89 --- /dev/null +++ b/tools/binman/test/229_pre_load_invalid_sha.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha2560,rsa2048"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/230_pre_load_invalid_algo.dts b/tools/binman/test/230_pre_load_invalid_algo.dts new file mode 100644 index 0000000000..34c8d34f15 --- /dev/null +++ b/tools/binman/test/230_pre_load_invalid_algo.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa20480"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/231_pre_load_invalid_key.dts b/tools/binman/test/231_pre_load_invalid_key.dts new file mode 100644 index 0000000000..08d5a75ddf --- /dev/null +++ b/tools/binman/test/231_pre_load_invalid_key.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa4096"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +};