using System; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; namespace PspCrypto { public static class KIRKEngine { // KIRK return values public const int KIRK_OPERATION_SUCCESS = 0; public const int KIRK_NOT_ENABLED = 1; public const int KIRK_INVALID_MODE = 2; public const int KIRK_HEADER_HASH_INVALID = 3; public const int KIRK_DATA_HASH_INVALID = 4; public const int KIRK_SIG_CHECK_INVALID = 5; public const int KIRK_UNK_1 = 6; public const int KIRK_UNK_2 = 7; public const int KIRK_UNK_3 = 8; public const int KIRK_UNK_4 = 9; public const int KIRK_UNK_5 = 0xA; public const int KIRK_UNK_6 = 0xB; public const int KIRK_NOT_INITIALIZED = 0xC; public const int KIRK_INVALID_OPERATION = 0xD; public const int KIRK_INVALID_SEED_CODE = 0xE; public const int KIRK_INVALID_SIZE = 0xF; public const int KIRK_DATA_SIZE_ZERO = 0x10; // sceUtilsBufferCopyWithRange modes public const int KIRK_CMD_DECRYPT_PRIVATE = 1; public const int KIRK_CMD_2 = 2; public const int KIRK_CMD_3 = 3; public const int KIRK_CMD_ENCRYPT_IV_0 = 4; public const int KIRK_CMD_ENCRYPT_IV_FUSE = 5; public const int KIRK_CMD_ENCRYPT_IV_USER = 6; public const int KIRK_CMD_DECRYPT_IV_0 = 7; public const int KIRK_CMD_DECRYPT_IV_FUSE = 8; public const int KIRK_CMD_DECRYPT_IV_USER = 9; public const int KIRK_CMD_PRIV_SIGN_CHECK = 10; public const int KIRK_CMD_SHA1_HASH = 11; public const int KIRK_CMD_ECDSA_GEN_KEYS = 12; public const int KIRK_CMD_ECDSA_MULTIPLY_POINT = 13; public const int KIRK_CMD_PRNG = 14; public const int KIRK_CMD_15 = 15; public const int KIRK_CMD_ECDSA_SIGN = 16; public const int KIRK_CMD_ECDSA_VERIFY = 17; // KIRK header modes public const int KIRK_MODE_CMD1 = 1; public const int KIRK_MODE_CMD2 = 2; public const int KIRK_MODE_CMD3 = 3; public const int KIRK_MODE_ENCRYPT_CBC = 4; public const int KIRK_MODE_DECRYPT_CBC = 5; // sceUtilsBufferCopyWithRange errors public const int SUBCWR_NOT_16_ALGINED = 0x90A; public const int SUBCWR_HEADER_HASH_INVALID = 0x920; public const int SUBCWR_BUFFER_TOO_SMALL = 0x1000; [StructLayout(LayoutKind.Sequential)] public struct KIRK_AES128CBC_HEADER { public int mode; public int unk_4; public int unk_8; public int keyseed; public int data_size; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public unsafe struct KIRK_CMD1_HEADER { // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] private fixed byte _AES_key[16]; public Span AES_Key { get { fixed (byte* ptr = _AES_key) { return new Span(ptr, 16); } } } public Span AESKeys { get { fixed (byte* ptr = _AES_key) { return new Span(ptr, 32); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] private fixed byte _CMAC_key[16]; // 0x10 public Span CMAC_key { get { fixed (byte* ptr = _CMAC_key) { return new Span(ptr, 16); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] private fixed byte _CMAC_header_hash[16]; // 0x20 public Span CMAC_header_hash { get { fixed (byte* ptr = _CMAC_header_hash) { return new Span(ptr, 16); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] private fixed byte _CMAC_data_hash[16]; // 0x30 public Span CMAC_data_hash { get { fixed (byte* ptr = _CMAC_data_hash) { return new Span(ptr, 16); } } } public Span content { get { fixed (byte* ptr = unused) { return new Span(ptr, 0x40); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] fixed byte unused[32]; // 0x40 public uint mode; // 0x60 public byte ecdsa_hash; // 0x64 // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] fixed byte unk3[11]; // 0x65 public int data_size; // 0x70 public Span off70 { get { fixed (int* ptr = &data_size) { return new Span(ptr, 0x10); } } } public int data_offset; // 0x74 // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] fixed byte unk4[8]; // 0x78 // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] fixed byte unk5[16]; // 0x80 } [StructLayout(LayoutKind.Sequential)] public unsafe struct KIRK_CMD1_ECDSA_HEADER { // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] private fixed byte _AES_key[16]; public Span AES_Key { get { fixed (byte* ptr = _AES_key) { return new Span(ptr, 16); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] private fixed byte _header_sig_r[20]; // 0x10 public Span header_sig_r { get { fixed (byte* ptr = _header_sig_r) { return new Span(ptr, 20); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] private fixed byte _header_sig_s[20]; // 0x24 public Span header_sig_s { get { fixed (byte* ptr = _header_sig_s) { return new Span(ptr, 20); } } } public Span header_sig { get { fixed (byte* ptr = _header_sig_r) { return new Span(ptr, 40); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] private fixed byte _data_sig_r[20]; // 0x38 public Span data_sig_r { get { fixed (byte* ptr = _data_sig_r) { return new Span(ptr, 20); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] private fixed byte _data_sig_s[20]; // 0x4c public Span data_sig_s { get { fixed (byte* ptr = _data_sig_s) { return new Span(ptr, 20); } } } public Span data_sig { get { fixed (byte* ptr = _data_sig_r) { return new Span(ptr, 40); } } } public uint mode; //0x60 public byte ecdsa_hash; // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] fixed byte unk3[11]; uint data_size; uint data_offset; // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] fixed byte unk4[8]; // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] fixed byte unk5[16]; } [StructLayout(LayoutKind.Sequential)] public unsafe struct ECDSA_SIG { private fixed byte _r[0x14]; public Span r { get { fixed (byte* ptr = _r) { return new Span(ptr, 0x14); } } } private fixed byte _s[0x14]; public Span s { get { fixed (byte* ptr = _s) { return new Span(ptr, 0x14); } } } public Span sig { get { fixed (byte* ptr = _r) { return new Span(ptr, 0x28); } } } } [StructLayout(LayoutKind.Sequential)] public unsafe struct ECDSA_POINT { private fixed byte _x[0x14]; public Span x { get { fixed (byte* ptr = _x) { return new Span(ptr, 0x14); } } } private fixed byte _y[0x14]; public Span y { get { fixed (byte* ptr = _y) { return new Span(ptr, 0x14); } } } public Span point { get { fixed (byte* ptr = _x) { return new Span(ptr, 0x28); } } } } [StructLayout(LayoutKind.Sequential)] public struct KIRK_SHA1_HEADER { public int data_size; } [StructLayout(LayoutKind.Sequential)] public unsafe struct KIRK_CMD12_BUFFER { private fixed byte private_key_[0x14]; public Span private_key { get { fixed (byte* ptr = private_key_) { return new Span(ptr, 0x14); } } } public ECDSA_POINT public_key; } [StructLayout(LayoutKind.Sequential)] public struct KIRK_CMD13_BUFFER { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x14)] public byte[] multiplier; public ECDSA_POINT public_key; } [StructLayout(LayoutKind.Sequential)] public unsafe struct KIRK_CMD16_BUFFER { // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] private fixed byte enc_private_[0x20]; public Span enc_private { get { fixed (byte* ptr = enc_private_) { return new Span(ptr, 0x20); } } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x14)] private fixed byte message_hash_[0x14]; public Span message_hash { get { fixed (byte* ptr = message_hash_) { return new Span(ptr, 0x14); } } } } [StructLayout(LayoutKind.Sequential)] public unsafe struct KIRK_CMD17_BUFFER { public ECDSA_POINT public_key; private fixed byte _message_hash[0x14]; public Span message_hash { get { fixed (byte* ptr = _message_hash) { return new Span(ptr, 0x14); } } } public ECDSA_SIG signature; } // KIRK commands /* // Private Sig + Cipher 0x01: Super-Duper decryption (no inverse) 0x02: Encrypt Operation (inverse of 0x03) 0x03: Decrypt Operation (inverse of 0x02) // Cipher 0x04: Encrypt Operation (inverse of 0x07) (IV=0) 0x05: Encrypt Operation (inverse of 0x08) (IV=FuseID) 0x06: Encrypt Operation (inverse of 0x09) (IV=UserDefined) 0x07: Decrypt Operation (inverse of 0x04) 0x08: Decrypt Operation (inverse of 0x05) 0x09: Decrypt Operation (inverse of 0x06) // Sig Gens 0x0A: Private Signature Check (checks for private SCE sig) 0x0B: SHA1 Hash 0x0C: Mul1 0x0D: Mul2 0x0E: Random Number Gen 0x0F: (absolutely no idea – could be KIRK initialization) 0x10: Signature Gen // Sig Checks 0x11: Signature Check (checks for generated sigs) 0x12: Certificate Check (idstorage signatures) */ // Internal variables [StructLayout(LayoutKind.Sequential)] private unsafe struct kirk16_data { private fixed byte fuseid_[8]; public Span fuseid { get { fixed (byte* ptr = fuseid_) { return new Span(ptr, 8); } } } private fixed byte mesh_[0x40]; public Span mesh { get { fixed (byte* ptr = mesh_) { return new Span(ptr, 0x40); } } } } //[StructLayout(LayoutKind.Sequential)] //private unsafe struct header_keys //{ // // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] // public fixed byte AES[16]; // // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] // public fixed byte CMAC[16]; //} private static uint g_fuse90; private static uint g_fuse94; private static Aes aes_kirk1; private static byte[] PRNG_DATA = new byte[0x14]; private static bool is_kirk_initialized; private static readonly byte[] IV0 = new byte[16]; // Internal functions private static byte[] kirk_4_7_get_key(int key_type) { if ((key_type < 0) || (key_type >= 0x80)) throw new Exception("KIRK_INVALID_SIZE"); return KeyVault.kirkKeys[key_type]; } private static Aes CreateAes() { var aes_ctx = Aes.Create(); aes_ctx.Mode = CipherMode.CBC; aes_ctx.Padding = PaddingMode.None; aes_ctx.IV = IV0; return aes_ctx; } public static void decrypt_kirk16_private(Span dA_out, Span dA_enc) { int i, k; kirk16_data keydata; byte[] subkey_1 = new byte[0x10], subkey_2 = new byte[0x10]; using var aes_ctx = CreateAes(); keydata.fuseid[7] = (byte)(g_fuse90 & 0xFF); keydata.fuseid[6] = (byte)((g_fuse90 >> 8) & 0xFF); keydata.fuseid[5] = (byte)((g_fuse90 >> 16) & 0xFF); keydata.fuseid[4] = (byte)((g_fuse90 >> 24) & 0xFF); keydata.fuseid[3] = (byte)(g_fuse94 & 0xFF); keydata.fuseid[2] = (byte)((g_fuse94 >> 8) & 0xFF); keydata.fuseid[1] = (byte)((g_fuse94 >> 16) & 0xFF); keydata.fuseid[0] = (byte)((g_fuse94 >> 24) & 0xFF); /* set encryption key */ aes_ctx.Key = KeyVault.kirk16_key; /* set the subkeys */ for (i = 0; i < 0x10; i++) { /* set to the fuseid */ subkey_2[i] = subkey_1[i] = keydata.fuseid[i % 8]; } /* do aes crypto */ using (var encryptor = aes_ctx.CreateEncryptor()) { using var decryptor = aes_ctx.CreateDecryptor(); for (i = 0; i < 3; i++) { /* encrypt + decrypt */ encryptor.TransformBlock(subkey_1, 0, subkey_1.Length, subkey_1, 0); decryptor.TransformBlock(subkey_2, 0, subkey_2.Length, subkey_2, 0); } } /* set new key */ aes_ctx.Key = subkey_1; using (var encryptor = aes_ctx.CreateEncryptor()) { /* now lets make the key mesh */ for (i = 0; i < 3; i++) { /* do encryption in group of 3 */ for (k = 0; k < 3; k++) { /* crypto */ encryptor.TransformBlock(subkey_2, 0, subkey_2.Length, subkey_2, 0); } /* copy to out block */ subkey_2.AsSpan().CopyTo(keydata.mesh[(i * 0x10)..]); } } /* set the key to the mesh */ aes_ctx.Key = keydata.mesh.Slice(0x20, 0x10).ToArray(); using (var encryptor = aes_ctx.CreateEncryptor()) { /* do the encryption routines for the aes key */ for (i = 0; i < 2; i++) { /* encrypt the data */ var tmp = encryptor.TransformFinalBlock(keydata.mesh.ToArray(), 0x10, 0x30); tmp.CopyTo(keydata.mesh[0x10..]); } } /* set the key to that mesh shit */ using var aes = CreateAes(); aes.Key = keydata.mesh.Slice(0x10, 0x10).ToArray(); /* cbc decrypt the dA */ AesHelper.AesDecrypt(aes, dA_out, dA_enc, 0x20); } public static void encrypt_kirk16_private(Span dA_out, Span dA_dec) { int i, k; kirk16_data keydata; byte[] subkey_1 = new byte[0x10], subkey_2 = new byte[0x10]; using var aes_ctx = CreateAes(); keydata.fuseid[7] = (byte)(g_fuse90 & 0xFF); keydata.fuseid[6] = (byte)((g_fuse90 >> 8) & 0xFF); keydata.fuseid[5] = (byte)((g_fuse90 >> 16) & 0xFF); keydata.fuseid[4] = (byte)((g_fuse90 >> 24) & 0xFF); keydata.fuseid[3] = (byte)(g_fuse94 & 0xFF); keydata.fuseid[2] = (byte)((g_fuse94 >> 8) & 0xFF); keydata.fuseid[1] = (byte)((g_fuse94 >> 16) & 0xFF); keydata.fuseid[0] = (byte)((g_fuse94 >> 24) & 0xFF); /* set encryption key */ aes_ctx.Key = KeyVault.kirk16_key; /* set the subkeys */ for (i = 0; i < 0x10; i++) { /* set to the fuseid */ subkey_2[i] = subkey_1[i] = keydata.fuseid[i % 8]; } /* do aes crypto */ using (var encryptor = aes_ctx.CreateEncryptor()) { using var decryptor = aes_ctx.CreateDecryptor(); for (i = 0; i < 3; i++) { /* encrypt + decrypt */ subkey_1 = encryptor.TransformFinalBlock(subkey_1, 0, subkey_1.Length); subkey_2 = decryptor.TransformFinalBlock(subkey_2, 0, subkey_2.Length); } } /* set new key */ aes_ctx.Key = subkey_1; using (var encryptor = aes_ctx.CreateEncryptor()) { /* now lets make the key mesh */ for (i = 0; i < 3; i++) { /* do encryption in group of 3 */ for (k = 0; k < 3; k++) { /* crypto */ encryptor.TransformBlock(subkey_2, 0, subkey_2.Length, subkey_2, 0); } /* copy to out block */ subkey_2.AsSpan().CopyTo(keydata.mesh.Slice(i * 0x10)); } } /* set the key to the mesh */ aes_ctx.Key = keydata.mesh.Slice(0x20, 0x10).ToArray(); using (var encryptor = aes_ctx.CreateEncryptor()) { /* do the encryption routines for the aes key */ for (i = 0; i < 2; i++) { /* encrypt the data */ var tmp = encryptor.TransformFinalBlock(keydata.mesh.ToArray(), 0x10, 0x30); tmp.CopyTo(keydata.mesh.Slice(0x10)); } } /* set the key to that mesh shit */ using var aes = CreateAes(); aes.Key = keydata.mesh.Slice(0x10, 0x10).ToArray(); /* cbc encrypt the dA */ AesHelper.AesEncrypt(aes, dA_out, dA_dec, 0x20); } static KIRKEngine() { kirk_init(); } // KIRK commands public static int kirk_init() { //94 90 //0x0000332 e6e050311 3000 //0x0000772 ccda50f12 2000 //0x1008010 1ef1e7101 vita return kirk_init2(Encoding.ASCII.GetBytes("Lazy Dev should have initialized!"), 33, 0xBABEF00D, 0xDEADBEEF); } public static int kirk_init2(byte[] rnd_seed, int seed_size, uint fuseid_90, uint fuseid_94) { Span temp = stackalloc byte[0x104]; // Another randomly selected data for a "key" to add to each randomization ReadOnlySpan key = stackalloc byte[] { 0x07, 0xAB, 0xEF, 0xF8, 0x96, 0x8C, 0xF3, 0xD6, 0x14, 0xE0, 0xEB, 0xB2, 0x9D, 0x8B, 0x4E, 0x74 }; uint curtime; is_kirk_initialized = true; //Set PRNG_DATA initially, otherwise use what ever uninitialized data is in the buffer if (seed_size > 0) { Span seedbuf = stackalloc byte[seed_size + 4]; RandomNumberGenerator.Fill(seedbuf); var seedheader = new KIRK_SHA1_HEADER { data_size = seed_size }; MemoryMarshal.Write(seedbuf, ref seedheader); kirk_CMD11(PRNG_DATA, seedbuf, seed_size + 4); } // Buffer.BlockCopy(PRNG_DATA, 0, header.data, 0, 0x14); PRNG_DATA.CopyTo(temp.Slice(4)); // This uses the standard C time function for portability. curtime = (uint)DateTimeOffset.Now.ToUnixTimeMilliseconds(); temp[0x18] = (byte)(curtime & 0xFF); temp[0x19] = (byte)((curtime >> 8) & 0xFF); temp[0x1A] = (byte)((curtime >> 16) & 0xFF); temp[0x1B] = (byte)((curtime >> 24) & 0xFF); // Buffer.BlockCopy(key, 0, header.data, 0x18, 0x10); key.CopyTo(temp.Slice(0x1c)); // This leaves the remainder of the 0x100 bytes in temp to whatever remains on the stack // in an uninitialized state. This should add unpredicableness to the results as well var header = new KIRK_SHA1_HEADER { data_size = 0x100 }; MemoryMarshal.Write(temp, ref header); kirk_CMD11(PRNG_DATA, temp, 0x104); //Set Fuse ID g_fuse90 = fuseid_90; g_fuse94 = fuseid_94; // Set KIRK1 main key aes_kirk1 = CreateAes(); aes_kirk1.Key = KeyVault.kirk1_key; return 0; } static int kirk_CMD0(Span outbuff, ReadOnlySpan inbuff, int size, bool generate_trash) { KIRK_CMD1_HEADER header = MemoryMarshal.AsRef(outbuff); // header_keys keys = Utils.AsRef(outbuff); int chk_size; Aes k1; Aes cmac_key; if (!is_kirk_initialized) return KIRK_NOT_INITIALIZED; inbuff[..size].CopyTo(outbuff); if (header.mode != KIRK_MODE_CMD1) return KIRK_INVALID_MODE; // FILL PREDATA WITH RANDOM DATA if (generate_trash) kirk_CMD14(outbuff[Unsafe.SizeOf()..], header.data_offset); // Make sure data is 16 aligned chk_size = header.data_size; if (chk_size % 16 != 0) chk_size += 16 - (chk_size % 16); // ENCRYPT DATA // ENCRYPT DATA using (k1 = CreateAes()) { k1.Key = header.AES_Key.ToArray(); AesHelper.AesEncrypt(k1, outbuff[(Unsafe.SizeOf() + header.data_offset)..], inbuff.Slice(Unsafe.SizeOf() + header.data_offset, header.data_size)); //byte[] encData = AesHelper.AesEncrypt(k1, inbuff.ToArray(), // Unsafe.SizeOf() + header.data_offset, // chk_size); //encData.CopyTo(outbuff.Slice(Unsafe.SizeOf() + header.data_offset)); } // CMAC HASHES using (cmac_key = CreateAes()) { cmac_key.Key = header.CMAC_key.ToArray(); //var cmac_header_hash = AesHelper.Cmac(cmac_key, outbuff.ToArray(), 0x60, 0x30); //var cmac_data_hash = AesHelper.Cmac(cmac_key, outbuff.ToArray(), 0x60, 0x30 + chk_size); Span cmac_header_hash = stackalloc byte[16]; Span cmac_data_hash = stackalloc byte[16]; AesHelper.Cmac(cmac_key, cmac_header_hash, outbuff.Slice(0x60, 0x30)); AesHelper.Cmac(cmac_key, cmac_data_hash, outbuff.Slice(0x60, 0x30 + chk_size)); cmac_header_hash.CopyTo(header.CMAC_header_hash); cmac_data_hash.CopyTo(header.CMAC_data_hash); } // ENCRYPT KEYS // AesHelper.AesEncrypt(aes_kirk1) return KIRK_OPERATION_SUCCESS; } static int kirk_CMD1(Span outbuff, ReadOnlySpan inbuff, int size) { KIRK_CMD1_HEADER header = MemoryMarshal.AsRef(inbuff); // header_keys keys; //0-15 AES key, 16-31 CMAC key Aes k1; if (size < 0x90) return KIRK_INVALID_SIZE; if (!is_kirk_initialized) return KIRK_NOT_INITIALIZED; if (header.mode != KIRK_MODE_CMD1) return KIRK_INVALID_MODE; // byte[] keytmp = AesHelper.AesDecrypt(aes_kirk1, inbuff.ToArray(), 0, 32); Span keytmp = stackalloc byte[32]; AesHelper.AesDecrypt(aes_kirk1, keytmp, inbuff, 32); if (header.ecdsa_hash == 1) { KIRK_CMD1_ECDSA_HEADER eheader = MemoryMarshal.AsRef(inbuff); var curve = ECDsaHelper.SetCurve(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b1, KeyVault.ec_N1, KeyVault.Gx1, KeyVault.Gy1); unsafe { var ecdsa = ECDsaHelper.Create(curve, KeyVault.ec_Priv1); if (!ecdsa.VerifyData(inbuff.Slice(0x60, 0x30), eheader.header_sig.ToArray(), HashAlgorithmName.SHA1)) { return KIRK_HEADER_HASH_INVALID; } ecdsa = ECDsaHelper.Create(curve, KeyVault.ec_Priv1); if (!ecdsa.VerifyData(inbuff[0x60..size], eheader.data_sig.ToArray(), HashAlgorithmName.SHA1)) { return KIRK_DATA_HASH_INVALID; } } } else { int ret = kirk_CMD10(inbuff, size); if (ret != KIRK_OPERATION_SUCCESS) return ret; } k1 = CreateAes(); k1.Key = keytmp.Slice(0, 16).ToArray(); //var outtmp = AesHelper.AesDecrypt(k1, inbuff.ToArray(), Unsafe.SizeOf() + header.data_offset, header.data_size); // Buffer.BlockCopy(outtmp, 0, outbuff, 0, (int)header.data_size); AesHelper.AesDecrypt(k1, outbuff, inbuff.Slice(Unsafe.SizeOf() + header.data_offset), header.data_size); return KIRK_OPERATION_SUCCESS; } static int kirk_CMD4(Span outbuff, ReadOnlySpan inbuff, int size) { KIRK_AES128CBC_HEADER header = MemoryMarshal.AsRef(inbuff); byte[] key; Aes aes; if (!is_kirk_initialized) return KIRK_NOT_INITIALIZED; if (header.mode != KIRK_MODE_ENCRYPT_CBC) return KIRK_INVALID_MODE; if (header.data_size == 0) return KIRK_DATA_SIZE_ZERO; // size = size - 20; using (aes = CreateAes()) { key = kirk_4_7_get_key(header.keyseed); // Set the key aes.Key = key; //var enc = AesHelper.AesEncrypt(aes, inbuff.ToArray(), Unsafe.SizeOf(), size); // Buffer.BlockCopy(enc, 0, outbuff, Marshal.SizeOf() + outOffset, size); //enc.AsSpan(0, size).CopyTo(outbuff.Slice(Unsafe.SizeOf(), size)); AesHelper.AesEncrypt(aes, outbuff.Slice(Unsafe.SizeOf()), inbuff.Slice(Unsafe.SizeOf(), header.data_size)); } return KIRK_OPERATION_SUCCESS; } static int kirk_CMD7(Span outbuff, ReadOnlySpan inbuff, int size) { KIRK_AES128CBC_HEADER header = MemoryMarshal.AsRef(inbuff); byte[] key; Aes aes; if (!is_kirk_initialized) return KIRK_NOT_INITIALIZED; if (header.mode != KIRK_MODE_DECRYPT_CBC) return KIRK_INVALID_MODE; if (header.data_size == 0) return KIRK_DATA_SIZE_ZERO; using (aes = CreateAes()) { key = kirk_4_7_get_key(header.keyseed); // Set the key aes.Key = key; //var enc = AesHelper.AesDecrypt(aes, inbuff.ToArray(), Unsafe.SizeOf(), header.data_size); //Buffer.BlockCopy(enc, 0, outbuff, outOffset, header.data_size); //enc.AsSpan(0, header.data_size).CopyTo(outbuff); AesHelper.AesDecrypt(aes, outbuff, inbuff.Slice(Unsafe.SizeOf()), header.data_size); } return KIRK_OPERATION_SUCCESS; } static int kirk_CMD10(ReadOnlySpan inbuff, int insize) { KIRK_CMD1_HEADER header = MemoryMarshal.AsRef(inbuff); // header_keys keys; //0-15 AES key, 16-31 CMAC key Span cmac_header_hash = stackalloc byte[16]; Span cmac_data_hash = stackalloc byte[16]; using Aes cmac_key = CreateAes(); int chk_size; if (!is_kirk_initialized) return KIRK_NOT_INITIALIZED; if (!(header.mode == KIRK_MODE_CMD1 || header.mode == KIRK_MODE_CMD2 || header.mode == KIRK_MODE_CMD3)) return KIRK_INVALID_MODE; if (header.data_size == 0) return KIRK_DATA_SIZE_ZERO; if (header.mode == KIRK_MODE_CMD1) { // var decdata = AesHelper.AesDecrypt(aes_kirk1, inbuff.ToArray(), 0, 32); Span tmpKey = stackalloc byte[32]; AesHelper.AesDecrypt(aes_kirk1, tmpKey, inbuff, 32); cmac_key.Key = tmpKey[16..].ToArray(); //cmac_header_hash = AesHelper.Cmac(cmac_key, inbuff.ToArray(), 0x60, 0x30); AesHelper.Cmac(cmac_key, cmac_header_hash, inbuff.Slice(0x60, 0x30)); // Make sure data is 16 aligned chk_size = header.data_size; if (chk_size % 16 != 0) chk_size += 16 - (chk_size % 16); //cmac_data_hash = AesHelper.Cmac(cmac_key, inbuff.ToArray(), 0x60, 0x30 + chk_size + header.data_offset); AesHelper.Cmac(cmac_key, cmac_data_hash, inbuff.Slice(0x60, 0x30 + chk_size + header.data_offset)); if (!header.CMAC_header_hash.SequenceEqual(cmac_header_hash)) return KIRK_HEADER_HASH_INVALID; if (!header.CMAC_data_hash.SequenceEqual(cmac_data_hash)) return KIRK_DATA_HASH_INVALID; return KIRK_OPERATION_SUCCESS; } return KIRK_SIG_CHECK_INVALID; //Checks for cmd 2 & 3 not included right now } static int kirk_CMD11(Span outbuff, ReadOnlySpan inbuff, int size) { KIRK_SHA1_HEADER header = MemoryMarshal.AsRef(inbuff); if (!is_kirk_initialized) return KIRK_NOT_INITIALIZED; if (header.data_size == 0 || size == 0) return KIRK_DATA_SIZE_ZERO; //byte[] buff = new byte[header.data_size + 4]; //using (var ms = new MemoryStream(buff)) //{ // using var bw = new BinaryWriter(ms); // bw.Write(inbuff.data_size); // bw.Write(inbuff.data); //} var sha = SHA1.Create(); var hash = sha.ComputeHash(inbuff.Slice(4, header.data_size).ToArray()); // Buffer.BlockCopy(hash, 0, outbuff, 0, hash.Length); hash.CopyTo(outbuff); return KIRK_OPERATION_SUCCESS; } static int kirk_CMD12(Span outbuff, int outsize) { if (outsize != 0x3C) return KIRK_INVALID_SIZE; var (d, q) = ECDsaHelper.GenerateKey(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b2, KeyVault.ec_N2, KeyVault.Gx2, KeyVault.Gy2); d.AsSpan(0, 0x14).CopyTo(outbuff); q.X.AsSpan(0, 0x14).CopyTo(outbuff.Slice(0x14)); q.Y.AsSpan(0, 0x14).CopyTo(outbuff.Slice(0x28)); //Buffer.BlockCopy(d, 0, outbuff, 0, 0x14); //Buffer.BlockCopy(q.X, 0, outbuff, 0x14, 0x14); //Buffer.BlockCopy(q.Y, 0, outbuff, 0x28, 0x14); return KIRK_OPERATION_SUCCESS; } //static int kirk_CMD13() static int kirk_CMD14(Span outbuff, int outsize) { Span temp = stackalloc byte[0x104]; // Some randomly selected data for a "key" to add to each randomization Span key = stackalloc byte[] { 0xA7, 0x2E, 0x4C, 0xB6, 0xC3, 0x34, 0xDF, 0x85, 0x70, 0x01, 0x49, 0xFC, 0xC0, 0x87, 0xC4, 0x77 }; uint curtime; if (outsize <= 0) return KIRK_OPERATION_SUCCESS; // Buffer.BlockCopy(PRNG_DATA, 0, header.data, 0, 0x14); PRNG_DATA.CopyTo(temp.Slice(4)); // This uses the standard C time function for portability. curtime = (uint)DateTimeOffset.Now.ToUnixTimeMilliseconds(); temp[0x18] = (byte)(curtime & 0xFF); temp[0x19] = (byte)((curtime >> 8) & 0xFF); temp[0x1A] = (byte)((curtime >> 16) & 0xFF); temp[0x1B] = (byte)((curtime >> 24) & 0xFF); // Buffer.BlockCopy(key, 0, header.data, 0x18, 0x10); key.CopyTo(temp[0x1C..]); // This leaves the remainder of the 0x100 bytes in temp to whatever remains on the stack // in an uninitialized state. This should add unpredicableness to the results as well // header.data_size = 0x100; var header = new KIRK_SHA1_HEADER { data_size = 0x100 }; MemoryMarshal.Write(temp, ref header); kirk_CMD11(PRNG_DATA, temp, 0x104); while (outsize > 0) { int blockrem = outsize % 0x14; int block = outsize / 0x14; if (block > 0) { // Buffer.BlockCopy(PRNG_DATA, 0, outbuff, outoff, 0x14); PRNG_DATA.CopyTo(outbuff); outsize -= 0x14; kirk_CMD14(outbuff[0x14..], outsize); } else if (blockrem > 0) { // Buffer.BlockCopy(PRNG_DATA, 0, outbuff, outoff, blockrem); PRNG_DATA.AsSpan(0, blockrem).CopyTo(outbuff); outsize -= blockrem; } } return KIRK_OPERATION_SUCCESS; } static int kirk_CMD16(Span outbuff, int outsize, ReadOnlySpan inbuff, int insize) { byte[] dec_private = new byte[0x20]; KIRK_CMD16_BUFFER signbuf = MemoryMarshal.AsRef(inbuff); //ECDSA_SIG sig = BufferToStruct(outbuff); if (insize != 0x34) return KIRK_INVALID_SIZE; if (outsize != 0x28) return KIRK_INVALID_SIZE; decrypt_kirk16_private(dec_private, signbuf.enc_private); // Clear out the padding for safety //Array.Clear(dec_private, 0x14, 0xC); var curve = ECDsaHelper.SetCurve(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b2, KeyVault.ec_N2, KeyVault.Gx2, KeyVault.Gy2); var ecdsa = ECDsaHelper.Create(curve, dec_private.Take(0x14).ToArray()); unsafe { var hashSig = ecdsa.SignHash(signbuf.message_hash.ToArray()); // Buffer.BlockCopy(hashSig, 0, outbuff, outoffset, 0x28); hashSig.CopyTo(outbuff); } return KIRK_OPERATION_SUCCESS; } static int kirk_CMD17(ReadOnlySpan inbuff, int insize) { KIRK_CMD17_BUFFER sig = MemoryMarshal.AsRef(inbuff); if (insize != 0x64) return KIRK_INVALID_SIZE; var curve = ECDsaHelper.SetCurve(KeyVault.ec_p, KeyVault.ec_a, KeyVault.ec_b2, KeyVault.ec_N2, KeyVault.Gx2, KeyVault.Gy2); unsafe { ECDsa ecdsa = ECDsaHelper.Create(curve, sig.public_key.x, sig.public_key.y); if (ecdsa.VerifyHash(sig.message_hash, sig.signature.sig)) { return KIRK_OPERATION_SUCCESS; } } return KIRK_SIG_CHECK_INVALID; } // SCE functions public static int sceUtilsBufferCopyWithRange(Span outbuff, int outsize, ReadOnlySpan inbuff, int insize, int cmd) { switch (cmd) { case KIRK_CMD_DECRYPT_PRIVATE: return kirk_CMD1(outbuff, inbuff, insize); case KIRK_CMD_ENCRYPT_IV_0: return kirk_CMD4(outbuff, inbuff, insize); case KIRK_CMD_DECRYPT_IV_0: return kirk_CMD7(outbuff, inbuff, insize); case KIRK_CMD_PRIV_SIGN_CHECK: return kirk_CMD10(inbuff, insize); case KIRK_CMD_SHA1_HASH: return kirk_CMD11(outbuff, inbuff, insize); case KIRK_CMD_ECDSA_GEN_KEYS: return kirk_CMD12(outbuff, outsize); //case KIRK_CMD_ECDSA_MULTIPLY_POINT: return kirk_CMD13(outbuff, outoffset, outsize, inbuff, insize); case KIRK_CMD_PRNG: return kirk_CMD14(outbuff, outsize); case KIRK_CMD_ECDSA_SIGN: return kirk_CMD16(outbuff, outsize, inbuff, insize); case KIRK_CMD_ECDSA_VERIFY: return kirk_CMD17(inbuff, insize); } return -1; } } }