summarylogtreecommitdiffstats
path: root/tpm-rebind.py
blob: 1e51738474a8a6e331cf5abf685a8fcd986f178e (plain)
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
#!/usr/bin/env python
from yaml import safe_load as yaml_load
from json import dumps as json_dumps
from base64 import urlsafe_b64encode
from subprocess import check_output, check_call

# User settings
PCR_IDS = [0, 7]
PCR_BANK = "sha256"
# End of user settings

_PCR_BANK_SIZES = {
    "sha1": 20,
    "sha256": 32
}

def get_rootdev() -> str:
    with open('/proc/cmdline', 'r') as f:
        cmdline = f.read().strip()

    args = cmdline.split()
    for arg in args:
        if not arg.startswith("cryptdevice="):
            continue
        
        devinfo = arg.split('=')
        if devinfo[1] != 'UUID':
            raise ValueError("Only UUID is supported for cryptdevice")
        
        devspl = devinfo[2].split(':')
        return f"/dev/disk/by-uuid/{devspl[0]}"

def find_clevis_tpm2_bind(dev: str) -> int | None:
    try:
        output = check_output(["clevis", "luks", "list", "-d", dev])
    except:
        return None

    for line in output.splitlines():
        lspl = line.split()
        if lspl[1] != b'tpm2':
            continue
        return int(lspl[0].removesuffix(b':'), 10)
    return None

def calculate_boot_pcr_digest(pcr_bank: str, pcrs: list[int]) -> str:
    pcrs_yaml = yaml_load(check_output(["tpm2_eventlog", "/sys/kernel/security/tpm0/binary_bios_measurements"]))
    pcrs_list: dict[int, int] = pcrs_yaml['pcrs'][pcr_bank]
    if not pcrs_list:
        raise ValueError("No PCR values found in the event log")

    pcr_size = _PCR_BANK_SIZES[pcr_bank]

    data = b""
    for pcr in pcrs:
        data += int.to_bytes(pcrs_list[pcr], pcr_size, 'big')
    return urlsafe_b64encode(data).rstrip(b"=").decode('utf-8')

def main():
    rootdev = get_rootdev()
    pcr_digest = calculate_boot_pcr_digest(PCR_BANK, PCR_IDS)

    clevis_config = {
        "pcr_bank": PCR_BANK,
        "pcr_ids": ",".join(map(str, PCR_IDS)),
        "pcr_digest": pcr_digest,
    }
    clevis_config_str = json_dumps(clevis_config)
    clevis_config_str = clevis_config_str.replace(' ', '')

    tpm2_bind = find_clevis_tpm2_bind(rootdev)

    print(clevis_config_str, tpm2_bind)

    if tpm2_bind is None:
        print("No TPM2 binding found for the root device, creating a new one...")
        check_call(["clevis", "luks", "bind", "-d", rootdev, "tpm2", clevis_config_str])
    else:
        print(f"TPM2 binding found for the root device: {tpm2_bind}, updating it...")
        check_call(["clevis", "luks", "edit", "-d", rootdev, "-s", str(tpm2_bind), "-c", clevis_config_str])

if __name__ == '__main__':
    main()