mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-10-02 16:58:08 -05:00
Merge pull request #385 from kwsch/b/memecrypto-winxp
B/memecrypto winxp
This commit is contained in:
@@ -793,6 +793,15 @@ private void openSAV(SaveFile sav, string path)
|
||||
if (sav == null || sav.Version == GameVersion.Invalid)
|
||||
{ Util.Error("Invalid save file loaded. Aborting.", path); return; }
|
||||
|
||||
if (!string.IsNullOrEmpty(path)) // If path is null, this is the default save
|
||||
{
|
||||
if (sav.RequiresMemeCrypto && !MemeCrypto.CanUseMemeCrypto())
|
||||
{
|
||||
Util.Error("Your platform does not support the required cryptography components.", "In order to be able to save your changes, you must either upgrade to a newer version of Windows or disable FIPS compliance mode.");
|
||||
// Don't abort loading; user can still view save and fix checksum on another platform.
|
||||
}
|
||||
}
|
||||
|
||||
// Finish setting up the save file.
|
||||
if (sav.IndeterminateGame && sav.Generation == 3)
|
||||
{
|
||||
|
||||
@@ -426,6 +426,7 @@
|
||||
</Compile>
|
||||
<Compile Include="Game\ComboItem.cs" />
|
||||
<Compile Include="Util\BigEndian.cs" />
|
||||
<Compile Include="Util\CryptoUtil.cs" />
|
||||
<Compile Include="Util\DataUtil.cs" />
|
||||
<Compile Include="Util\DateUtil.cs" />
|
||||
<Compile Include="Util\DeployUtil.cs" />
|
||||
|
||||
@@ -819,29 +819,48 @@ private void setWC6(MysteryGift wc6, int index)
|
||||
// Writeback Validity
|
||||
public override string MiscSaveChecks()
|
||||
{
|
||||
string r = "";
|
||||
var r = new StringBuilder();
|
||||
|
||||
// MemeCrypto check
|
||||
if (RequiresMemeCrypto && !MemeCrypto.CanUseMemeCrypto())
|
||||
{
|
||||
r.AppendLine("Platform does not support required cryptography providers.");
|
||||
r.AppendLine("Checksum will be broken until the file is saved using an OS without FIPS compliance enabled or a newer OS.");
|
||||
r.AppendLine();
|
||||
}
|
||||
|
||||
// FFFF checks
|
||||
byte[] FFFF = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
|
||||
for (int i = 0; i < Data.Length / 0x200; i++)
|
||||
{
|
||||
if (!FFFF.SequenceEqual(Data.Skip(i * 0x200).Take(0x200))) continue;
|
||||
r = $"0x200 chunk @ 0x{(i*0x200).ToString("X5")} is FF'd."
|
||||
+ Environment.NewLine + "Cyber will screw up (as of August 31st 2014)." + Environment.NewLine + Environment.NewLine;
|
||||
r.AppendLine($"0x200 chunk @ 0x{(i * 0x200).ToString("X5")} is FF'd.");
|
||||
r.AppendLine("Cyber will screw up (as of August 31st 2014).");
|
||||
r.AppendLine();
|
||||
|
||||
// Check to see if it is in the Pokedex
|
||||
if (i * 0x200 > PokeDex && i * 0x200 < PokeDex + 0x900)
|
||||
{
|
||||
r += "Problem lies in the Pokedex. ";
|
||||
r.Append("Problem lies in the Pokedex. ");
|
||||
if (i * 0x200 == PokeDex + 0x400)
|
||||
r += "Remove a language flag for a species < 585, ie Petilil";
|
||||
r.Append("Remove a language flag for a species < 585, ie Petilil");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
return r.ToString();
|
||||
}
|
||||
public override string MiscSaveInfo()
|
||||
{
|
||||
return Blocks.Aggregate("", (current, b) => current +
|
||||
$"{b.ID.ToString("00")}: {b.Offset.ToString("X5")}-{(b.Offset + b.Length).ToString("X5")}, {b.Length.ToString("X5")}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
public override bool RequiresMemeCrypto
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,5 +550,7 @@ public void setData(byte[] input, int Offset)
|
||||
input.CopyTo(Data, Offset);
|
||||
Edited = true;
|
||||
}
|
||||
|
||||
public virtual bool RequiresMemeCrypto { get { return false; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ internal static ushort check16(byte[] data, int blockID, ushort initial = 0)
|
||||
}
|
||||
public static byte[] Resign7(byte[] sav7)
|
||||
{
|
||||
return MemeCrypto.Resign(sav7);
|
||||
return MemeCrypto.Resign(sav7, false);
|
||||
}
|
||||
/// <summary>Calculates the 32bit checksum over an input byte array. Used in GBA save files.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
|
||||
@@ -14,7 +14,7 @@ private static byte[] AESECBEncrypt(byte[] key, byte[] data)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (var aes = new AesCryptoServiceProvider())
|
||||
using (var aes = Util.GetAesProvider())
|
||||
{
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
@@ -34,7 +34,7 @@ private static byte[] AESECBDecrypt(byte[] key, byte[] data)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (var aes = new AesCryptoServiceProvider())
|
||||
using (var aes = Util.GetAesProvider())
|
||||
{
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
@@ -204,64 +204,102 @@ private static byte[] ReverseCrypt(byte[] input)
|
||||
if (input.Length != 0x80)
|
||||
throw new ArgumentException("Invalid Memecrypto decryption byte[]!");
|
||||
|
||||
var sha1 = new SHA1CryptoServiceProvider();
|
||||
using (var sha1 = Util.GetSHA1Provider())
|
||||
{
|
||||
byte[] PubKeyDer = "307C300D06092A864886F70D0101010500036B003068026100B61E192091F90A8F76A6EAAA9A3CE58C863F39AE253F037816F5975854E07A9A456601E7C94C29759FE155C064EDDFA111443F81EF1A428CF6CD32F9DAC9D48E94CFB3F690120E8E6B9111ADDAF11E7C96208C37C0143FF2BF3D7E831141A9730203010001".ToByteArray();
|
||||
byte[] enc = new byte[0x60];
|
||||
Array.Copy(input, 0x20, enc, 0, 0x60);
|
||||
|
||||
byte[] PubKeyDer = "307C300D06092A864886F70D0101010500036B003068026100B61E192091F90A8F76A6EAAA9A3CE58C863F39AE253F037816F5975854E07A9A456601E7C94C29759FE155C064EDDFA111443F81EF1A428CF6CD32F9DAC9D48E94CFB3F690120E8E6B9111ADDAF11E7C96208C37C0143FF2BF3D7E831141A9730203010001".ToByteArray();
|
||||
byte[] enc = new byte[0x60];
|
||||
Array.Copy(input, 0x20, enc, 0, 0x60);
|
||||
byte[] keybuf = new byte[PubKeyDer.Length + 0x20];
|
||||
Array.Copy(PubKeyDer, keybuf, PubKeyDer.Length);
|
||||
Array.Copy(input, 0, keybuf, PubKeyDer.Length, 0x20);
|
||||
byte[] key = sha1.ComputeHash(keybuf).Take(0x10).ToArray();
|
||||
|
||||
byte[] keybuf = new byte[PubKeyDer.Length + 0x20];
|
||||
Array.Copy(PubKeyDer, keybuf, PubKeyDer.Length);
|
||||
Array.Copy(input, 0, keybuf, PubKeyDer.Length, 0x20);
|
||||
byte[] key = sha1.ComputeHash(keybuf).Take(0x10).ToArray();
|
||||
|
||||
byte[] RSA = RSAEncrypt(enc);
|
||||
var dec = MemeCryptoAESDecrypt(key, RSA);
|
||||
if (sha1.ComputeHash(dec).Take(0x8).SequenceEqual(dec.Skip(0x58)))
|
||||
return dec;
|
||||
RSA[0] |= 0x80;
|
||||
dec = MemeCryptoAESDecrypt(key, RSA);
|
||||
if (sha1.ComputeHash(dec).Take(0x8).SequenceEqual(dec.Skip(0x58)))
|
||||
return dec;
|
||||
byte[] RSA = RSAEncrypt(enc);
|
||||
var dec = MemeCryptoAESDecrypt(key, RSA);
|
||||
if (sha1.ComputeHash(dec).Take(0x8).SequenceEqual(dec.Skip(0x58)))
|
||||
return dec;
|
||||
RSA[0] |= 0x80;
|
||||
dec = MemeCryptoAESDecrypt(key, RSA);
|
||||
if (sha1.ComputeHash(dec).Take(0x8).SequenceEqual(dec.Skip(0x58)))
|
||||
return dec;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] Resign(byte[] sav7)
|
||||
/// <summary>
|
||||
/// Resigns save data.
|
||||
/// </summary>
|
||||
/// <param name="sav7">The save data to resign.</param>
|
||||
/// <param name="throwIfUnsupported">If true, throw an <see cref="InvalidOperationException"/> if MemeCrypto is unsupported. If false, calling this function will have no effect.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown if the current platform has FIPS mode enabled on a platform that does not support the required crypto service providers.</exception>
|
||||
/// <returns>The resigned save data.</returns>
|
||||
public static byte[] Resign(byte[] sav7, bool throwIfUnsupported = true)
|
||||
{
|
||||
if (sav7 == null || sav7.Length != 0x6BE00)
|
||||
return null;
|
||||
return null;
|
||||
|
||||
var sha1 = new SHA1CryptoServiceProvider();
|
||||
try
|
||||
{
|
||||
byte[] outSav = (byte[])sav7.Clone();
|
||||
|
||||
byte[] outSav = (byte[])sav7.Clone();
|
||||
using (var sha1 = Util.GetSHA1Provider())
|
||||
{
|
||||
using (var sha256 = Util.GetSHA256Provider())
|
||||
{
|
||||
byte[] CurSig = new byte[0x80];
|
||||
Array.Copy(sav7, 0x6BB00, CurSig, 0, 0x80);
|
||||
|
||||
byte[] CurSig = new byte[0x80];
|
||||
Array.Copy(sav7, 0x6BB00, CurSig, 0, 0x80);
|
||||
byte[] ChecksumTable = new byte[0x140];
|
||||
Array.Copy(sav7, 0x6BC00, ChecksumTable, 0, 0x140);
|
||||
byte[] Hash = sha256.ComputeHash(ChecksumTable);
|
||||
|
||||
byte[] ChecksumTable = new byte[0x140];
|
||||
Array.Copy(sav7, 0x6BC00, ChecksumTable, 0, 0x140);
|
||||
byte[] Hash = new SHA256CryptoServiceProvider().ComputeHash(ChecksumTable);
|
||||
byte[] PubKeyDer = "307C300D06092A864886F70D0101010500036B003068026100B61E192091F90A8F76A6EAAA9A3CE58C863F39AE253F037816F5975854E07A9A456601E7C94C29759FE155C064EDDFA111443F81EF1A428CF6CD32F9DAC9D48E94CFB3F690120E8E6B9111ADDAF11E7C96208C37C0143FF2BF3D7E831141A9730203010001".ToByteArray();
|
||||
|
||||
byte[] PubKeyDer = "307C300D06092A864886F70D0101010500036B003068026100B61E192091F90A8F76A6EAAA9A3CE58C863F39AE253F037816F5975854E07A9A456601E7C94C29759FE155C064EDDFA111443F81EF1A428CF6CD32F9DAC9D48E94CFB3F690120E8E6B9111ADDAF11E7C96208C37C0143FF2BF3D7E831141A9730203010001".ToByteArray();
|
||||
byte[] keybuf = new byte[PubKeyDer.Length + 0x20];
|
||||
Array.Copy(PubKeyDer, keybuf, PubKeyDer.Length);
|
||||
Array.Copy(Hash, 0, keybuf, PubKeyDer.Length, 0x20);
|
||||
byte[] key = sha1.ComputeHash(keybuf).Take(0x10).ToArray();
|
||||
|
||||
byte[] keybuf = new byte[PubKeyDer.Length + 0x20];
|
||||
Array.Copy(PubKeyDer, keybuf, PubKeyDer.Length);
|
||||
Array.Copy(Hash, 0, keybuf, PubKeyDer.Length, 0x20);
|
||||
byte[] key = sha1.ComputeHash(keybuf).Take(0x10).ToArray();
|
||||
byte[] secret = ReverseCrypt(CurSig) ?? new byte[0x60];
|
||||
byte[] secretWorkBuf = new byte[0x78];
|
||||
Hash.CopyTo(secretWorkBuf, 0);
|
||||
Array.Copy(secret, 0, secretWorkBuf, 0x20, 0x58);
|
||||
Array.Copy(sha1.ComputeHash(secretWorkBuf), 0, secret, 0x58, 8);
|
||||
|
||||
byte[] secret = ReverseCrypt(CurSig) ?? new byte[0x60];
|
||||
byte[] secretWorkBuf = new byte[0x78];
|
||||
Hash.CopyTo(secretWorkBuf, 0);
|
||||
Array.Copy(secret, 0, secretWorkBuf, 0x20, 0x58);
|
||||
Array.Copy(sha1.ComputeHash(secretWorkBuf), 0, secret, 0x58, 8);
|
||||
Hash.CopyTo(outSav, 0x6BB00);
|
||||
byte[] MemeCrypted = MemeCryptoAESEncrypt(key, secret);
|
||||
MemeCrypted[0] &= 0x7F;
|
||||
var RSA = RSADecrypt(MemeCrypted);
|
||||
Array.Copy(RSA, 0, outSav, 0x6BB20, 0x60);
|
||||
}
|
||||
}
|
||||
return outSav;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
if (throwIfUnsupported)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (byte[])sav7.Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Hash.CopyTo(outSav, 0x6BB00);
|
||||
byte[] MemeCrypted = MemeCryptoAESEncrypt(key, secret);
|
||||
MemeCrypted[0] &= 0x7F;
|
||||
var RSA = RSADecrypt(MemeCrypted);
|
||||
Array.Copy(RSA, 0, outSav, 0x6BB20, 0x60);
|
||||
|
||||
return outSav;
|
||||
public static bool CanUseMemeCrypto()
|
||||
{
|
||||
try
|
||||
{
|
||||
Util.GetSHA256Provider();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
66
PKHeX/Util/CryptoUtil.cs
Normal file
66
PKHeX/Util/CryptoUtil.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PKHeX
|
||||
{
|
||||
public partial class Util
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="SHA1CryptoServiceProvider"/>, or <see cref="SHA1Managed"/> if not supported by the current platform.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if FIPS mode is enabled on a platform that does not support <see cref="SHA1CryptoServiceProvider"/>.</exception>
|
||||
public static SHA1 GetSHA1Provider()
|
||||
{
|
||||
SHA1 provider;
|
||||
try
|
||||
{
|
||||
provider = new SHA1CryptoServiceProvider();
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
provider = new SHA1Managed();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="SHA256CryptoServiceProvider"/>, or <see cref="SHA256Managed"/> if not supported by the current platform.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if FIPS mode is enabled on a platform that does not support <see cref="SHA256CryptoServiceProvider"/>.</exception>
|
||||
public static SHA256 GetSHA256Provider()
|
||||
{
|
||||
SHA256 provider;
|
||||
try
|
||||
{
|
||||
provider = new SHA256CryptoServiceProvider();
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
provider = new SHA256Managed();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="AesCryptoServiceProvider"/>, or <see cref="AesManaged"/> if not supported by the current platform.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if FIPS mode is enabled on a platform that does not support <see cref="AesCryptoServiceProvider"/>.</exception>
|
||||
public static Aes GetAesProvider()
|
||||
{
|
||||
Aes provider;
|
||||
try
|
||||
{
|
||||
provider = new AesCryptoServiceProvider();
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
provider = new AesManaged();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user