mirror of https://github.com/rusefi/UnlockECU.git
Add KI203:(3 definitions)
References https://github.com/jglim/UnlockECU/issues/30
This commit is contained in:
parent
3eb48cd295
commit
fd9b2e8460
|
@ -0,0 +1,92 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UnlockECU
|
||||
{
|
||||
/// <summary>
|
||||
/// KI203Algo1 : https://github.com/jglim/UnlockECU/issues/30
|
||||
/// Collaborative effort by @rumator, Sergey (@Feezex) and Vladyslav Lupashevskyi (@VladLupashevskyi)
|
||||
/// This implementation is the original variant as reversed by @VladLupashevskyi using a firmware dump from @rumator,
|
||||
/// and it is preferred as the root keys can be directly used from a firmware dump.
|
||||
/// The root keys are often found in the firmware as a 7-element array of 32-bit integers, one for each level
|
||||
/// https://github.com/jglim/UnlockECU/issues/30#issuecomment-1881151971
|
||||
/// </summary>
|
||||
class KI203Algo1 : SecurityProvider
|
||||
{
|
||||
public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
|
||||
{
|
||||
if ((inSeed.Length != 4) || (outKey.Length != 4))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint root = BytesToInt(GetParameterBytearray(parameters, "K"), Endian.Big);
|
||||
int ones = CountOnes(root);
|
||||
|
||||
uint val = (uint)(
|
||||
(inSeed[2] << 0) |
|
||||
(inSeed[0] << 8) |
|
||||
(inSeed[3] << 16) |
|
||||
(inSeed[1] << 24)
|
||||
);
|
||||
|
||||
val = RotateLeft(val, 3);
|
||||
val ^= root;
|
||||
val = RotateRight(val, ones);
|
||||
|
||||
outKey[0] = GetByte(val, 0);
|
||||
outKey[1] = GetByte(val, 2);
|
||||
outKey[2] = GetByte(val, 3);
|
||||
outKey[3] = GetByte(val, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Condensed version of the original algo by @Feezex, where the shifts and transpositions
|
||||
/// have been folded into the root key, and only a single transpose op is applied once before the xor
|
||||
/// This implementation is here for reference and is not directly used by the application
|
||||
/// </summary>
|
||||
private static void CondensedGenerateKey()
|
||||
{
|
||||
byte[] input = new byte[] { 0x3B, 0x1C, 0x0D, 0xDD };
|
||||
byte[] root = new byte[] { 0xF8, 0x20, 0x5A, 0x4F };
|
||||
|
||||
byte[] result = new byte[4]
|
||||
{
|
||||
(byte)(((input[3] & 0xF) << 4) | (input[0] >> 4)),
|
||||
(byte)(((input[2] & 0xF) << 4) | (input[1] >> 4)),
|
||||
(byte)(((input[0] & 0xF) << 4) | (input[2] >> 4)),
|
||||
(byte)(((input[1] & 0xF) << 4) | (input[3] >> 4))
|
||||
};
|
||||
|
||||
for (int i = 0; i < root.Length; i++)
|
||||
{
|
||||
result[i] ^= root[i];
|
||||
}
|
||||
// Expects result to be 2BF1EA82
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a condensed key into a original key
|
||||
/// This implementation is here for reference and is not directly used by the application
|
||||
/// </summary>
|
||||
static uint ConvertCondensedKeyToOriginal(uint val)
|
||||
{
|
||||
// Shared by @Feezex in https://github.com/jglim/UnlockECU/issues/30#issuecomment-1881815230
|
||||
// 203XXXXXXX_0223: 0x758A9A61 -> 30BACD45
|
||||
// 203XXXXXXX_0287: 0xF8205A4F-> 27FC2D10
|
||||
// 203XXXXXXX_0290: 0x054EDE92-> 4902EF27
|
||||
uint prekey =
|
||||
(0xFF00_0000 & (val << 16)) |
|
||||
(0x00FF_0000 & (val)) |
|
||||
(0x0000_FF00 & (val << 8)) |
|
||||
(0x0000_00FF & (val >> 24));
|
||||
return RotateLeft(prekey, CountOnes(prekey));
|
||||
}
|
||||
|
||||
public override string GetProviderName()
|
||||
{
|
||||
return "KI203Algo1";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,202 +1,245 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnlockECU
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic SecurityProvider to be inherited from. This class should not be directly initialized.
|
||||
/// </summary>
|
||||
public class SecurityProvider
|
||||
{
|
||||
public virtual string GetProviderName()
|
||||
{
|
||||
return "ProviderName was not initialized";
|
||||
}
|
||||
|
||||
public virtual bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
|
||||
{
|
||||
throw new Exception("GenerateKey was not overridden");
|
||||
}
|
||||
|
||||
public static byte GetParameterByte(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "Byte"))
|
||||
{
|
||||
return (byte)(int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber));
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch byte parameter for key: {key}");
|
||||
}
|
||||
public static int GetParameterInteger(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "Int32"))
|
||||
{
|
||||
return int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch Int32 parameter for key: {key}");
|
||||
}
|
||||
public static long GetParameterLong(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "Int64"))
|
||||
{
|
||||
return long.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch Int64 parameter for key: {key}");
|
||||
}
|
||||
public static byte[] GetParameterBytearray(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "ByteArray"))
|
||||
{
|
||||
return BitUtility.BytesFromHex(row.Value);
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch ByteArray parameter for key: {key}");
|
||||
}
|
||||
|
||||
private static bool IsInitialized = false;
|
||||
private static List<SecurityProvider> SecurityProviders = new();
|
||||
|
||||
public static List<SecurityProvider> GetSecurityProviders()
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
return SecurityProviders;
|
||||
}
|
||||
SecurityProviders = new List<SecurityProvider>();
|
||||
|
||||
System.Reflection.Assembly
|
||||
.GetExecutingAssembly()
|
||||
.GetTypes()
|
||||
.Where(x => x.IsSubclassOf(typeof(SecurityProvider)))
|
||||
.ToList()
|
||||
.ForEach(x => SecurityProviders.Add((SecurityProvider)Activator.CreateInstance(x)));
|
||||
IsInitialized = true;
|
||||
|
||||
return SecurityProviders;
|
||||
}
|
||||
|
||||
public enum Endian
|
||||
{
|
||||
Big,
|
||||
Little,
|
||||
}
|
||||
|
||||
public static uint BytesToInt(byte[] inBytes, Endian endian, int offset = 0)
|
||||
{
|
||||
uint result = 0;
|
||||
if (endian == Endian.Big)
|
||||
{
|
||||
result |= (uint)inBytes[offset++] << 24;
|
||||
result |= (uint)inBytes[offset++] << 16;
|
||||
result |= (uint)inBytes[offset++] << 8;
|
||||
result |= (uint)inBytes[offset++] << 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= (uint)inBytes[offset++] << 0;
|
||||
result |= (uint)inBytes[offset++] << 8;
|
||||
result |= (uint)inBytes[offset++] << 16;
|
||||
result |= (uint)inBytes[offset++] << 24;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static void IntToBytes(uint inInt, byte[] outBytes, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Big)
|
||||
{
|
||||
outBytes[0] = (byte)(inInt >> 24);
|
||||
outBytes[1] = (byte)(inInt >> 16);
|
||||
outBytes[2] = (byte)(inInt >> 8);
|
||||
outBytes[3] = (byte)(inInt >> 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outBytes[3] = (byte)(inInt >> 24);
|
||||
outBytes[2] = (byte)(inInt >> 16);
|
||||
outBytes[1] = (byte)(inInt >> 8);
|
||||
outBytes[0] = (byte)(inInt >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: endian unaware:
|
||||
public static byte GetBit(byte inByte, int bitPosition)
|
||||
{
|
||||
if (bitPosition > 7)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
||||
}
|
||||
return (byte)((inByte >> bitPosition) & 1);
|
||||
}
|
||||
|
||||
public static byte GetByte(uint inInt, int bytePosition)
|
||||
{
|
||||
if (bytePosition > 3)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
||||
}
|
||||
return (byte)(inInt >> (8 * bytePosition));
|
||||
}
|
||||
|
||||
public static byte SetBit(byte inByte, int bitPosition)
|
||||
{
|
||||
if (bitPosition > 7)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
||||
}
|
||||
return inByte |= (byte)(1 << bitPosition);
|
||||
}
|
||||
|
||||
public static uint SetByte(uint inInt, byte byteToSet, int bytePosition)
|
||||
{
|
||||
if (bytePosition > 3)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
||||
}
|
||||
int bitPosition = 8 * bytePosition;
|
||||
inInt &= ~(uint)(0xFF << bitPosition);
|
||||
inInt |= (uint)(byteToSet << bitPosition);
|
||||
return inInt;
|
||||
}
|
||||
|
||||
public static byte[] ExpandByteArrayToNibbles(byte[] inputArray)
|
||||
{
|
||||
// Primarily used for IC172
|
||||
byte[] result = new byte[inputArray.Length * 2];
|
||||
for (int i = 0; i < inputArray.Length; i++)
|
||||
{
|
||||
result[i * 2] = (byte)((inputArray[i] >> 4) & 0xF);
|
||||
result[i * 2 + 1] = (byte)(inputArray[i] & 0xF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] CollapseByteArrayFromNibbles(byte[] inputArray)
|
||||
{
|
||||
// Primarily used for IC172
|
||||
if ((inputArray.Length % 2) != 0)
|
||||
{
|
||||
throw new Exception("Attempted to form a byte array from an odd-numbered set of nibbles.");
|
||||
}
|
||||
byte[] result = new byte[inputArray.Length / 2];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
result[i] = (byte)((inputArray[i * 2] << 4) | (inputArray[i * 2 + 1]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnlockECU
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic SecurityProvider to be inherited from. This class should not be directly initialized.
|
||||
/// </summary>
|
||||
public class SecurityProvider
|
||||
{
|
||||
public virtual string GetProviderName()
|
||||
{
|
||||
return "ProviderName was not initialized";
|
||||
}
|
||||
|
||||
public virtual bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
|
||||
{
|
||||
throw new Exception("GenerateKey was not overridden");
|
||||
}
|
||||
|
||||
public static byte GetParameterByte(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "Byte"))
|
||||
{
|
||||
return (byte)(int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber));
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch byte parameter for key: {key}");
|
||||
}
|
||||
public static int GetParameterInteger(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "Int32"))
|
||||
{
|
||||
return int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch Int32 parameter for key: {key}");
|
||||
}
|
||||
public static long GetParameterLong(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "Int64"))
|
||||
{
|
||||
return long.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch Int64 parameter for key: {key}");
|
||||
}
|
||||
public static byte[] GetParameterBytearray(List<Parameter> parameters, string key)
|
||||
{
|
||||
foreach (Parameter row in parameters)
|
||||
{
|
||||
if ((row.Key == key) && (row.DataType == "ByteArray"))
|
||||
{
|
||||
return BitUtility.BytesFromHex(row.Value);
|
||||
}
|
||||
}
|
||||
throw new Exception($"Failed to fetch ByteArray parameter for key: {key}");
|
||||
}
|
||||
|
||||
private static bool IsInitialized = false;
|
||||
private static List<SecurityProvider> SecurityProviders = new();
|
||||
|
||||
public static List<SecurityProvider> GetSecurityProviders()
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
return SecurityProviders;
|
||||
}
|
||||
SecurityProviders = new List<SecurityProvider>();
|
||||
|
||||
System.Reflection.Assembly
|
||||
.GetExecutingAssembly()
|
||||
.GetTypes()
|
||||
.Where(x => x.IsSubclassOf(typeof(SecurityProvider)))
|
||||
.ToList()
|
||||
.ForEach(x => SecurityProviders.Add((SecurityProvider)Activator.CreateInstance(x)));
|
||||
IsInitialized = true;
|
||||
|
||||
return SecurityProviders;
|
||||
}
|
||||
|
||||
public enum Endian
|
||||
{
|
||||
Big,
|
||||
Little,
|
||||
}
|
||||
|
||||
public static uint BytesToInt(byte[] inBytes, Endian endian, int offset = 0)
|
||||
{
|
||||
uint result = 0;
|
||||
if (endian == Endian.Big)
|
||||
{
|
||||
result |= (uint)inBytes[offset++] << 24;
|
||||
result |= (uint)inBytes[offset++] << 16;
|
||||
result |= (uint)inBytes[offset++] << 8;
|
||||
result |= (uint)inBytes[offset++] << 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= (uint)inBytes[offset++] << 0;
|
||||
result |= (uint)inBytes[offset++] << 8;
|
||||
result |= (uint)inBytes[offset++] << 16;
|
||||
result |= (uint)inBytes[offset++] << 24;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static void IntToBytes(uint inInt, byte[] outBytes, Endian endian)
|
||||
{
|
||||
if (endian == Endian.Big)
|
||||
{
|
||||
outBytes[0] = (byte)(inInt >> 24);
|
||||
outBytes[1] = (byte)(inInt >> 16);
|
||||
outBytes[2] = (byte)(inInt >> 8);
|
||||
outBytes[3] = (byte)(inInt >> 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outBytes[3] = (byte)(inInt >> 24);
|
||||
outBytes[2] = (byte)(inInt >> 16);
|
||||
outBytes[1] = (byte)(inInt >> 8);
|
||||
outBytes[0] = (byte)(inInt >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: endian unaware:
|
||||
public static byte GetBit(byte inByte, int bitPosition)
|
||||
{
|
||||
if (bitPosition > 7)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
||||
}
|
||||
return (byte)((inByte >> bitPosition) & 1);
|
||||
}
|
||||
|
||||
public static byte GetByte(uint inInt, int bytePosition)
|
||||
{
|
||||
if (bytePosition > 3)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
||||
}
|
||||
return (byte)(inInt >> (8 * bytePosition));
|
||||
}
|
||||
|
||||
public static byte SetBit(byte inByte, int bitPosition)
|
||||
{
|
||||
if (bitPosition > 7)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
||||
}
|
||||
return inByte |= (byte)(1 << bitPosition);
|
||||
}
|
||||
|
||||
public static uint SetByte(uint inInt, byte byteToSet, int bytePosition)
|
||||
{
|
||||
if (bytePosition > 3)
|
||||
{
|
||||
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
||||
}
|
||||
int bitPosition = 8 * bytePosition;
|
||||
inInt &= ~(uint)(0xFF << bitPosition);
|
||||
inInt |= (uint)(byteToSet << bitPosition);
|
||||
return inInt;
|
||||
}
|
||||
|
||||
public static byte[] ExpandByteArrayToNibbles(byte[] inputArray)
|
||||
{
|
||||
// Primarily used for IC172
|
||||
byte[] result = new byte[inputArray.Length * 2];
|
||||
for (int i = 0; i < inputArray.Length; i++)
|
||||
{
|
||||
result[i * 2] = (byte)((inputArray[i] >> 4) & 0xF);
|
||||
result[i * 2 + 1] = (byte)(inputArray[i] & 0xF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] CollapseByteArrayFromNibbles(byte[] inputArray)
|
||||
{
|
||||
// Primarily used for IC172
|
||||
if ((inputArray.Length % 2) != 0)
|
||||
{
|
||||
throw new Exception("Attempted to form a byte array from an odd-numbered set of nibbles.");
|
||||
}
|
||||
byte[] result = new byte[inputArray.Length / 2];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
result[i] = (byte)((inputArray[i * 2] << 4) | (inputArray[i * 2 + 1]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static uint RotateLeft(uint val, int count)
|
||||
{
|
||||
uint mask = ~(uint.MaxValue >> 1);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
bool bitSet = (val & mask) > 0;
|
||||
val <<= 1;
|
||||
if (bitSet)
|
||||
{
|
||||
val |= 1;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public static uint RotateRight(uint val, int count)
|
||||
{
|
||||
uint mask = ~(uint.MaxValue >> 1);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
bool bitSet = (val & 1) > 0;
|
||||
val >>= 1;
|
||||
if (bitSet)
|
||||
{
|
||||
val |= mask;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public static int CountOnes(uint val)
|
||||
{
|
||||
int result = 0;
|
||||
while (val > 0)
|
||||
{
|
||||
if ((val & 1) > 0)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
val >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5274,6 +5274,54 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"EcuName": "KI203",
|
||||
"Aliases": [],
|
||||
"AccessLevel": 1,
|
||||
"SeedLength": 4,
|
||||
"KeyLength": 4,
|
||||
"Provider": "KI203Algo1",
|
||||
"Origin": "KI203_203_0223_L7_@Feezex-@rumator-@VladLupashevskyi",
|
||||
"Parameters": [
|
||||
{
|
||||
"Key": "K",
|
||||
"Value": "30BACD45",
|
||||
"DataType": "ByteArray"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"EcuName": "KI203",
|
||||
"Aliases": [],
|
||||
"AccessLevel": 1,
|
||||
"SeedLength": 4,
|
||||
"KeyLength": 4,
|
||||
"Provider": "KI203Algo1",
|
||||
"Origin": "KI203_203_0287_L7_@Feezex-@rumator-@VladLupashevskyi",
|
||||
"Parameters": [
|
||||
{
|
||||
"Key": "K",
|
||||
"Value": "27FC2D10",
|
||||
"DataType": "ByteArray"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"EcuName": "KI203",
|
||||
"Aliases": [],
|
||||
"AccessLevel": 1,
|
||||
"SeedLength": 4,
|
||||
"KeyLength": 4,
|
||||
"Provider": "KI203Algo1",
|
||||
"Origin": "KI203_203_0290_L7_@Feezex-@rumator-@VladLupashevskyi",
|
||||
"Parameters": [
|
||||
{
|
||||
"Key": "K",
|
||||
"Value": "4902EF27",
|
||||
"DataType": "ByteArray"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"EcuName": "KI211",
|
||||
"Aliases": [],
|
||||
|
|
Loading…
Reference in New Issue