diff --git a/scripts/bootloader/do_sign.py b/scripts/bootloader/do_sign.py index 4672714324d3..fe866a36d99b 100755 --- a/scripts/bootloader/do_sign.py +++ b/scripts/bootloader/do_sign.py @@ -7,14 +7,15 @@ import argparse import contextlib -import hashlib import sys from pathlib import Path from typing import BinaryIO, Generator +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.serialization import load_pem_private_key -from ecdsa.keys import SigningKey # type: ignore[import-untyped] from intelhex import IntelHex # type: ignore[import-untyped] @@ -66,11 +67,14 @@ def hex_to_binary(input_hex_file: str) -> bytes: def sign_with_ecdsa( private_key_file: Path, input_file: Path, output_file: Path | None = None ) -> int: - with open(private_key_file, 'r') as f: - private_key = SigningKey.from_pem(f.read()) + with open(private_key_file, 'rb') as f: + private_key = load_pem_private_key(f.read(), password=None) + if not isinstance(private_key, EllipticCurvePrivateKey): + raise SystemExit(f'Private key file {private_key_file} is not Elliptic Curve key') + with open(input_file, 'rb') as f: data = f.read() - signature = private_key.sign(data, hashfunc=hashlib.sha256) + signature = private_key.sign(data, ec.ECDSA(hashes.SHA256())) with open_stream(output_file) as stream: stream.write(signature) return 0 @@ -80,7 +84,10 @@ def sign_with_ed25519( private_key_file: Path, input_file: Path, output_file: Path | None = None ) -> int: with open(private_key_file, 'rb') as f: - private_key: Ed25519PrivateKey = load_pem_private_key(f.read(), password=None) # type: ignore[assignment] + private_key = load_pem_private_key(f.read(), password=None) + if not isinstance(private_key, Ed25519PrivateKey): + raise SystemExit(f'Private key file {private_key_file} is not Ed25519 key') + if str(input_file).endswith('.hex'): data = hex_to_binary(str(input_file)) else: @@ -92,13 +99,16 @@ def sign_with_ed25519( return 0 +ALGORITHMS = { + 'ecdsa': sign_with_ecdsa, + 'ed25519': sign_with_ed25519, +} + + def main(argv=None) -> int: args = parse_args(argv) - if args.algorithm == 'ecdsa': - return sign_with_ecdsa(args.private_key, args.infile, args.outfile) - if args.algorithm == 'ed25519': - return sign_with_ed25519(args.private_key, args.infile, args.outfile) - return 1 + sign_function = ALGORITHMS[args.algorithm] + return sign_function(args.private_key, args.infile, args.outfile) if __name__ == '__main__': diff --git a/scripts/bootloader/keygen.py b/scripts/bootloader/keygen.py index a87b79df81cb..58552eabc10a 100755 --- a/scripts/bootloader/keygen.py +++ b/scripts/bootloader/keygen.py @@ -6,16 +6,15 @@ from __future__ import annotations +import abc import argparse import sys from hashlib import sha256, sha512 -from typing import BinaryIO +from typing import BinaryIO, Type from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric import ed25519 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, ed25519 from cryptography.hazmat.primitives.serialization import load_pem_private_key @@ -62,26 +61,43 @@ def generate_legal_key_for_ed25519(): return key -class EllipticCurveKeysGenerator: - """Generate private and public keys for Elliptic Curve cryptography.""" +class KeysGeneratorBase(abc.ABC): def __init__(self, infile: BinaryIO | None = None) -> None: """ :param infile: A file-like object to read the private key. """ if infile is None: - self.private_key = generate_legal_key_for_elliptic_curve() + self.private_key = self._generate_private_key() else: self.private_key = load_pem_private_key(infile.read(), password=None) self.public_key = self.private_key.public_key() @property + @abc.abstractmethod def private_key_pem(self) -> bytes: - return self.private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ) + pass + + @property + @abc.abstractmethod + def public_key_pem(self) -> bytes: + pass + + @abc.abstractmethod + def _generate_private_key(self): + pass + + @staticmethod + @abc.abstractmethod + def sign_message(private_key, message: bytes) -> bytes: + pass + + @staticmethod + @abc.abstractmethod + def verify_signature( + public_key, message: bytes, signature: bytes + ) -> bool: + pass def write_private_key_pem(self, outfile: BinaryIO) -> bytes: """ @@ -89,17 +105,9 @@ def write_private_key_pem(self, outfile: BinaryIO) -> bytes: :param outfile: A file-like object to write the private key. """ - if outfile is not None: - outfile.write(self.private_key_pem) + outfile.write(self.private_key_pem) return self.private_key_pem - @property - def public_key_pem(self) -> bytes: - return self.public_key.public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - def write_public_key_pem(self, outfile: BinaryIO) -> bytes: """ Write public key pem to file and return it. @@ -109,8 +117,32 @@ def write_public_key_pem(self, outfile: BinaryIO) -> bytes: outfile.write(self.public_key_pem) return self.public_key_pem + +class EllipticCurveKeysGenerator(KeysGeneratorBase): + """Generate private and public keys for Elliptic Curve cryptography.""" + + def _generate_private_key(self): + return generate_legal_key_for_elliptic_curve() + + @property + def private_key_pem(self) -> bytes: + return self.private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + + @property + def public_key_pem(self) -> bytes: + return self.public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + @staticmethod - def verify_signature(public_key, message: bytes, signature: bytes) -> bool: + def verify_signature( + public_key: ec.EllipticCurvePublicKey, message: bytes, signature: bytes + ) -> bool: try: public_key.verify(signature, message, ec.ECDSA(hashes.SHA256())) return True @@ -118,22 +150,15 @@ def verify_signature(public_key, message: bytes, signature: bytes) -> bool: return False @staticmethod - def sign_message(private_key, message: bytes) -> bytes: + def sign_message(private_key: ec.EllipticCurvePrivateKey, message: bytes) -> bytes: return private_key.sign(message, ec.ECDSA(hashes.SHA256())) -class Ed25519KeysGenerator: +class Ed25519KeysGenerator(KeysGeneratorBase): """Generate private and public keys for ED25519 cryptography.""" - def __init__(self, infile: BinaryIO | None = None) -> None: - """ - :param infile: A file-like object to read the private key. - """ - if infile is None: - self.private_key: ed25519.Ed25519PrivateKey = generate_legal_key_for_ed25519() - else: - self.private_key = load_pem_private_key(infile.read(), password=None) # type: ignore[assignment] - self.public_key: ed25519.Ed25519PublicKey = self.private_key.public_key() + def _generate_private_key(self): + return generate_legal_key_for_ed25519() @property def private_key_pem(self) -> bytes: @@ -143,15 +168,6 @@ def private_key_pem(self) -> bytes: encryption_algorithm=serialization.NoEncryption() ) - def write_private_key_pem(self, outfile: BinaryIO) -> bytes: - """ - Write private key pem to file and return it. - - :param outfile: A file-like object to write the private key. - """ - outfile.write(self.private_key_pem) - return self.private_key_pem - @property def public_key_pem(self) -> bytes: return self.public_key.public_bytes( @@ -159,18 +175,10 @@ def public_key_pem(self) -> bytes: format=serialization.PublicFormat.SubjectPublicKeyInfo ) - def write_public_key_pem(self, outfile: BinaryIO) -> bytes: - """ - Write public key pem to file and return it. - - :param outfile: A file-like object to write the public key. - """ - if outfile is not None: - outfile.write(self.public_key_pem) - return self.public_key_pem - @staticmethod - def verify_signature(public_key: ed25519.Ed25519PublicKey, message: bytes, signature: bytes) -> bool: + def verify_signature( + public_key: ed25519.Ed25519PublicKey, message: bytes, signature: bytes + ) -> bool: try: public_key.verify(signature, message) return True @@ -178,15 +186,22 @@ def verify_signature(public_key: ed25519.Ed25519PublicKey, message: bytes, signa return False @staticmethod - def sign_message(private_key, message: bytes) -> bytes: + def sign_message(private_key: ed25519.Ed25519PrivateKey, message: bytes) -> bytes: return private_key.sign(message) +ALGORITHMS: dict[str, Type[KeysGeneratorBase]] = { + "ed25519": Ed25519KeysGenerator, + "ec": EllipticCurveKeysGenerator, +} + + def main(argv=None) -> int: parser = argparse.ArgumentParser( description='Generate PEM file.', formatter_class=argparse.RawDescriptionHelpFormatter, - allow_abbrev=False) + allow_abbrev=False + ) priv_pub_group = parser.add_mutually_exclusive_group(required=True) priv_pub_group.add_argument('--private', required=False, action='store_true', @@ -207,19 +222,15 @@ def main(argv=None) -> int: args = parser.parse_args(argv) - if args.algorithm == 'ed25519': - ed25519_generator = Ed25519KeysGenerator(args.infile) - if args.private: - ed25519_generator.write_private_key_pem(args.out) - if args.public: - ed25519_generator.write_public_key_pem(args.out) - else: - ec_generator = EllipticCurveKeysGenerator(args.infile) - if args.private: - ec_generator.write_private_key_pem(args.out) - elif args.public: - ec_generator.write_public_key_pem(args.out) + try: + generator = ALGORITHMS[args.algorithm](args.infile) + except KeyError: + sys.exit(f'Unknown algorithm {args.algorithm}.') + if args.private: + generator.write_private_key_pem(args.out) + if args.public: + generator.write_public_key_pem(args.out) return 0 diff --git a/scripts/bootloader/provision.py b/scripts/bootloader/provision.py index 80128d0527e9..524d255c1b36 100644 --- a/scripts/bootloader/provision.py +++ b/scripts/bootloader/provision.py @@ -3,15 +3,15 @@ # Copyright (c) 2018 Nordic Semiconductor ASA # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - - -from intelhex import IntelHex - import argparse +import os import struct -from ecdsa import VerifyingKey from hashlib import sha256 -import os + +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec, ed25519 +from cryptography.hazmat.primitives.serialization import load_pem_public_key +from intelhex import IntelHex # Size of implementation ID in OTP in bytes IMPLEMENTATION_ID_SIZE = 0x20 @@ -21,6 +21,7 @@ BL_MONOTONIC_COUNTERS_DESC_NSIB = 1 BL_MONOTONIC_COUNTERS_DESC_MCUBOOT_ID0 = 2 + def generate_provision_intel_hex_file(provision_data, provision_address, output, max_size): assert len(provision_data) <= max_size, """Provisioning data doesn't fit. Reduce the number of public keys or counter slots and try again.""" @@ -37,10 +38,10 @@ def add_hw_counters(provision_data, num_counter_slots_version, mcuboot_counters_ return provision_data assert num_counter_slots_version % 2 == 0, "--num-counters-slots-version must be an even number" - assert mcuboot_counters_slots % 2 == 0, "--mcuboot-counters-slots must be an even number" + assert mcuboot_counters_slots % 2 == 0, "--mcuboot-counters-slots must be an even number" - provision_data += struct.pack('H', 1) # Type "counter collection" - provision_data += struct.pack('H', num_counters) # Could be 0, 1, or 2 + provision_data += struct.pack('H', 1) # Type "counter collection" + provision_data += struct.pack('H', num_counters) # Could be 0, 1, or 2 if num_counter_slots_version > 0: provision_data += struct.pack('H', BL_MONOTONIC_COUNTERS_DESC_NSIB) @@ -98,7 +99,7 @@ def generate_provision_hex_file(s0_address, s1_address, hashes, provision_addres idx = 0 for mhash in hashes: - provision_data += struct.pack('I', 0x50FAFFFF | (idx << 24)) # Invalidation token + provision_data += struct.pack('I', 0x50FAFFFF | (idx << 24)) # Invalidation token provision_data += mhash idx += 1 @@ -135,12 +136,26 @@ def parse_args(): return parser.parse_args() +def public_key_to_string(public_key) -> bytes: + if isinstance(public_key, ec.EllipticCurvePublicKey): + return public_key.public_bytes( + encoding=serialization.Encoding.X962, + format=serialization.PublicFormat.UncompressedPoint + )[1:] + if isinstance(public_key, ed25519.Ed25519PublicKey): + return public_key.public_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw + ) + raise NotImplementedError + + def get_hashes(public_key_files, verify_hashes): hashes = list() for fn in public_key_files: with open(fn, 'rb') as f: - digest = sha256(VerifyingKey.from_pem(f.read()).to_string()).digest()[:16] - if verify_hashes and any([digest[n:n+2] == b'\xff\xff' for n in range(0, len(digest), 2)]): + digest = sha256(public_key_to_string(load_pem_public_key(f.read()))).digest()[:16] + if verify_hashes and any([digest[n:n + 2] == b'\xff\xff' for n in range(0, len(digest), 2)]): raise RuntimeError("Hash of key in '%s' contains 0xffff. Please regenerate the key." % os.path.abspath(f.name)) hashes.append(digest) @@ -171,7 +186,6 @@ def main(): ) return - s0_address = args.s0_addr s1_address = args.s1_addr if args.s1_addr is not None else s0_address @@ -179,7 +193,7 @@ def main(): # rest of the provisioning data so add it to the given base # address provision_address = args.provision_addr + num_bytes_provisioned_elsewhere - max_size = args.max_size - num_bytes_provisioned_elsewhere + max_size = args.max_size - num_bytes_provisioned_elsewhere hashes = [] if args.public_key_files: diff --git a/scripts/bootloader/tests/do_sign_test.py b/scripts/bootloader/tests/do_sign_test.py index c21bdb988816..60d4c7d573b1 100644 --- a/scripts/bootloader/tests/do_sign_test.py +++ b/scripts/bootloader/tests/do_sign_test.py @@ -3,23 +3,11 @@ # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -import hashlib - -from ecdsa.keys import VerifyingKey, BadSignatureError # type: ignore[import-untyped] -from ecdsa.util import sigdecode_string # type: ignore[import-untyped] - +from cryptography.hazmat.primitives.serialization import load_pem_public_key from do_sign import sign_with_ecdsa, sign_with_ed25519 from keygen import Ed25519KeysGenerator, EllipticCurveKeysGenerator -def verify_ecdsa_signature(public_key: VerifyingKey, message: bytes, signature: bytes) -> bool: - try: - public_key.verify(signature, message, hashlib.sha256, sigdecode=sigdecode_string) - return True - except BadSignatureError: - return False - - def test_if_file_is_properly_signed_with_ec_key(tmpdir): generator = EllipticCurveKeysGenerator() private_key_file = tmpdir / 'private.pem' @@ -39,9 +27,10 @@ def test_if_file_is_properly_signed_with_ec_key(tmpdir): output_file=signature_file, ) - public_key = VerifyingKey.from_pem(public_key_file.open('br').read()) - signature = signature_file.open('rb').read() - assert verify_ecdsa_signature(public_key=public_key, message=message, signature=signature) + public_key = load_pem_public_key(public_key_file.open('rb').read()) + assert EllipticCurveKeysGenerator.verify_signature( + public_key, message, signature_file.open('br').read() + ) def test_if_validation_does_not_pass_for_wrong_ec_key(tmpdir): @@ -62,10 +51,9 @@ def test_if_validation_does_not_pass_for_wrong_ec_key(tmpdir): output_file=signature_file, ) - public_key = VerifyingKey.from_pem(public_key_file.open('br').read()) - signature = signature_file.open('rb').read() - assert verify_ecdsa_signature( - public_key=public_key, message=message, signature=signature + public_key = load_pem_public_key(public_key_file.open('rb').read()) + assert EllipticCurveKeysGenerator.verify_signature( + public_key, message, signature_file.open('br').read() ) is False diff --git a/scripts/bootloader/tests/validation_data_test.py b/scripts/bootloader/tests/validation_data_test.py index 0c213bec5dda..e7176ba25828 100644 --- a/scripts/bootloader/tests/validation_data_test.py +++ b/scripts/bootloader/tests/validation_data_test.py @@ -6,16 +6,13 @@ import hashlib import textwrap -import ecdsa # type: ignore[import-untyped] - import do_sign +from asn1parse import get_ecdsa_signature +from cryptography.hazmat.primitives.serialization import load_pem_public_key from hash import generate_hash_digest from keygen import Ed25519KeysGenerator, EllipticCurveKeysGenerator -from validation_data import ( - Ed25519SignatureValidator, - EcdsaSignatureValidator, - main as validation_data_main -) +from validation_data import EcdsaSignatureValidator, Ed25519SignatureValidator +from validation_data import main as validation_data_main DUMMY_ZEPHYR_HEX = textwrap.dedent("""\ :1098000050110020EDA60000E5DE0000D9A6000002 @@ -56,7 +53,7 @@ def test_data_validation_for_ec(tmpdir): do_sign.sign_with_ecdsa(private_key_file, hash_file, message_signature_file) - public_key = ecdsa.VerifyingKey.from_pem(public_key_file.read()) + public_key = load_pem_public_key(public_key_file.open('rb').read()) EcdsaSignatureValidator(hashfunc=hashlib.sha256).append_validation_data( signature_file=message_signature_file, input_file=zephyr_hex_file, @@ -66,7 +63,7 @@ def test_data_validation_for_ec(tmpdir): output_bin=output_bin_file.open('wb'), magic_value=magic_value ) - assert message_signature_file.open('rb').read() in output_bin_file.open('rb').read() + assert get_ecdsa_signature(message_signature_file.open('rb').read(), 32) in output_bin_file.open('rb').read() # check with CLI command too assert validation_data_main( [ @@ -80,7 +77,8 @@ def test_data_validation_for_ec(tmpdir): '--magic-value', magic_value ] ) == 0 - assert message_signature_file.open('rb').read() in output_bin_file.open('rb').read() + + assert get_ecdsa_signature(message_signature_file.open('rb').read(), 32) in output_bin_file.open('rb').read() def test_data_validation_for_ed25519(tmpdir): @@ -163,3 +161,32 @@ def test_data_validation_for_signed_hex_file_with_ed25519(tmpdir): ] ) == 0 assert message_signature_file.open('rb').read() in output_bin_file.open('rb').read() + + +def test_ecdsa_public_key_to_string(tmpdir): + public_key_data = textwrap.dedent("""\ + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEihHfHl4KigTP4uFhlo1M1IkezteI + DLYmPEVtPrPYrQtqpctToFIG8uV3g6MAMhqPG/h69polPuvN3Lo56YnXhA== + -----END PUBLIC KEY----- + """) + public_key = load_pem_public_key(public_key_data.encode()) + validator = EcdsaSignatureValidator(hashfunc=hashlib.sha256) + expected_bytes = (b'\x8a\x11\xdf\x1e^\n\x8a\x04\xcf\xe2\xe1a\x96\x8dL\xd4\x89\x1e' + b'\xce\xd7\x88\x0c\xb6&\xb3\xd8\xad\x0bj\xa5\xcbS\xa0R\x06' + b'\xf2\xe5w\x83\xa3\x002\x1a\x8f\x1b\xf8z\xf6\x9a%>\xeb\xcd\xdc' + b'\xba9\xe9\x89\xd7\x84') + assert validator.to_string(public_key) == expected_bytes + + +def test_ed25519_public_key_to_string(tmpdir): + public_key_data = textwrap.dedent("""\ + -----BEGIN PUBLIC KEY----- + MCowBQYDK2VwAyEAHq4VC8pj/yLm8a7IzOZfkjB4o6Vk6UvRGGKAgpl24q8= + -----END PUBLIC KEY----- + """) + public_key = load_pem_public_key(public_key_data.encode()) + validator = Ed25519SignatureValidator() + expected_bytes = (b'\x1e\xae\x15\x0b\xcac\xff"\xe6\xf1\xae\xc8\xcc\xe6_\x920x' + b'\xa3\xa5d\xe9K\xd1\x18b\x80\x82\x99v\xe2\xaf') + assert validator.to_string(public_key) == expected_bytes diff --git a/scripts/bootloader/validation_data.py b/scripts/bootloader/validation_data.py index 1dfbe48e312b..f2857338272c 100755 --- a/scripts/bootloader/validation_data.py +++ b/scripts/bootloader/validation_data.py @@ -12,15 +12,20 @@ import struct import sys from pathlib import Path -from typing import TextIO, BinaryIO +from typing import BinaryIO, TextIO -import ecdsa # type: ignore[import-untyped] -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ed25519 +from asn1parse import get_ecdsa_signature +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, ed25519 from cryptography.hazmat.primitives.serialization import load_pem_public_key from intelhex import IntelHex # type: ignore[import-untyped] +def load_public_key(file_name: Path): + with open(file_name, 'rb') as f: + return load_pem_public_key(f.read()) + + class BaseValidator(abc.ABC): def __init__(self, hashfunc=None) -> None: @@ -45,7 +50,7 @@ def get_validation_data( self, signature_bytes: bytes, input_hex: IntelHex, - public_key: ecdsa.VerifyingKey | ed25519.Ed25519PublicKey, + public_key, magic_value: bytes ) -> bytes: hash_bytes = self.get_hash(input_hex) @@ -66,7 +71,7 @@ def append_validation_data( self, signature_file: Path, input_file: Path, - public_key: ecdsa.VerifyingKey | ed25519.Ed25519PublicKey, + public_key: ec.EllipticCurvePublicKey | ed25519.Ed25519PublicKey, offset: int, output_hex: TextIO, output_bin: BinaryIO | None, @@ -74,7 +79,8 @@ def append_validation_data( ) -> None: with open(input_file, 'r', encoding='UTF-8') as f: ih = IntelHex(f) - ih.start_addr = None # OBJCOPY incorrectly inserts x86 specific records, remove the start_addr as it is wrong. + # OBJCOPY incorrectly inserts x86 specific records, remove the start_addr as it is wrong. + ih.start_addr = None minimum_offset = ((ih.maxaddr() // 4) + 1) * 4 if offset != 0 and offset < minimum_offset: @@ -118,11 +124,41 @@ def _read_text(self, file, encoding=None) -> str: class EcdsaSignatureValidator(BaseValidator): - def to_string(self, public_key) -> bytes: - return public_key.to_string() + def get_validation_data( + self, + signature_bytes: bytes, + input_hex: IntelHex, + public_key, + magic_value: bytes + ) -> bytes: + hash_bytes = self.get_hash(input_hex) + public_key_bytes = self.to_string(public_key) - def verify(self, public_key: ecdsa.VerifyingKey, signature_bytes: bytes, message_bytes: bytes): - public_key.verify(signature_bytes, message_bytes, hashfunc=self.hashfunc) + # Will raise an exception if it fails + self.verify(public_key, signature_bytes, hash_bytes) + + validation_bytes = magic_value + validation_bytes += struct.pack('I', input_hex.addresses()[0]) + validation_bytes += hash_bytes + validation_bytes += public_key_bytes + validation_bytes += get_ecdsa_signature(signature_bytes, 32) + + return validation_bytes + + def to_string(self, public_key: ec.EllipticCurvePublicKey) -> bytes: + public_key_bytes = public_key.public_bytes( + encoding=serialization.Encoding.X962, + format=serialization.PublicFormat.UncompressedPoint, + ) + return public_key_bytes[1:] + + def verify( + self, + public_key: ec.EllipticCurvePublicKey, + signature_bytes: bytes, + message_bytes: bytes + ): + public_key.verify(signature_bytes, message_bytes, ec.ECDSA(hashes.SHA256())) class Ed25519SignatureValidator(BaseValidator): @@ -135,8 +171,12 @@ def to_string(self, public_key) -> bytes: ) return public_key_bytes - def verify(self, public_key: ed25519.Ed25519PublicKey, signature_bytes: bytes, - message_bytes: bytes): + def verify( + self, + public_key: ed25519.Ed25519PublicKey, + signature_bytes: bytes, + message_bytes: bytes + ): public_key.verify(signature_bytes, message_bytes) def get_validation_data( @@ -163,7 +203,7 @@ def append_validation_data( self, signature_file: Path, input_file: Path, - public_key: ecdsa.VerifyingKey | ed25519.Ed25519PublicKey, + public_key: ec.EllipticCurvePublicKey | ed25519.Ed25519PublicKey, offset: int, output_hex: TextIO, output_bin: BinaryIO | None, @@ -182,7 +222,8 @@ def append_validation_data( with open(input_file, 'r', encoding='UTF-8') as f: ih = IntelHex(f) - ih.start_addr = None # OBJCOPY incorrectly inserts x86 specific records, remove the start_addr as it is wrong. + # OBJCOPY incorrectly inserts x86 specific records, remove the start_addr as it is wrong. + ih.start_addr = None minimum_offset = ((ih.maxaddr() // 4) + 1) * 4 if offset != 0 and offset < minimum_offset: @@ -260,37 +301,25 @@ def parse_args(argv=None): def main(argv=None) -> int: args = parse_args(argv) + signature_validator: EcdsaSignatureValidator | Ed25519SignatureValidator if args.algorithm == 'ecdsa': - with open(args.public_key, 'r', encoding='UTF-8') as f: - public_key = ecdsa.VerifyingKey.from_pem(f.read()) - EcdsaSignatureValidator(hashfunc=hashlib.sha256).append_validation_data( - signature_file=args.signature, - input_file=args.input, - public_key=public_key, - offset=args.offset, - output_hex=args.output_hex, - output_bin=args.output_bin, - magic_value=args.magic_value - ) + signature_validator = EcdsaSignatureValidator(hashfunc=hashlib.sha256) elif args.algorithm == 'ed25519': - with open(args.public_key, 'rb') as f: - public_key = load_pem_public_key(f.read()) - if args.hash == 'sha512': - hashfunction = hashlib.sha512 - else: - hashfunction = None - Ed25519SignatureValidator(hashfunction).append_validation_data( - signature_file=args.signature, - input_file=args.input, - public_key=public_key, - offset=args.offset, - output_hex=args.output_hex, - output_bin=args.output_bin, - magic_value=args.magic_value - ) + hashfunction = hashlib.sha512 if args.hash == 'sha512' else None + signature_validator = Ed25519SignatureValidator(hashfunction) else: - raise SystemExit('Not implemented') - + raise SystemExit('Algorithm not implemented') + + public_key = load_public_key(args.public_key) + signature_validator.append_validation_data( + signature_file=args.signature, + input_file=args.input, + public_key=public_key, + offset=args.offset, + output_hex=args.output_hex, + output_bin=args.output_bin, + magic_value=args.magic_value + ) return 0 diff --git a/scripts/requirements-test.txt b/scripts/requirements-test.txt index f59168095eb9..bbaf0df33b21 100644 --- a/scripts/requirements-test.txt +++ b/scripts/requirements-test.txt @@ -1,5 +1,4 @@ # packages required to run tests for Python scripts cryptography -ecdsa intelhex pytest