Files
proxmark3/tools/ePassport/tests/test_tab_sources.py
T
Paul KilarandClaude Opus 5 ce9148d9ad ePassport: name the files each detail tab actually drew from
The tab headers carried their file names as fixed text - "Additional
personal details  ·  EF_DG11" - so a Polish passport, which ships DG13 and
no DG11, was told its details came from a file it does not carry.  The
ISSUER header claimed EF_DG12 on the same document, and SECURITY listed
EF_DG15 whether or not it was there.

personal_files, document_files and security_files report what each tab drew
from, and the pages compose their header from those.  They sit on the
record rather than in the widgets so they can be tested without a display,
which is how the rest of the suite is built.

PERSONAL names EF_DG1 when the personal number came from the MRZ, that
being the file which supplied it, rather than crediting DG11 for a value
DG11 did not provide.  With nothing read the header stays the bare
description: the record is an empty PassportRecord at startup rather than
None, and reporting an absence on a chip nobody read reads as a fault.
"none present" rather than "nothing on this chip", since the app opens
offline dumps too.

The same document showed the other half of this: "Personal number (from
DG13)" renders 203px against a dp(190) caption column, so it wrapped and
left the source on a line of its own.  Without the "from" it is 167px.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 14:17:22 -04:00

75 lines
2.3 KiB
Python

"""Which files each detail tab actually drew from.
The tab headers named their files as fixed text - "Additional personal
details · EF_DG11" - so a document without DG11 was told its details came
from a file it does not carry, and the SECURITY header listed EF_DG15 whether
or not it was there.
"""
from __future__ import annotations
from pathlib import Path
from epassport.emrtd import dg, tlv
from epassport.emrtd.model import PassportRecord
from epassport.emrtd.mrz import Checked, Mrz
def _pesel_dg13(digits: str = "10030912345") -> bytes:
return tlv.encode(
0x6D, tlv.encode(0x5C, b"\x5f\x70") + tlv.encode(0x5F70, digits.encode())
)
def _polish_record() -> PassportRecord:
"""DG13 and no DG11, the shape that exposed this."""
record = PassportRecord()
blank = Checked("")
record.mrz = Mrz(
kind="TD3",
lines=[],
document_number=blank,
date_of_birth=blank,
date_of_expiry=blank,
composite=blank,
nationality="POL",
optional_data=Checked(""),
)
record.optional = dg.parse_dg13(_pesel_dg13())
return record
def test_personal_names_dg11_when_the_document_carries_it(td3: Path) -> None:
assert "EF_DG11" in dg.load_dump(td3).personal_files
def test_personal_names_dg13_when_that_is_where_the_number_came_from() -> None:
record = _polish_record()
assert record.personal_files == ["EF_DG13"]
assert "EF_DG11" not in record.personal_files
def test_personal_names_nothing_when_nothing_fed_it(tmp_path: Path) -> None:
assert dg.load_dump(tmp_path).personal_files == []
def test_document_follows_dg12(td3: Path, td1: Path) -> None:
assert dg.load_dump(td3).document_files == ["EF_DG12"]
assert dg.load_dump(td1).document_files == []
def test_security_lists_only_the_files_that_are_there(td3: Path) -> None:
files = dg.load_dump(td3).security_files
assert "EF_SOD" in files
assert files == [f for f in ("EF_SOD", "EF_DG14", "EF_DG15") if f in files]
def test_security_drops_a_missing_dg15(tmp_path: Path, td3: Path) -> None:
import shutil
for name in ("EF_SOD.bin", "EF_DG14.bin"):
shutil.copy(td3 / name, tmp_path / name)
files = dg.load_dump(tmp_path).security_files
assert "EF_DG15" not in files
assert "EF_DG14" in files