from __future__ import annotations import subprocess import sys from pathlib import Path import pytest HERE = Path(__file__).resolve().parent TOOL = HERE.parent / "nl_sign.py" sys.path.insert(0, str(HERE.parent)) import nl_sign # noqa: E402 @pytest.fixture(scope="module") def key(): """Ein eigener 4096-Bit-Schluessel, damit kein echter Signierschluessel in die Tests gerät. Einmal pro Modul, weil das Erzeugen ein paar Sekunden dauert.""" return nl_sign.generate_test_key() def test_signature_is_512_bytes(key, tmp_path): raw = b"\xa5" * 4096 signed = nl_sign.sign_image(raw, key.private_pem) assert len(signed) == len(raw) + 512 assert signed[: len(raw)] == raw def test_signature_verifies(key): raw = b"firmware" * 512 signed = nl_sign.sign_image(raw, key.private_pem) assert nl_sign.verify_image(signed, key.public_der) is True def test_modified_payload_fails_verification(key): raw = bytearray(b"firmware" * 512) signed = bytearray(nl_sign.sign_image(bytes(raw), key.private_pem)) signed[17] ^= 0xFF assert nl_sign.verify_image(bytes(signed), key.public_der) is False def test_signing_is_deterministic(key): raw = b"x" * 1024 assert nl_sign.sign_image(raw, key.private_pem) == nl_sign.sign_image(raw, key.private_pem) def test_cli_roundtrip(key, tmp_path): src = tmp_path / "in_gz.img" dst = tmp_path / "out.img" pub = tmp_path / "pub.der" priv = tmp_path / "key.pem" src.write_bytes(b"\x01\x02\x03" * 1000) priv.write_bytes(key.private_pem) pub.write_bytes(key.public_der) subprocess.run([sys.executable, str(TOOL), "sign", "--key", str(priv), "--in", str(src), "--out", str(dst)], check=True) assert dst.stat().st_size == src.stat().st_size + 512 subprocess.run([sys.executable, str(TOOL), "verify", "--pub", str(pub), "--in", str(dst)], check=True) def test_cli_verify_rejects_tampered(key, tmp_path): src = tmp_path / "in_gz.img" dst = tmp_path / "out.img" pub = tmp_path / "pub.der" priv = tmp_path / "key.pem" src.write_bytes(b"\x07" * 2000) priv.write_bytes(key.private_pem) pub.write_bytes(key.public_der) subprocess.run([sys.executable, str(TOOL), "sign", "--key", str(priv), "--in", str(src), "--out", str(dst)], check=True) data = bytearray(dst.read_bytes()) data[0] ^= 0x01 dst.write_bytes(bytes(data)) r = subprocess.run([sys.executable, str(TOOL), "verify", "--pub", str(pub), "--in", str(dst)]) assert r.returncode != 0 def test_refuses_already_signed_looking_input(key, tmp_path): """Ein Image mit ninlight-Trailer ist ein OTA-Image, kein Rohbild - zweimal 512 Byte anzuhaengen ergaebe eine Datei, die die alte Firmware ablehnt.""" raw = b"\x00" * 1024 + b"NINL" + b"\x01" + b"\x00" * 507 with pytest.raises(nl_sign.SignError): nl_sign.sign_image(raw, key.private_pem)