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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace UnlockECU
|
namespace UnlockECU
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Basic SecurityProvider to be inherited from. This class should not be directly initialized.
|
/// Basic SecurityProvider to be inherited from. This class should not be directly initialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SecurityProvider
|
public class SecurityProvider
|
||||||
{
|
{
|
||||||
public virtual string GetProviderName()
|
public virtual string GetProviderName()
|
||||||
{
|
{
|
||||||
return "ProviderName was not initialized";
|
return "ProviderName was not initialized";
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
|
public virtual bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List<Parameter> parameters)
|
||||||
{
|
{
|
||||||
throw new Exception("GenerateKey was not overridden");
|
throw new Exception("GenerateKey was not overridden");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte GetParameterByte(List<Parameter> parameters, string key)
|
public static byte GetParameterByte(List<Parameter> parameters, string key)
|
||||||
{
|
{
|
||||||
foreach (Parameter row in parameters)
|
foreach (Parameter row in parameters)
|
||||||
{
|
{
|
||||||
if ((row.Key == key) && (row.DataType == "Byte"))
|
if ((row.Key == key) && (row.DataType == "Byte"))
|
||||||
{
|
{
|
||||||
return (byte)(int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber));
|
return (byte)(int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Exception($"Failed to fetch byte parameter for key: {key}");
|
throw new Exception($"Failed to fetch byte parameter for key: {key}");
|
||||||
}
|
}
|
||||||
public static int GetParameterInteger(List<Parameter> parameters, string key)
|
public static int GetParameterInteger(List<Parameter> parameters, string key)
|
||||||
{
|
{
|
||||||
foreach (Parameter row in parameters)
|
foreach (Parameter row in parameters)
|
||||||
{
|
{
|
||||||
if ((row.Key == key) && (row.DataType == "Int32"))
|
if ((row.Key == key) && (row.DataType == "Int32"))
|
||||||
{
|
{
|
||||||
return int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
return int.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Exception($"Failed to fetch Int32 parameter for key: {key}");
|
throw new Exception($"Failed to fetch Int32 parameter for key: {key}");
|
||||||
}
|
}
|
||||||
public static long GetParameterLong(List<Parameter> parameters, string key)
|
public static long GetParameterLong(List<Parameter> parameters, string key)
|
||||||
{
|
{
|
||||||
foreach (Parameter row in parameters)
|
foreach (Parameter row in parameters)
|
||||||
{
|
{
|
||||||
if ((row.Key == key) && (row.DataType == "Int64"))
|
if ((row.Key == key) && (row.DataType == "Int64"))
|
||||||
{
|
{
|
||||||
return long.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
return long.Parse(row.Value, System.Globalization.NumberStyles.HexNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Exception($"Failed to fetch Int64 parameter for key: {key}");
|
throw new Exception($"Failed to fetch Int64 parameter for key: {key}");
|
||||||
}
|
}
|
||||||
public static byte[] GetParameterBytearray(List<Parameter> parameters, string key)
|
public static byte[] GetParameterBytearray(List<Parameter> parameters, string key)
|
||||||
{
|
{
|
||||||
foreach (Parameter row in parameters)
|
foreach (Parameter row in parameters)
|
||||||
{
|
{
|
||||||
if ((row.Key == key) && (row.DataType == "ByteArray"))
|
if ((row.Key == key) && (row.DataType == "ByteArray"))
|
||||||
{
|
{
|
||||||
return BitUtility.BytesFromHex(row.Value);
|
return BitUtility.BytesFromHex(row.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Exception($"Failed to fetch ByteArray parameter for key: {key}");
|
throw new Exception($"Failed to fetch ByteArray parameter for key: {key}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsInitialized = false;
|
private static bool IsInitialized = false;
|
||||||
private static List<SecurityProvider> SecurityProviders = new();
|
private static List<SecurityProvider> SecurityProviders = new();
|
||||||
|
|
||||||
public static List<SecurityProvider> GetSecurityProviders()
|
public static List<SecurityProvider> GetSecurityProviders()
|
||||||
{
|
{
|
||||||
if (IsInitialized)
|
if (IsInitialized)
|
||||||
{
|
{
|
||||||
return SecurityProviders;
|
return SecurityProviders;
|
||||||
}
|
}
|
||||||
SecurityProviders = new List<SecurityProvider>();
|
SecurityProviders = new List<SecurityProvider>();
|
||||||
|
|
||||||
System.Reflection.Assembly
|
System.Reflection.Assembly
|
||||||
.GetExecutingAssembly()
|
.GetExecutingAssembly()
|
||||||
.GetTypes()
|
.GetTypes()
|
||||||
.Where(x => x.IsSubclassOf(typeof(SecurityProvider)))
|
.Where(x => x.IsSubclassOf(typeof(SecurityProvider)))
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(x => SecurityProviders.Add((SecurityProvider)Activator.CreateInstance(x)));
|
.ForEach(x => SecurityProviders.Add((SecurityProvider)Activator.CreateInstance(x)));
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
|
|
||||||
return SecurityProviders;
|
return SecurityProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Endian
|
public enum Endian
|
||||||
{
|
{
|
||||||
Big,
|
Big,
|
||||||
Little,
|
Little,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint BytesToInt(byte[] inBytes, Endian endian, int offset = 0)
|
public static uint BytesToInt(byte[] inBytes, Endian endian, int offset = 0)
|
||||||
{
|
{
|
||||||
uint result = 0;
|
uint result = 0;
|
||||||
if (endian == Endian.Big)
|
if (endian == Endian.Big)
|
||||||
{
|
{
|
||||||
result |= (uint)inBytes[offset++] << 24;
|
result |= (uint)inBytes[offset++] << 24;
|
||||||
result |= (uint)inBytes[offset++] << 16;
|
result |= (uint)inBytes[offset++] << 16;
|
||||||
result |= (uint)inBytes[offset++] << 8;
|
result |= (uint)inBytes[offset++] << 8;
|
||||||
result |= (uint)inBytes[offset++] << 0;
|
result |= (uint)inBytes[offset++] << 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result |= (uint)inBytes[offset++] << 0;
|
result |= (uint)inBytes[offset++] << 0;
|
||||||
result |= (uint)inBytes[offset++] << 8;
|
result |= (uint)inBytes[offset++] << 8;
|
||||||
result |= (uint)inBytes[offset++] << 16;
|
result |= (uint)inBytes[offset++] << 16;
|
||||||
result |= (uint)inBytes[offset++] << 24;
|
result |= (uint)inBytes[offset++] << 24;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
public static void IntToBytes(uint inInt, byte[] outBytes, Endian endian)
|
public static void IntToBytes(uint inInt, byte[] outBytes, Endian endian)
|
||||||
{
|
{
|
||||||
if (endian == Endian.Big)
|
if (endian == Endian.Big)
|
||||||
{
|
{
|
||||||
outBytes[0] = (byte)(inInt >> 24);
|
outBytes[0] = (byte)(inInt >> 24);
|
||||||
outBytes[1] = (byte)(inInt >> 16);
|
outBytes[1] = (byte)(inInt >> 16);
|
||||||
outBytes[2] = (byte)(inInt >> 8);
|
outBytes[2] = (byte)(inInt >> 8);
|
||||||
outBytes[3] = (byte)(inInt >> 0);
|
outBytes[3] = (byte)(inInt >> 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outBytes[3] = (byte)(inInt >> 24);
|
outBytes[3] = (byte)(inInt >> 24);
|
||||||
outBytes[2] = (byte)(inInt >> 16);
|
outBytes[2] = (byte)(inInt >> 16);
|
||||||
outBytes[1] = (byte)(inInt >> 8);
|
outBytes[1] = (byte)(inInt >> 8);
|
||||||
outBytes[0] = (byte)(inInt >> 0);
|
outBytes[0] = (byte)(inInt >> 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: endian unaware:
|
// WARNING: endian unaware:
|
||||||
public static byte GetBit(byte inByte, int bitPosition)
|
public static byte GetBit(byte inByte, int bitPosition)
|
||||||
{
|
{
|
||||||
if (bitPosition > 7)
|
if (bitPosition > 7)
|
||||||
{
|
{
|
||||||
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
||||||
}
|
}
|
||||||
return (byte)((inByte >> bitPosition) & 1);
|
return (byte)((inByte >> bitPosition) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte GetByte(uint inInt, int bytePosition)
|
public static byte GetByte(uint inInt, int bytePosition)
|
||||||
{
|
{
|
||||||
if (bytePosition > 3)
|
if (bytePosition > 3)
|
||||||
{
|
{
|
||||||
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
||||||
}
|
}
|
||||||
return (byte)(inInt >> (8 * bytePosition));
|
return (byte)(inInt >> (8 * bytePosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte SetBit(byte inByte, int bitPosition)
|
public static byte SetBit(byte inByte, int bitPosition)
|
||||||
{
|
{
|
||||||
if (bitPosition > 7)
|
if (bitPosition > 7)
|
||||||
{
|
{
|
||||||
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
throw new Exception("Attempted to shift beyond 8 bits in a byte");
|
||||||
}
|
}
|
||||||
return inByte |= (byte)(1 << bitPosition);
|
return inByte |= (byte)(1 << bitPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint SetByte(uint inInt, byte byteToSet, int bytePosition)
|
public static uint SetByte(uint inInt, byte byteToSet, int bytePosition)
|
||||||
{
|
{
|
||||||
if (bytePosition > 3)
|
if (bytePosition > 3)
|
||||||
{
|
{
|
||||||
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
throw new Exception("Attempted to shift beyond 4 bytes in an uint");
|
||||||
}
|
}
|
||||||
int bitPosition = 8 * bytePosition;
|
int bitPosition = 8 * bytePosition;
|
||||||
inInt &= ~(uint)(0xFF << bitPosition);
|
inInt &= ~(uint)(0xFF << bitPosition);
|
||||||
inInt |= (uint)(byteToSet << bitPosition);
|
inInt |= (uint)(byteToSet << bitPosition);
|
||||||
return inInt;
|
return inInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] ExpandByteArrayToNibbles(byte[] inputArray)
|
public static byte[] ExpandByteArrayToNibbles(byte[] inputArray)
|
||||||
{
|
{
|
||||||
// Primarily used for IC172
|
// Primarily used for IC172
|
||||||
byte[] result = new byte[inputArray.Length * 2];
|
byte[] result = new byte[inputArray.Length * 2];
|
||||||
for (int i = 0; i < inputArray.Length; i++)
|
for (int i = 0; i < inputArray.Length; i++)
|
||||||
{
|
{
|
||||||
result[i * 2] = (byte)((inputArray[i] >> 4) & 0xF);
|
result[i * 2] = (byte)((inputArray[i] >> 4) & 0xF);
|
||||||
result[i * 2 + 1] = (byte)(inputArray[i] & 0xF);
|
result[i * 2 + 1] = (byte)(inputArray[i] & 0xF);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] CollapseByteArrayFromNibbles(byte[] inputArray)
|
public static byte[] CollapseByteArrayFromNibbles(byte[] inputArray)
|
||||||
{
|
{
|
||||||
// Primarily used for IC172
|
// Primarily used for IC172
|
||||||
if ((inputArray.Length % 2) != 0)
|
if ((inputArray.Length % 2) != 0)
|
||||||
{
|
{
|
||||||
throw new Exception("Attempted to form a byte array from an odd-numbered set of nibbles.");
|
throw new Exception("Attempted to form a byte array from an odd-numbered set of nibbles.");
|
||||||
}
|
}
|
||||||
byte[] result = new byte[inputArray.Length / 2];
|
byte[] result = new byte[inputArray.Length / 2];
|
||||||
for (int i = 0; i < result.Length; i++)
|
for (int i = 0; i < result.Length; i++)
|
||||||
{
|
{
|
||||||
result[i] = (byte)((inputArray[i * 2] << 4) | (inputArray[i * 2 + 1]));
|
result[i] = (byte)((inputArray[i * 2] << 4) | (inputArray[i * 2 + 1]));
|
||||||
}
|
}
|
||||||
return result;
|
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",
|
"EcuName": "KI211",
|
||||||
"Aliases": [],
|
"Aliases": [],
|
||||||
|
|
Loading…
Reference in New Issue