Add KI203:(3 definitions)

References https://github.com/jglim/UnlockECU/issues/30
This commit is contained in:
JinGen Lim 2024-01-12 15:31:22 +08:00
parent 3eb48cd295
commit fd9b2e8460
3 changed files with 385 additions and 202 deletions

View File

@ -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";
}
}
}

View File

@ -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;
}
}
}

View File

@ -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": [],