Right now, binman can only create pre-load header using rsa. We add the support of ecdsa.
Reviewed-by: Simon Glass <[email protected]> Reviewed-by: Raymond Mao <[email protected]> Signed-off-by: Philippe Reynes <[email protected]> --- 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 v5: - compute ecdsa521 sig instead of using hardcoded value 132 - fix english: don't -> doesn't - avoid line too long v6: - no change v7: - no change tools/binman/etype/pre_load.py | 78 ++++++++++++++++--- 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, 214 insertions(+), 9 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 0d953cb258e..7890c1c62a8 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': (521 + 7) / 8 * 2 +} + SHAS = { 'sha256': SHA256, 'sha384': SHA384, @@ -86,24 +94,17 @@ 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)) # 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") + self.Raise("The key " + self.key_name + " doesn't have the expected size") # Compute the hash hash_image = SHAS[hash_name].new() @@ -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 + " doesn'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): """Create a placeholder for the header""" self.SetContents(tools.get_bytes(0, self.header_size)) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 9a3811c1732..5c88bec9b46 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -5910,12 +5910,62 @@ fdt fdtmap Extract the devicetree blob from the fdtmap image_fname = tools.get_output_filename('image.bin') is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key")) + 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 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("Node '/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 doesn'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

