Skip to content

Commit 09ea605

Browse files
bri3dBrian Ledbetter
authored and
Brian Ledbetter
committed
initial commit
0 parents  commit 09ea605

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SA2 Seed Key
2+
3+
SA2 Seed/Key authentication is a mechanism for authorizing test / tool clients with Volkswagen Auto Group control units, usually used to unlock a Programming session to re-flash the control units.
4+
5+
The SA2 Seed/Key "script" is contained in the FRF or ODX flash container, and consists of a small bytecode machine in which simple opcodes are applied to the "seed" provided by the ECU to generate the "Key".
6+
7+
# Usage:
8+
9+
```
10+
vs = Sa2SeedKey([0x68, 0x02, 0x81, 0x49, 0x93, 0xa5, 0x5a, 0x55, 0xaa, 0x4a, 0x05, 0x87, 0x81, 0x05, 0x95, 0x26, 0x68, 0x05, 0x82, 0x49, 0x84, 0x5a, 0xa5, 0xaa, 0x55, 0x87, 0x03, 0xf7, 0x80, 0x6a, 0x4c], 0x1a1b1c1d)
11+
result = vs.execute() # 0x6a37f02e
12+
```

sa2_seed_key/__init__.py

Whitespace-only changes.

sa2_seed_key/sa2_seed_key.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from collections import deque
2+
3+
class Sa2SeedKey:
4+
register: int
5+
carry_flag: int = 0
6+
instruction_tape: bytearray
7+
instruction_pointer: int = 0
8+
for_pointers: deque = deque()
9+
for_iterations: deque = deque()
10+
11+
def __init__(self, instruction_tape, seed):
12+
self.instruction_tape = instruction_tape
13+
self.register = seed
14+
15+
def rsl(self):
16+
self.carry_flag = self.register & 0x80000000
17+
self.register = self.register << 1
18+
if(self.carry_flag):
19+
self.register = self.register | 0x1
20+
self.register = self.register & 0xFFFFFFFF
21+
self.instruction_pointer += 1
22+
23+
def rsr(self):
24+
self.carry_flag = self.register & 0x1
25+
self.register = self.register >> 1
26+
if(self.carry_flag):
27+
self.register = self.register | 0x80000000
28+
self.instruction_pointer += 1
29+
30+
def add(self):
31+
self.carry_flag = 0
32+
operands = self.instruction_tape[self.instruction_pointer + 1:self.instruction_pointer + 5]
33+
add_int = operands[0] << 24 | operands[1] << 16 | operands[2] << 8 | operands[3]
34+
output_register = self.register + add_int
35+
if (output_register > 0xffffffff):
36+
self.carry_flag = 1
37+
output_register = output_register & 0xffffffff
38+
self.register = output_register
39+
self.instruction_pointer += 5
40+
41+
def sub(self):
42+
self.carry_flag = 0
43+
operands = self.instruction_tape[self.instruction_pointer + 1:self.instruction_pointer + 5]
44+
sub_int = operands[0] << 24 | operands[1] << 16 | operands[2] << 8 | operands[3]
45+
output_register = self.register - sub_int
46+
if (output_register < 0):
47+
self.carry_flag = 1
48+
output_register = output_register & 0xffffffff
49+
self.register = output_register
50+
self.instruction_pointer += 5
51+
52+
def eor(self):
53+
operands = self.instruction_tape[self.instruction_pointer + 1:self.instruction_pointer + 5]
54+
xor_int = operands[0] << 24 | operands[1] << 16 | operands[2] << 8 | operands[3]
55+
self.register = self.register ^ xor_int
56+
self.instruction_pointer += 5
57+
58+
def for_loop(self):
59+
operands = self.instruction_tape[self.instruction_pointer + 1:self.instruction_pointer + 2]
60+
self.for_iterations.appendleft(operands[0] - 1)
61+
self.instruction_pointer += 2
62+
self.for_pointers.appendleft(self.instruction_pointer)
63+
64+
def next_loop(self):
65+
if(self.for_iterations[0] > 0):
66+
self.for_iterations[0] -= 1
67+
self.instruction_pointer = self.for_pointers[0]
68+
else:
69+
self.for_iterations.popleft()
70+
self.for_pointers.popleft()
71+
self.instruction_pointer += 1
72+
73+
# bcc = branch conditional
74+
def bcc(self):
75+
operands = self.instruction_tape[self.instruction_pointer + 1:self.instruction_pointer + 2]
76+
skip_count = operands[0] + 2
77+
if(self.carry_flag == 0):
78+
self.instruction_pointer += skip_count
79+
else:
80+
self.instruction_pointer += 2
81+
82+
# bra = branch unconditional
83+
def bra(self):
84+
operands = self.instruction_tape[self.instruction_pointer + 1:self.instruction_pointer + 2]
85+
skip_count = operands[0] + 2
86+
self.instruction_pointer += skip_count
87+
88+
def finish(self):
89+
self.instruction_pointer += 1
90+
91+
def execute(self):
92+
instruction_set = {
93+
0x81 : self.rsl,
94+
0x82 : self.rsr,
95+
0x93 : self.add,
96+
0x84 : self.sub,
97+
0x87 : self.eor,
98+
0x68 : self.for_loop,
99+
0x49 : self.next_loop,
100+
0x4A : self.bcc,
101+
0x6B : self.bra,
102+
0x4C : self.finish
103+
}
104+
while(self.instruction_pointer < len(self.instruction_tape)):
105+
instruction_set[self.instruction_tape[self.instruction_pointer]]()
106+
return self.register

