Skip to content

Commit

Permalink
Initial Upload
Browse files Browse the repository at this point in the history
  • Loading branch information
cainan-c committed Aug 28, 2024
1 parent 61a0d7a commit e8240b9
Show file tree
Hide file tree
Showing 5 changed files with 454 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Taiko no Tatsujin: Rhythm Festival (PC/XBOX/PS5) File Decryption and Encryption Tool

Simple CLI program designed to decrypt and re-encrypt files for the "next-gen" ports of Taiko no Tatsujin: Rhythm Festival.
Credit to [TraceEntertains](https://github.com/TraceEntertains) for his work on decompiling and figuring out how this game handles encryption, along with writing the cryptgraphy code this project relies on.

```
Usage:
tns2tool.exe -e -inFile {file} [-gzip] -> Encrypt the file (optionally compress with GZIP)
tns2tool.exe -d -inFile {file} [-gzip] -> Decrypt the file (optionally decompress with GZIP)
tns2tool.exe -e -inPath {folder} [-gzip] -> Encrypt all files in the folder (optionally compress with GZIP)
tns2tool.exe -d -inPath {folder} [-gzip] -> Decrypt all files in the folder (optionally decompress with GZIP)
```
25 changes: 25 additions & 0 deletions tns2tool.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tns2tool", "tns2tool\tns2tool.csproj", "{B354F10B-3B9F-44A6-9DD4-255C6FCA6FC5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B354F10B-3B9F-44A6-9DD4-255C6FCA6FC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B354F10B-3B9F-44A6-9DD4-255C6FCA6FC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B354F10B-3B9F-44A6-9DD4-255C6FCA6FC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B354F10B-3B9F-44A6-9DD4-255C6FCA6FC5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5574CF17-39BE-4042-8D88-42300837B0E0}
EndGlobalSection
EndGlobal
212 changes: 212 additions & 0 deletions tns2tool/Cryptography.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
using System.IO.Compression;
using System.Text;
using System.Security.Cryptography;

namespace TNS2Crypto
{
public static class Cryptography
{
public static readonly string KatsuName = "V1EzaGNwdEZTNFhrcHVnQ24zZldBM3FjUHpTYlk2Zm1f";
public static readonly string DonName = "VlIzMkpoR1YzNG5hdUdNTGJORkh2UGM1WHFBa3dqaUJf";

// don count is used in key
public static readonly int DonCount = 0xffe156;

// katsu count is used in iv
public static readonly int KatsuCount = 0xfeac1;

public static byte[] ReadAllAesAndGZipBytes(string path)
{
byte[] byteBuffer = ReadAllAesBytes(path);
using MemoryStream dataStream = new(byteBuffer);
using MemoryStream outStream = new();
using GZipStream gzipStream = new(dataStream, CompressionMode.Decompress);

gzipStream.CopyTo(outStream);
// outStream.Position = 0 is not required due to the use of outStream.ToArray()

// we can reuse the byteBuffer variable as the array is being overwritten entirely
byteBuffer = outStream.ToArray();
return byteBuffer;
}

public static byte[] ReadAllAesBytes(string path)
{
byte[] fileBuffer = File.ReadAllBytes(path);

int count = fileBuffer.Length;
byte[] outBuffer = new byte[count];

using Aes aes = Aes.Create();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;

// I tried both PKCS7 | Zeros and PKCS7, PKCS7 | Zeros works but PKCS7 doesn't even though the game itself uses it lol
aes.Padding = PaddingMode.Zeros;

int kSize = aes.KeySize;
int bSize = aes.BlockSize;

string name = Path.GetFileNameWithoutExtension(path);

CreateKey(kSize, out byte[] keyOut, bSize, out byte[] ivOut, name);

aes.IV = ivOut;
aes.Key = keyOut;

using ICryptoTransform transform = aes.CreateDecryptor();
using MemoryStream fileBufferStream = new(fileBuffer, 0, count);
using CryptoStream decryptStream = new(fileBufferStream, transform, CryptoStreamMode.Read);
using BinaryReader binaryReader = new(decryptStream);

binaryReader.Read(outBuffer, 0, count);

transform.Dispose();
return outBuffer;
}

// this method is half broken and uses deprecated code
//public static byte[] ReadAllAesBytes(string path)
//{
// byte[] fileBuffer = File.ReadAllBytes(path);

// int count = fileBuffer.Length;
// byte[] outBuffer = new byte[count];

// using AesCryptoServiceProvider cryptoProvider = new()
// {
// BlockSize = 128,
// KeySize = 256,
// Mode = CipherMode.CBC,
// Padding = PaddingMode.PKCS7
// };

// int kSize = cryptoProvider.KeySize;
// int bSize = cryptoProvider.BlockSize;

// string name = Path.GetFileNameWithoutExtension(path);

// CreateKey(kSize, out byte[] keyOut, bSize, out byte[] ivOut, name);

// cryptoProvider.IV = ivOut;
// cryptoProvider.Key = keyOut;

// using ICryptoTransform transform = cryptoProvider.CreateDecryptor();
// using MemoryStream fileBufferStream = new(fileBuffer, 0, count);
// using CryptoStream decryptStream = new(fileBufferStream, transform, CryptoStreamMode.Read);
// using BinaryReader binaryReader = new(decryptStream);

// binaryReader.Read(outBuffer, 0, count);

// transform.Dispose();
// return outBuffer;
//}

public static void CreateKey(int kSize, out byte[] keyOut, int bSize, out byte[] ivOut, string name)
{
int donCount;
int katsuCount;
string donNameString;
byte[] donNameBuffer;
string katsuNameString;
byte[] katsuNameBuffer;

donNameBuffer = Convert.FromBase64String(DonName);
donNameString = string.Concat(Encoding.UTF8.GetString(donNameBuffer), name);

katsuNameBuffer = Convert.FromBase64String(KatsuName);
katsuNameString = string.Concat(Encoding.UTF8.GetString(katsuNameBuffer), name);

katsuCount = KatsuCount;
donCount = DonCount;

donNameBuffer = Encoding.UTF8.GetBytes(donNameString);
katsuNameBuffer = Encoding.UTF8.GetBytes(katsuNameString);

keyOut = CurryYomogi_Match(donNameBuffer, katsuNameBuffer, donCount ^ 0xffffff, (int)((kSize >> 0x1f & 7U) + kSize) >> 3);
ivOut = CurryYomogi_Match(donNameBuffer, katsuNameBuffer, katsuCount ^ 0xfffff, (int)((bSize >> 0x1f & 7U) + bSize) >> 3);
return;
}

private static byte[] CurryYomogi_Match(byte[] a, byte[] b, int c, int d)
{
byte[] array;
byte[] shiftArray = new byte[b.Length + 4];
byte[] finalShiftArray;
using MemoryStream memStream = new();

using HMACSHA256 hS256 = new(a);
int iVar2 = (int)((hS256.HashSize >> 0x1f & 7U) + hS256.HashSize) >> 3;

int iVar9 = iVar2 + 1;
if ((hS256.HashSize & 7) == 0)
{
iVar9 = iVar2;
}

try
{
if ((d <= iVar9 * 0xffffffff) && d > -1)
{
iVar2 = d / iVar9 + 1;
if (d % iVar9 == 0)
{
iVar2 = d / iVar9;
}

Buffer.BlockCopy(b, 0, shiftArray, 0, b.Length);

int calcLoopIterator = 0;
while (calcLoopIterator < iVar2)
{
iVar9 = calcLoopIterator + 1;

if (shiftArray.Length <= b.Length) return shiftArray;
shiftArray[b.Length] = (byte)((uint)iVar9 >> 24);
shiftArray[b.Length + 1] = (byte)((uint)iVar9 >> 16);
shiftArray[b.Length + 2] = (byte)((uint)iVar9 >> 8);
shiftArray[b.Length + 3] = (byte)(calcLoopIterator + 1);

array = hS256.ComputeHash(shiftArray);
Array.Clear(shiftArray, b.Length, 4);
finalShiftArray = array;

for (iVar9 = 1; iVar9 < c; iVar9++)
{
finalShiftArray = hS256.ComputeHash(finalShiftArray);
int calcLoopIterator2 = 0;
while (true)
{
if (array.Length <= calcLoopIterator2) break;
array[calcLoopIterator2] ^= finalShiftArray[calcLoopIterator2];
calcLoopIterator2++;
}
}
memStream.Position = 0;
memStream.Write(array, 0, array.Length);

Array.Clear(finalShiftArray, 0, finalShiftArray.Length);
Array.Clear(array, 0, array.Length);
calcLoopIterator++;
}
finalShiftArray = new byte[d];
memStream.Position = 0;
memStream.Read(finalShiftArray, 0, d);
memStream.Position = 0;

memStream.Close();
Array.Clear(shiftArray, 0, shiftArray.Length);
return finalShiftArray;
}
}
catch (ArgumentOutOfRangeException ex)
{
Console.WriteLine(ex.Message);
throw;
}

return shiftArray;
}
}
}
Loading

0 comments on commit e8240b9

Please sign in to comment.