diff --git a/tools/keytools/sign.py b/tools/keytools/sign.py index 6acd49aa..75d57a10 100755 --- a/tools/keytools/sign.py +++ b/tools/keytools/sign.py @@ -764,9 +764,9 @@ if (delta): base_version = re.split("_", (re.split("_v", delta_base_file)[1]))[0] header = make_header(tmp_outfile, fw_version, [[HDR_IMG_DELTA_BASE, 4, struct.pack(" end: + break + if htype == want_type: + return length + p += 4 + length + return None + + +def ensure_bmdiff(): + if os.path.exists(BMDIFF): + return True + delta_dir = os.path.join(ROOT, "tools", "delta") + try: + subprocess.run( + ["gcc", "-o", "delta.o", "-c", os.path.join(ROOT, "src", "delta.c"), + "-I" + os.path.join(ROOT, "include"), "-DDELTA_UPDATES", + "-DWOLFBOOT_SECTOR_SIZE=0x%x" % SECTOR_SIZE], + cwd=delta_dir, check=True) + subprocess.run( + ["gcc", "-o", "bmdiff.o", "-c", "bmdiff.c", + "-I" + os.path.join(ROOT, "include"), "-DDELTA_UPDATES", + "-DWOLFBOOT_SECTOR_SIZE=0x%x" % SECTOR_SIZE], + cwd=delta_dir, check=True) + subprocess.run(["gcc", "-o", "bmdiff", "delta.o", "bmdiff.o"], + cwd=delta_dir, check=True) + except (subprocess.CalledProcessError, OSError): + return False + return os.path.exists(BMDIFF) + + +def main(): + try: + import wolfcrypt # noqa: F401 + except Exception: + skip("python wolfcrypt module not available") + if not os.path.exists(SIGN_PY): + skip("sign.py not found") + if not ensure_bmdiff(): + skip("could not build tools/delta/bmdiff") + + with tempfile.TemporaryDirectory() as work: + key = os.path.join(work, "priv.der") + with open(key, "wb") as f: + f.write(b"\x42" * 32) # 32-byte raw ed25519 private seed + + base = os.path.join(work, "image_v1.bin") + upd = os.path.join(work, "image_v2.bin") + payload = bytes((i * 7) & 0xFF for i in range(2048)) + with open(base, "wb") as f: + f.write(payload) + with open(upd, "wb") as f: + # small localized change so the patch stays well under 64 KB + f.write(payload[:512] + b"PATCHED!" + payload[520:]) + + env = dict(os.environ) + env["WOLFBOOT_SECTOR_SIZE"] = str(SECTOR_SIZE) + + # Sign the base image (v1) so it can be used as the delta base. + r = subprocess.run( + [sys.executable, SIGN_PY, "--ed25519", "--sha256", base, key, "1"], + cwd=ROOT, env=env, capture_output=True, text=True) + if r.returncode != 0: + skip("sign.py base sign failed: " + r.stderr.strip()) + signed_base = base.replace(".bin", "_v1_signed.bin") + if not os.path.exists(signed_base): + skip("sign.py did not produce a signed base image") + + # Sign the delta image (v2) against the signed base. + r = subprocess.run( + [sys.executable, SIGN_PY, "--ed25519", "--sha256", "--delta", + signed_base, upd, key, "2"], + cwd=ROOT, env=env, capture_output=True, text=True) + if r.returncode != 0: + skip("sign.py delta sign failed: " + r.stderr.strip()) + diff = upd.replace(".bin", "_v2_signed_diff.bin") + if not os.path.exists(diff): + skip("sign.py did not produce a delta image") + + with open(diff, "rb") as f: + header = f.read(IMAGE_HEADER_SIZE) + + failures = [] + for name, tag in (("HDR_IMG_DELTA_SIZE", HDR_IMG_DELTA_SIZE), + ("HDR_IMG_DELTA_INVERSE_SIZE", HDR_IMG_DELTA_INVERSE_SIZE)): + length = find_tlv(header, tag) + if length is None: + failures.append("%s TLV not found in delta header" % name) + elif length != SIZEOF_UINT32: + failures.append( + "%s encoded with length %d, but wolfBoot_get_delta_info() " + "requires sizeof(uint32_t)=%d; bootloader will reject the " + "image" % (name, length, SIZEOF_UINT32)) + + if failures: + for msg in failures: + print("FAIL unit-sign-delta-tlv: " + msg) + sys.exit(1) + + print("unit-sign-delta-tlv: OK") + sys.exit(0) + + +if __name__ == "__main__": + main()