Files
chimera-gfx-Public/tests/test_siecaf_header_parser.py
Chimera GFX release export a6037502d7
phase0-ci / build-and-audit (push) Successful in 2m14s
Publish Chimera GFX source
2026-09-03 03:27:14 +02:00

325 lines
11 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
"""Host-only malformed-input tests for the read-only SIECAF parser."""
from __future__ import annotations
import argparse
from io import BytesIO
import importlib.util
from pathlib import Path
import sys
from types import ModuleType
from typing import Callable
def load_module(name: str, path: Path) -> ModuleType:
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise RuntimeError(f"could not load {path}")
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
return module
def require(condition: bool, message: str) -> None:
if not condition:
raise RuntimeError(message)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=Path, required=True)
args = parser.parse_args()
module = load_module(
"siecaf_inspector", args.root / "tools/inspect_siecaf_header.py"
)
def build_archive(
*,
count: int = 1,
magic: bytes | None = None,
unknown_u64: int = 1,
version: int = 3,
metadata_ids: list[int] | None = None,
hash_ids: list[int] | None = None,
overlap: bool = False,
out_of_range: bool = False,
hash_seed: int = 1,
trailing: int = 0,
aligned_length: int | None = None,
unaligned_length: int = 123,
) -> bytes:
alignment = module.ALIGNMENT
file_offset = alignment
segment_length = alignment if aligned_length is None else aligned_length
data_size = count * alignment
source_size = file_offset + data_size + trailing
metadata_ids = metadata_ids or list(range(count))
hash_ids = hash_ids or list(range(count))
header = module.HEADER.pack(
magic or module.MAGIC,
unknown_u64,
1,
0,
version,
0,
bytes(range(16)),
bytes(range(12)),
0,
count,
file_offset,
data_size,
)
metadata = bytearray()
for index in range(count):
offset = file_offset + index * alignment
if overlap and index == 1:
offset = file_offset
if out_of_range and index == count - 1:
offset = file_offset + data_size
metadata.extend(
module.SEGMENT_META.pack(
metadata_ids[index],
0,
0,
offset,
segment_length,
3,
1,
b"\x00" * 12,
0,
unaligned_length,
)
)
hashes = bytearray()
for index in range(count):
hashes.extend(
module.SECTION_HASH.pack(
hash_ids[index],
0,
bytes([(hash_seed + index) % 256]) * 16,
b"\x00" * 24,
)
)
tables = header + metadata + hashes
require(len(tables) <= file_offset, "synthetic tables exceed preamble")
return (
tables
+ b"\x00" * (file_offset - len(tables))
+ b"\xa5" * data_size
+ b"\xee" * trailing
)[:source_size]
def inspect(value: bytes) -> dict[str, object]:
return module.inspect_siecaf(
BytesIO(value), len(value), source_label="synthetic"
)
cases: list[tuple[str, Callable[[], None]]] = []
def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]:
def register(function: Callable[[], None]) -> Callable[[], None]:
cases.append((name, function))
return function
return register
@case("01 valid structure parses")
def _() -> None:
result = inspect(build_archive())
require(
result["classification"] == "SIECAF_VALID_STRUCTURE",
str(result),
)
require(not result["errors"], "valid structure returned errors")
require(
"key_or_unknown_16_hex" not in result["header"]
and "raw_hex" not in result["header"]
and result["header"]["key_or_unknown_16_redacted"] is True,
"cryptographic header material was exposed",
)
@case("02 magic must be exact")
def _() -> None:
result = inspect(build_archive(magic=b"BADCAF\x00\x00"))
require(result["classification"] == "SIECAF_MALFORMED", str(result))
@case("03 unsupported version fails closed")
def _() -> None:
result = inspect(build_archive(version=7))
require(
result["classification"] == "SIECAF_UNSUPPORTED_VERSION",
str(result),
)
@case("04 version six uses the published fixed-width layout")
def _() -> None:
result = inspect(build_archive(version=6))
require(
result["classification"] == "SIECAF_VALID_STRUCTURE",
str(result),
)
@case("05 truncated header is rejected")
def _() -> None:
result = inspect(b"SIECAF\x00\x00")
require(result["classification"] == "SIECAF_MALFORMED", str(result))
@case("06 segment-count multiplication overflow is rejected")
def _() -> None:
header = module.HEADER.pack(
module.MAGIC,
1,
1,
0,
3,
0,
b"\x00" * 16,
b"\x00" * 12,
0,
module.UINT64_MAX,
module.ALIGNMENT,
module.ALIGNMENT,
)
result = module.inspect_siecaf(
BytesIO(header), len(header), source_label="overflow"
)
require(result["classification"] == "SIECAF_MALFORMED", str(result))
require(
any("overflow" in error for error in result["errors"]),
"overflow was not reported",
)
@case("06 out-of-range offsets are rejected")
def _() -> None:
result = inspect(build_archive(out_of_range=True))
require(result["classification"] == "SIECAF_MALFORMED", str(result))
require(
any("outside" in error for error in result["errors"]),
"out-of-range section was not reported",
)
@case("07 overlaps are reported")
def _() -> None:
result = inspect(build_archive(count=2, overlap=True))
require(result["classification"] == "SIECAF_MALFORMED", str(result))
require(bool(result["overlaps"]), "overlap record is absent")
@case("08 duplicate metadata IDs are reported")
def _() -> None:
result = inspect(build_archive(count=2, metadata_ids=[0, 0], hash_ids=[0, 1]))
require(result["classification"] == "SIECAF_MALFORMED", str(result))
require(
result["duplicate_metadata_section_ids"] == [0],
"duplicate metadata ID was not preserved",
)
@case("09 metadata and hash ID sets must agree")
def _() -> None:
result = inspect(build_archive(count=2, metadata_ids=[0, 1], hash_ids=[0, 2]))
require(result["classification"] == "SIECAF_MALFORMED", str(result))
require(
any("table indexes" in error for error in result["errors"]),
"mismatched ID sets were not reported",
)
@case("10 repeated section IDs are valid when part numbers differ")
def _() -> None:
value = bytearray(build_archive(count=2, metadata_ids=[7, 7]))
second_meta = module.HEADER.size + module.SEGMENT_META.size
fields = list(
module.SEGMENT_META.unpack(
value[second_meta : second_meta + module.SEGMENT_META.size]
)
)
fields[2] = 1
value[second_meta : second_meta + module.SEGMENT_META.size] = (
module.SEGMENT_META.pack(*fields)
)
result = inspect(bytes(value))
require(
result["classification"] == "SIECAF_VALID_STRUCTURE",
str(result),
)
require(
result["repeated_metadata_section_ids"] == [7],
"multi-part ID was not reported",
)
@case("11 aligned length must be coherent")
def _() -> None:
result = inspect(build_archive(aligned_length=module.ALIGNMENT - 1))
require(result["classification"] == "SIECAF_MALFORMED", str(result))
@case("12 unaligned length cannot exceed aligned length")
def _() -> None:
result = inspect(build_archive(unaligned_length=module.ALIGNMENT + 1))
require(result["classification"] == "SIECAF_MALFORMED", str(result))
@case("13 zero-length sections are coherent")
def _() -> None:
result = inspect(build_archive(aligned_length=0, unaligned_length=0))
require(
result["classification"] == "SIECAF_VALID_STRUCTURE",
str(result),
)
@case("14 trailing data is visible but not silently discarded")
def _() -> None:
result = inspect(build_archive(trailing=7))
require(
result["classification"] == "SIECAF_VALID_STRUCTURE",
str(result),
)
require(result["trailing_data_bytes"] == 7, "trailing bytes not counted")
require(bool(result["warnings"]), "trailing bytes not warned")
@case("15 structural exact requires all structural hashes")
def _() -> None:
left = inspect(build_archive())
right = inspect(build_archive())
comparison = module.compare_structures(left, right)
require(
comparison["classification"] == "SIECAF_STRUCTURAL_EXACT",
str(comparison),
)
@case("16 equal layout with different hashes is not structural exact")
def _() -> None:
left = inspect(build_archive(hash_seed=1))
right = inspect(build_archive(hash_seed=9))
comparison = module.compare_structures(left, right)
require(
comparison["classification"] == "SIECAF_LAYOUT_MATCH_HASHES_DIFFER",
str(comparison),
)
@case("17 different segment layout is classified")
def _() -> None:
left = inspect(build_archive(count=1))
right = inspect(build_archive(count=2))
comparison = module.compare_structures(left, right)
require(
comparison["classification"] == "SIECAF_LAYOUT_DIFFERENT",
str(comparison),
)
failures: list[str] = []
for name, function in cases:
try:
function()
except Exception as error:
failures.append(f"{name}: {error}")
if failures:
for failure in failures:
print(f"FAIL: {failure}")
return 1
print(f"SIECAF header parser tests: {len(cases)}/{len(cases)} PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())