112 lines
4.3 KiB
Python
112 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate src/keystore/embedded.rs and KEY_ENTRY_NAMES for mod.rs from REFERENCES/mf_keys.txt"""
|
|
import re
|
|
|
|
MF_KEYS_PATH = "REFERENCES/mf_keys.txt"
|
|
# Star Line uses same key as SL_A2-A4; we add type 20 so KeyStore.star_line_mf_key is set
|
|
STAR_LINE_KEY_HEX = "9BF7F89BF8FE78DA"
|
|
|
|
def parse_mf_keys(path: str):
|
|
entries = [] # (type_id, key_u64, name)
|
|
vag_bytes = []
|
|
with open(path) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
# VAG block: "0000: xx xx ..." or "0020: xx xx ..."
|
|
if re.match(r"^[0-9A-Fa-f]{4}:\s", line):
|
|
hex_part = line.split(":", 1)[1].strip()
|
|
for h in hex_part.split():
|
|
vag_bytes.append(int(h, 16))
|
|
continue
|
|
# KEY:TYPE:NAME
|
|
parts = line.split(":", 2)
|
|
if len(parts) != 3:
|
|
continue
|
|
key_hex, type_str, name = parts
|
|
key_hex = key_hex.strip()
|
|
type_id = int(type_str.strip())
|
|
name = name.strip()
|
|
key_u64 = int(key_hex, 16)
|
|
entries.append((type_id, key_u64, name))
|
|
# Append Star Line (type 20) so load_kia_keys sets star_line_mf_key
|
|
entries.append((20, int(STAR_LINE_KEY_HEX, 16), "Star Line"))
|
|
return entries, vag_bytes
|
|
|
|
def u64_to_le_bytes(x: int) -> bytes:
|
|
return x.to_bytes(8, "little")
|
|
|
|
def build_blob(entries, vag_bytes):
|
|
blob = bytearray()
|
|
blob.extend(b"KATK")
|
|
blob.extend((len(entries)).to_bytes(2, "little"))
|
|
for type_id, key_u64, _ in entries:
|
|
blob.extend(type_id.to_bytes(4, "little"))
|
|
blob.extend(u64_to_le_bytes(key_u64))
|
|
blob.extend(b"VAG ")
|
|
assert len(vag_bytes) == 64, f"expected 64 VAG bytes, got {len(vag_bytes)}"
|
|
blob.extend(bytes(vag_bytes))
|
|
return bytes(blob)
|
|
|
|
def rust_byte_literal(blob: bytes, indent=" ") -> str:
|
|
lines = []
|
|
for i in range(0, len(blob), 12):
|
|
chunk = blob[i : i + 12]
|
|
hexes = ", ".join(f"0x{b:02X}" for b in chunk)
|
|
lines.append(indent + hexes + ",")
|
|
return "\n".join(lines)
|
|
|
|
def main():
|
|
entries, vag_bytes = parse_mf_keys(MF_KEYS_PATH)
|
|
blob = build_blob(entries, vag_bytes)
|
|
|
|
# embedded.rs
|
|
embedded_rs = '''//! Embedded keystore blob (standard encrypted + VAG raw).
|
|
//! Data is stored as binary to avoid plain-text keys in config.
|
|
//! Format: "KATK" magic, n_entries (u16 LE), then per entry: type_id (u32 LE), key (u64 LE), then "VAG " + 64 bytes.
|
|
//! Key bytes are little-endian (LSB first); the resulting u64 matches reference hex (MSB-first notation).
|
|
//! Generated from REFERENCES/mf_keys.txt by scripts/build_keystore_from_mf_keys.py
|
|
|
|
/// Blob: KATK + ''' + str(len(entries)) + ''' entries + VAG 64 bytes.
|
|
#[rustfmt::skip]
|
|
pub const KEYSTORE_BLOB: &[u8] = &[
|
|
'''
|
|
# Split blob for readable comments: magic + n, then entries (each 12 bytes), then VAG tag + 64
|
|
off = 0
|
|
embedded_rs += " // magic + n_entries\n"
|
|
embedded_rs += rust_byte_literal(blob[0:6], " ") + "\n"
|
|
off = 6
|
|
entry_size = 12
|
|
for i, (ty, key, name) in enumerate(entries):
|
|
if i % 8 == 0:
|
|
embedded_rs += f" // entries {i}..\n"
|
|
embedded_rs += rust_byte_literal(blob[off : off + entry_size], " ") + "\n"
|
|
off += entry_size
|
|
embedded_rs += " // VAG tag + 64 bytes\n"
|
|
embedded_rs += rust_byte_literal(blob[off:], " ") + "\n"
|
|
embedded_rs += "];\n"
|
|
|
|
with open("src/keystore/embedded.rs", "w") as f:
|
|
f.write(embedded_rs)
|
|
|
|
# KEY_ENTRY_NAMES for mod.rs (same order as blob entries)
|
|
names = [e[2] for e in entries]
|
|
|
|
# Update mod.rs: replace KEY_ENTRY_NAMES
|
|
mod_rs_path = "src/keystore/mod.rs"
|
|
with open(mod_rs_path) as f:
|
|
mod_content = f.read()
|
|
escaped = [n.replace("\\", "\\\\").replace('"', '\\"') for n in names]
|
|
new_block = "const KEY_ENTRY_NAMES: &[&str] = &[\n " + ",\n ".join(f'"{s}"' for s in escaped) + ",\n];"
|
|
pattern = r"const KEY_ENTRY_NAMES: &\[&str\] = &\[\n.*?\n\];"
|
|
mod_content = re.sub(pattern, new_block, mod_content, flags=re.DOTALL)
|
|
with open(mod_rs_path, "w") as f:
|
|
f.write(mod_content)
|
|
|
|
print(f"Wrote embedded blob with {len(entries)} entries, {len(vag_bytes)} VAG bytes")
|
|
print(f"Updated KEY_ENTRY_NAMES with {len(names)} names")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|