public virtual int hleSdSetMember(SD_Ctx2 ctx, sbyte[] data, int Length) { if (Length == 0) { return 0; } if ((Length & 0xF) != 0) { return -1; } // Parse the data in 0x800 blocks first. int index = 0; if (Length >= 0x800) { for (index = 0; Length >= 0x800; index += 0x800) { cryptMember(ctx, data, index, 0x800); Length -= 0x800; } } // Finally parse the rest of the data. if (Length >= 0x10) { cryptMember(ctx, data, index, Length); } return 0; }
public virtual int hleSdCleanList(SD_Ctx2 ctx) { ctx.mode = 0; ctx.unk = 0; for (int i = 0; i < 0x10; i++) { ctx.buf[i] = 0; } return 0; }
public virtual sbyte[] DecryptSavedata(sbyte[] buf, int size, sbyte[] key) { // Initialize the context structs. int sdDecMode; SD_Ctx1 ctx1 = new SD_Ctx1(); SD_Ctx2 ctx2 = new SD_Ctx2(); // Setup the buffers. int alignedSize = (((size + 0xF) >> 4) << 4) - 0x10; sbyte[] decbuf = new sbyte[size - 0x10]; sbyte[] tmpbuf = new sbyte[alignedSize]; // Set the decryption mode. if (isNullKey(key)) { sdDecMode = 1; } else { // After firmware version 2.7.1 the decryption mode used is 5. // Note: Due to a mislabel, 3 games from firmware 2.8.1 (Sonic Rivals, // Star Trek: Tactical Assault and Brothers in Arms: D-Day) // still use the decryption mode 3. if (Emulator.Instance.FirmwareVersion > 271 && !((State.discId.Equals("ULUS10195") || State.discId.Equals("ULES00622")) || (State.discId.Equals("ULUS10193") || State.discId.Equals("ULES00608")) || (State.discId.Equals("ULUS10150") || State.discId.Equals("ULES00623")))) { sdDecMode = 5; } else { sdDecMode = 3; } } // Perform the decryption. hleSdSetIndex(ctx1, sdDecMode); hleSdCreateList(ctx2, sdDecMode, 2, buf, key); hleSdRemoveValue(ctx1, buf, 0x10); Array.Copy(buf, 0x10, tmpbuf, 0, size - 0x10); hleSdRemoveValue(ctx1, tmpbuf, alignedSize); hleSdSetMember(ctx2, tmpbuf, alignedSize); // Clear context 2. hleSdCleanList(ctx2); // Copy back the data. Array.Copy(tmpbuf, 0, decbuf, 0, size - 0x10); return decbuf; }
public virtual sbyte[] EncryptSavedata(sbyte[] buf, int size, sbyte[] key) { // Initialize the context structs. int sdEncMode; SD_Ctx1 ctx1 = new SD_Ctx1(); SD_Ctx2 ctx2 = new SD_Ctx2(); // Setup the buffers. int alignedSize = ((size + 0xF) >> 4) << 4; sbyte[] tmpbuf1 = new sbyte[alignedSize + 0x10]; sbyte[] tmpbuf2 = new sbyte[alignedSize]; sbyte[] hash = new sbyte[0x10]; // Copy the plain data to tmpbuf. Array.Copy(buf, 0, tmpbuf1, 0x10, size); // Set the encryption mode. if (isNullKey(key)) { sdEncMode = 1; } else { // After firmware version 2.7.1 the encryption mode used is 5. // Note: Due to a mislabel, 3 games from firmware 2.8.1 (Sonic Rivals, // Star Trek: Tactical Assault and Brothers in Arms: D-Day) // still use the encryption mode 3. if (Emulator.Instance.FirmwareVersion > 271 && !((State.discId.Equals("ULUS10195") || State.discId.Equals("ULES00622")) || (State.discId.Equals("ULUS10193") || State.discId.Equals("ULES00608")) || (State.discId.Equals("ULUS10150") || State.discId.Equals("ULES00623")))) { sdEncMode = 5; } else { sdEncMode = 3; } } // Generate the encryption IV (first 0x10 bytes). hleSdCreateList(ctx2, sdEncMode, 1, tmpbuf1, key); hleSdSetIndex(ctx1, sdEncMode); hleSdRemoveValue(ctx1, tmpbuf1, 0x10); Array.Copy(tmpbuf1, 0x10, tmpbuf2, 0, alignedSize); hleSdSetMember(ctx2, tmpbuf2, alignedSize); // Clear extra bytes. for (int i = 0; i < (alignedSize - size); i++) { tmpbuf2[size + i] = 0; } // Encrypt the data. hleSdRemoveValue(ctx1, tmpbuf2, alignedSize); // Copy back the encrypted data + IV. for (int i = 0; i < (tmpbuf1.Length - 0x10); i++) { tmpbuf1[0x10 + i] = 0; } Array.Copy(tmpbuf2, 0, tmpbuf1, 0x10, alignedSize); Array.Copy(tmpbuf1, 0, buf, 0, buf.Length); // Clear context 2. hleSdCleanList(ctx2); // Generate a file hash for this data. hleSdGetLastIndex(ctx1, hash, key); return hash; }
public virtual int hleSdCreateList(SD_Ctx2 ctx, int encMode, int genMode, sbyte[] data, sbyte[] key) { // If the key is not a 16-byte key, return an error. if (!isNullKey(key) && key.Length < 0x10) { return -1; } // Set the mode and the unknown parameters. ctx.mode = encMode; ctx.unk = 0x1; // Key generator mode 0x1 (encryption): use an encrypted pseudo random number before XORing the data with the given key. if (genMode == 0x1) { sbyte[] header = new sbyte[0x24]; sbyte[] seed = new sbyte[0x14]; // Generate SHA-1 to act as seed for encryption. ByteBuffer bSeed = ByteBuffer.wrap(seed); kirk.hleUtilsBufferCopyWithRange(bSeed, 0x14, null, 0, KIRK.PSP_KIRK_CMD_PRNG); // Propagate SHA-1 in kirk header. Array.Copy(bSeed.array(), 0, header, 0, 0x14); Array.Copy(bSeed.array(), 0, header, 0x14, 0x10); header[0x20] = 0; header[0x21] = 0; header[0x22] = 0; header[0x23] = 0; // Encryption mode 0x1: encrypt with KIRK CMD4 and XOR with the given key. if (ctx.mode == 0x1) { ScrambleSD(header, 0x10, 0x4, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); Array.Copy(header, 0x14, ctx.buf, 0, 0x10); Array.Copy(header, 0x14, data, 0, 0x10); // If the key is not null, XOR the hash with it. if (!isNullKey(key)) { ctx.buf = xorKey(ctx.buf, 0, key, 0, 0x10); } return 0; } else if (ctx.mode == 0x2) { // Encryption mode 0x2: encrypt with KIRK CMD5 and XOR with the given key. ScrambleSD(header, 0x10, 0x100, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT_FUSE); Array.Copy(header, 0x14, ctx.buf, 0, 0x10); Array.Copy(header, 0x14, data, 0, 0x10); // If the key is not null, XOR the hash with it. if (!isNullKey(key)) { ctx.buf = xorKey(ctx.buf, 0, key, 0, 0x10); } return 0; } else if (ctx.mode == 0x3) { // Encryption mode 0x3: XOR with SD keys, encrypt with KIRK CMD4 and XOR with the given key. header = xorHash(header, 0x14, KeyVault.sdHashKey3, 0, 0x10); ScrambleSD(header, 0x10, 0xE, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); header = xorHash(header, 0x14, KeyVault.sdHashKey4, 0, 0x10); Array.Copy(header, 0x14, ctx.buf, 0, 0x10); Array.Copy(header, 0x14, data, 0, 0x10); // If the key is not null, XOR the hash with it. if (!isNullKey(key)) { ctx.buf = xorKey(ctx.buf, 0, key, 0, 0x10); } return 0; } else if (ctx.mode == 0x4) { // Encryption mode 0x4: XOR with SD keys, encrypt with KIRK CMD5 and XOR with the given key. header = xorHash(header, 0x14, KeyVault.sdHashKey3, 0, 0x10); ScrambleSD(header, 0x10, 0x100, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT_FUSE); header = xorHash(header, 0x14, KeyVault.sdHashKey4, 0, 0x10); Array.Copy(header, 0x14, ctx.buf, 0, 0x10); Array.Copy(header, 0x14, data, 0, 0x10); // If the key is not null, XOR the hash with it. if (!isNullKey(key)) { ctx.buf = xorKey(ctx.buf, 0, key, 0, 0x10); } return 0; } else if (ctx.mode == 0x6) { // Encryption mode 0x6: XOR with new SD keys, encrypt with KIRK CMD5 and XOR with the given key. header = xorHash(header, 0x14, KeyVault.sdHashKey6, 0, 0x10); ScrambleSD(header, 0x10, 0x100, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT_FUSE); header = xorHash(header, 0x14, KeyVault.sdHashKey7, 0, 0x10); Array.Copy(header, 0x14, ctx.buf, 0, 0x10); Array.Copy(header, 0x14, data, 0, 0x10); // If the key is not null, XOR the hash with it. if (!isNullKey(key)) { ctx.buf = xorKey(ctx.buf, 0, key, 0, 0x10); } return 0; } else { // Encryption mode 0x5: XOR with new SD keys, encrypt with KIRK CMD4 and XOR with the given key. header = xorHash(header, 0x14, KeyVault.sdHashKey6, 0, 0x10); ScrambleSD(header, 0x10, 0x12, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); header = xorHash(header, 0x14, KeyVault.sdHashKey7, 0, 0x10); Array.Copy(header, 0x14, ctx.buf, 0, 0x10); Array.Copy(header, 0x14, data, 0, 0x10); // If the key is not null, XOR the hash with it. if (!isNullKey(key)) { ctx.buf = xorKey(ctx.buf, 0, key, 0, 0x10); } return 0; } } else if (genMode == 0x2) { // Key generator mode 0x02 (decryption): directly XOR the data with the given key. // Grab the data hash (first 16-bytes). Array.Copy(data, 0, ctx.buf, 0, 0x10); // If the key is not null, XOR the hash with it. if (!isNullKey(key)) { ctx.buf = xorKey(ctx.buf, 0, key, 0, 0x10); } return 0; } else { // Invalid mode. return -1; } }
private void cryptMember(SD_Ctx2 ctx, sbyte[] data, int data_offset, int Length) { int finalSeed; sbyte[] dataBuf = new sbyte[Length + 0x14]; sbyte[] keyBuf1 = new sbyte[0x10]; sbyte[] keyBuf2 = new sbyte[0x10]; sbyte[] hashBuf = new sbyte[0x10]; // Copy the hash stored by hleSdCreateList. Array.Copy(ctx.buf, 0, dataBuf, 0x14, 0x10); if (ctx.mode == 0x1) { // Decryption mode 0x01: decrypt the hash directly with KIRK CMD7. ScrambleSD(dataBuf, 0x10, 0x4, 5, KIRK.PSP_KIRK_CMD_DECRYPT); finalSeed = 0x53; } else if (ctx.mode == 0x2) { // Decryption mode 0x02: decrypt the hash directly with KIRK CMD8. ScrambleSD(dataBuf, 0x10, 0x100, 5, KIRK.PSP_KIRK_CMD_DECRYPT_FUSE); finalSeed = 0x53; } else if (ctx.mode == 0x3) { // Decryption mode 0x03: XOR the hash with SD keys and decrypt with KIRK CMD7. dataBuf = xorHash(dataBuf, 0x14, KeyVault.sdHashKey4, 0, 0x10); ScrambleSD(dataBuf, 0x10, 0xE, 5, KIRK.PSP_KIRK_CMD_DECRYPT); dataBuf = xorHash(dataBuf, 0, KeyVault.sdHashKey3, 0, 0x10); finalSeed = 0x57; } else if (ctx.mode == 0x4) { // Decryption mode 0x04: XOR the hash with SD keys and decrypt with KIRK CMD8. dataBuf = xorHash(dataBuf, 0x14, KeyVault.sdHashKey4, 0, 0x10); ScrambleSD(dataBuf, 0x10, 0x100, 5, KIRK.PSP_KIRK_CMD_DECRYPT_FUSE); dataBuf = xorHash(dataBuf, 0, KeyVault.sdHashKey3, 0, 0x10); finalSeed = 0x57; } else if (ctx.mode == 0x6) { // Decryption mode 0x06: XOR the hash with new SD keys and decrypt with KIRK CMD8. dataBuf = xorHash(dataBuf, 0x14, KeyVault.sdHashKey7, 0, 0x10); ScrambleSD(dataBuf, 0x10, 0x100, 5, KIRK.PSP_KIRK_CMD_DECRYPT_FUSE); dataBuf = xorHash(dataBuf, 0, KeyVault.sdHashKey6, 0, 0x10); finalSeed = 0x64; } else { // Decryption mode 0x05: XOR the hash with new SD keys and decrypt with KIRK CMD7. dataBuf = xorHash(dataBuf, 0x14, KeyVault.sdHashKey7, 0, 0x10); ScrambleSD(dataBuf, 0x10, 0x12, 5, KIRK.PSP_KIRK_CMD_DECRYPT); dataBuf = xorHash(dataBuf, 0, KeyVault.sdHashKey6, 0, 0x10); finalSeed = 0x64; } // Store the calculated key. Array.Copy(dataBuf, 0, keyBuf2, 0, 0x10); // Apply extra padding if ctx.unk is not 1. if (ctx.unk != 0x1) { Array.Copy(keyBuf2, 0, keyBuf1, 0, 0xC); keyBuf1[0xC] = unchecked((sbyte)((ctx.unk - 1) & 0xFF)); keyBuf1[0xD] = unchecked((sbyte)(((ctx.unk - 1) >> 8) & 0xFF)); keyBuf1[0xE] = unchecked((sbyte)(((ctx.unk - 1) >> 16) & 0xFF)); keyBuf1[0xF] = unchecked((sbyte)(((ctx.unk - 1) >> 24) & 0xFF)); } // Copy the first 0xC bytes of the obtained key and replicate them // across a new list buffer. As a terminator, add the ctx1.seed parameter's // 4 bytes (endian swapped) to achieve a full numbered list. for (int i = 0x14; i < (Length + 0x14); i += 0x10) { Array.Copy(keyBuf2, 0, dataBuf, i, 0xC); dataBuf[i + 0xC] = unchecked((sbyte)(ctx.unk & 0xFF)); dataBuf[i + 0xD] = unchecked((sbyte)((ctx.unk >> 8) & 0xFF)); dataBuf[i + 0xE] = unchecked((sbyte)((ctx.unk >> 16) & 0xFF)); dataBuf[i + 0xF] = unchecked((sbyte)((ctx.unk >> 24) & 0xFF)); ctx.unk++; } // Copy the generated hash to hashBuf. Array.Copy(dataBuf, Length + 0x04, hashBuf, 0, 0x10); // Decrypt the hash with KIRK CMD7. ScrambleSD(dataBuf, Length, finalSeed, 5, KIRK.PSP_KIRK_CMD_DECRYPT); // XOR the first 16-bytes of data with the saved key to generate a new hash. dataBuf = xorKey(dataBuf, 0, keyBuf1, 0, 0x10); // Copy back the last hash from the list to the first keyBuf. Array.Copy(hashBuf, 0, keyBuf1, 0, 0x10); // Finally, XOR the full list with the given data. xorKey(data, data_offset, dataBuf, 0, Length); }