-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidate.py
More file actions
executable file
·141 lines (120 loc) · 4.56 KB
/
Copy pathvalidate.py
File metadata and controls
executable file
·141 lines (120 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2026 Acompany Co., Ltd.
import argparse
import hashlib
import sys
from pathlib import Path
DEFAULT_RTMR = "/sys/class/misc/tdx_guest/measurements/rtmr2:sha384"
DEFAULT_LOG = "/sys/kernel/security/ima/ascii_runtime_measurements_sha384"
DEFAULT_SYSFS = "/sys/kernel/ima_rtmr"
def parse_digest(token: str) -> bytes:
return bytes.fromhex(token.split(":", 1)[-1])
def load_log(path: str) -> list[bytes]:
with open(path, encoding="utf-8", errors="surrogateescape") as f:
digests = [parse_digest(line.split()[1]) for line in f if line.strip()]
# Violation entries log an all-zero digest; the module extends 0xFF to match
# the kernel's PCR invalidation, so replay 0xFF in their place.
return [d if any(d) else b"\xff" * len(d) for d in digests]
def replay_from(
baseline: bytes, digests: list[bytes], actual: bytes, start: int
) -> int | None:
if baseline == actual:
return start
r = baseline
for i in range(start, len(digests)):
r = hashlib.sha384(r + digests[i]).digest()
if r == actual:
return i + 1
return None
def main() -> int:
p = argparse.ArgumentParser()
p.add_argument(
"initial_rtmr",
nargs="?",
help=f"hex digest; default: read from {DEFAULT_SYSFS}/initial",
)
p.add_argument(
"skip",
nargs="?",
type=int,
help=f"entries to skip; default: read from {DEFAULT_SYSFS}/skip_count",
)
p.add_argument("--rtmr", default=DEFAULT_RTMR)
p.add_argument("--log", default=DEFAULT_LOG)
p.add_argument("--sysfs", default=DEFAULT_SYSFS)
p.add_argument("--no-search", action="store_true")
p.add_argument(
"--ignore-disabled",
action="store_true",
help=(
"proceed even if the module fail-stopped (disabled=1); "
"for forensic inspection only"
),
)
args = p.parse_args()
sysfs = Path(args.sysfs)
# Fail-closed against the module's fail-stop flag. A kernel_write failure
# leaves RTMR diverged from the IMA log; any later replay match would be
# accidental. Verifiers MUST reject attestation in this state.
disabled_path = sysfs / "disabled"
if disabled_path.exists() and disabled_path.read_text().strip() != "0":
if not args.ignore_disabled:
print(
f"REJECT: {disabled_path} is set; "
"module fail-stopped and RTMR may diverge from the IMA log. "
"Pass --ignore-disabled only for forensic inspection.",
file=sys.stderr,
)
return 2
print(
f"WARNING: {disabled_path} is set; "
"proceeding due to --ignore-disabled (forensic mode).",
file=sys.stderr,
)
initial_hex = args.initial_rtmr or (sysfs / "initial").read_text().strip()
skip = (
args.skip
if args.skip is not None
else int((sysfs / "skip_count").read_text().strip())
)
baseline: bytes = bytes.fromhex(initial_hex)
with open(args.rtmr, "rb") as f:
actual: bytes = f.read()
if len(baseline) != len(actual):
print(
f"initial RTMR length {len(baseline)} does not match actual {len(actual)}",
file=sys.stderr,
)
return 1
digests: list[bytes] = load_log(args.log)
total: int = len(digests)
print(f"log entries: {total}")
print(f"actual rtmr: {actual.hex()}")
end = replay_from(baseline, digests, actual, skip)
if end is not None:
print(f"MATCH (direct): skip={skip} end={end} replayed={end - skip}")
return 0
print(f"no match with skip={skip}")
if args.no_search:
return 1
print("scanning start indices...")
for s in range(total):
if s == skip:
continue
end = replay_from(baseline, digests, actual, s)
if end is not None:
# A match at a start index other than the expected skip is NOT a
# success: it means skip_count is wrong or the RTMR/IMA-log ordering
# is off by (s - skip). Report it as a failure so an off-by-one or
# ordering bug cannot be masked by the scan.
print(
f"MISMATCH (scan): matched at start={s} but expected skip={skip} "
f"(delta {s - skip:+d}); RTMR does not match the expected position",
file=sys.stderr,
)
return 1
print(f"NO MATCH for any start in [0, {total})", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())