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; }
/* * sceSd - chnnlsv.prx */ public virtual int hleSdSetIndex(SD_Ctx1 ctx, int encMode) { // Set all parameters to 0 and assign the encMode. ctx.mode = encMode; ctx.padSize = 0; for (int i = 0; i < 0x10; i++) { ctx.pad[i] = 0; } for (int i = 0; i < 0x10; i++) { ctx.key[i] = 0; } return 0; }
private sbyte[] GenerateSavedataHash(sbyte[] data, int size, int mode) { SD_Ctx1 ctx1 = new SD_Ctx1(); sbyte[] hash = new sbyte[0x10]; // Generate a new hash using a key. hleSdSetIndex(ctx1, mode); hleSdRemoveValue(ctx1, data, size); if (hleSdGetLastIndex(ctx1, hash, null) < 0) { for (int i = 0; i < 0x10; i++) { // Generate a dummy hash in case of failure. hash[i] = 1; } } return hash; }
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 hleSdGetLastIndex(SD_Ctx1 ctx, sbyte[] hash, sbyte[] key) { if (ctx.padSize > 0x10) { // Invalid key Length. return -1; } // Calculate the seed. int seed = getModeSeed(ctx.mode); // Set up the buffer. sbyte[] scrambleBuf = new sbyte[0x800 + 0x14]; // Set up necessary buffers. sbyte[] keyBuf = new sbyte[0x10]; sbyte[] resultBuf = new sbyte[0x10]; // Encrypt the buffer with KIRK CMD 4. ScrambleSD(scrambleBuf, 0x10, seed, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); // Store the generated key. Array.Copy(scrambleBuf, 0x14, keyBuf, 0, 0x10); // Apply custom padding management to the stored key. sbyte b = ((keyBuf[0] & unchecked((sbyte) 0x80)) != 0) ? unchecked((sbyte) 0x87) : 0; for (int i = 0; i < 0xF; i++) { int b1 = ((keyBuf[i] & 0xFF) << 1); int b2 = ((keyBuf[i + 1] & 0xFF) >> 7); keyBuf[i] = (sbyte)(b1 | b2); } sbyte t = (sbyte)((keyBuf[0xF] & 0xFF) << 1); keyBuf[0xF] = (sbyte)(t ^ b); if (ctx.padSize < 0x10) { sbyte bb = ((keyBuf[0] < 0)) ? unchecked((sbyte) 0x87) : 0; for (int i = 0; i < 0xF; i++) { int bb1 = ((keyBuf[i] & 0xFF) << 1); int bb2 = ((keyBuf[i + 1] & 0xFF) >> 7); keyBuf[i] = (sbyte)(bb1 | bb2); } sbyte tt = (sbyte)((keyBuf[0xF] & 0xFF) << 1); keyBuf[0xF] = (sbyte)(tt ^ bb); ctx.pad[ctx.padSize] = unchecked((sbyte) 0x80); if ((ctx.padSize + 1) < 0x10) { for (int i = 0; i < (0x10 - ctx.padSize - 1); i++) { ctx.pad[ctx.padSize + 1 + i] = 0; } } } // XOR previous pad key with new one and copy the result back to the buffer. ctx.pad = xorKey(ctx.pad, 0, keyBuf, 0, 0x10); Array.Copy(ctx.pad, 0, scrambleBuf, 0x14, 0x10); // Save the previous result key. Array.Copy(ctx.key, 0, resultBuf, 0, 0x10); // XOR the decrypted key with the result key. scrambleBuf = xorKey(scrambleBuf, 0x14, resultBuf, 0, 0x10); // Encrypt the key with KIRK CMD 4. ScrambleSD(scrambleBuf, 0x10, seed, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); // Copy back the key into the result buffer. Array.Copy(scrambleBuf, 0x14, resultBuf, 0, 0x10); // If ctx.mode is new mode 0x5 or 0x6, XOR with the new hash key 5, else, XOR with hash key 2. if ((ctx.mode == 0x5) || (ctx.mode == 0x6)) { resultBuf = xorHash(resultBuf, 0, KeyVault.sdHashKey5, 0, 0x10); } else if ((ctx.mode == 0x3) || (ctx.mode == 0x4)) { resultBuf = xorHash(resultBuf, 0, KeyVault.sdHashKey2, 0, 0x10); } // If mode is 2, 4 or 6, encrypt again with KIRK CMD 5 and then KIRK CMD 4. if ((ctx.mode == 0x2) || (ctx.mode == 0x4) || (ctx.mode == 0x6)) { // Copy the result buffer into the data buffer. Array.Copy(resultBuf, 0, scrambleBuf, 0x14, 0x10); // Encrypt with KIRK CMD 5 (seed is always 0x100). ScrambleSD(scrambleBuf, 0x10, 0x100, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT_FUSE); // Encrypt again with KIRK CMD 4. ScrambleSD(scrambleBuf, 0x10, seed, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); // Copy back into result buffer. Array.Copy(scrambleBuf, 0x14, resultBuf, 0, 0x10); } // XOR with the supplied key and encrypt with KIRK CMD 4. if (key != null) { // XOR result buffer with user key. resultBuf = xorKey(resultBuf, 0, key, 0, 0x10); // Copy the result buffer into the data buffer. Array.Copy(resultBuf, 0, scrambleBuf, 0x14, 0x10); // Encrypt with KIRK CMD 4. ScrambleSD(scrambleBuf, 0x10, seed, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); // Copy back into the result buffer. Array.Copy(scrambleBuf, 0x14, resultBuf, 0, 0x10); } // Copy back the generated hash. Array.Copy(resultBuf, 0, hash, 0, 0x10); // Clear the context fields. ctx.mode = 0; ctx.padSize = 0; for (int i = 0; i < 0x10; i++) { ctx.pad[i] = 0; } for (int i = 0; i < 0x10; i++) { ctx.key[i] = 0; } return 0; }
public virtual int hleSdRemoveValue(SD_Ctx1 ctx, sbyte[] data, int Length) { if (ctx.padSize > 0x10 || (Length < 0)) { // Invalid key or Length. return -1; } else if (((ctx.padSize + Length) <= 0x10)) { // The key hasn't been set yet. // Extract the hash from the data and set it as the key. Array.Copy(data, 0, ctx.pad, ctx.padSize, Length); ctx.padSize += Length; return 0; } else { // Calculate the seed. int seed = getModeSeed(ctx.mode); // Setup the buffer. sbyte[] scrambleBuf = new sbyte[0x800 + 0x14]; // Copy the previous pad key to the buffer. Array.Copy(ctx.pad, 0, scrambleBuf, 0x14, ctx.padSize); // Calculate new key Length. int kLen = ((ctx.padSize + Length) & 0x0F); if (kLen == 0) { kLen = 0x10; } // Calculate new data Length. int nLen = ctx.padSize; ctx.padSize = kLen; // Copy data's footer to make a new key. int remaining = Length - kLen; Array.Copy(data, remaining, ctx.pad, 0, kLen); // Process the encryption in 0x800 blocks. int blockSize = 0x800; for (int i = 0; i < remaining; i++) { if (nLen == blockSize) { // XOR with result and encrypt with KIRK CMD 4. scrambleBuf = xorKey(scrambleBuf, 0x14, ctx.key, 0, 0x10); ScrambleSD(scrambleBuf, blockSize, seed, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); Array.Copy(scrambleBuf, blockSize + 0x4, ctx.key, 0, 0x10); // Reset Length. nLen = 0; } // Keep copying data. scrambleBuf[0x14 + nLen] = data[i]; nLen++; } // Process any leftover data. if (nLen > 0) { scrambleBuf = xorKey(scrambleBuf, 0x14, ctx.key, 0, 0x10); ScrambleSD(scrambleBuf, nLen, seed, 0x4, KIRK.PSP_KIRK_CMD_ENCRYPT); Array.Copy(scrambleBuf, nLen + 0x4, ctx.key, 0, 0x10); } return 0; } }