diff --git a/.github/workflows/zephyr_build.yaml b/.github/workflows/zephyr_build.yaml index a34872d64..c5032b6e4 100644 --- a/.github/workflows/zephyr_build.yaml +++ b/.github/workflows/zephyr_build.yaml @@ -1,4 +1,5 @@ # Copyright (c) 2022-2023 Nordic Semiconductor ASA +# Copyright (c) 2024, Arm Limited # SPDX-License-Identifier: Apache-2.0 name: Build Zephyr samples with Twister @@ -91,6 +92,7 @@ jobs: export ZEPHYR_TOOLCHAIN_VARIANT=zephyr echo "Using Zephyr version: ${{ env.ZEPHYR_VERSION }}" echo "Using Mcuboot version: ${{ env.MCUBOOT_VERSION }}" + pip install -r ../bootloader/mcuboot/scripts/requirements.txt ./scripts/twister --inline-logs -v -N -M --integration --overflow-as-errors --retry-failed 2 ${test_paths} - name: Upload Tests Results diff --git a/ci/fih_test_docker/execute_test.sh b/ci/fih_test_docker/execute_test.sh index cc67d846a..5defacfd0 100755 --- a/ci/fih_test_docker/execute_test.sh +++ b/ci/fih_test_docker/execute_test.sh @@ -1,6 +1,6 @@ #!/bin/bash -x -# Copyright (c) 2020-2023 Arm Limited +# Copyright (c) 2020-2024 Arm Limited # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,6 +30,9 @@ else CMAKE_FIH_LEVEL="-DMCUBOOT_FIH_PROFILE=\"$FIH_LEVEL\"" fi +# Install imgtool dependencies +pip install -r $MCUBOOT_PATH/scripts/requirements.txt + # build TF-M with MCUBoot mkdir -p $TFM_BUILD_PATH $TFM_SPE_BUILD_PATH diff --git a/scripts/imgtool/dumpinfo.py b/scripts/imgtool/dumpinfo.py index 55446574c..20cc63df7 100644 --- a/scripts/imgtool/dumpinfo.py +++ b/scripts/imgtool/dumpinfo.py @@ -23,8 +23,10 @@ import click import yaml +from intelhex import IntelHex from imgtool import image +from imgtool.image import INTEL_HEX_EXT HEADER_ITEMS = ("magic", "load_addr", "hdr_size", "protected_tlv_size", "img_size", "flags", "version") @@ -129,11 +131,16 @@ def dump_imginfo(imgfile, outfile=None, silent=False): trailer = {} key_field_len = None + ext = os.path.splitext(imgfile)[1][1:].lower() try: - with open(imgfile, "rb") as f: - b = f.read() + if ext == INTEL_HEX_EXT: + ih = IntelHex(imgfile) + b = ih.tobinstr() + else: + with open(imgfile, "rb") as f: + b = f.read() except FileNotFoundError: - raise click.UsageError("Image file not found ({})".format(imgfile)) + raise click.UsageError(f"Image file not found: {imgfile}") # Parsing the image header _header = struct.unpack('IIHHIIBBHI', b[:28]) diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 115d04ec9..094d27121 100644 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -333,7 +333,7 @@ def load(self, path): self.infile_data = f.read() self.payload = copy.copy(self.infile_data) except FileNotFoundError: - raise click.UsageError("Input file not found") + raise click.UsageError(f"Image file not found: {path}") self.image_size = len(self.payload) # Add the image header if needed. @@ -779,7 +779,7 @@ def verify(imgfile, key): with open(imgfile, 'rb') as f: b = f.read() except FileNotFoundError: - raise click.UsageError(f"Image file {imgfile} not found") + raise click.UsageError(f"Image file not found: {imgfile}") magic, _, header_size, _, img_size = struct.unpack('IIHHI', b[:16]) version = struct.unpack('BBHI', b[20:28]) diff --git a/scripts/imgtool/keys/__init__.py b/scripts/imgtool/keys/__init__.py index ed2fed57e..86e5e3d63 100644 --- a/scripts/imgtool/keys/__init__.py +++ b/scripts/imgtool/keys/__init__.py @@ -1,5 +1,5 @@ # Copyright 2017 Linaro Limited -# Copyright 2023 Arm Limited +# Copyright 2023-2024 Arm Limited # # SPDX-License-Identifier: Apache-2.0 # @@ -58,14 +58,24 @@ def load(path, passwd=None): except TypeError as e: msg = str(e) if "private key is encrypted" in msg: + print(msg) return None raise e - except ValueError: + except ValueError as e: + msg1 = str(e) # This seems to happen if the key is a public key, let's try # loading it as a public key. - pk = serialization.load_pem_public_key( + try: + pk = serialization.load_pem_public_key( raw_pem, backend=default_backend()) + except ValueError as e: + # If loading as public key also fails, that indicates wrong + # passphrase input + msg2 = str(e) + if ("password may be incorrect" in msg1 and + "Are you sure this is a public key" in msg2): + raise Exception("Invalid passphrase") if isinstance(pk, RSAPrivateKey): if pk.key_size not in RSA_KEY_SIZES: diff --git a/scripts/imgtool/keys/general.py b/scripts/imgtool/keys/general.py index 4ff700c49..675a6c234 100644 --- a/scripts/imgtool/keys/general.py +++ b/scripts/imgtool/keys/general.py @@ -2,15 +2,25 @@ # SPDX-License-Identifier: Apache-2.0 -import binascii -import io import os import sys + +from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes, PublicKeyTypes from cryptography.hazmat.primitives.hashes import Hash, SHA256 +from imgtool import keys + AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */" +def key_types_matching(key: PrivateKeyTypes, enckey: PublicKeyTypes): + type_dict = {keys.ECDSA256P1: keys.ECDSA256P1Public, + keys.ECDSA384P1: keys.ECDSA384P1Public, + keys.Ed25519: keys.X25519Public, + keys.RSA: keys.RSAPublic} + return type_dict[type(key)] == type(enckey) + + class FileHandler(object): def __init__(self, file, *args, **kwargs): self.file_in = file @@ -34,7 +44,7 @@ def _emit(self, header, trailer, encoded_bytes, indent, file=sys.stdout, len_format=None): with FileHandler(file, 'w') as file: self._emit_to_output(header, trailer, encoded_bytes, indent, - file, len_format) + file, len_format) def _emit_to_output(self, header, trailer, encoded_bytes, indent, file, len_format): @@ -62,27 +72,27 @@ def _emit_raw(self, encoded_bytes, file): def emit_c_public(self, file=sys.stdout): self._emit( - header="const unsigned char {}_pub_key[] = {{" - .format(self.shortname()), - trailer="};", - encoded_bytes=self.get_public_bytes(), - indent=" ", - len_format="const unsigned int {}_pub_key_len = {{}};" - .format(self.shortname()), - file=file) + header="const unsigned char {}_pub_key[] = {{" + .format(self.shortname()), + trailer="};", + encoded_bytes=self.get_public_bytes(), + indent=" ", + len_format="const unsigned int {}_pub_key_len = {{}};" + .format(self.shortname()), + file=file) def emit_c_public_hash(self, file=sys.stdout): digest = Hash(SHA256()) digest.update(self.get_public_bytes()) self._emit( - header="const unsigned char {}_pub_key_hash[] = {{" - .format(self.shortname()), - trailer="};", - encoded_bytes=digest.finalize(), - indent=" ", - len_format="const unsigned int {}_pub_key_hash_len = {{}};" - .format(self.shortname()), - file=file) + header="const unsigned char {}_pub_key_hash[] = {{" + .format(self.shortname()), + trailer="};", + encoded_bytes=digest.finalize(), + indent=" ", + len_format="const unsigned int {}_pub_key_hash_len = {{}};" + .format(self.shortname()), + file=file) def emit_raw_public(self, file=sys.stdout): self._emit_raw(self.get_public_bytes(), file=file) @@ -94,12 +104,12 @@ def emit_raw_public_hash(self, file=sys.stdout): def emit_rust_public(self, file=sys.stdout): self._emit( - header="static {}_PUB_KEY: &[u8] = &[" - .format(self.shortname().upper()), - trailer="];", - encoded_bytes=self.get_public_bytes(), - indent=" ", - file=file) + header="static {}_PUB_KEY: &[u8] = &[" + .format(self.shortname().upper()), + trailer="];", + encoded_bytes=self.get_public_bytes(), + indent=" ", + file=file) def emit_public_pem(self, file=sys.stdout): with FileHandler(file, 'w') as file: @@ -107,9 +117,9 @@ def emit_public_pem(self, file=sys.stdout): def emit_private(self, minimal, format, file=sys.stdout): self._emit( - header="const unsigned char enc_priv_key[] = {", - trailer="};", - encoded_bytes=self.get_private_bytes(minimal, format), - indent=" ", - len_format="const unsigned int enc_priv_key_len = {};", - file=file) + header="const unsigned char enc_priv_key[] = {", + trailer="};", + encoded_bytes=self.get_private_bytes(minimal, format), + indent=" ", + len_format="const unsigned int enc_priv_key_len = {};", + file=file) diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index 009d9234c..2b40dfca3 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # # Copyright 2017-2020 Linaro Limited -# Copyright 2019-2023 Arm Limited +# Copyright 2019-2024 Arm Limited # # SPDX-License-Identifier: Apache-2.0 # @@ -28,6 +28,7 @@ import hashlib import base64 from imgtool import image, imgtool_version +from imgtool.keys.general import key_types_matching from imgtool.version import decode_version from imgtool.dumpinfo import dump_imginfo from .keys import ( @@ -99,12 +100,29 @@ def save_signature(sigfile, sig): def load_key(keyfile): - # TODO: better handling of invalid pass-phrase - key = keys.load(keyfile) + try: + key = keys.load(keyfile) + except FileNotFoundError as e: + print(f"Key file not found: {keyfile}") + raise e if key is not None: return key + + # Key is password protected passwd = getpass.getpass("Enter key passphrase: ").encode('utf-8') - return keys.load(keyfile, passwd) + try: + key = keys.load(keyfile, passwd) + except Exception as e: + msg = str(e) + if "Invalid passphrase" in msg: + print(msg) + exit(1) + else: + raise e + if key is None: + print("Could not load key for unknown error") + exit(1) + return key def get_password(): @@ -157,9 +175,8 @@ def getpub(key, encoding, lang, output): if not output: output = sys.stdout - if key is None: - print("Invalid passphrase") - elif lang == 'c' or encoding == 'lang-c': + + if lang == 'c' or encoding == 'lang-c': key.emit_c_public(file=output) elif lang == 'rust' or encoding == 'lang-rust': key.emit_rust_public(file=output) @@ -189,9 +206,8 @@ def getpubhash(key, output, encoding): if not output: output = sys.stdout - if key is None: - print("Invalid passphrase") - elif encoding == 'lang-c': + + if encoding == 'lang-c': key.emit_c_public_hash(file=output) elif encoding == 'raw': key.emit_raw_public_hash(file=output) @@ -212,8 +228,6 @@ def getpubhash(key, output, encoding): @click.command(help='Dump private key from keypair') def getpriv(key, minimal, format): key = load_key(key) - if key is None: - print("Invalid passphrase") try: key.emit_private(minimal, format) except (RSAUsageError, ECDSAUsageError, Ed25519UsageError, @@ -460,16 +474,9 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, img.load(infile) key = load_key(key) if key else None enckey = load_key(encrypt) if encrypt else None - if enckey and key: - if ((isinstance(key, keys.ECDSA256P1) and - not isinstance(enckey, keys.ECDSA256P1Public)) - or (isinstance(key, keys.ECDSA384P1) and - not isinstance(enckey, keys.ECDSA384P1Public)) - or (isinstance(key, keys.RSA) and - not isinstance(enckey, keys.RSAPublic))): - # FIXME - raise click.UsageError("Signing and encryption must use the same " - "type of key") + if enckey and key and not key_types_matching(key, enckey): + raise click.UsageError("Encryption must use the public pair of the " + "same type of key used for signing") if pad_sig and hasattr(key, 'pad_sig'): key.pad_sig = True diff --git a/scripts/tests/assets/images/one.bin b/scripts/tests/assets/images/one.bin new file mode 100644 index 000000000..0fa327f63 --- /dev/null +++ b/scripts/tests/assets/images/one.bin @@ -0,0 +1 @@ +1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 \ No newline at end of file diff --git a/scripts/tests/assets/images/one_offset.hex b/scripts/tests/assets/images/one_offset.hex new file mode 100644 index 000000000..ede08e5d5 --- /dev/null +++ b/scripts/tests/assets/images/one_offset.hex @@ -0,0 +1,65 @@ +:10000A0031313131313131313131313131313131D6 +:10001A0031313131313131313131313131313131C6 +:10002A0031313131313131313131313131313131B6 +:10003A0031313131313131313131313131313131A6 +:10004A003131313131313131313131313131313196 +:10005A003131313131313131313131313131313186 +:10006A003131313131313131313131313131313176 +:10007A003131313131313131313131313131313166 +:10008A003131313131313131313131313131313156 +:10009A003131313131313131313131313131313146 +:1000AA003131313131313131313131313131313136 +:1000BA003131313131313131313131313131313126 +:1000CA003131313131313131313131313131313116 +:1000DA003131313131313131313131313131313106 +:1000EA0031313131313131313131313131313131F6 +:1000FA0031313131313131313131313131313131E6 +:10010A0031313131313131313131313131313131D5 +:10011A0031313131313131313131313131313131C5 +:10012A0031313131313131313131313131313131B5 +:10013A0031313131313131313131313131313131A5 +:10014A003131313131313131313131313131313195 +:10015A003131313131313131313131313131313185 +:10016A003131313131313131313131313131313175 +:10017A003131313131313131313131313131313165 +:10018A003131313131313131313131313131313155 +:10019A003131313131313131313131313131313145 +:1001AA003131313131313131313131313131313135 +:1001BA003131313131313131313131313131313125 +:1001CA003131313131313131313131313131313115 +:1001DA003131313131313131313131313131313105 +:1001EA0031313131313131313131313131313131F5 +:1001FA0031313131313131313131313131313131E5 +:10020A0031313131313131313131313131313131D4 +:10021A0031313131313131313131313131313131C4 +:10022A0031313131313131313131313131313131B4 +:10023A0031313131313131313131313131313131A4 +:10024A003131313131313131313131313131313194 +:10025A003131313131313131313131313131313184 +:10026A003131313131313131313131313131313174 +:10027A003131313131313131313131313131313164 +:10028A003131313131313131313131313131313154 +:10029A003131313131313131313131313131313144 +:1002AA003131313131313131313131313131313134 +:1002BA003131313131313131313131313131313124 +:1002CA003131313131313131313131313131313114 +:1002DA003131313131313131313131313131313104 +:1002EA0031313131313131313131313131313131F4 +:1002FA0031313131313131313131313131313131E4 +:10030A0031313131313131313131313131313131D3 +:10031A0031313131313131313131313131313131C3 +:10032A0031313131313131313131313131313131B3 +:10033A0031313131313131313131313131313131A3 +:10034A003131313131313131313131313131313193 +:10035A003131313131313131313131313131313183 +:10036A003131313131313131313131313131313173 +:10037A003131313131313131313131313131313163 +:10038A003131313131313131313131313131313153 +:10039A003131313131313131313131313131313143 +:1003AA003131313131313131313131313131313133 +:1003BA003131313131313131313131313131313123 +:1003CA003131313131313131313131313131313113 +:1003DA003131313131313131313131313131313103 +:1003EA0031313131313131313131313131313131F3 +:1003FA0031313131313131313131313131313131E3 +:00000001FF diff --git a/scripts/tests/assets/images/signed/basic/ecdsa-p256.bin b/scripts/tests/assets/images/signed/basic/ecdsa-p256.bin new file mode 100644 index 000000000..7cb8ef214 Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/ecdsa-p256.bin differ diff --git a/scripts/tests/assets/images/signed/basic/ecdsa-p384.bin b/scripts/tests/assets/images/signed/basic/ecdsa-p384.bin new file mode 100644 index 000000000..cf6b69cff Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/ecdsa-p384.bin differ diff --git a/scripts/tests/assets/images/signed/basic/ed25519.bin b/scripts/tests/assets/images/signed/basic/ed25519.bin new file mode 100644 index 000000000..0ff927137 Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/ed25519.bin differ diff --git a/scripts/tests/assets/images/signed/basic/rsa-2048.bin b/scripts/tests/assets/images/signed/basic/rsa-2048.bin new file mode 100644 index 000000000..2f4ed1f5b Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/rsa-2048.bin differ diff --git a/scripts/tests/assets/images/signed/basic/rsa-3072.bin b/scripts/tests/assets/images/signed/basic/rsa-3072.bin new file mode 100644 index 000000000..b37f3c2ca Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/rsa-3072.bin differ diff --git a/scripts/tests/assets/images/signed/basic/rsa-3072_invHash.bin b/scripts/tests/assets/images/signed/basic/rsa-3072_invHash.bin new file mode 100644 index 000000000..905623758 Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/rsa-3072_invHash.bin differ diff --git a/scripts/tests/assets/images/signed/basic/rsa-3072_invMagic.bin b/scripts/tests/assets/images/signed/basic/rsa-3072_invMagic.bin new file mode 100644 index 000000000..a6999a7d1 Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/rsa-3072_invMagic.bin differ diff --git a/scripts/tests/assets/images/signed/basic/rsa-3072_invTLVMagic.bin b/scripts/tests/assets/images/signed/basic/rsa-3072_invTLVMagic.bin new file mode 100644 index 000000000..0fb0e565b Binary files /dev/null and b/scripts/tests/assets/images/signed/basic/rsa-3072_invTLVMagic.bin differ diff --git a/scripts/tests/assets/images/signed/customTLV/ecdsa-p256.bin b/scripts/tests/assets/images/signed/customTLV/ecdsa-p256.bin new file mode 100644 index 000000000..9b2bd4e36 Binary files /dev/null and b/scripts/tests/assets/images/signed/customTLV/ecdsa-p256.bin differ diff --git a/scripts/tests/assets/images/signed/customTLV/ecdsa-p384.bin b/scripts/tests/assets/images/signed/customTLV/ecdsa-p384.bin new file mode 100644 index 000000000..73f5c7c80 Binary files /dev/null and b/scripts/tests/assets/images/signed/customTLV/ecdsa-p384.bin differ diff --git a/scripts/tests/assets/images/signed/customTLV/ed25519.bin b/scripts/tests/assets/images/signed/customTLV/ed25519.bin new file mode 100644 index 000000000..8708a49bf Binary files /dev/null and b/scripts/tests/assets/images/signed/customTLV/ed25519.bin differ diff --git a/scripts/tests/assets/images/signed/customTLV/rsa-2048.bin b/scripts/tests/assets/images/signed/customTLV/rsa-2048.bin new file mode 100644 index 000000000..6a7e82d96 Binary files /dev/null and b/scripts/tests/assets/images/signed/customTLV/rsa-2048.bin differ diff --git a/scripts/tests/assets/images/signed/customTLV/rsa-3072.bin b/scripts/tests/assets/images/signed/customTLV/rsa-3072.bin new file mode 100644 index 000000000..9cbfc4c22 Binary files /dev/null and b/scripts/tests/assets/images/signed/customTLV/rsa-3072.bin differ diff --git a/scripts/tests/assets/images/signed/encryptedClear/ecdsa-p256.enc b/scripts/tests/assets/images/signed/encryptedClear/ecdsa-p256.enc new file mode 100644 index 000000000..80466ba87 Binary files /dev/null and b/scripts/tests/assets/images/signed/encryptedClear/ecdsa-p256.enc differ diff --git a/scripts/tests/assets/images/signed/encryptedClear/ecdsa-p384.enc b/scripts/tests/assets/images/signed/encryptedClear/ecdsa-p384.enc new file mode 100644 index 000000000..cb10e4da3 Binary files /dev/null and b/scripts/tests/assets/images/signed/encryptedClear/ecdsa-p384.enc differ diff --git a/scripts/tests/assets/images/signed/encryptedClear/ed25519.enc b/scripts/tests/assets/images/signed/encryptedClear/ed25519.enc new file mode 100644 index 000000000..c4fa60628 Binary files /dev/null and b/scripts/tests/assets/images/signed/encryptedClear/ed25519.enc differ diff --git a/scripts/tests/assets/images/signed/encryptedClear/rsa-2048.enc b/scripts/tests/assets/images/signed/encryptedClear/rsa-2048.enc new file mode 100644 index 000000000..c931e759b Binary files /dev/null and b/scripts/tests/assets/images/signed/encryptedClear/rsa-2048.enc differ diff --git a/scripts/tests/assets/images/signed/encryptedClear/rsa-3072.enc b/scripts/tests/assets/images/signed/encryptedClear/rsa-3072.enc new file mode 100644 index 000000000..1f13d3268 Binary files /dev/null and b/scripts/tests/assets/images/signed/encryptedClear/rsa-3072.enc differ diff --git a/scripts/tests/assets/images/signed/hex/zero_hex-addr_0.hex b/scripts/tests/assets/images/signed/hex/zero_hex-addr_0.hex new file mode 100644 index 000000000..6f70b811b --- /dev/null +++ b/scripts/tests/assets/images/signed/hex/zero_hex-addr_0.hex @@ -0,0 +1,88 @@ +:100000003DB8F3960000000020000000FF03000050 +:1000100000000000010000000000000000000000DF +:1000200001010101010101010101010101010101C0 +:1000300001010101010101010101010101010101B0 +:1000400001010101010101010101010101010101A0 +:100050000101010101010101010101010101010190 +:100060000101010101010101010101010101010180 +:100070000101010101010101010101010101010170 +:100080000101010101010101010101010101010160 +:100090000101010101010101010101010101010150 +:1000A0000101010101010101010101010101010140 +:1000B0000101010101010101010101010101010130 +:1000C0000101010101010101010101010101010120 +:1000D0000101010101010101010101010101010110 +:1000E0000101010101010101010101010101010100 +:1000F00001010101010101010101010101010101F0 +:1001000001010101010101010101010101010101DF +:1001100001010101010101010101010101010101CF +:1001200001010101010101010101010101010101BF +:1001300001010101010101010101010101010101AF +:10014000010101010101010101010101010101019F +:10015000010101010101010101010101010101018F +:10016000010101010101010101010101010101017F +:10017000010101010101010101010101010101016F +:10018000010101010101010101010101010101015F +:10019000010101010101010101010101010101014F +:1001A000010101010101010101010101010101013F +:1001B000010101010101010101010101010101012F +:1001C000010101010101010101010101010101011F +:1001D000010101010101010101010101010101010F +:1001E00001010101010101010101010101010101FF +:1001F00001010101010101010101010101010101EF +:1002000001010101010101010101010101010101DE +:1002100001010101010101010101010101010101CE +:1002200001010101010101010101010101010101BE +:1002300001010101010101010101010101010101AE +:10024000010101010101010101010101010101019E +:10025000010101010101010101010101010101018E +:10026000010101010101010101010101010101017E +:10027000010101010101010101010101010101016E +:10028000010101010101010101010101010101015E +:10029000010101010101010101010101010101014E +:1002A000010101010101010101010101010101013E +:1002B000010101010101010101010101010101012E +:1002C000010101010101010101010101010101011E +:1002D000010101010101010101010101010101010E +:1002E00001010101010101010101010101010101FE +:1002F00001010101010101010101010101010101EE +:1003000001010101010101010101010101010101DD +:1003100001010101010101010101010101010101CD +:1003200001010101010101010101010101010101BD +:1003300001010101010101010101010101010101AD +:10034000010101010101010101010101010101019D +:10035000010101010101010101010101010101018D +:10036000010101010101010101010101010101017D +:10037000010101010101010101010101010101016D +:10038000010101010101010101010101010101015D +:10039000010101010101010101010101010101014D +:1003A000010101010101010101010101010101013D +:1003B000010101010101010101010101010101012D +:1003C000010101010101010101010101010101011D +:1003D000010101010101010101010101010101010D +:1003E00001010101010101010101010101010101FD +:1003F00001010101010101010101010101010101ED +:1004000001010101010101010101010101010101DC +:1004100001010101010101010101010101010107C6 +:100420006950011000200034E38DD5950696DF9EBB +:10043000E843F3857825F27DAA652686898B3231DB +:1004400030DF88657666D701002000ACBE8D1B7258 +:100450003E1A8D47471D15D84D14A98FF71C62DD34 +:10046000F1EF34C3F71C724B8E20462000000120B0 +:1004700026407977596FE45645D988435802087E5B +:1004800068A2FA1DAA56818D8647444EE0C56B8648 +:100490005B7411B94A5C9177F035C88D3C63A7C68F +:1004A0000AA6D76DD20DE37A0DF6A8739FB16776D1 +:1004B0004F319BE664F6AA3D9434FA5AB7D14A8A82 +:1004C00020FF56580AA80B859603420A5EA1822196 +:1004D00060BD99A7027D6A787C0E1B44740F84F37B +:1004E000AD9BDF6D7777BF0AF08D918882482675C6 +:1004F000A9B083CDED31C3FD9582ECD240543C0CC4 +:100500007256C0A1E75AE5BBF3B101753499E81BF7 +:10051000D7184411709F13BDC5417BDD6F8CF26904 +:1005200069C1025A15D6E5C191B70F9DB522FF9852 +:100530003A3C68A2E00470AA9AAA15D77D4E538D62 +:100540000240AB3003888CA2EA5BA6E9B5621E9A32 +:10055000FD9F1F74FB7786EC19D97D571C527B0CCD +:0F0560000F2336A4684349944EF10AC97C8DC21B +:00000001FF diff --git a/scripts/tests/assets/images/signed/hex/zero_hex-addr_16.hex b/scripts/tests/assets/images/signed/hex/zero_hex-addr_16.hex new file mode 100644 index 000000000..c36e7849e --- /dev/null +++ b/scripts/tests/assets/images/signed/hex/zero_hex-addr_16.hex @@ -0,0 +1,88 @@ +:100010003DB8F3960000000020000000FF03000040 +:1000200000000000010000000000000000000000CF +:1000300001010101010101010101010101010101B0 +:1000400001010101010101010101010101010101A0 +:100050000101010101010101010101010101010190 +:100060000101010101010101010101010101010180 +:100070000101010101010101010101010101010170 +:100080000101010101010101010101010101010160 +:100090000101010101010101010101010101010150 +:1000A0000101010101010101010101010101010140 +:1000B0000101010101010101010101010101010130 +:1000C0000101010101010101010101010101010120 +:1000D0000101010101010101010101010101010110 +:1000E0000101010101010101010101010101010100 +:1000F00001010101010101010101010101010101F0 +:1001000001010101010101010101010101010101DF +:1001100001010101010101010101010101010101CF +:1001200001010101010101010101010101010101BF +:1001300001010101010101010101010101010101AF +:10014000010101010101010101010101010101019F +:10015000010101010101010101010101010101018F +:10016000010101010101010101010101010101017F +:10017000010101010101010101010101010101016F +:10018000010101010101010101010101010101015F +:10019000010101010101010101010101010101014F +:1001A000010101010101010101010101010101013F +:1001B000010101010101010101010101010101012F +:1001C000010101010101010101010101010101011F +:1001D000010101010101010101010101010101010F +:1001E00001010101010101010101010101010101FF +:1001F00001010101010101010101010101010101EF +:1002000001010101010101010101010101010101DE +:1002100001010101010101010101010101010101CE +:1002200001010101010101010101010101010101BE +:1002300001010101010101010101010101010101AE +:10024000010101010101010101010101010101019E +:10025000010101010101010101010101010101018E +:10026000010101010101010101010101010101017E +:10027000010101010101010101010101010101016E +:10028000010101010101010101010101010101015E +:10029000010101010101010101010101010101014E +:1002A000010101010101010101010101010101013E +:1002B000010101010101010101010101010101012E +:1002C000010101010101010101010101010101011E +:1002D000010101010101010101010101010101010E +:1002E00001010101010101010101010101010101FE +:1002F00001010101010101010101010101010101EE +:1003000001010101010101010101010101010101DD +:1003100001010101010101010101010101010101CD +:1003200001010101010101010101010101010101BD +:1003300001010101010101010101010101010101AD +:10034000010101010101010101010101010101019D +:10035000010101010101010101010101010101018D +:10036000010101010101010101010101010101017D +:10037000010101010101010101010101010101016D +:10038000010101010101010101010101010101015D +:10039000010101010101010101010101010101014D +:1003A000010101010101010101010101010101013D +:1003B000010101010101010101010101010101012D +:1003C000010101010101010101010101010101011D +:1003D000010101010101010101010101010101010D +:1003E00001010101010101010101010101010101FD +:1003F00001010101010101010101010101010101ED +:1004000001010101010101010101010101010101DC +:1004100001010101010101010101010101010101CC +:1004200001010101010101010101010101010107B6 +:100430006950011000200034E38DD5950696DF9EAB +:10044000E843F3857825F27DAA652686898B3231CB +:1004500030DF88657666D701002000ACBE8D1B7248 +:100460003E1A8D47471D15D84D14A98FF71C62DD24 +:10047000F1EF34C3F71C724B8E2046200000019D23 +:1004800068458A1A3EFF5B9C5765B3ECF9609EBED7 +:10049000736E2429860007387BA6DF2086BEAF82D4 +:1004A000C3D771F0EFC128C7BB96D97CD92EF5868A +:1004B000751CEF59B1ED98FD03C9008D1A49635DB4 +:1004C0009E65DF1B526AA09D631A2FE8961074E4A4 +:1004D000FF684382FBAE86B1EA222297EC16B0E9B0 +:1004E0006EEAD60FAB8173BB3040FDBD1C5884262D +:1004F00071630BCB07513811304B6391D97DA92A19 +:10050000BBD25F6ECF1CC066991C524251C3B6F07D +:10051000406E43862BD66BE0A4102D6C5E754FF4B5 +:10052000198460107E2E197B65EA94F848D49C6388 +:10053000ADA12B7F01639D922CD11548BB3EDC956C +:100540002FC2C924006FD5C7B20E4620FB5D48A854 +:1005500068055D49D58D3A4F4FFD86852E7DD9665C +:1005600070AFEFE9FB2FB4F71A4E3EFEBE94931F17 +:0F057000F2BB6D8B96403FA14413682358D572A0 +:00000001FF diff --git a/scripts/tests/assets/images/signed/hex/zero_hex-addr_35.hex b/scripts/tests/assets/images/signed/hex/zero_hex-addr_35.hex new file mode 100644 index 000000000..9954f2209 --- /dev/null +++ b/scripts/tests/assets/images/signed/hex/zero_hex-addr_35.hex @@ -0,0 +1,88 @@ +:100023003DB8F3960000000020000000FF0300002D +:1000330000000000010000000000000000000000BC +:10004300010101010101010101010101010101019D +:10005300010101010101010101010101010101018D +:10006300010101010101010101010101010101017D +:10007300010101010101010101010101010101016D +:10008300010101010101010101010101010101015D +:10009300010101010101010101010101010101014D +:1000A300010101010101010101010101010101013D +:1000B300010101010101010101010101010101012D +:1000C300010101010101010101010101010101011D +:1000D300010101010101010101010101010101010D +:1000E30001010101010101010101010101010101FD +:1000F30001010101010101010101010101010101ED +:1001030001010101010101010101010101010101DC +:1001130001010101010101010101010101010101CC +:1001230001010101010101010101010101010101BC +:1001330001010101010101010101010101010101AC +:10014300010101010101010101010101010101019C +:10015300010101010101010101010101010101018C +:10016300010101010101010101010101010101017C +:10017300010101010101010101010101010101016C +:10018300010101010101010101010101010101015C +:10019300010101010101010101010101010101014C +:1001A300010101010101010101010101010101013C +:1001B300010101010101010101010101010101012C +:1001C300010101010101010101010101010101011C +:1001D300010101010101010101010101010101010C +:1001E30001010101010101010101010101010101FC +:1001F30001010101010101010101010101010101EC +:1002030001010101010101010101010101010101DB +:1002130001010101010101010101010101010101CB +:1002230001010101010101010101010101010101BB +:1002330001010101010101010101010101010101AB +:10024300010101010101010101010101010101019B +:10025300010101010101010101010101010101018B +:10026300010101010101010101010101010101017B +:10027300010101010101010101010101010101016B +:10028300010101010101010101010101010101015B +:10029300010101010101010101010101010101014B +:1002A300010101010101010101010101010101013B +:1002B300010101010101010101010101010101012B +:1002C300010101010101010101010101010101011B +:1002D300010101010101010101010101010101010B +:1002E30001010101010101010101010101010101FB +:1002F30001010101010101010101010101010101EB +:1003030001010101010101010101010101010101DA +:1003130001010101010101010101010101010101CA +:1003230001010101010101010101010101010101BA +:1003330001010101010101010101010101010101AA +:10034300010101010101010101010101010101019A +:10035300010101010101010101010101010101018A +:10036300010101010101010101010101010101017A +:10037300010101010101010101010101010101016A +:10038300010101010101010101010101010101015A +:10039300010101010101010101010101010101014A +:1003A300010101010101010101010101010101013A +:1003B300010101010101010101010101010101012A +:1003C300010101010101010101010101010101011A +:1003D300010101010101010101010101010101010A +:1003E30001010101010101010101010101010101FA +:1003F30001010101010101010101010101010101EA +:1004030001010101010101010101010101010101D9 +:1004130001010101010101010101010101010101C9 +:1004230001010101010101010101010101010101B9 +:1004330001010101010101010101010101010107A3 +:100443006950011000200034E38DD5950696DF9E98 +:10045300E843F3857825F27DAA652686898B3231B8 +:1004630030DF88657666D701002000ACBE8D1B7235 +:100473003E1A8D47471D15D84D14A98FF71C62DD11 +:10048300F1EF34C3F71C724B8E204620000001624B +:100493000C085DF7F51FA1EC60386FDB5AFE1759A6 +:1004A3009103B96CC6887010413CC925524FD45C86 +:1004B300F708EF57A4F234A2B21E635B6EBA9277C9 +:1004C300D9566B452C7E6FBC0B9273CDE306A8C146 +:1004D300F3F85B52F806D82A01FEB7BC7F8D982C3F +:1004E300BD6F5F97E93B7C49B05806EAFD07640A94 +:1004F300EC54A2AF45F613C74E8666C6062E678E2A +:100503007EF6D79B1FB252E485D77750D4D7C9E77D +:10051300D9283DDB2D7DF5467E8DE4CEE06A19F8C2 +:10052300FB499767772636B0D8C6D97C25A159EF02 +:10053300F5237FDD4ED26296B1C4EE3471A4991FC8 +:1005430088617DD545BDAD005F8C69F306B1CDAA49 +:100553006EDE73FD2430C4697136FB6855F2A783E0 +:100563002C199DE3F1F5032DD62B89639AB7BC595A +:100573004E3C2B9B4728218CF904C7B510E12A195F +:0F0583007AE0FB58627856A4F6F5EA20C0F7ED4F +:00000001FF diff --git a/scripts/tests/assets/images/signed/noKey/noKey256.bin b/scripts/tests/assets/images/signed/noKey/noKey256.bin new file mode 100644 index 000000000..b8e68fb31 Binary files /dev/null and b/scripts/tests/assets/images/signed/noKey/noKey256.bin differ diff --git a/scripts/tests/assets/images/signed/no_pad_sig_enc_16.bin b/scripts/tests/assets/images/signed/no_pad_sig_enc_16.bin new file mode 100644 index 000000000..15eaadbdd Binary files /dev/null and b/scripts/tests/assets/images/signed/no_pad_sig_enc_16.bin differ diff --git a/scripts/tests/assets/images/signed/no_pad_sig_enc_16_inv.bin b/scripts/tests/assets/images/signed/no_pad_sig_enc_16_inv.bin new file mode 100644 index 000000000..d3ced947a Binary files /dev/null and b/scripts/tests/assets/images/signed/no_pad_sig_enc_16_inv.bin differ diff --git a/scripts/tests/assets/images/signed/pad/padNoEnc.bin b/scripts/tests/assets/images/signed/pad/padNoEnc.bin new file mode 100644 index 000000000..dfa44cfcd Binary files /dev/null and b/scripts/tests/assets/images/signed/pad/padNoEnc.bin differ diff --git a/scripts/tests/assets/images/signed/pad_2.3K_sig_enc_16_inv.bin b/scripts/tests/assets/images/signed/pad_2.3K_sig_enc_16_inv.bin new file mode 100644 index 000000000..8b81eebb3 Binary files /dev/null and b/scripts/tests/assets/images/signed/pad_2.3K_sig_enc_16_inv.bin differ diff --git a/scripts/tests/assets/images/signed/pad_sig_enc_16.bin b/scripts/tests/assets/images/signed/pad_sig_enc_16.bin new file mode 100644 index 000000000..747d7aa98 Binary files /dev/null and b/scripts/tests/assets/images/signed/pad_sig_enc_16.bin differ diff --git a/scripts/tests/assets/images/signed/pad_sig_enc_16_inv.bin b/scripts/tests/assets/images/signed/pad_sig_enc_16_inv.bin new file mode 100644 index 000000000..7ebccb2a2 Binary files /dev/null and b/scripts/tests/assets/images/signed/pad_sig_enc_16_inv.bin differ diff --git a/scripts/tests/assets/images/signed/pad_sig_enc_32.bin b/scripts/tests/assets/images/signed/pad_sig_enc_32.bin new file mode 100644 index 000000000..ff12b2191 Binary files /dev/null and b/scripts/tests/assets/images/signed/pad_sig_enc_32.bin differ diff --git a/scripts/tests/assets/images/signed/pubKeyOnly/ecdsa-p256.bin b/scripts/tests/assets/images/signed/pubKeyOnly/ecdsa-p256.bin new file mode 100644 index 000000000..c740f5090 Binary files /dev/null and b/scripts/tests/assets/images/signed/pubKeyOnly/ecdsa-p256.bin differ diff --git a/scripts/tests/assets/images/signed/pubKeyOnly/ecdsa-p384.bin b/scripts/tests/assets/images/signed/pubKeyOnly/ecdsa-p384.bin new file mode 100644 index 000000000..544f8f1df Binary files /dev/null and b/scripts/tests/assets/images/signed/pubKeyOnly/ecdsa-p384.bin differ diff --git a/scripts/tests/assets/images/signed/pubKeyOnly/rsa-2048.bin b/scripts/tests/assets/images/signed/pubKeyOnly/rsa-2048.bin new file mode 100644 index 000000000..ace017c7b Binary files /dev/null and b/scripts/tests/assets/images/signed/pubKeyOnly/rsa-2048.bin differ diff --git a/scripts/tests/assets/images/signed/pubKeyOnly/rsa-3072.bin b/scripts/tests/assets/images/signed/pubKeyOnly/rsa-3072.bin new file mode 100644 index 000000000..9f69fd658 Binary files /dev/null and b/scripts/tests/assets/images/signed/pubKeyOnly/rsa-3072.bin differ diff --git a/scripts/tests/assets/images/zero.bin b/scripts/tests/assets/images/zero.bin new file mode 100644 index 000000000..06d740502 Binary files /dev/null and b/scripts/tests/assets/images/zero.bin differ diff --git a/scripts/tests/assets/images/zero.hex b/scripts/tests/assets/images/zero.hex new file mode 100644 index 000000000..e48389c5a --- /dev/null +++ b/scripts/tests/assets/images/zero.hex @@ -0,0 +1,65 @@ +:10000A0001010101010101010101010101010101D6 +:10001A0001010101010101010101010101010101C6 +:10002A0001010101010101010101010101010101B6 +:10003A0001010101010101010101010101010101A6 +:10004A000101010101010101010101010101010196 +:10005A000101010101010101010101010101010186 +:10006A000101010101010101010101010101010176 +:10007A000101010101010101010101010101010166 +:10008A000101010101010101010101010101010156 +:10009A000101010101010101010101010101010146 +:1000AA000101010101010101010101010101010136 +:1000BA000101010101010101010101010101010126 +:1000CA000101010101010101010101010101010116 +:1000DA000101010101010101010101010101010106 +:1000EA0001010101010101010101010101010101F6 +:1000FA0001010101010101010101010101010101E6 +:10010A0001010101010101010101010101010101D5 +:10011A0001010101010101010101010101010101C5 +:10012A0001010101010101010101010101010101B5 +:10013A0001010101010101010101010101010101A5 +:10014A000101010101010101010101010101010195 +:10015A000101010101010101010101010101010185 +:10016A000101010101010101010101010101010175 +:10017A000101010101010101010101010101010165 +:10018A000101010101010101010101010101010155 +:10019A000101010101010101010101010101010145 +:1001AA000101010101010101010101010101010135 +:1001BA000101010101010101010101010101010125 +:1001CA000101010101010101010101010101010115 +:1001DA000101010101010101010101010101010105 +:1001EA0001010101010101010101010101010101F5 +:1001FA0001010101010101010101010101010101E5 +:10020A0001010101010101010101010101010101D4 +:10021A0001010101010101010101010101010101C4 +:10022A0001010101010101010101010101010101B4 +:10023A0001010101010101010101010101010101A4 +:10024A000101010101010101010101010101010194 +:10025A000101010101010101010101010101010184 +:10026A000101010101010101010101010101010174 +:10027A000101010101010101010101010101010164 +:10028A000101010101010101010101010101010154 +:10029A000101010101010101010101010101010144 +:1002AA000101010101010101010101010101010134 +:1002BA000101010101010101010101010101010124 +:1002CA000101010101010101010101010101010114 +:1002DA000101010101010101010101010101010104 +:1002EA0001010101010101010101010101010101F4 +:1002FA0001010101010101010101010101010101E4 +:10030A0001010101010101010101010101010101D3 +:10031A0001010101010101010101010101010101C3 +:10032A0001010101010101010101010101010101B3 +:10033A0001010101010101010101010101010101A3 +:10034A000101010101010101010101010101010193 +:10035A000101010101010101010101010101010183 +:10036A000101010101010101010101010101010173 +:10037A000101010101010101010101010101010163 +:10038A000101010101010101010101010101010153 +:10039A000101010101010101010101010101010143 +:1003AA000101010101010101010101010101010133 +:1003BA000101010101010101010101010101010123 +:1003CA000101010101010101010101010101010113 +:1003DA000101010101010101010101010101010103 +:1003EA0001010101010101010101010101010101F3 +:0F03FA00010101010101010101010101010101E5 +:00000001FF diff --git a/scripts/tests/assets/keys/ecdsa-p256.key b/scripts/tests/assets/keys/ecdsa-p256.key new file mode 100644 index 000000000..ce910351d --- /dev/null +++ b/scripts/tests/assets/keys/ecdsa-p256.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8lT9/Ie8bqdLrE8V +V5tv22tuins00FLALIUZbf2sfMGhRANCAATSHV4f7Ekdq/IbuDH5tP3PHyNtY5tR +4P+OQnrmZo7XcNXCfq+uCT3lOZxQH3YqcTaUXY+wPrUAF9alXPLcQDJx +-----END PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/ecdsa-p256_passwd.key b/scripts/tests/assets/keys/ecdsa-p256_passwd.key new file mode 100644 index 000000000..5bf822536 --- /dev/null +++ b/scripts/tests/assets/keys/ecdsa-p256_passwd.key @@ -0,0 +1,7 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjYjJg+mOKfAQICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEGpBWXTQ3/vzFGEa1oQ5kqMEgZDu +ZJi/Se4UZRylq89IvQ/NUAJqM5jNjZIigKNMBPqWtwpRJGq1rgQX1daXZS7xoOGN +d9CYouvudlPb84pfhm2eF+ugMkS9N5s6UIjdqoG7NkWKMeTf3l+3KRQlYi9M2Ux9 +EDd6HLKC0DqPml9jTHYmBMM0DJZjgDYDGX2MOX8cIAujImTpBT4y7zXI1VIEVF8= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/ecdsa-p384.key b/scripts/tests/assets/keys/ecdsa-p384.key new file mode 100644 index 000000000..ddc74ccb0 --- /dev/null +++ b/scripts/tests/assets/keys/ecdsa-p384.key @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDtfiX2sq+e/pUzSoRU +ZynSgF5LjuFC+vRKqFohPrOtVmv0zzf3wy3H8e3MFDQ+PRWhZANiAARP0qTM0V+4 +qwcZkwP9b0j+gLDkLeQoxfPNiL5bvflfC+Rvt4YAbKrEWDoE+9bNH6vr4A5YMB/a +rvN1nMF6B/I/6ymvjpzJzWaTaAeBxeaWtwiJ7xk3aQEntfR71wubqAo= +-----END PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/ecdsa-p384_passwd.key b/scripts/tests/assets/keys/ecdsa-p384_passwd.key new file mode 100644 index 000000000..fb5c79067 --- /dev/null +++ b/scripts/tests/assets/keys/ecdsa-p384_passwd.key @@ -0,0 +1,8 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBHDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI6gYH2/bOBzgCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDn3lId85/jWGXuMmdKMdy7BIHA +ifbSyiBvpSCpun12zs6XdZ9AmSUoDSHLDLJhPm12uDT7FSH2hBUXxPfQdFWygXFM +6Pgz3hg6gWWTWKo+FAgbvjWb+5Mj5jGUAXr++TsAjbGF6rDzdYPYNx/Spl7eaCuF +H2q2cmWR3UR73AfWzkUmkvAnxtQJdJx6cvVV0JDixelJ69jDjfj3TVe2dx5InnWR +8JxNmNFjBlLSABkk9VLvj3VZM3zMp7kkDDJ38rMtT0YGcRPUrGPtbxdGwyQhQpe7 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/ed25519.key b/scripts/tests/assets/keys/ed25519.key new file mode 100644 index 000000000..96575818a --- /dev/null +++ b/scripts/tests/assets/keys/ed25519.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEICAZRgn1bHHzFW2pUUPHmhUn8w3F1pp8ppkXLzy8cize +-----END PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/ed25519_passwd.key b/scripts/tests/assets/keys/ed25519_passwd.key new file mode 100644 index 000000000..29a301c1f --- /dev/null +++ b/scripts/tests/assets/keys/ed25519_passwd.key @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAgBGyz0bjJxbQICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEKrnSOrOdfYVyacQVXdjddIEQMsC +Fw2raJQtVfxWHDOfNcI62J/ZQ3mNSe4tCfAOsn+N3JfWOmac84Ba4BxzVTHj9jzs +jik6pPaqDeealBCaWXM= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/public/ecdsa-p256-pem.pub b/scripts/tests/assets/keys/public/ecdsa-p256-pem.pub new file mode 100644 index 000000000..76e8536fc --- /dev/null +++ b/scripts/tests/assets/keys/public/ecdsa-p256-pem.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0h1eH+xJHavyG7gx+bT9zx8jbWOb +UeD/jkJ65maO13DVwn6vrgk95TmcUB92KnE2lF2PsD61ABfWpVzy3EAycQ== +-----END PUBLIC KEY----- diff --git a/scripts/tests/assets/keys/public/ecdsa-p384-pem.pub b/scripts/tests/assets/keys/public/ecdsa-p384-pem.pub new file mode 100644 index 000000000..e35349dd0 --- /dev/null +++ b/scripts/tests/assets/keys/public/ecdsa-p384-pem.pub @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAET9KkzNFfuKsHGZMD/W9I/oCw5C3kKMXz +zYi+W735Xwvkb7eGAGyqxFg6BPvWzR+r6+AOWDAf2q7zdZzBegfyP+spr46cyc1m +k2gHgcXmlrcIie8ZN2kBJ7X0e9cLm6gK +-----END PUBLIC KEY----- diff --git a/scripts/tests/assets/keys/public/rsa-2048-pem.pub b/scripts/tests/assets/keys/public/rsa-2048-pem.pub new file mode 100644 index 000000000..48d9eb2b6 --- /dev/null +++ b/scripts/tests/assets/keys/public/rsa-2048-pem.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArfUYKq0prScrSCjwnElK +nitXPUU2NVnQmlTTDLZnwyzVen/dPzrUYznG+cHDLAZqGs0GhDMFsarV/I8fImWe +qBHVJqYUfdMEmuvCYC7i/pwslprpInGhu0rakVuLJQdmC9jReoBa8gGZDy/NvuT6 +tMHD0Rw5WFzujEdbuNnK87GCTJXOzDvkWEZ2hhqHMO+zXHYIz31k5qD56X9/CFuk +hl4O28QZlIseV5YkWbae6DlmqtHLa/YhMNS38GPSmma2MYtqQ/6UFT4d1K/6frze +CbpeYRLbRVcrsYy9jQ8SZChGHzDH2FNYiQeTkrI/ONa4mAN+tGH9pTnrAOQuKLMT +yQIDAQAB +-----END PUBLIC KEY----- diff --git a/scripts/tests/assets/keys/public/rsa-3072-pem.pub b/scripts/tests/assets/keys/public/rsa-3072-pem.pub new file mode 100644 index 000000000..6f5ab6f7c --- /dev/null +++ b/scripts/tests/assets/keys/public/rsa-3072-pem.pub @@ -0,0 +1,11 @@ +-----BEGIN PUBLIC KEY----- +MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAirmhia5JyAeMl7P9kSCV +a9OrShpatLbAwOfZRJS/8YqnJgUV6wZJwy+psxBd/3zfexN2fIUtOmqeDobeNAKC +hNUeXWlwU0zL1omRzj3dYMfuGJqOrqeyC2MI7v4hkhrS7pw0wVWFefsoNtCQ/7bs +dozAytZnyxPI8uiuPZI2yyc4G5aWiHH1gpwm+cEDn38qPZXDR20EI5tv1mvRRgJQ +JrKrqVbh0ciFMspvfNoHhpC9Cc75KbSK4RzteMsCPGSkXjM+bupWaAPaeC1BHwTX +PH2Qxpdb/vqivNEvobdbjdleN4T8+IKQLal51SvKX8cr26+sKC/ZB3JUcpltayr1 +X72SMFmXUW1+wx7ZWAPctIyFa7ADDh8jEDzzrhN8OJdyz2juhrVdE09KjfxoRTQ7 +PWaPzZP1IadV4YmsRMlQdEJjoaXTHB1Bp2N3AUy0wrixuTtnfqKJ1GBBcYQ+5eo7 +NaP0rdvIl2c2R65cuFW2R/xI7HqWSBYooyfBf6anspdrAgMBAAE= +-----END PUBLIC KEY----- diff --git a/scripts/tests/assets/keys/public/x25519-pem.pub b/scripts/tests/assets/keys/public/x25519-pem.pub new file mode 100644 index 000000000..eeb2ca091 --- /dev/null +++ b/scripts/tests/assets/keys/public/x25519-pem.pub @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VuAyEALSB7AUCjpej325Z7jEeKyMJRvoUfifRSozGkNVHJ02I= +-----END PUBLIC KEY----- diff --git a/scripts/tests/assets/keys/rsa-2048.key b/scripts/tests/assets/keys/rsa-2048.key new file mode 100644 index 000000000..08942e49c --- /dev/null +++ b/scripts/tests/assets/keys/rsa-2048.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCt9RgqrSmtJytI +KPCcSUqeK1c9RTY1WdCaVNMMtmfDLNV6f90/OtRjOcb5wcMsBmoazQaEMwWxqtX8 +jx8iZZ6oEdUmphR90wSa68JgLuL+nCyWmukicaG7StqRW4slB2YL2NF6gFryAZkP +L82+5Pq0wcPRHDlYXO6MR1u42crzsYJMlc7MO+RYRnaGGocw77NcdgjPfWTmoPnp +f38IW6SGXg7bxBmUix5XliRZtp7oOWaq0ctr9iEw1LfwY9KaZrYxi2pD/pQVPh3U +r/p+vN4Jul5hEttFVyuxjL2NDxJkKEYfMMfYU1iJB5OSsj841riYA360Yf2lOesA +5C4osxPJAgMBAAECggEAD0lNYVqsIeIiRIoMcr9iBjoqxWCSneeRlC5GCYTx403K +SKR5zbeeuEV4fbQQMYsywb4oh29wXad0fGgbRUuGwKAoiefoPxJPAkobX5ZfLh6N +MILWX8d6fdnbERDpTkyzI/FenxQuabvINnRt8QtwGHg6e+KuC1zHPZ57qefBlN5s +Am4V+jQymGDkFalDRJ6QZYs9X9qIlejpsDfoKoL876U0YEWWAfKNdAboKfnMfXJH +6pFNhwTjLmPPW9DID9S65rabyadae5e3vScmjqIiUs3O3RoX3NNxW7X61/ncRHqP +CZ8M+aFmz4OX6/da+mtBa0GDPd0QvxHUX/nd94C5gQKBgQDvN9/12cGFUJJVMhw/ +JALk+OTH1VLlRy69fumCyifkmocSaaxt1iEr89EPkyMDoml7dLwiywhyTeDuRdoK +F1t3N20rCwgirGsxhnVS07Jrq1AqVPKW0yBL9rnxOqiop8lN2qvZWP7dbdsK7SB1 +ZCPrXgDhAttxDYY9zlgKZYHb2QKBgQC6KTLULYwSdTZPWyJ3+aPnaSPcVVdrjiHX +A9/7KFMoT6xPo5HEvyUHNkd+zJ+tQ9Jvutp9GHx/wUII5M8LkB9C6d7V/hoLCd18 +6gnywecnVhixXKzfAol1Ny+ALWJyKqodjhVrSe0cKPsKBampZAvYt4vRVIScUpj8 +SVczIGCxcQKBgQCIc9/NdhvcNrxx2nagUaNRBuRBVN7HKBZyQx1h3Hxq+vZHO33+ +3SDo2FgNQUm8vfzbThgu5I953BCM+c1I6k/jLfuYjN9kH+O5SVBNIbJpljvRGcmS +3PVUP3WLM4GXSrYaMbzK6ufeWNaUYfSc4bVXgbSQpTeVQjirsrMnowUy6QKBgF64 +i7ZOHpFdtm6WTmmBShZ69tYrTjnZrfo7bIHcuMMwFtrGvWrAX1TWF3a7IxwEAFT9 +XCJ+jQpsCO+BjJ+zi1R+rvVvP00SjfH7G2i7pKPtOM81+nfWUJ6ln7O2Y/r3Hmu/ +WcKncM/9Qg6NZxBbaXOxoKC4QmlP1au4nXfUJ57hAoGBANMYGrQtnzwbB9o0srUQ +b7kLjpkxLQMgKOdUYFcdOTkdamkRoeJa7nyP6QYOWp9FZPAjSXYPT5oyNKQkEzF0 +knEVAlAFfAQLQwbkEa+DVd0oPIPHZb5OZi0M+Kksv7aCEroaTb8/ZyXtCsaxjAYw +ECXsGM49SPXGAahzWtLgVrEG +-----END PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/rsa-2048_passwd.key b/scripts/tests/assets/keys/rsa-2048_passwd.key new file mode 100644 index 000000000..b5d6f1d66 --- /dev/null +++ b/scripts/tests/assets/keys/rsa-2048_passwd.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIm9I3BalGfDwCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDFx18QvRG2MwgxKfynmoAlBIIE +0JOQRfuKgiZVjn7yKHaQI9jX8y84bn5z/o5V8HBEqAGrwAhZnRq1yqjM4iZOkGua +oQ3yI+SXQ/9IZSsWTI1tkJThBIIXmA0owpLRRzDLwkpfy++EQgKlONz1KuDdWtEA +sAPFSdO7cmJW6QglV98DRDx1QCynAHjyfQA3hOl9W/tCwqcItObBlWvhFvUbU4Or +LKQ+D4NUAg+gXVgcbeC/iw4TL9au9RzvHcKQ+b/e+0eF9F7BlQ+vrWypVg6aJu8T +XJ43QRgYqWu5wyRLVWUYDaRoas3w62c8oR2zNfwSAvqt6/K3idFZANSix3oW2yqW +ckGVD1MH9fQXEkME8yhZUbZ+enqUqQbdSvoBe+oLO04V4eLgub0pDArk+sTRcPm/ +HU97ZRwleFlK0jJMrXhj3KmFmUwttqCWsU9wiKHpQpil/vq7V/QprV1mjWxVLjxC +u3xv2I3rS9HwMCAFySy6hMT86Fdpb8auFQiW4YI6VqdkXDGq3wmckpJq6DN2CgrD +/9rKHiY8vxEM0UJaVC5JWcyTnFztQG38LVSYDiOEXLxcNozlEjNNQ8jHYhijlnRW +EJAbi63obF34O43WBLlh6blPmLjTf7+5BxrdAOT972/0XwxlCE9TO8VatXjNp2aN +afjNeRI7R1OuycDIhyVwy3w5xdHwVXqh8/vGcc0XzHy15pmqQ/A1UzECflVVAipT +kERDm/3Qrcc0O/R2csylvMviCXdiu7Lu49je4HJaQUGIEYoYTTSN0XsbxxpS8vK+ +Up9hSTQrAi9J+98GtBTJXDSCB9zMN+I1pE/2h4fMqmP12Jlk9kd3mVBlbyRpkMVg +U+P7GLziXk+Rx4bacxKOtUA2bDV0pLDAKcnAMwZw2hV/ifCVHkWJSbky/G1QlmoN +BU84t2UNdenHQx/EoL5qgNtee/NlZjQNv7eV2FuhCrt+qwOAouEQURw5eLyep/kB +ZXJjqs7Ei5EGRuxsoA/zzUsqigEmcL2DimazNwybe1//ulLsEqmeSeCAzetFBlT3 +/69u9t5Rm6inyZoBZ0hoC2J3ehidC28xBltbw55vsLou6loneZBbAwbBMqLpSeIp +/+D+FOxdI2+zo+afafqUBsZ1tC8mFMFZpCIZ63i3Yvx+8+JW7a/TRRZXhasTTu2o +6ggHMlGQy3djXV3S41oQBcXocaF1jDIeiHqH6ZD5sktlx0d/eRKobuMEfgKaDPHi +fs5HBAWnP4jbjM5C3s0o0c5+Tc1x19hF9L1rDeot5/WP7Qvs6OEHSw+M9wT/RrF6 +04juVN+nykQ5WyfCzBrLSZ7qP+Onvvw5Ffvj8+EN3OuFQFNZc/1Z5QFG3tmhlof7 +oO5jJNMuQHIlI5xxm8o14howIC+UAfOM6H6b0nfQ0g8lUwjuuMxh1UumB2DDO56e +G9rq0Xvz+mXpdFdk2fZIQxU03A3KBpV284Yx2SRyykMKfICFPvR9k0/S90nvQMv/ +H6ZrNO/mm3VBlNY2UO1wa4wqTTxZJuPfj8GEV+coAueZnLUHTkp2GpCPVkBVXoKr +Alg+P6hLDQYTRjNEMCDMtUSQkM5g9ACIZ4L8+wZTZlPV5GjSXz0VGSOH0Ig7rXd+ +nOjxFybeX41LN8NEEfrxKW/VsbgVvPJAPEhAbUTJ6+G5 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/rsa-3072.key b/scripts/tests/assets/keys/rsa-3072.key new file mode 100644 index 000000000..ec92360d8 --- /dev/null +++ b/scripts/tests/assets/keys/rsa-3072.key @@ -0,0 +1,40 @@ +-----BEGIN PRIVATE KEY----- +MIIG/QIBADANBgkqhkiG9w0BAQEFAASCBucwggbjAgEAAoIBgQCKuaGJrknIB4yX +s/2RIJVr06tKGlq0tsDA59lElL/xiqcmBRXrBknDL6mzEF3/fN97E3Z8hS06ap4O +ht40AoKE1R5daXBTTMvWiZHOPd1gx+4Ymo6up7ILYwju/iGSGtLunDTBVYV5+yg2 +0JD/tux2jMDK1mfLE8jy6K49kjbLJzgblpaIcfWCnCb5wQOffyo9lcNHbQQjm2/W +a9FGAlAmsqupVuHRyIUyym982geGkL0JzvkptIrhHO14ywI8ZKReMz5u6lZoA9p4 +LUEfBNc8fZDGl1v++qK80S+ht1uN2V43hPz4gpAtqXnVK8pfxyvbr6woL9kHclRy +mW1rKvVfvZIwWZdRbX7DHtlYA9y0jIVrsAMOHyMQPPOuE3w4l3LPaO6GtV0TT0qN +/GhFNDs9Zo/Nk/Uhp1XhiaxEyVB0QmOhpdMcHUGnY3cBTLTCuLG5O2d+oonUYEFx +hD7l6js1o/St28iXZzZHrly4VbZH/EjsepZIFiijJ8F/pqeyl2sCAwEAAQKCAYA+ +CeMse+WiD4I367YHu48nIAuXwa5z59DO1ECtc2pdflje32Jcbzz4ja2yFy06TyFw +XJVd/X7rA3g43easUr0E43xHYRprHkqAYJh0p+UDXZXkqeYKRJOSlREPNFZWDSns +rK3JElJ1aTUkQmS9/xPDtpGXhIf5YRk6TIVzoppfguQMLm2WioQ1i1jA06knoIqG +7R1eKWoy9FXxCdmWmf03OSscuW6WzzqGKtLt/mFkNAFeTBHQrV14a+PlY8BVutnA +YF7UH6JLtQPmU5e+ysQhHKPiAYr0Rgz58VmCpsr1udJuaQg2p/kIh/PYXaBsMzSh +5BiHwXY3Nd0DKNDvOYRT2zW+RODjrMFPXFtLEvyJrt2YEhHMR43puAd36fKESbCu +BFQ6R9gYdJSbAFUYVoiNlYYgXRgvKtZlRO6VVnc0oeFjZjU4vPgjhlCd3ejMFBn8 +dKZfZoP1Gjnih4nDVsMA3oZy6py18P1sE+bJuBNPOVEm9hFGLDIUYw+cJ+L1an0C +gcEAwysnNs4qlwNwilpBwp+GqeXctXAf+WOGkUzJx0ajL8ZqOzJYDy0qZjFvaYUw +SoJA3/8LKkrQ2o4yEdo6FeZ+1THE0nmDee7QCXLv2f/Kz+BJMbY8bFDFJ5iZGYJo +4qJZ5eYw7RwZtUVIEN6J2IehCeDoZ796lZCDym1kyfd9trAvLgyeeqYyIo5tTU/o +m5iutWHf4E7h1wGGYLu0JOc8lfjNr1kLG2crOhGW6xv4GH/aN5uoJYyj5nT5MpJR +FmSvAoHBALX2wUUlmLi9BUEt4HDyxfsQysjbaL60O816FkWPQ6wSeKZtKnnZ9SVG +Zfc9YI/8XWbvw4RQAXK6foLQRqZBdTbyPvK4wcvsraZgDNZylZR57g0/lsEZb2x6 +rdkyGqYk636TkxZgjM9nobiUFl08DYCQx6+7agX317XJM2IgVHwCCN8yFCSBWMWv +/0/FImYnwSaZcwoFl2NyNYp0Hm+46JbgeqNLRh/q5LP5ctswaf5ow1fQal7Pnujp +epvTrc9gBQKBwAd8lkqB6hxU43JLOA6RNAc8bBUBshhM+xKD0ZG1YfkpMVYH/lzG +rMOyzcVMeKfDC14MWI9Mw2o+3ijVKDlC2EaxXA1+385DnPNf11eTemIzBlvD09mL +16RHNEhIOmHHPI+XHvVZiJrth3AuOTd3LHRGjmO8cqVeH9AaFi2xMr9ZTtYDYeju +iynMMOEwWYnud3U5xe7C1+L5igAX8zcfL7HJE4rQbZ6lfIEDv7hRIFlAPky3yeF2 +3aqvfge0mSu0vQKBwQC0hSV+LOtTMyrYW+QTCbbB+gnDtHA46WN+2QTpY1TWi8io +Cxsdoc6jeTDFVF3M2qfq7CiCxkB5imzh6DB+1eesaC1s7UBNx/MKYLW7theRIlm5 +SQGNdmdlLlv0YtGvTGdYvp0okQaA6KW3ESsSufYHOxrvyzVG0HdvADl6uuo10Gru +yqbTBIOGM7FNeRxETmI/ofJP2fYzkWVugv6DBeWxcOgZwh7CoWzc47c/nss4usQ5 +BSnR/5X+KUsa9qizV+UCgcBs3GCVSvurlVKHbTHexZID4Lw1V6RygY7DOlqKAAr4 +B/mNG3c0hIv2IqbA2pXKznA4J5ofafa7f1tdAk9J1MgUqpcfhoNzhAX2PidS3xg+ +nTvTKM/LhFVfhiav66unQ+BOqSeODiipTuO2SxgSlMSKKN9YiCeK+lys/bYjmrV4 +i6B9cTRIaZCCzMR3VDpQkyp0ZObdp3vdz+l236IQteNVWK9s2E+fAkYHfMWOcMRf +GnUCBmjiF4K0RZKvgQnw3R8= +-----END PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/rsa-3072_passwd.key b/scripts/tests/assets/keys/rsa-3072_passwd.key new file mode 100644 index 000000000..bc8bb6ae2 --- /dev/null +++ b/scripts/tests/assets/keys/rsa-3072_passwd.key @@ -0,0 +1,42 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIHbTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIRZc+6IinbS0CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCCemozgSC7rEyzT3BmsXC6BIIH +EAnUppECIx5AcoXh7SnIE4D6C/NWU0OgLWyaHuEi3mBbrR0f9PSBtpBXsxZHiiMd +1wnYAzs/C/8Uk6U2JxKG+rWfcQJaz6oeH5ujLenOHfadmbHFVFEWPLMWtRl/wJ4c +dKn7wBxPzY8auWNIOa1BRifo9jGy8GVCB3AqxAUwtqvoMBQY2Eki0F33kik6CWNR +KFjy/rUm6BOVK3/X4P848yirVIPfYiLNt0iwaDtJTO30Y7y5sKAY1WZoI/hr1hfa +0yCZSB1G4UoArSYGgSb3/i6B1gpw/AOR6WcNkjvjTMCRlXDH9xTQ6X7mP4Nj/E9A +2uPP6MAivGZAi/OY0Z3ezXPIRSmS61F+tUej9JOSjG2OuajqJGOnV5E9+1uvveIQ +v4XXB7R/HTCGs59KRhO06xG2Xv+OHH4ibuYvIrXuaqjPIATThzzYJNuc/YLvob7W +0ENAEyoERTL+QoNaFhbitAFxT5vzFXdB8XIdjikxWVjWrnZj26uhQJAszbQJOGVW +6yAnCK4kCowS/g1mfHpiGopgr2dpcPSgI+Oyv9yFkY/oQb5+WvGhdbzhENDmFB2W +FtSp9jTAKMN0hbXtV/kopgXAiWsDOOPHl61KHmQT9ZjnyAxt8smypAXQTXJEwnw7 +QtSnhBElii50LOlDa5hs8WwJ4j6guKiEzFMDHB6dsdl9ANOFPdagQ6VEEN+9pyLK +ORtI3s4HMRYVuVC0Kuahz2NXldOvgKmcUlgtaEcIcjXSJtf6SOKEbclLPrgGrJp7 +e/3zEvLb9W/133XeWl6OKIaBLXsAAg13rrlwh5lbQZWAP/EkdQfUqHxV533q2DfU +/janFmuhcuHgMNaRGOPyXXfTM1t5zXeioWJMZr4Sknn+Ny1GMrlF9ZGlEIrzTMML +NnuBV+daoUzuWiCSroBs/FD4wfDqBQSlSSdmzpZXZvQXuerOLFZWW8TCVL7BW8Qq +Mwo4STSGxrHqv7haNZU6MuaUEQ2MFv7wwgu+PnxWfnQF0B6bHP6Nx5EIw/y86SVa +WiTgU89mTsYywURTTslzDIH+d2s7/HN1oKwsLBPGhjAif9hpl6jN8JbQDSczZhDn +4o6RsHB+bvChboxyVBEisJJ4+bWBIW+Q6nwSQIVb7k06cq8FcXgXKb90NNOhV3jU +h9IHdvjjhXa1FEDFb059YS+kXhFUw4yJ81GuPxANZXTW9h9JuFpLrSajjdCLmw1j +NaCUJpkYO8dYlIxIXK1QBrQP6WflqeFZtwQvsWmAoQHD4DoVmE5ch3vWc0Oe/ywL +i74adE0uf2WOnMP8aqtLSnxtordKiVBywjZGSuC1GbvYc4d/CbSqdXxxLA+QGOwP +s0VEt1e5SbgpQmPQCIBg6gre75yLdWm2ZEGwmsEWaHeTWw+MyUBjr7xcosGunyG2 +3HwE5tb4v33mG+ZkS3gCyOhq/HkUYimwpOi9oIs2qeBVdRtYgtSthrk309JSy7CU +AYftZ0kYJLPh9z7w3mzvz5cQEcoCguZLaNV1z7Ew5OrwAsY/bPqHSMo2MEtrXj/n +lLp7xrl2bA2HMpn/Ciak0r4OV45ZMxT71XRkbOKINBBrCw5/0JcOXJC5OXW8ZsJ2 +LGNz0s0dGiUSFVoFRYTURSLjjMlZ+YvjM1l/pNEiwb/xskbGlkBveEGhejx/bYAe +SZRMs/0P+Cc1IXpwFcf86UnGB7wYDxpVrMArt1tqdnzdk+bkTtcrscE02nlqN2Qv +fPoMv9AV2FGT+PuJhAo1g9Mlnidcu66dHuGYFYRzTwNYJXKRrPgZv34t0GaQb+hM +Q3eS51VSrIyuieN4n6XxQIjWzcNnIZ4G0LF5BXzar85/qiY2mB5vfei1M+ghbET0 +11s54Sm59aFQ8+kmE1wwBBy0cnEJlf4Rs98QONcOYVNPmb//rbmQF191tB4xXecS +7z9pd7vv+QL0x26MMNNUxA9/Sbbx52WoQJY5HofecN+cZSdpskbEicA6AcwJvSg2 +OAyg8NyvW/c9dd+ka98i0Z+rR8HjGrz7Bv/ta8Wsy6EGRTfeKnCeI8LKtgJRWp90 +1hawqLyjmFfKZKkS3cRZkzVEElY71+SF4ehjZAMUm5lejF9YfLOrOPb0d9rtmVob +qYfOw2gK4qF+Krk6Nq1SrvdFeSZuVK2GWyv2MPIofSygegYUJm1EXfeWyr2MHtIZ +S2eyvK7Ffcs70sWPFJiDHo+j7yAwuzlV8R0AqLEbg3tfiy2cQzaiiThOqXx5FgMK +tkD6tLbfN8GV1BLXXIqeCE4sqWOmhneT5gITl91TJDZU05w9OYLB0r/GYMsvMtHo +LXey/l0sYYgFrK3iKCQ7u4CmSM2fE5MoFAkyIJNbwv9X5AE7ySuzqAr8cZ2s0ae+ +2u/YJspILVJl1Ythr5tWUelYrTJpSDOuSfL3Y05CRwpg +-----END ENCRYPTED PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/x25519.key b/scripts/tests/assets/keys/x25519.key new file mode 100644 index 000000000..98ed2fdaa --- /dev/null +++ b/scripts/tests/assets/keys/x25519.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VuBCIEIOj5tRNYWq9IVzU6CXw2w76dR81c+eVSCdZ9q5cKmYJl +-----END PRIVATE KEY----- diff --git a/scripts/tests/assets/keys/x25519_passwd.key b/scripts/tests/assets/keys/x25519_passwd.key new file mode 100644 index 000000000..2cb6c8bc3 --- /dev/null +++ b/scripts/tests/assets/keys/x25519_passwd.key @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjs2EEY5ee3DAICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEB1A8DsYxCImWNVt3PbkhOgEQDkd +PJcS5uRJgb8yQQqjroVLf5pUFGnYaIp1Ils2TrYl/UtctIi2L0TIg1XJaS/wRIk6 +nCr0DZ3/gaVth13tRwo= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/scripts/tests/conftest.py b/scripts/tests/conftest.py index 797bf25d1..d0f90ac6e 100644 --- a/scripts/tests/conftest.py +++ b/scripts/tests/conftest.py @@ -16,13 +16,23 @@ # List of tests expected to fail for some reason XFAILED_TESTS = { - "tests/test_keys.py::test_getpriv[openssl-ed25519]", - "tests/test_keys.py::test_getpriv[openssl-x25519]", - "tests/test_keys.py::test_getpriv[pkcs8-rsa-2048]", - "tests/test_keys.py::test_getpriv[pkcs8-rsa-3072]", - "tests/test_keys.py::test_getpriv[pkcs8-ed25519]", - "tests/test_keys.py::test_getpub[pem-ed25519]", - "tests/test_keys.py::test_sign_verify[x25519]", + # Unsupported key_type and format combinations + "tests/test_keys.py::TestGetPriv::test_getpriv[openssl-ed25519]", + "tests/test_keys.py::TestGetPriv::test_getpriv[openssl-x25519]", + "tests/test_keys.py::TestGetPriv::test_getpriv[pkcs8-rsa-2048]", + "tests/test_keys.py::TestGetPriv::test_getpriv[pkcs8-rsa-3072]", + "tests/test_keys.py::TestGetPriv::test_getpriv[pkcs8-ed25519]", + "tests/test_keys.py::TestGetPriv::test_getpriv_with_password[openssl-ed25519]", + "tests/test_keys.py::TestGetPriv::test_getpriv_with_password[openssl-x25519]", + "tests/test_keys.py::TestGetPriv::test_getpriv_with_password[pkcs8-rsa-2048]", + "tests/test_keys.py::TestGetPriv::test_getpriv_with_password[pkcs8-rsa-3072]", + "tests/test_keys.py::TestGetPriv::test_getpriv_with_password[pkcs8-ed25519]", + # 'Ed25519' object has no attribute 'get_public_pem' + "tests/test_keys.py::TestGetPub::test_getpub[pem-ed25519]", + "tests/test_keys.py::TestGetPub::test_getpub_with_encoding[pem-ed25519]", + "tests/test_keys.py::TestGetPub::test_getpub_no_outfile[pem-ed25519]", + "tests/test_keys.py::TestLoading::test_load_key_public[pem-ed25519]", + "tests/test_keys.py::TestLoading::test_load_key_public_with_password[pem-ed25519]", } diff --git a/scripts/tests/constants.py b/scripts/tests/constants.py new file mode 100644 index 000000000..6dc383ed1 --- /dev/null +++ b/scripts/tests/constants.py @@ -0,0 +1,43 @@ +# Copyright 2024 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from imgtool import main as imgtool_main + +# all supported key types for 'keygen' +KEY_TYPES = [*imgtool_main.keygens] +KEY_ENCODINGS = [*imgtool_main.valid_encodings] +KEY_LANGS = [*imgtool_main.valid_langs] +PUB_HASH_ENCODINGS = [*imgtool_main.valid_hash_encodings] +PVT_KEY_FORMATS = [*imgtool_main.valid_formats] + +OPENSSL_KEY_TYPES = { + "rsa-2048": "Private-Key: (2048 bit, 2 primes)", + "rsa-3072": "Private-Key: (3072 bit, 2 primes)", + "ecdsa-p256": "Private-Key: (256 bit)", + "ecdsa-p384": "Private-Key: (384 bit)", + "ed25519": "ED25519 Private-Key:", + "x25519": "X25519 Private-Key:", +} +GEN_KEY_EXT = ".key" +PUB_KEY_EXT = ".pub" +PUB_KEY_HASH_EXT = ".pubhash" + +images_dir = "./tests/assets/images" +signed_images_dir = images_dir + "/signed" +keys_dir = "./tests/assets/keys/" + +def tmp_name(tmp_path, key_type, suffix=""): + return tmp_path / (key_type + suffix) diff --git a/scripts/imgtool/keys/ecdsa_test.py b/scripts/tests/keys/ecdsa_test.py similarity index 100% rename from scripts/imgtool/keys/ecdsa_test.py rename to scripts/tests/keys/ecdsa_test.py diff --git a/scripts/imgtool/keys/ed25519_test.py b/scripts/tests/keys/ed25519_test.py similarity index 100% rename from scripts/imgtool/keys/ed25519_test.py rename to scripts/tests/keys/ed25519_test.py diff --git a/scripts/imgtool/keys/rsa_test.py b/scripts/tests/keys/rsa_test.py similarity index 100% rename from scripts/imgtool/keys/rsa_test.py rename to scripts/tests/keys/rsa_test.py diff --git a/scripts/tests/test_dumpinfo.py b/scripts/tests/test_dumpinfo.py new file mode 100644 index 000000000..1b8c4cda5 --- /dev/null +++ b/scripts/tests/test_dumpinfo.py @@ -0,0 +1,337 @@ +# Copyright 2024 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pathlib import Path +from tempfile import mktemp + +import pytest +from click.testing import CliRunner + +from imgtool.main import imgtool +from tests.constants import tmp_name, KEY_TYPES, signed_images_dir, images_dir + +DUMPINFO_SUCCESS_LITERAL = "dumpinfo has run successfully" + +DUMPINFO_TRAILER = """ +swap status: (len: unknown) +enc. keys: Image not encrypted +swap size: unknown +swap_info: INVALID (0xff) +copy_done: INVALID (0xff) +image_ok: SET (0x1) +boot magic: """ + +DUMPINFO_ENCRYPTED_TRAILER = """ +swap status: (len: unknown) +enc. keys: (len: 0x20, if BOOT_SWAP_SAVE_ENCTLV is unset) +swap size: unknown +swap_info: INVALID (0xff) +copy_done: INVALID (0xff) +image_ok: INVALID (0xff) +boot magic: """ + + +class TestDumpInfo: + image = None + image_signed = None + runner = CliRunner() + dumpfile = mktemp("dump.dat") + + @pytest.fixture(scope="session") + def tmp_path_persistent(self, tmp_path_factory): + return tmp_path_factory.mktemp("keys") + + @pytest.fixture(autouse=True) + def setup(self, tmp_path_persistent, key_type="rsa-2048"): + """Generate keys and images for testing""" + + self.key = tmp_name(tmp_path_persistent, key_type, ".key") + self.runner.invoke( + imgtool, ["keygen", "--key", str(self.key), "--type", key_type] + ) + + self.image = images_dir + "/zero.bin" + self.image_signed = tmp_name(tmp_path_persistent, "image", ".signed") + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + def test_dumpinfo(self, tmp_path_persistent, key_type): + self.image_signed = signed_images_dir + "/basic/" + key_type + ".bin" + + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + + result = self.runner.invoke( + imgtool, ["dumpinfo", "invalid"] + ) + assert result.exit_code != 0 + assert "Error: Image file not found" in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + def test_dumpinfo_silent(self, tmp_path_persistent, key_type): + self.image_signed = signed_images_dir + "/basic/" + key_type + ".bin" + + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed), "-s"] + ) + assert result.exit_code == 0 + assert result.stdout == "" + + result = self.runner.invoke( + imgtool, ["dumpinfo", "invalid", "-s"] + ) + assert result.exit_code != 0 + assert "Error: Image file not found" in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + def test_dumpinfo_tlv(self, tmp_path_persistent, key_type): + self.image_signed = signed_images_dir + "/customTLV/" + key_type + ".bin" + + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert "type: UNKNOWN (0xa0)" in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + def test_dumpinfo_tlv_outfile(self, tmp_path_persistent, key_type): + self.image_signed = signed_images_dir + "/customTLV/" + key_type + ".bin" + + result = self.runner.invoke( + imgtool, ["dumpinfo", "-o", self.dumpfile, str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert Path(self.dumpfile).exists() + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + @pytest.mark.parametrize("align", ('1', '2', '4', '8', '16', '32',)) + def test_dumpinfo_align_pad(self, tmp_path_persistent, key_type, align): + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + self.key, + "--align", + align, + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--pad", + "--confirm", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == 0 + + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert DUMPINFO_TRAILER in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + @pytest.mark.parametrize("align", ('1', '2', '4', '8', '16', '32',)) + def test_dumpinfo_align_pad_outfile(self, tmp_path_persistent, key_type, align): + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + self.key, + "--align", + align, + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--pad", + "--confirm", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == 0 + + result = self.runner.invoke( + imgtool, ["dumpinfo", "-o", self.dumpfile, str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert DUMPINFO_TRAILER in result.stdout + assert Path(self.dumpfile).exists() + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + @pytest.mark.parametrize("align", ('1', '2', '4', '8', '16', '32',)) + def test_dumpinfo_max_align_pad(self, tmp_path_persistent, key_type, align): + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + self.key, + "--align", + align, + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--pad", + "--confirm", + "--max-align", + "32", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == 0 + + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert DUMPINFO_TRAILER in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES[:-1]) + def test_dumpinfo_encrypted_clear(self, tmp_path_persistent, key_type): + encoding = "pem" + enckey = "./keys/" + key_type + ".key" + pub_key = tmp_name(tmp_path_persistent, key_type, ".pub." + encoding) + self.image_signed = signed_images_dir + "/encryptedClear/" + key_type + ".enc" + + self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + str(enckey), + "--output", + str(pub_key), + "--encoding", + encoding, + ], + ) + + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + def test_dumpinfo_encrypted_padded_outfile(self, tmp_path_persistent, key_type): + self.image_signed = signed_images_dir + "/pad_sig_enc_16.bin" + result = self.runner.invoke( + imgtool, ["dumpinfo", "-o", self.dumpfile, self.image_signed] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert DUMPINFO_ENCRYPTED_TRAILER in result.stdout + assert Path(self.dumpfile).exists() + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + def test_dumpinfo_encrypted_padded_invalid(self, tmp_path_persistent, key_type): + self.image_signed = signed_images_dir + "/pad_2.3K_sig_enc_16_inv.bin" + result = self.runner.invoke( + imgtool, ["dumpinfo", self.image_signed] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert "Warning: the trailer magic value is invalid!" in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-2048",)) + def test_dumpinfo_padded_multiflag(self, tmp_path_persistent, key_type): + encoding = "pem" + enckey = tmp_name(tmp_path_persistent, key_type, ".key") + pub_key = tmp_name(tmp_path_persistent, key_type, ".pub." + encoding) + + self.runner.invoke( + imgtool, ["keygen", "--key", str(enckey), "--type", key_type] + ) + + self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + str(enckey), + "--output", + str(pub_key), + "--encoding", + encoding, + ], + ) + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + self.key, + "--encrypt", + pub_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x2300", + "--pad-header", + "--pad", + "--rom-fixed", + "32", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == 0 + + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed)] + ) + assert result.exit_code == 0 + assert "ENCRYPTED_AES128" in result.stdout + assert "ROM_FIXED" in result.stdout + assert DUMPINFO_SUCCESS_LITERAL in result.stdout + assert DUMPINFO_ENCRYPTED_TRAILER in result.stdout + + @pytest.mark.parametrize("hex_addr", ("0", "16", "35")) + def test_dumpinfo_hex(self, tmp_path_persistent, hex_addr): + self.image_signed = signed_images_dir + "/hex/" + f"zero_hex-addr_{hex_addr}" + ".hex" + result = self.runner.invoke( + imgtool, ["dumpinfo", str(self.image_signed)] + ) + assert result.exit_code == 0 + assert DUMPINFO_SUCCESS_LITERAL in result.stdout diff --git a/scripts/tests/test_keys.py b/scripts/tests/test_keys.py index a812ba2f1..2fadd8b7a 100644 --- a/scripts/tests/test_keys.py +++ b/scripts/tests/test_keys.py @@ -1,3 +1,6 @@ +# Copyright 2024 Denis Mingulov +# Copyright 2024 Arm Limited +# # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,242 +15,588 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pytest - +import re import subprocess +from tempfile import mktemp + +import pytest from click.testing import CliRunner -from imgtool import main as imgtool_main -from imgtool.main import imgtool -# all supported key types for 'keygen' -KEY_TYPES = [*imgtool_main.keygens] -KEY_ENCODINGS = [*imgtool_main.valid_encodings] -PUB_HASH_ENCODINGS = [*imgtool_main.valid_hash_encodings] -PVT_KEY_FORMATS = [*imgtool_main.valid_formats] - -OPENSSL_KEY_TYPES = { - "rsa-2048": "Private-Key: (2048 bit, 2 primes)", - "rsa-3072": "Private-Key: (3072 bit, 2 primes)", - "ecdsa-p256": "Private-Key: (256 bit)", - "ecdsa-p384": "Private-Key: (384 bit)", - "ed25519": "ED25519 Private-Key:", - "x25519": "X25519 Private-Key:", +from imgtool.main import imgtool +from tests.constants import KEY_TYPES, GEN_KEY_EXT, tmp_name, OPENSSL_KEY_TYPES, KEY_ENCODINGS, PUB_KEY_EXT, \ + PVT_KEY_FORMATS, KEY_LANGS, PUB_HASH_ENCODINGS, PUB_KEY_HASH_EXT, keys_dir + +UNSUPPORTED_TYPES = [("openssl", "ed25519"), + ("openssl", "x25519"), + ("pkcs8", "rsa-2048"), + ("pkcs8", "rsa-3072"), + ("pkcs8", "ed25519"), ] + +OPENSSL_PUBLIC_KEY_TYPES = { + "rsa-2048": "Public-Key: (2048 bit)", + "rsa-3072": "Public-Key: (3072 bit)", + "ecdsa-p256": "Public-Key: (256 bit)", + "ecdsa-p384": "Public-Key: (384 bit)", + "ed25519": "ED25519 Public-Key:", + "x25519": "X25519 Public-Key:", } -GEN_KEY_EXT = ".key" GEN_ANOTHER_KEY_EXT = ".another.key" -PUB_KEY_EXT = ".pub" -PUB_KEY_HASH_EXT = ".pubhash" - - -def tmp_name(tmp_path, key_type, suffix=""): - return tmp_path / (key_type + suffix) - - -@pytest.fixture(scope="session") -def tmp_path_persistent(tmp_path_factory): - return tmp_path_factory.mktemp("keys") - - -@pytest.mark.parametrize("key_type", KEY_TYPES) -def test_keygen(key_type, tmp_path_persistent): - """Generate keys by imgtool""" - - runner = CliRunner() - - gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) - - assert not gen_key.exists() - result = runner.invoke( - imgtool, ["keygen", "--key", str(gen_key), "--type", key_type] - ) - assert result.exit_code == 0 - assert gen_key.exists() - assert gen_key.stat().st_size > 0 - - # another key - gen_key2 = tmp_name(tmp_path_persistent, key_type, GEN_ANOTHER_KEY_EXT) - - assert str(gen_key2) != str(gen_key) - - assert not gen_key2.exists() - result = runner.invoke( - imgtool, ["keygen", "--key", str(gen_key2), "--type", key_type] - ) - assert result.exit_code == 0 - assert gen_key2.exists() - assert gen_key2.stat().st_size > 0 - - # content must be different - assert gen_key.read_bytes() != gen_key2.read_bytes() -@pytest.mark.parametrize("key_type", KEY_TYPES) -def test_keygen_type(key_type, tmp_path_persistent): +def verify_key(key_type, key, password=""): """Check generated keys""" assert key_type in OPENSSL_KEY_TYPES - gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) - result = subprocess.run( - ["openssl", "pkey", "-in", str(gen_key), "-check", "-noout", "-text"], + ["openssl", "pkey", "-in", str(key), "-check", "-noout", "-text", "-passin", "pass:" + password], capture_output=True, text=True, + ) assert result.returncode == 0 assert "Key is valid" in result.stdout assert OPENSSL_KEY_TYPES[key_type] in result.stdout -@pytest.mark.parametrize("key_type", KEY_TYPES) -@pytest.mark.parametrize("format", PVT_KEY_FORMATS) -def test_getpriv(key_type, format, tmp_path_persistent): - """Get private key""" - runner = CliRunner() - - gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) - - result = runner.invoke( - imgtool, - [ - "getpriv", - "--key", - str(gen_key), - "--format", - format, - ], - ) - assert result.exit_code == 0 - - -@pytest.mark.parametrize("key_type", KEY_TYPES) -@pytest.mark.parametrize("encoding", KEY_ENCODINGS) -def test_getpub(key_type, encoding, tmp_path_persistent): - """Get public key""" - runner = CliRunner() +def verify_public_key(key_type, encoding, key, password=""): + """Check generated keys""" - gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) - pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT - + "." + encoding) - - assert not pub_key.exists() - result = runner.invoke( - imgtool, - [ - "getpub", - "--key", - str(gen_key), - "--output", - str(pub_key), - "--encoding", - encoding, - ], - ) - assert result.exit_code == 0 - assert pub_key.exists() - assert pub_key.stat().st_size > 0 + key_file = str(key) + if encoding not in ['pem', 'raw']: + # convert to raw + # read file key to key_str + with open(key, 'r') as file: + hex_values = re.findall(r'0x([a-fA-F0-9]{2}),', file.read()) + byte_data = bytes(int(h, 16) for h in hex_values) + raw_file = mktemp("key.raw") + with open(raw_file, 'wb') as file: + file.write(byte_data) -@pytest.mark.parametrize("key_type", KEY_TYPES) -@pytest.mark.parametrize("encoding", PUB_HASH_ENCODINGS) -def test_getpubhash(key_type, encoding, tmp_path_persistent): - """Get the hash of the public key""" - runner = CliRunner() + key_file = raw_file - gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) - pub_key_hash = tmp_name( - tmp_path_persistent, key_type, PUB_KEY_HASH_EXT + "." + encoding - ) - - assert not pub_key_hash.exists() - result = runner.invoke( - imgtool, - [ - "getpubhash", - "--key", - str(gen_key), - "--output", - str(pub_key_hash), - "--encoding", - encoding, - ], + result = subprocess.run( + ["openssl", "pkey", "-in", key_file, "-pubin", "-pubcheck", "-noout", "-text"], + capture_output=True, + text=True, ) - assert result.exit_code == 0 - assert pub_key_hash.exists() - assert pub_key_hash.stat().st_size > 0 + assert result.returncode == 0 + assert "Key is valid" in result.stdout + assert OPENSSL_PUBLIC_KEY_TYPES[key_type] in result.stdout -@pytest.mark.parametrize("key_type", KEY_TYPES) -def test_sign_verify(key_type, tmp_path_persistent): - """Test basic sign and verify""" +class TestKeys: runner = CliRunner() - - gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) - wrong_key = tmp_name(tmp_path_persistent, key_type, GEN_ANOTHER_KEY_EXT) - image = tmp_name(tmp_path_persistent, "image", "bin") - image_signed = tmp_name(tmp_path_persistent, "image", "signed") - - with image.open("wb") as f: - f.write(b"\x00" * 1024) - - # not all required arguments are provided - result = runner.invoke( - imgtool, - [ - "sign", - "--key", - str(gen_key), - str(image), - str(image_signed), - ], - ) - assert result.exit_code != 0 - assert not image_signed.exists() - - result = runner.invoke( - imgtool, - [ - "sign", - "--key", - str(gen_key), - "--align", - "16", - "--version", - "1.0.0", - "--header-size", - "0x400", - "--slot-size", - "0x10000", - "--pad-header", - str(image), - str(image_signed), - ], - ) - assert result.exit_code == 0 - assert image_signed.exists() - assert image_signed.stat().st_size > 0 - - # original key can be used to verify a signed image - result = runner.invoke( - imgtool, - [ - "verify", - "--key", - str(gen_key), - str(image_signed), - ], - ) - assert result.exit_code == 0 - - # 'another' key is not valid to verify a signed image - result = runner.invoke( - imgtool, - [ - "verify", - "--key", - str(wrong_key), - str(image_signed), - ], - ) - assert result.exit_code != 0 - image_signed.unlink() + password = "12345" # Password for the keys in ./keys directory + + @pytest.fixture(scope="session") + def tmp_path_persistent(self, tmp_path_factory): + return tmp_path_factory.mktemp("keys") + + +class TestKeygen(TestKeys): + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_keygen(self, key_type, tmp_path_persistent): + """Generate keys by imgtool""" + + gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + + assert not gen_key.exists() + result = self.runner.invoke( + imgtool, ["keygen", "--key", str(gen_key), "--type", key_type] + ) + assert result.exit_code == 0 + assert gen_key.exists() + assert gen_key.stat().st_size > 0 + verify_key(key_type, gen_key) + + # another key + gen_key2 = tmp_name(tmp_path_persistent, key_type, GEN_ANOTHER_KEY_EXT) + + assert str(gen_key2) != str(gen_key) + + assert not gen_key2.exists() + result = self.runner.invoke( + imgtool, ["keygen", "--key", str(gen_key2), "--type", key_type] + ) + assert result.exit_code == 0 + assert gen_key2.exists() + assert gen_key2.stat().st_size > 0 + verify_key(key_type, gen_key2) + + # content must be different + assert gen_key.read_bytes() != gen_key2.read_bytes() + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_keygen_with_password(self, key_type, tmp_path_persistent, monkeypatch): + """Generate keys by imgtool with password""" + + gen_key = tmp_name(tmp_path_persistent, key_type + "_passwd", GEN_KEY_EXT) + assert not gen_key.exists() + monkeypatch.setattr('getpass.getpass', lambda _: self.password) + result = self.runner.invoke( + imgtool, ["keygen", "--key", str(gen_key), "--type", key_type, "-p"] + ) + + assert result.exit_code == 0 + assert gen_key.exists() + assert gen_key.stat().st_size > 0 + verify_key(key_type, gen_key, self.password) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_keygen_with_mismatching_password(self, key_type, tmp_path_persistent, monkeypatch): + """Generate keys by imgtool with mismatching password should print error""" + + gen_key = tmp_name(tmp_path_persistent, key_type + "-null", GEN_KEY_EXT) + assert not gen_key.exists() + passwords = iter([self.password, 'invalid']) + monkeypatch.setattr('getpass.getpass', lambda _: next(passwords)) + result = self.runner.invoke( + imgtool, ["keygen", "--key", str(gen_key), "--type", key_type, "-p"], + ) + + assert result.exit_code != 0 + assert not gen_key.exists() + assert "Passwords do not match, try again" in result.stdout + + +class TestGetPriv(TestKeys): + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("key_format", PVT_KEY_FORMATS) + def test_getpriv(self, key_type, key_format, tmp_path_persistent): + """Get private key""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + + result = self.runner.invoke( + imgtool, + [ + "getpriv", + "--key", + str(gen_key), + "--format", + key_format, + ], + ) + assert result.exit_code == 0 + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("key_format", PVT_KEY_FORMATS) + def test_getpriv_with_password(self, key_type, key_format, tmp_path_persistent, monkeypatch): + """Get private key with password""" + + gen_key = keys_dir + "/" + key_type + "_passwd" + GEN_KEY_EXT + + monkeypatch.setattr('getpass.getpass', lambda _: self.password) + result = self.runner.invoke( + imgtool, + [ + "getpriv", + "--key", + str(gen_key), + "--format", + key_format, + ] + ) + assert result.exit_code == 0 + + @pytest.mark.parametrize("key_format, key_type", UNSUPPORTED_TYPES) + def test_getpriv_unsupported(self, key_format, key_type, tmp_path_persistent): + """Get private key for unsupported combinations should print error message""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + + result = self.runner.invoke( + imgtool, + [ + "getpriv", + "--key", + str(gen_key), + "--format", + key_format, + ], + ) + + assert result.exit_code == 2 + assert "not support" in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("key_format", PVT_KEY_FORMATS) + def test_getpriv_with_invalid_pass(self, key_type, key_format, tmp_path_persistent, monkeypatch): + """Get private key with invalid password should print 'Invalid passphrase' in stdout""" + + gen_key = keys_dir + "/" + key_type + "_passwd" + GEN_KEY_EXT + + monkeypatch.setattr('getpass.getpass', lambda _: 'invalid') + result = self.runner.invoke( + imgtool, + [ + "getpriv", + "--key", + str(gen_key), + "--format", + key_format, + ], + ) + assert result.exit_code != 0 + assert "Invalid passphrase" in result.stdout + + +class TestGetPub(TestKeys): + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_getpub(self, key_type, tmp_path_persistent): + """Get public key - Default lang is c """ + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + "default" + PUB_KEY_EXT) + + assert not pub_key.exists() + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + ], + ) + assert result.exit_code == 0 + assert pub_key.exists() + assert pub_key.stat().st_size > 0 + verify_public_key(key_type, "c", pub_key) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", KEY_ENCODINGS) + def test_getpub_with_encoding(self, key_type, encoding, tmp_path_persistent): + """Get public key with encoding""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + encoding + PUB_KEY_EXT) + + assert not pub_key.exists() + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + "--encoding", + encoding, + ], + ) + assert result.exit_code == 0 + assert pub_key.exists() + assert pub_key.stat().st_size > 0 + verify_public_key(key_type, encoding, pub_key) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("lang", KEY_LANGS) + def test_getpub_with_lang(self, key_type, lang, tmp_path_persistent): + """Get public key with specified lang""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT + "." + lang) + + assert not pub_key.exists() + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + "--lang", + lang, + ], + ) + assert result.exit_code == 0 + assert pub_key.exists() + assert pub_key.stat().st_size > 0 + verify_public_key(key_type, lang, pub_key) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", KEY_ENCODINGS) + def test_getpub_no_outfile(self, key_type, encoding, tmp_path_persistent): + """Get public key without output file should dump only to stdout""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + encoding + "-null" + PUB_KEY_EXT) + + assert not pub_key.exists() + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--encoding", + encoding, + ], + ) + assert result.exit_code == 0 + assert len(result.stdout) != 0 + assert not pub_key.exists() + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_getpub_invalid_encoding(self, key_type, tmp_path_persistent): + """Get public key with invalid encoding should error message in stdout""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + PUB_KEY_EXT) + + assert not pub_key.exists() + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + "--encoding", + "invalid", + ], + ) + assert result.exit_code != 0 + assert "Invalid value for '-e' / '--encoding':" in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_getpub_invalid_lang(self, key_type, tmp_path_persistent): + """Get public key with invalid lang should print error message in stdout""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + PUB_KEY_EXT) + + assert not pub_key.exists() + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + "--lang", + "invalid", + ], + ) + assert result.exit_code != 0 + assert "Invalid value for '-l' / '--lang':" in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", KEY_ENCODINGS) + @pytest.mark.parametrize("lang", KEY_LANGS) + def test_getpub_with_encoding_and_lang(self, key_type, encoding, lang, tmp_path_persistent): + """Get public key with both encoding and lang should print error message""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + lang + encoding + PUB_KEY_EXT) + + assert not pub_key.exists() + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + "--encoding", + encoding, + "--lang", + lang + ], + ) + assert result.exit_code != 0 + assert "Please use only one of `--encoding/-e` or `--lang/-l`" in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", KEY_ENCODINGS) + def test_getpub_invalid_pass(self, key_type, encoding, tmp_path_persistent, monkeypatch): + """Get public key with invalid password should print 'Invalid passphrase' in stdout""" + + gen_key = keys_dir + "/" + key_type + "_passwd" + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + encoding + PUB_KEY_EXT) + + monkeypatch.setattr('getpass.getpass', lambda _: 'invalid') + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + "--encoding", + encoding, + ] + ) + assert result.exit_code != 0 + assert "Invalid passphrase" in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", KEY_ENCODINGS) + def test_getpub_null_pass(self, key_type, encoding, tmp_path_persistent, monkeypatch): + """Get public key with invalid password should print 'Invalid passphrase' in stdout""" + + gen_key = keys_dir + "/" + key_type + "_passwd" + GEN_KEY_EXT + pub_key = tmp_name(tmp_path_persistent, key_type, "." + encoding + PUB_KEY_EXT) + + monkeypatch.setattr('getpass.getpass', lambda _: '') + result = self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + gen_key, + "--output", + str(pub_key), + "--encoding", + encoding, + ] + ) + assert result.exit_code != 0 + assert "Password was not given" in result.stdout + + +class TestGetPubHash(TestKeys): + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_getpubhash(self, key_type, tmp_path_persistent): + """Get the hash of the public key - Default encoding is lang-c""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key_hash = tmp_name( + tmp_path_persistent, key_type, "." + "default" + PUB_KEY_HASH_EXT + ) + + assert not pub_key_hash.exists() + result = self.runner.invoke( + imgtool, + [ + "getpubhash", + "--key", + str(gen_key), + "--output", + str(pub_key_hash), + ], + ) + assert result.exit_code == 0 + assert pub_key_hash.exists() + assert pub_key_hash.stat().st_size > 0 + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", PUB_HASH_ENCODINGS) + def test_getpubhash_with_encoding(self, key_type, encoding, tmp_path_persistent): + """Get the hash of the public key with encoding""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key_hash = tmp_name( + tmp_path_persistent, key_type, "." + encoding + PUB_KEY_HASH_EXT + ) + + assert not pub_key_hash.exists() + result = self.runner.invoke( + imgtool, + [ + "getpubhash", + "--key", + str(gen_key), + "--output", + str(pub_key_hash), + "--encoding", + encoding, + ], + ) + assert result.exit_code == 0 + assert pub_key_hash.exists() + assert pub_key_hash.stat().st_size > 0 + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", PUB_HASH_ENCODINGS) + def test_getpubhash_with_no_output(self, key_type, encoding, tmp_path_persistent): + """Get the hash of the public key without outfile should dump only to stdout""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key_hash = tmp_name( + tmp_path_persistent, key_type, "." + encoding + "-null" + PUB_KEY_HASH_EXT + ) + + assert not pub_key_hash.exists() + result = self.runner.invoke( + imgtool, + [ + "getpubhash", + "--key", + str(gen_key), + "--encoding", + encoding, + ], + ) + assert result.exit_code == 0 + assert len(result.stdout) != 0 + assert not pub_key_hash.exists() + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_getpubhash_with_invalid_encoding(self, key_type, tmp_path_persistent): + """Get the hash of the public key with encoding""" + + gen_key = keys_dir + "/" + key_type + GEN_KEY_EXT + pub_key_hash = tmp_name( + tmp_path_persistent, key_type, "." + "invalid" + PUB_KEY_HASH_EXT + ) + + assert not pub_key_hash.exists() + result = self.runner.invoke( + imgtool, + [ + "getpubhash", + "--key", + str(gen_key), + "--output", + str(pub_key_hash), + "--encoding", + "invalid", + ], + ) + assert result.exit_code != 0 + assert "Invalid value for '-e' / '--encoding':" in result.stdout + assert not pub_key_hash.exists() + + @pytest.mark.parametrize("key_type", KEY_TYPES) + @pytest.mark.parametrize("encoding", PUB_HASH_ENCODINGS) + def test_getpubhash_with_invalid_pass(self, key_type, encoding, tmp_path_persistent, monkeypatch): + """Get the hash of the public key with invalid password should print 'Invalid passphrase' in stdout""" + + gen_key = keys_dir + "/" + key_type + "_passwd" + GEN_KEY_EXT + pub_key_hash = tmp_name( + tmp_path_persistent, key_type, "." + encoding + "-null" + PUB_KEY_HASH_EXT + ) + + assert not pub_key_hash.exists() + + monkeypatch.setattr('getpass.getpass', lambda _: 'invalid') + result = self.runner.invoke( + imgtool, + [ + "getpubhash", + "--key", + str(gen_key), + "--output", + str(pub_key_hash), + "--encoding", + encoding, + ], + input="invalid" + ) + assert result.exit_code != 0 + assert not pub_key_hash.exists() + assert "Invalid passphrase" in result.stdout diff --git a/scripts/tests/test_sign.py b/scripts/tests/test_sign.py new file mode 100644 index 000000000..e3a5437a8 --- /dev/null +++ b/scripts/tests/test_sign.py @@ -0,0 +1,1364 @@ +# Copyright 2024 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import io +import os +from contextlib import redirect_stdout +from pathlib import Path + +import pytest +from click.testing import CliRunner + +from imgtool.dumpinfo import dump_imginfo +from imgtool.main import imgtool +from tests.constants import KEY_TYPES, GEN_KEY_EXT, PUB_KEY_EXT, tmp_name, images_dir, keys_dir + +try: + KEY_TYPES.remove("x25519") # x25519 is not used for signing, so directory does not contain any such image +except ValueError: + pass + + +def assert_image_signed(result, image_signed, verify=True): + assert result.exit_code == 0 + assert image_signed.exists() + assert image_signed.stat().st_size > 0 + + # Verify unless specified otherwise + if verify: + runner = CliRunner() + + result = runner.invoke( + imgtool, ["verify", str(image_signed), ], + ) + assert result.exit_code == 0 + + +class TestSign: + image = None + image_signed = None + runner = CliRunner() + key = None + + @pytest.fixture(scope="class") + def tmp_path_persistent(self, tmp_path_factory): + return tmp_path_factory.mktemp("keys") + + @pytest.fixture(autouse=True) + def setup(self, tmp_path_persistent): + """Generate keys and images for testing""" + + self.image = Path(images_dir + "/zero.bin") + self.image_signed = tmp_name(tmp_path_persistent, "image", ".signed") + + @pytest.fixture(autouse=True) + def teardown(self): + if os.path.exists(str(self.image_signed)): + os.remove(str(self.image_signed)) + + +class TestSignBasic(TestSign): + + def test_sign_basic(self, tmp_path_persistent): + """Test basic sign and verify""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("max_align", ("8", "16", "32")) + def test_sign_max_align(self, tmp_path_persistent, max_align): + """Test sign with max align""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--max-align", + max_align, + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("version, expected", [ + ("0.0.0", 0), + ("1.0.0", 0), + ("12.2.34", 0), + ("12.3.45+67", 0), + ("0.0.0+0", 0), + ("asd", 2), + ("2.2.a", 2), + ("a.2.3", 2), + ("1.2.3.4", 2), + ("0.0.0+00", 2), + + ]) + def test_sign_validations_version(self, tmp_path_persistent, version, expected): + """Test Signature validation""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + version, + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == expected + + @pytest.mark.parametrize("sec_counter, expected", [ + ("auto", 0), + ("1", 0), + ("2", 0), + ("inv", 2), + + ]) + def test_sign_validations_security_counter(self, tmp_path_persistent, sec_counter, expected): + """Test security counter validation""" + + # validate security counter + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--security-counter", + sec_counter, + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == expected + + @pytest.mark.parametrize( + ("header_size", "expected"), + ((0, 2), + (64, 0), + ("invalid", 2),), + ) + def test_sign_validations_header_size(self, tmp_path_persistent, header_size, expected): + """Test header size validation""" + + # Header size validations + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + str(header_size), + "--slot-size", + "0x10000", + "--pad-header", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == expected + + @pytest.mark.parametrize("dependencies, expected", [ + ("(1, 1.2.3+0)", 0), + ("", 2), + ("(1, 1.2.3+0)(2)", 2), + ("(1, 1.)", 2), + ("1, 1.2)", 2), + + ]) + def test_sign_dependencies(self, tmp_path_persistent, dependencies, expected): + """Test with dependencies""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + str(self.image), + str(self.image_signed), + "--dependencies", + dependencies, + ], + ) + assert result.exit_code == expected + + def test_sign_header_not_padded(self, tmp_path_persistent): + """Test with un-padded header with non-zero start should fail""" + + self.image = Path(images_dir + "/one.bin") + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Header padding was not requested" in result.output + + def test_sign_pad(self, tmp_path_persistent): + """Test sign pad""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--pad", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("erased_val", ("0", "0xff")) + def test_sign_pad_erased_val(self, tmp_path_persistent, erased_val): + """Test sign pad""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--pad", + "--erased-val", + erased_val, + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + # Capture dumpinfo stdout to check value used for padding + f = io.StringIO() + with redirect_stdout(f): + dump_imginfo(self.image_signed) + result = f.getvalue() + + assert f"padding ({erased_val}" in result + + def test_sign_confirm(self, tmp_path_persistent): + """Test with confirm""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--confirm", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == 0 + + def test_sign_max_sectors(self, tmp_path_persistent): + """Test with max sectors""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--max-sectors", + "256", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("boot_record, expected", [ + ("BABE", 0), + ("DEADBEEF", 0), + ("DEADBEEFF00D", 0), + ("DEADBEEFF00DB", 2), + ("DEADBEEFF00DBABE", 2), + ]) + def test_sign_boot_record(self, tmp_path_persistent, boot_record, expected): + """Test with boot record""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--boot-record", + boot_record, + str(self.image), + str(self.image_signed), + ], + ) + if expected != 0: + assert result.exit_code != 0 + assert "is too long" in result.stdout + else: + assert_image_signed(result, self.image_signed) + + def test_sign_overwrite_only(self, tmp_path_persistent): + """Test sign overwrite""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--overwrite-only", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("endian", ("little", "big")) + def test_sign_endian(self, tmp_path_persistent, endian): + """Test sign endian""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--endian", + endian, + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed, verify=False) + + def test_sign_rom_fixed(self, tmp_path_persistent): + """Test sign with rom fixed""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--rom-fixed", + "1", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("vector", ("digest", "payload",)) + def test_sign_vector_to_sign(self, tmp_path_persistent, vector): + """Test with vector to sign""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--vector-to-sign", + vector, + str(self.image), + str(self.image_signed), + ], + ) + # Don't verify as output is not expected to be a valid image + assert_image_signed(result, self.image_signed, verify=False) + + +class TestSignKey(TestSign): + @pytest.fixture(autouse=True) + def setup_key(self, request, tmp_path_persistent, key_type): + """Generate keys for testing""" + self.key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + self.runner.invoke( + imgtool, ["keygen", "--key", str(self.key), "--type", key_type] + ) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_missing_args(self, key_type, tmp_path_persistent): + """Test basic sign with missing args should fail""" + + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + + # not all required arguments are provided + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert not self.image_signed.exists() + assert "Error: Missing option" in result.stdout + + @pytest.mark.parametrize("key_format", ("hash", "full",)) + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_pub_key_format(self, key_type, tmp_path_persistent, key_format): + """Test with pub key format""" + + loaded_key = keys_dir + key_type + ".key" + + result = self.runner.invoke( + imgtool, + [ + "sign", + + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--public-key-format", + key_format, + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_fix_sig(self, tmp_path_persistent, key_type): + """Test with fix signature""" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--fix-sig", + "/dev/null", + "--fix-sig-pubkey", + self.key, + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + self.key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--fix-sig", + "/dev/null", + "--fix-sig-pubkey", + self.key, + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Can not sign using key and provide fixed-signature at" in result.output + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--fix-sig", + "/dev/null", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "public key of the fixed signature is not specified" in result.output + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_sig_out(self, tmp_path_persistent, key_type): + """Test signature output""" + + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + self.runner.invoke( + imgtool, ["keygen", "--key", str(key), "--type", key_type] + ) + sig_file = tmp_name(tmp_path_persistent, "signature", "sig") + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + str(self.image), + str(self.image_signed), + "--sig-out", + str(sig_file), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_load_addr(self, key_type, tmp_path_persistent): + """Test sign with load addr""" + + loaded_key = keys_dir + key_type + ".key" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--load-addr", + "1", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_load_addr_rom_fixed(self, key_type, tmp_path_persistent): + """Test sign with both load addr and rom fixed should fail""" + + loaded_key = keys_dir + key_type + ".key" + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2500", + "--pad-header", + "--load-addr", + "1", + "--rom-fixed", + "1", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Can not set rom_fixed and load_addr at the same time" in result.output + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_custom_tlv(self, tmp_path_persistent, key_type): + """Test signing with custom TLV""" + + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + + # Sign + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--custom-tlv", + "0x00a0", + "0x00ff", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + # Sign - TLV value in octal + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--custom-tlv", + "256", + "64", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code == 0 + + # Fail - same TLV twice + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--custom-tlv", + "0x00a0", + "0x00ff", + "--custom-tlv", + "0x00a0", + "0x00ff", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "already exists" in result.stdout + + # Fail - predefined TLV + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--custom-tlv", + "0x00a0", + "0x00ff", + "--custom-tlv", + "0x0010", + "0x00ff", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "conflicts with predefined TLV" in result.stdout + + # Fail - odd TLV length + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--custom-tlv", + "0x00a0", + "0x00fff", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Custom TLV length is odd" in result.stdout + + # Fail - TLV < 0x00a0 + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--custom-tlv", + "159", # MIN is 160 + "DEAD", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Invalid custom TLV type value" in result.stdout + + # Fail - TLV > 0xfffe + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--custom-tlv", + "65535", # MAX is 65534 + "DEAD", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Invalid custom TLV type value" in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_pad_sig(self, tmp_path_persistent, key_type): + """Test pad signature""" + + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--pad-sig", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + +class TestSignEncrypt(TestSign): + + @pytest.fixture(autouse=True) + def setup_enc(self, tmp_path_persistent, key_type): + """Generate keys and images for testing""" + self.key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + self.runner.invoke( + imgtool, ["keygen", "--key", str(self.key), "--type", key_type] + ) + + encoding = "pem" + self.pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT + "." + encoding) + + self.runner.invoke( + imgtool, ["keygen", "--key", self.key, "--type", key_type] + ) + + # Get public pair for key + self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + self.key, + "--output", + self.pub_key, + "--encoding", + encoding, + ], + ) + + @pytest.mark.parametrize("key_len", ("128", "256",)) + @pytest.mark.parametrize("align", ("1", "2", "4", "8", "16", "32",)) + @pytest.mark.parametrize("key_type", KEY_TYPES[:-1]) + def test_sign_enc_key(self, tmp_path_persistent, key_type, align, key_len): + """Test signing with encryption""" + + encoding = "pem" + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT + "." + encoding) + + # Sign and encrypt + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--encrypt", + pub_key, + "--align", + align, + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--encrypt-keylen", + key_len, + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed, verify=False) + + # Encryption key is not public should fail + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--encrypt", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + + @pytest.mark.parametrize("align", ("1", "2", "4", "8", "16", "32",)) + @pytest.mark.parametrize("key_type", KEY_TYPES[:-1]) + def test_sign_enc_key_non_16(self, tmp_path_persistent, key_type, align): + """Test signing with encryption with file with size that is not divisible by 16""" + + encoding = "pem" + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT + "." + encoding) + self.image = tmp_name(tmp_path_persistent, "1023", ".bin") + with self.image.open("wb") as f: + f.write(b"\x00" * 1023) + + # Sign and encrypt + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--encrypt", + pub_key, + "--align", + align, + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + str(self.image), + str(self.image_signed), + ], + ) + # Don't verify as verify command does not support encrypted images yet + assert_image_signed(result, self.image_signed, verify=False) + + @pytest.mark.parametrize("key_type", ("ed25519",)) + def test_sign_enc_key_x25519(self, tmp_path_persistent, key_type): + """Test signing with ed25519 and encrypting with x25519""" + + encoding = "pem" + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + enckey = tmp_name(tmp_path_persistent, "x25519", GEN_KEY_EXT) + pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT + "." + encoding) + + self.runner.invoke( + imgtool, ["keygen", "--key", str(enckey), "--type", "x25519"] + ) + + # Get public pair for key + self.runner.invoke( + imgtool, + [ + "getpub", + "--key", + str(enckey), + "--output", + str(pub_key), + "--encoding", + encoding, + ], + ) + + # Sign and encrypt + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--encrypt", + pub_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--clear", + str(self.image), + str(self.image_signed), + ], + ) + # Can be verified as --clean option used during signing + assert_image_signed(result, self.image_signed) + + # Encryption key is not public should fail + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--encrypt", + key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + + @pytest.mark.parametrize("key_type", KEY_TYPES[:-1]) + def test_sign_enc_save_enctlv(self, tmp_path_persistent, key_type): + """Test with save enctlv""" + + encoding = "pem" + key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT + "." + encoding) + + # Sign and encrypt + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + key, + "--encrypt", + pub_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + "--save-enctlv", + str(self.image), + str(self.image_signed), + ], + ) + # Don't verify as verify command does not support encrypted images yet + assert_image_signed(result, self.image_signed, verify=False) + + +class TestSignHex(TestSign): + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_basic_hex(self, key_type, tmp_path_persistent): + """Test basic sign and verify hex file""" + + loaded_key = keys_dir + key_type + ".key" + self.image = images_dir + "/zero.hex" + self.image_signed = tmp_name(tmp_path_persistent, "image", ".hex") + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2048", + "--pad-header", + "--hex-addr", + "0", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_hex_file_not_exists(self, key_type, tmp_path_persistent): + """Test sign hex file with non-existent file""" + + loaded_key = keys_dir + key_type + ".key" + self.image = "./images/invalid/path/file.hex" + self.image_signed = tmp_name(tmp_path_persistent, "image", ".hex") + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2048", + "--pad-header", + "--hex-addr", + "0", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Image file not found" in result.output + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_hex_padded(self, key_type, tmp_path_persistent): + """Test hex file with pad""" + + loaded_key = keys_dir + key_type + ".key" + self.image = images_dir + "/one_offset.hex" + # image above generated as below: + # ih = IntelHex() + # ih.puts(10, '1' * 1024) + # ih.write_hex_file(str(self.image)) + + self.image_signed = tmp_name(tmp_path_persistent, "image", ".hex") + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2048", + "--pad-header", + "--hex-addr", + "1", + "--pad", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_hex_confirm(self, key_type, tmp_path_persistent): + """Test hex file with confirm""" + + loaded_key = keys_dir + key_type + ".key" + self.image = images_dir + "/zero.hex" + self.image_signed = tmp_name(tmp_path_persistent, "image", ".hex") + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x2048", + "--pad-header", + "--hex-addr", + "0", + "--confirm", + str(self.image), + str(self.image_signed), + ], + ) + assert_image_signed(result, self.image_signed) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_sign_basic_hex_bad_image_size(self, key_type, tmp_path_persistent): + """Test basic sign hex file with insufficient size should fail""" + + loaded_key = keys_dir + key_type + ".key" + self.image = images_dir + "/zero.hex" + self.image_signed = tmp_name(tmp_path_persistent, "image", ".hex") + + result = self.runner.invoke( + imgtool, + [ + "sign", + "--key", + loaded_key, + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x20", + "--slot-size", + "0x1024", + "--pad-header", + str(self.image), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "exceeds requested size" in result.output diff --git a/scripts/tests/test_verify.py b/scripts/tests/test_verify.py new file mode 100644 index 000000000..833693685 --- /dev/null +++ b/scripts/tests/test_verify.py @@ -0,0 +1,443 @@ +# Copyright 2024 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from click.testing import CliRunner + +from imgtool.main import imgtool +from tests.constants import KEY_TYPES, GEN_KEY_EXT, tmp_name, signed_images_dir, keys_dir + +KEY_TYPE_MISMATCH_TLV = "Key type does not match TLV record" +NO_SIG_FOR_KEY = "No signature found for the given key" + +try: + KEY_TYPES.remove("x25519") # x25519 is not used for signing, so directory does not contain any such image +except ValueError: + pass + + +def assert_valid(result): + assert result.exit_code == 0 + assert "Image was correctly validated" in result.stdout + + +class TestVerify: + image = None + image_signed = None + runner = CliRunner() + + @pytest.fixture(scope="session") + def tmp_path_persistent(self, tmp_path_factory): + return tmp_path_factory.mktemp("keys") + + +class TestVerifyBasic(TestVerify): + key = None + gen_key = None + test_signed_images_dir = signed_images_dir + "/basic/" + + @pytest.fixture(autouse=True) + def setup(self, request, tmp_path_persistent, key_type): + self.key = keys_dir + key_type + ".key" + self.gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + self.runner.invoke( + imgtool, ["keygen", "--key", str(self.gen_key), "--type", key_type] + ) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_basic(self, key_type, tmp_path_persistent): + """Test verify basic image""" + + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert_valid(result) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_wrong_key(self, key_type, tmp_path_persistent): + """Test verify basic image with wrong key""" + + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.gen_key), + self.image_signed, + ], + ) + assert result.exit_code != 0 + assert NO_SIG_FOR_KEY in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_key_not_exists(self, key_type, tmp_path_persistent): + """Test verify basic image with non-existent key""" + + inv_key = "./invalidPath" + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(inv_key), + self.image_signed, + ], + ) + assert result.exit_code != 0 + assert "Key file not found" in result.stdout + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_image_not_exists(self, key_type, tmp_path_persistent): + """Test verify basic image with non-existent image""" + + self.image_signed = "./invalid" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + self.key, + self.image_signed, + ], + ) + assert result.exit_code != 0 + assert "Image file not found" in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-3072",)) + def test_verify_key_inv_magic(self, key_type, tmp_path_persistent): + """Test verify basic image with invalid magic""" + + self.image_signed = self.test_signed_images_dir + key_type + "_invMagic.bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Invalid image magic" in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-3072",)) + def test_verify_key_inv_tlv_magic(self, key_type, tmp_path_persistent): + """Test verify basic image with invalid tlv magic""" + + self.image_signed = self.test_signed_images_dir + key_type + "_invTLVMagic.bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Invalid TLV info magic" in result.stdout + + @pytest.mark.parametrize("key_type", ("rsa-3072",)) + def test_verify_key_inv_hash(self, key_type, tmp_path_persistent): + """ Test verify basic image with invalid hash""" + + self.image_signed = self.test_signed_images_dir + key_type + "_invHash.bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert "Image has an invalid hash" in result.stdout + + +class TestVerifyEncryptedClear(TestVerify): + key = None + gen_key = None + test_signed_images_dir = signed_images_dir + "/encryptedClear/" + + @pytest.fixture(autouse=True) + def setup(self, request, tmp_path_persistent, key_type): + self.key = keys_dir + key_type + ".key" + self.gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + self.runner.invoke( + imgtool, ["keygen", "--key", str(self.gen_key), "--type", key_type] + ) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_encrypted_clear(self, key_type, tmp_path_persistent): + """Test verify encrypted imaged with clear option""" + + self.image_signed = self.test_signed_images_dir + key_type + ".enc" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert_valid(result) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_encrypted_clear_wrong_key(self, key_type, tmp_path_persistent): + """Test verify with wrong key should fail""" + + self.image_signed = self.test_signed_images_dir + key_type + ".enc" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.gen_key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert NO_SIG_FOR_KEY in result.stdout + + +class TestVerifyCustomTLV(TestVerify): + key = None + gen_key = None + test_signed_images_dir = signed_images_dir + "/customTLV/" + + @pytest.fixture(autouse=True) + def setup(self, request, tmp_path_persistent, key_type): + self.key = keys_dir + key_type + ".key" + self.gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + self.runner.invoke( + imgtool, ["keygen", "--key", str(self.gen_key), "--type", key_type] + ) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_custom_tlv(self, key_type, tmp_path_persistent): + """Test verify image with customTLV""" + + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert_valid(result) + + @pytest.mark.parametrize("key_type", KEY_TYPES) + def test_verify_custom_tlv_no_key(self, key_type, tmp_path_persistent): + """Test verify image with customTLV""" + + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + str(self.image_signed), + ], + ) + assert_valid(result) + + +class TestVerifyNoKey(TestVerify): + """Tests for images that are signed without --key option """ + key = None + test_signed_images_dir = signed_images_dir + "/noKey/" + + def test_verify_no_key(self): + """Test verify image signed without key """ + + self.image_signed = self.test_signed_images_dir + "noKey256" + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + str(self.image_signed), + ], + ) + assert_valid(result) + + def test_verify_no_key_image_with_key(self): + """Test verify image signed without key, attempt to verify with a key should fail on signature check""" + + self.key = keys_dir + "rsa-2048" + ".key" + self.image_signed = self.test_signed_images_dir + "noKey256" + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert NO_SIG_FOR_KEY in result.stdout + + def test_verify_no_key_image_with_wrong_key(self): + """Test verify image signed without key, attempt to verify with wrong key should fail on hash check""" + + self.key = keys_dir + "ecdsa-p384" + ".key" + self.image_signed = self.test_signed_images_dir + "noKey256" + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert KEY_TYPE_MISMATCH_TLV in result.stdout + + +class TestVerifyPubKey(TestVerify): + """Tests for images that are signed without --key option and with --fix-sig-pubkey""" + key = None + gen_key = None + test_signed_images_dir = signed_images_dir + "/pubKeyOnly/" + + @pytest.fixture(autouse=True) + def setup(self, request, tmp_path_persistent, key_type): + self.key = keys_dir + key_type + ".key" + self.gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + self.runner.invoke( + imgtool, ["keygen", "--key", str(self.gen_key), "--type", key_type] + ) + + @pytest.mark.parametrize("key_type", KEY_TYPES[:-1]) + def test_verify_no_key(self, key_type): + """Test verify image signed with --fix-sig, verify without key """ + + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + str(self.image_signed), + ], + ) + assert_valid(result) + + @pytest.mark.parametrize("key_type", ("ecdsa-p384",)) + def test_verify_384_key(self, key_type): + """Test verify image without key """ + + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + str(self.image_signed), + ], + ) + assert_valid(result) + + @pytest.mark.parametrize("key_type", KEY_TYPES[:-2]) + def test_verify_key_not_matching(self, key_type, tmp_path_persistent): + """Test verify image with mismatching key """ + + self.key = keys_dir + "ecdsa-p384" + ".key" + self.image_signed = self.test_signed_images_dir + key_type + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert KEY_TYPE_MISMATCH_TLV in result.stdout + + @pytest.mark.parametrize("key_type", ("ecdsa-p256",)) + def test_verify_key_not_matching_384(self, key_type, tmp_path_persistent): + """Test verify image with mismatching key """ + + self.image_signed = self.test_signed_images_dir + "ecdsa-p384" + ".bin" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert result.exit_code != 0 + assert KEY_TYPE_MISMATCH_TLV in result.stdout + + +class TestVerifyHex(TestVerify): + key = None + test_signed_images_dir = signed_images_dir + "/hex/" + + @pytest.fixture(autouse=True) + def setup(self, request, tmp_path_persistent, key_type="rsa-2048"): + self.key = keys_dir + key_type + ".key" + + @pytest.mark.parametrize("hex_addr", ("0", "16", "35")) + def test_verify_basic(self, hex_addr, tmp_path_persistent): + """Test verify basic image""" + + self.image_signed = self.test_signed_images_dir + f"zero_hex-addr_{hex_addr}" + ".hex" + + result = self.runner.invoke( + imgtool, + [ + "verify", + "--key", + str(self.key), + str(self.image_signed), + ], + ) + assert_valid(result)