diff --git a/UnlockECU/UnlockECU/Security/KI203Algo1.cs b/UnlockECU/UnlockECU/Security/KI203Algo1.cs
new file mode 100644
index 0000000..3ba2438
--- /dev/null
+++ b/UnlockECU/UnlockECU/Security/KI203Algo1.cs
@@ -0,0 +1,92 @@
+using System.Collections.Generic;
+
+namespace UnlockECU
+{
+ ///
+ /// 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
+ ///
+ class KI203Algo1 : SecurityProvider
+ {
+ public override bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List 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;
+ }
+
+ ///
+ /// 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
+ ///
+ 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
+ }
+
+ ///
+ /// Converts a condensed key into a original key
+ /// This implementation is here for reference and is not directly used by the application
+ ///
+ 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";
+ }
+ }
+}
+
diff --git a/UnlockECU/UnlockECU/Security/SecurityProvider.cs b/UnlockECU/UnlockECU/Security/SecurityProvider.cs
index 7388946..401e84b 100644
--- a/UnlockECU/UnlockECU/Security/SecurityProvider.cs
+++ b/UnlockECU/UnlockECU/Security/SecurityProvider.cs
@@ -1,202 +1,245 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace UnlockECU
-{
- ///
- /// Basic SecurityProvider to be inherited from. This class should not be directly initialized.
- ///
- public class SecurityProvider
- {
- public virtual string GetProviderName()
- {
- return "ProviderName was not initialized";
- }
-
- public virtual bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List parameters)
- {
- throw new Exception("GenerateKey was not overridden");
- }
-
- public static byte GetParameterByte(List 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 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 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 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 SecurityProviders = new();
-
- public static List GetSecurityProviders()
- {
- if (IsInitialized)
- {
- return SecurityProviders;
- }
- SecurityProviders = new List();
-
- 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
+{
+ ///
+ /// Basic SecurityProvider to be inherited from. This class should not be directly initialized.
+ ///
+ public class SecurityProvider
+ {
+ public virtual string GetProviderName()
+ {
+ return "ProviderName was not initialized";
+ }
+
+ public virtual bool GenerateKey(byte[] inSeed, byte[] outKey, int accessLevel, List parameters)
+ {
+ throw new Exception("GenerateKey was not overridden");
+ }
+
+ public static byte GetParameterByte(List 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 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 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 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 SecurityProviders = new();
+
+ public static List GetSecurityProviders()
+ {
+ if (IsInitialized)
+ {
+ return SecurityProviders;
+ }
+ SecurityProviders = new List();
+
+ 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;
+ }
+ }
+}
diff --git a/UnlockECU/db.json b/UnlockECU/db.json
index b026412..50819fe 100644
--- a/UnlockECU/db.json
+++ b/UnlockECU/db.json
@@ -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": [],