public static byte[] SignMemeData(byte[] input, MemeKeyIndex keyIndex = MemeKeyIndex.PokedexAndSaveFile) { // Validate Input if (input.Length < 0x60) { throw new ArgumentException("Cannot memesign a buffer less than 0x60 bytes in size!"); } var memekey = new MemeKey(keyIndex); if (!memekey.CanResign) { throw new ArgumentException("Cannot sign with the specified memekey!"); } var output = (byte[])input.Clone(); // Copy in the SHA1 signature using (var sha1 = SHA1.Create()) { Array.Copy(sha1.ComputeHash(input, 0, input.Length - 8), 0, output, output.Length - 8, 8); } // Perform AES operations output = memekey.AesEncrypt(output); var sigBuffer = new byte[0x60]; Array.Copy(output, output.Length - 0x60, sigBuffer, 0, 0x60); sigBuffer[0] &= 0x7F; sigBuffer = memekey.RsaPrivate(sigBuffer); sigBuffer.CopyTo(output, output.Length - 0x60); return(output); }
public static bool VerifyMemeData(byte[] input, out byte[] output, MemeKeyIndex keyIndex) { output = null; if (input.Length < 0x60) { return(false); } var memekey = new MemeKey(keyIndex); output = (byte[])input.Clone(); var sigBuffer = new byte[0x60]; Array.Copy(input, input.Length - 0x60, sigBuffer, 0, 0x60); sigBuffer = memekey.RsaPublic(sigBuffer); using (var sha1 = SHA1.Create()) foreach (var orVal in new byte[] { 0, 0x80 }) { sigBuffer[0x0] |= orVal; sigBuffer.CopyTo(output, output.Length - 0x60); memekey.AesDecrypt(output).CopyTo(output, 0); // Check for 8-byte equality. if (BitConverter.ToUInt64(sha1.ComputeHash(output, 0, output.Length - 0x8), 0) == BitConverter.ToUInt64(output, output.Length - 0x8)) { return(true); } } output = null; return(false); }
public static bool VerifyMemeData(byte[] input, out byte[] output, MemeKeyIndex keyIndex) { if (input.Length < 0x60) { output = input; return(false); } var key = new MemeKey(keyIndex); output = (byte[])input.Clone(); var sigBuffer = key.RsaPublic(input.SliceEnd(input.Length - 0x60)); using var sha1 = SHA1.Create(); if (DecryptCompare(output, sigBuffer, key, sha1)) { return(true); } sigBuffer[0x0] |= 0x80; if (DecryptCompare(output, sigBuffer, key, sha1)) { return(true); } output = input; return(false); }
private static byte[] GetMemeData(MemeKeyIndex key) { return(key switch { MemeKeyIndex.LocalWireless => DER_LW, MemeKeyIndex.FriendlyCompetition => DER_0, MemeKeyIndex.LiveCompetition => DER_1, MemeKeyIndex.RentalTeam => DER_2, MemeKeyIndex.PokedexAndSaveFile => DER_3, MemeKeyIndex.GaOle => DER_4, MemeKeyIndex.MagearnaEvent => DER_5, MemeKeyIndex.MoncolleGet => DER_6, MemeKeyIndex.IslandScanEventSpecial => DER_7, MemeKeyIndex.TvTokyoDataBroadcasting => DER_8, MemeKeyIndex.CapPikachuEvent => DER_9, MemeKeyIndex.Unknown10 => DER_A, MemeKeyIndex.Unknown11 => DER_B, MemeKeyIndex.Unknown12 => DER_C, MemeKeyIndex.Unknown13 => DER_D, _ => throw new ArgumentOutOfRangeException(nameof(key), key, null) });
// Constructor public MemeKey(MemeKeyIndex key) { DER = GetMemeData(key); var _N = DER.AsSpan(0x18, 0x61).ToArray(); var _E = DER.AsSpan(0x7B, 3).ToArray(); _N.AsSpan().Reverse(); _E.AsSpan().Reverse(); N = new BigInteger(_N); E = new BigInteger(_E); if (key == MemeKeyIndex.PokedexAndSaveFile) { var _D = D_3.AsSpan().ToArray(); _D.AsSpan().Reverse(); D = new BigInteger(_D); } else { D = INVALID; } }
// Constructor public MemeKey(MemeKeyIndex key) { DER = GetMemeData(key); var _N = new byte[0x61]; var _E = new byte[0x3]; Array.Copy(DER, 0x18, _N, 0, 0x61); Array.Copy(DER, 0x7B, _E, 0, 3); Array.Reverse(_N); N = new BigInteger(_N); Array.Reverse(_E); E = new BigInteger(_E); if (key == MemeKeyIndex.PokedexAndSaveFile) { var _D = (byte[])D_3.Clone(); Array.Reverse(_D); D = new BigInteger(_D); } else { D = INVALID; } }
// Constructor public MemeKey(MemeKeyIndex key) { GetMemeData(key, out byte[] d, out byte[] der); DER = der; var _N = new byte[0x61]; var _E = new byte[0x3]; Array.Copy(der, 0x18, _N, 0, 0x61); Array.Copy(der, 0x7B, _E, 0, 3); Array.Reverse(_N); N = new BigInteger(_N); Array.Reverse(_E); E = new BigInteger(_E); if (d != null) { var _D = (byte[])d.Clone(); Array.Reverse(_D); D = new BigInteger(_D); } else { D = INVALID; } }
private static byte[] GetMemeData(MemeKeyIndex key) => key switch {
public static bool VerifyMemeData(byte[] input, out byte[] output, int offset, int length, MemeKeyIndex keyIndex) { var data = new byte[length]; Array.Copy(input, offset, data, 0, length); if (VerifyMemeData(data, out output, keyIndex)) { var newOutput = (byte[])input.Clone(); output.CopyTo(newOutput, offset); output = newOutput; return(true); } output = null; return(false); }
// Helper Method to retrieve data for loading private static void GetMemeData(MemeKeyIndex key, out byte[] d, out byte[] der) { d = null; switch (key) { case MemeKeyIndex.LocalWireless: der = DER_LW; break; case MemeKeyIndex.FriendlyCompetition: der = DER_0; break; case MemeKeyIndex.LiveCompetition: der = DER_1; break; case MemeKeyIndex.RentalTeam: der = DER_2; break; case MemeKeyIndex.PokedexAndSaveFile: der = DER_3; d = D_3; break; case MemeKeyIndex.GaOle: der = DER_4; break; case MemeKeyIndex.MagearnaEvent: der = DER_5; break; case MemeKeyIndex.MoncolleGet: der = DER_6; break; case MemeKeyIndex.IslandScanEventSpecial: der = DER_7; break; case MemeKeyIndex.TvTokyoDataBroadcasting: der = DER_8; break; case MemeKeyIndex.CapPikachuEvent: der = DER_9; break; case MemeKeyIndex.Unknown10: der = DER_A; break; case MemeKeyIndex.Unknown11: der = DER_B; break; case MemeKeyIndex.Unknown12: der = DER_C; break; case MemeKeyIndex.Unknown13: der = DER_D; break; default: throw new ArgumentOutOfRangeException(nameof(key), key, null); } }
public void TestVerifyKnownKeys(MemeKeyIndex keyIndex, byte[] key) { MemeCrypto.VerifyMemeData(key, out _, keyIndex).Should().BeTrue("because they key should be valid"); }