Skip to content

Instantly share code, notes, and snippets.

@JJTech0130
Last active July 27, 2026 15:32
Show Gist options
  • Select an option

  • Save JJTech0130/3ff9b5c0b348326f12f7c41d30464158 to your computer and use it in GitHub Desktop.

Select an option

Save JJTech0130/3ff9b5c0b348326f12f7c41d30464158 to your computer and use it in GitHub Desktop.
Ford UDS SecurityAccess seed-key algorithms [untested]
"""
Ford ECU SecurityAccess seed-key algorithms, reversed from libfnvfsa.so on the ECG2 GWM
Algorithm 0: legacy 24-bit Galois LFSR cipher (3-byte seed, 5-byte/40-bit secret)
Described in https://flaviodgarcia.com/publications/BtB.pdf
Algorithms 1/2: modern HMAC-SHA1 (16-byte seed, 12-byte fixedBytes)
TODO: Test against a real vehicle
"""
import argparse
from cryptography.hazmat.primitives import hashes, hmac
FORD_LFSR_INIT = 0xC541A9
FORD_LFSR_TAPS = 0x109028
def ford_legacy_key(seed: bytes, fixed: bytes) -> bytes:
"""Algorithm 0. seed: 3 bytes. fixed: 5 bytes (40-bit magic secret)."""
if len(seed) != 3 or len(fixed) != 5:
raise ValueError("algorithm 0 needs a 3-byte seed and 5-byte fixed value")
f = int.from_bytes(fixed, "big")
cc = [f & 0xFF, (f >> 8) & 0xFF, (f >> 16) & 0xFF, (f >> 24) & 0xFF, (f >> 32) & 0xFF,
seed[2], seed[1], seed[0]]
state = FORD_LFSR_INIT
for _ in range(64):
bbit = (state & 1) ^ (cc[7] & 1)
state = ((state >> 1) + bbit * 0x800000)
if bbit:
state ^= FORD_LFSR_TAPS
cc[7] = (cc[7] >> 1) & 0xFF
for a in range(7, 0, -1):
cc[a] = (cc[a] + (cc[a - 1] & 1) * 128) & 0xFF
cc[a - 1] = cc[a - 1] >> 1
return bytes([
(state >> 4) & 0xFF,
(((state >> 12) & 0xF) << 4) + ((state >> 20) & 0xF),
((state >> 16) & 0xF) + ((state & 0xF) << 4),
])
def ford_hmac_key(mode: int, seed: bytes, fixed_bytes: bytes) -> bytes:
"""Algorithm 1 or 2. seed: 16 bytes. fixed_bytes: 12 bytes."""
if len(seed) != 16 or len(fixed_bytes) != 12:
raise ValueError("algorithms 1/2 need a 16-byte seed and 12-byte fixedBytes")
if mode == 1:
data = b"zaCK" + seed
elif mode == 2:
data = seed + b"JaKe"
else:
raise ValueError("mode must be 1 or 2")
h = hmac.HMAC(fixed_bytes, hashes.SHA1())
h.update(data)
digest = h.finalize()
return digest[4:20] if mode == 1 else digest[0:18]
def get_unlock_key(algorithm: int, seed: bytes, fixed_bytes: bytes) -> bytes:
if algorithm == 0:
return ford_legacy_key(seed, fixed_bytes)
if algorithm in (1, 2):
return ford_hmac_key(algorithm, seed, fixed_bytes)
raise ValueError(f"unknown algorithm {algorithm}")
def main():
p = argparse.ArgumentParser(description="Ford ECU SecurityAccess seed-key calculator")
p.add_argument("algorithm", type=int, choices=[0, 1, 2])
p.add_argument("seed", help="hex challenge/seed")
p.add_argument("fixed_bytes", help="hex secret/fixedBytes")
args = p.parse_args()
key = get_unlock_key(args.algorithm, bytes.fromhex(args.seed), bytes.fromhex(args.fixed_bytes))
print(key.hex())
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment