-
Notifications
You must be signed in to change notification settings - Fork 1
/
unseal-tesla-car-creds.py
131 lines (104 loc) · 3.63 KB
/
unseal-tesla-car-creds.py
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
import sys, json
import Crypto.IO.PEM as pem
import asn1tools
import tpm_obj
from recover_rsa_key import rsa_privkey_from_n_and_p, privkey_to_pem_key
from gen_prim_seed import primary_object_seed
from amdnvtool.nv_data import NVData
TPM_KEY_DECODER = asn1tools.compile_string("""
TPMKey DEFINITIONS ::= BEGIN
TPMKey ::= SEQUENCE {
type OBJECT IDENTIFIER,
authEmpty [0] BOOLEAN OPTIONAL,
parent INTEGER,
publicKey OCTET STRING,
privateKey OCTET STRING
}
END
""")
def load_tpm_key_file(filename : str):
with open(filename) as f:
data, usage, _ = pem.decode(f.read())
assert usage == "TSS2 PRIVATE KEY"
return TPM_KEY_DECODER.decode("TPMKey", data)
def load_json_file(filename : str):
with open(filename) as f:
return json.load(f)
def print_or_write_str(key_str, output_file):
if output_file is not None:
with open(output_file, "w") as f:
f.write(key_str)
else:
print(key_str)
TESLA_TEMPLATE = bytes.fromhex('0023000b00030472000000060080004300100003001000000000')
def nvdata_and_tpm_bytes_to_privkey(nvdata, tpm_pub_bytes, tpm_priv_bytes):
primary_seed = primary_object_seed(nvdata, TESLA_TEMPLATE, 0x20)
tpm_keys = tpm_obj.TpmPersistanceKeys.from_data(primary_seed[::-1])
tpm_public = tpm_obj.Tpm2bPublic.from_data(tpm_pub_bytes)
tpm_private = tpm_obj.Tpm2bPrivate.from_data(tpm_priv_bytes)
tpm_sensitive = tpm_obj.get_sensitive(tpm_keys, tpm_public, tpm_private)
return rsa_privkey_from_n_and_p(
tpm_public.unique.modulus, tpm_sensitive.sensitive.prime_p
)
if __name__ == "__main__":
def print_usage():
print("Usage:")
print(f" {sys.argv[0]} <command> [<args ...>]")
print("Commands:")
print("""
from-nvdata <nvdata.json> <car-creds.key> [<car-creds-unsealed.key>]
from-image <flash-image.bin> <secret (hex)> <car-creds.key> [<car-creds-unsealed.key>]
""")
if len(sys.argv) < 2:
print("Error: no command given!")
print_usage()
exit(-1)
command = sys.argv[1]
if command == "from-nvdata":
if len(sys.argv) < 3:
print("Error: <nvdata.json> not given!")
print_usage()
exit(-1)
nvdata = load_json_file(sys.argv[2])
if len(sys.argv) < 4:
print("Error: <car-creds.key> not given!")
print_usage()
exit(-1)
car_creds = load_tpm_key_file(sys.argv[3])
output_file = sys.argv[4] if len(sys.argv) > 4 else None
privkey = nvdata_and_tpm_bytes_to_privkey(
nvdata, car_creds["publicKey"], car_creds["privateKey"]
)
key_str = privkey_to_pem_key(privkey)
print_or_write_str(key_str, output_file)
elif command == "from-image":
if len(sys.argv) < 3:
print("Error: <flash-image.bin> not given!")
print_usage()
exit(-1)
flash_image = sys.argv[2]
if len(sys.argv) < 4:
print("Error: <secret (hex)> not given!")
print_usage()
exit(-1)
secret_hex = sys.argv[3]
if len(sys.argv) < 5:
print("Error: <car-creds.key> not given!")
print_usage()
exit(-1)
car_creds = load_tpm_key_file(sys.argv[4])
output_file = sys.argv[5] if len(sys.argv) > 5 else None
nvdata = NVData.from_file_and_secret_hex(flash_image, secret_hex)
nvdata = [{
'context' : context_id,
'sequence' : [[bs.hex() for bs in content] if content else None for content in sequence],
} for (context_id, sequence) in nvdata.by_context.items()]
privkey = nvdata_and_tpm_bytes_to_privkey(
nvdata, car_creds["publicKey"], car_creds["privateKey"]
)
key_str = privkey_to_pem_key(privkey)
print_or_write_str(key_str, output_file)
else:
print(f"Error: unknown command \"{command}\" given!")
print_usage()
exit(-1)