Add @bri3d 's SA2 implementation

No actual definitions added, only a security provider based on @bri3d 's repo at  from https://github.com/bri3d/sa2_seed_key
This commit is contained in:
JinGen Lim 2020-12-01 00:06:47 +08:00
parent 7341a0a35a
commit 2c8b4ea9cc
4 changed files with 219 additions and 3 deletions

View File

@ -17,11 +17,10 @@ namespace UnlockECU
List<Definition> definitions = System.Text.Json.JsonSerializer.Deserialize<List<Definition>>(definitionJson);
List<SecurityProvider> providers = SecurityProvider.GetSecurityProviders();
ReverseKey(BitUtility.BytesFromHex("BA00D268 972D452D"), BitUtility.BytesFromHex("BE515D46")); // reverse key 0x3F9C71A5 (CRD3S2SEC9A)
Console.ReadKey();
}
/*
static void ReverseKey(byte[] inSeed, byte[] outKeyBytes)
{
long kA = 1103515245L;
@ -66,5 +65,8 @@ namespace UnlockECU
}
return result;
}
*/
}
}

View File

@ -0,0 +1,187 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnlockECU
{
/// <summary>
/// Volkswagen SA2 implementation from https://github.com/bri3d/sa2_seed_key
/// </summary>
class VolkswagenSA2 : SecurityProvider
{
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
{
byte[] tape = GetParameterBytearray(parameters, "InstructionTape");
if ((inSeed.Length != 4) || (outKey.Length != 4))
{
return false;
}
SA2SeedKey sk = new SA2SeedKey(tape, BytesToInt(inSeed, Endian.Big, 0));
IntToBytes(sk.Execute(), outKey, Endian.Big);
return true;
}
public override string GetProviderName()
{
return "VolkswagenSA2";
}
}
public class SA2SeedKey
{
uint Register = 0;
uint CarryFlag = 0;
byte[] InstructionTape = Array.Empty<byte>();
uint InstructionPointer = 0;
List<uint> ForPointers = new List<uint>();
List<int> ForIterations = new List<int>();
public SA2SeedKey(byte[] tape, uint seed)
{
InstructionTape = tape;
Register = seed;
}
private void RegisterShiftLeft()
{
CarryFlag = Register & 0x80000000;
Register <<= 1;
if (CarryFlag > 0)
{
Register |= 1;
}
InstructionPointer++;
}
private void RegisterShiftRight()
{
CarryFlag = Register & 1;
Register >>= 1;
if (CarryFlag > 0)
{
Register |= 0x80000000;
}
InstructionPointer++;
}
private void Add()
{
CarryFlag = 0;
long outputRegister = Register;
outputRegister += FetchOperand();
if (outputRegister > 0xFFFFFFFF)
{
CarryFlag = 1;
outputRegister &= 0xFFFFFFFF;
}
Register = (uint)outputRegister;
InstructionPointer += 5;
}
private void Subtract()
{
CarryFlag = 0;
long outputRegister = Register;
outputRegister -= FetchOperand();
if (outputRegister > 0xFFFFFFFF)
{
CarryFlag = 1;
outputRegister &= 0xFFFFFFFF;
}
Register = (uint)outputRegister;
InstructionPointer += 5;
}
private void ExclusiveOr()
{
uint op = FetchOperand();
Register ^= op;
InstructionPointer += 5;
}
private void LoopInit()
{
ForIterations.Insert(0, FetchOperandByte() - 1);
InstructionPointer += 2;
ForPointers.Insert(0, InstructionPointer);
}
private void LoopNext()
{
if (ForIterations[0] > 0)
{
ForIterations[0]--;
InstructionPointer = ForPointers[0];
}
else
{
ForIterations.RemoveAt(0);
ForPointers.RemoveAt(0);
InstructionPointer++;
}
}
private void BranchConditional()
{
uint skipCount = (uint)FetchOperandByte() + 2;
if (CarryFlag == 0)
{
InstructionPointer += skipCount;
}
else
{
InstructionPointer += 2;
}
}
private void BranchUnconditional()
{
InstructionPointer += (uint)FetchOperandByte() + 2;
}
private void EndExecution()
{
InstructionPointer++;
}
private uint FetchOperand()
{
byte[] operands = InstructionTape.Skip((int)InstructionPointer + 1).Take(4).ToArray();
uint interpretedInt = (uint)operands[0] << 24 | (uint)operands[1] << 16 | (uint)operands[2] << 8 | (uint)operands[3];
return interpretedInt;
}
private byte FetchOperandByte()
{
return InstructionTape[InstructionPointer + 1];
}
public uint Execute()
{
Dictionary<int, Action> instructionSet = new Dictionary<int, Action>();
instructionSet.Add(0x81, RegisterShiftLeft);
instructionSet.Add(0x82, RegisterShiftRight);
instructionSet.Add(0x93, Add);
instructionSet.Add(0x84, Subtract);
instructionSet.Add(0x87, ExclusiveOr);
instructionSet.Add(0x68, LoopInit);
instructionSet.Add(0x49, LoopNext);
instructionSet.Add(0x4A, BranchConditional);
instructionSet.Add(0x6B, BranchUnconditional);
instructionSet.Add(0x4C, EndExecution);
while (InstructionPointer < InstructionTape.Length)
{
// Uncomment to trace execution
// Console.WriteLine($"IP: 0x{InstructionPointer:X} Reg: 0x{Register:X8} CF: 0x{CarryFlag:X8} Op: {instructionSet[InstructionTape[InstructionPointer]].Method.Name} ");
instructionSet[InstructionTape[InstructionPointer]]();
}
Console.WriteLine($"Exec done, reg = 0x{Register:X8}");
return Register;
}
}
}

View File

@ -31,9 +31,36 @@ namespace UnlockECUTests
QuickTest(definitions.Find(x => (x.EcuName == "CR6NFZ") && (x.AccessLevel == 1) && (x.Provider == "PowertrainSecurityAlgo2")), providers);
QuickTest(definitions.Find(x => (x.EcuName == "DCDC223") && (x.AccessLevel == 17) && (x.Provider == "EsLibEd25519")), providers);
VWTest(definitions, providers);
Assert.Pass();
}
static void VWTest(List<Definition> definitions, List<SecurityProvider> providers)
{
SecurityProvider vwProvider = providers.Find(x => x.GetProviderName() == "VolkswagenSA2");
Definition vw1 = definitions.Find(x => x.EcuName == "VW_SA2_TEST1");
Definition vw2 = definitions.Find(x => x.EcuName == "VW_SA2_TEST2");
byte[] vwKey = new byte[4];
byte[] expectedOutput;
expectedOutput = BitUtility.BytesFromHex("3C7876D8");
vwProvider.GenerateKey(BitUtility.BytesFromHex("A04EB1ED"), vwKey, 1, vw1.Parameters); // 0x3C7876D8
if (!vwKey.SequenceEqual(expectedOutput))
{
Assert.Fail();
}
expectedOutput = BitUtility.BytesFromHex("6A37F02E");
vwProvider.GenerateKey(BitUtility.BytesFromHex("1A1B1C1D"), vwKey, 1, vw2.Parameters); // 0x6a37f02e
if (!vwKey.SequenceEqual(expectedOutput))
{
Assert.Fail();
}
}
static string GetLibraryFolder()
{
return $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}Library{Path.DirectorySeparatorChar}";

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>key.ico</ApplicationIcon>