1import json
2import sys
3
4store_verity_type = "@NIX_STORE_VERITY@" # replaced at import by Nix
5
6
7def extract_uki_cmdline_params(ukify_json: dict) -> dict[str, str]:
8 """
9 Return a dict of the parameters in the .cmdline section of the UKI
10 Exits early if "usrhash" is not included.
11 """
12 cmdline = ukify_json.get(".cmdline", {}).get("text")
13 if cmdline is None:
14 print("Failed to get cmdline from ukify output")
15
16 params = {}
17 for param in cmdline.split():
18 key, val = param.partition("=")[::2]
19 params[key] = val
20
21 if "usrhash" not in params:
22 print(
23 f"UKI cmdline does not contain a usrhash:\n{cmdline}"
24 )
25 exit(1)
26
27 return params
28
29
30def hashes_match(partition: dict[str, str], expected: str) -> bool:
31 """
32 Checks if the value of the "roothash" key in the passed partition object matches `expected`.
33 """
34 if partition.get("roothash") != expected:
35 pretty_part = json.dumps(partition, indent=2)
36 print(
37 f"hash mismatch, expected to find roothash {expected} in:\n{pretty_part}"
38 )
39 return False
40 else:
41 return True
42
43
44def check_partitions(
45 partitions: list[dict], uki_params: dict[str, str]
46) -> bool:
47 """
48 Checks if the usrhash from `uki_params` has a matching roothash
49 for the corresponding partition in `partitions`.
50 """
51 for part in partitions:
52 if part.get("type") == store_verity_type:
53 expected = uki_params["usrhash"]
54 return hashes_match(part, expected)
55
56 return False
57
58
59def main() -> None:
60 ukify_json = json.load(sys.stdin)
61 repart_json_output = sys.argv[1]
62
63 with open(repart_json_output, "r") as r:
64 repart_json = json.load(r)
65
66 uki_params = extract_uki_cmdline_params(ukify_json)
67
68 if check_partitions(repart_json, uki_params):
69 print("UKI and repart verity hashes match")
70 else:
71 print("Compatibility check for UKI and image failed!")
72 print(f"UKI cmdline parameters:\n{uki_params}")
73 print(f"repart config: {repart_json_output}")
74 exit(1)
75
76
77if __name__ == "__main__":
78 main()