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

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", "EcuName": "KI211",
"Aliases": [], "Aliases": [],