setup.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import setuptools
2+
3+
with open("README.md", "r") as fh:
4+
long_description = fh.read()
5+
6+
setuptools.setup(
7+
name="sa2-seed-key",
8+
version="0.0.1",
9+
author="Brian Ledbetter",
10+
author_email="[email protected]",
11+
description="SA2 Seed/Key Authorization for Volkswagen AG Vehicles",
12+
long_description=long_description,
13+
long_description_content_type="text/markdown",
14+
url="https://github.com/bri3d/sa2_seed_key",
15+
packages=setuptools.find_packages(),
16+
classifiers=[
17+
"Programming Language :: Python :: 3",
18+
"License :: OSI Approved :: MIT License",
19+
"Operating System :: OS Independent",
20+
],
21+
python_requires='>=3.6',
22+
)

tests/test_sa2_seed_key.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import unittest
2+
from sa2_seed_key.sa2_seed_key import Sa2SeedKey
3+
4+
class TestSa2SeedKey(unittest.TestCase):
5+
def test_security(self):
6+
vs = Sa2SeedKey([0x68, 0x02, 0x81, 0x49, 0x93, 0xa5, 0x5a, 0x55, 0xaa, 0x4a, 0x05, 0x87, 0x81, 0x05, 0x95, 0x26, 0x68, 0x05, 0x82, 0x49, 0x84, 0x5a, 0xa5, 0xaa, 0x55, 0x87, 0x03, 0xf7, 0x80, 0x6a, 0x4c], 0x1a1b1c1d)
7+
self.assertEqual(vs.execute(), 0x6a37f02e)
8+
vs = Sa2SeedKey([0x68, 0x02, 0x81, 0x4A, 0x10, 0x68, 0x04, 0x93, 0x08, 0x08, 0x20, 0x09, 0x4A, 0x05, 0x87, 0x22, 0x12, 0x19, 0x54, 0x82, 0x49, 0x93, 0x07, 0x12, 0x20, 0x11, 0x82, 0x4A, 0x05, 0x87, 0x03, 0x11, 0x20, 0x10, 0x82, 0x4A, 0x01, 0x81, 0x49, 0x4C], 0xa04eb1ed)
9+
res = vs.execute()
10+
self.assertEqual(vs.execute(), 0x3C7876D8)
11+
12+
if __name__ == '__main__':
13+
unittest.main()

0 commit comments

Comments
 (0)