// Decompiled with JetBrains decompiler // Type: libWiiSharp.Ticket // Assembly: NUS Downloader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null // MVID: DDAF9FEC-76DE-4BD8-8A6D-D7CAD5827AC6 // Assembly location: C:\dotpeek\NUS Downloader.exe using System; using System.IO; using System.Security.Cryptography; namespace libWiiSharp { public class Ticket : IDisposable { private byte newKeyIndex; private byte[] decryptedTitleKey = new byte[16]; private bool fakeSign; private bool titleKeyChanged; private byte[] newEncryptedTitleKey = new byte[0]; private bool reDecrypt; private uint signatureExponent = 65537; private byte[] signature = new byte[256]; private byte[] padding = new byte[60]; private byte[] issuer = new byte[64]; private byte[] unknown = new byte[63]; private byte[] encryptedTitleKey = new byte[16]; private byte unknown2; private ulong ticketId; private uint consoleId; private ulong titleId; private ushort unknown3 = ushort.MaxValue; private ushort numOfDlc; private ulong unknown4; private byte padding2; private byte commonKeyIndex; private byte[] unknown5 = new byte[48]; private byte[] unknown6 = new byte[32]; private ushort padding3; private uint enableTimeLimit; private uint timeLimit; private byte[] padding4 = new byte[88]; private bool dsitik; private bool isDisposed; public byte[] TitleKey { get => this.decryptedTitleKey; set { this.decryptedTitleKey = value; this.titleKeyChanged = true; this.reDecrypt = false; } } public CommonKeyType CommonKeyIndex { get => (CommonKeyType) this.newKeyIndex; set => this.newKeyIndex = (byte) value; } public ulong TicketID { get => this.ticketId; set => this.ticketId = value; } public uint ConsoleID { get => this.consoleId; set => this.consoleId = value; } public ulong TitleID { get => this.titleId; set { this.titleId = value; if (!this.reDecrypt) return; this.reDecryptTitleKey(); } } public ushort NumOfDLC { get => this.numOfDlc; set => this.numOfDlc = value; } public bool FakeSign { get => this.fakeSign; set => this.fakeSign = value; } public bool TitleKeyChanged => this.titleKeyChanged; public bool DSiTicket { get => this.dsitik; set { this.dsitik = value; this.decryptTitleKey(); } } ~Ticket() => this.Dispose(false); public void Dispose() { this.Dispose(true); GC.SuppressFinalize((object) this); } protected virtual void Dispose(bool disposing) { if (disposing && !this.isDisposed) { this.decryptedTitleKey = (byte[]) null; this.newEncryptedTitleKey = (byte[]) null; this.signature = (byte[]) null; this.padding = (byte[]) null; this.issuer = (byte[]) null; this.unknown = (byte[]) null; this.encryptedTitleKey = (byte[]) null; this.unknown5 = (byte[]) null; this.unknown6 = (byte[]) null; this.padding4 = (byte[]) null; } this.isDisposed = true; } public static Ticket Load(string pathToTicket) => Ticket.Load(File.ReadAllBytes(pathToTicket)); public static Ticket Load(byte[] ticket) { Ticket ticket1 = new Ticket(); MemoryStream ticketFile = new MemoryStream(ticket); try { ticket1.parseTicket((Stream) ticketFile); } catch { ticketFile.Dispose(); throw; } ticketFile.Dispose(); return ticket1; } public static Ticket Load(Stream ticket) { Ticket ticket1 = new Ticket(); ticket1.parseTicket(ticket); return ticket1; } public void LoadFile(string pathToTicket) => this.LoadFile(File.ReadAllBytes(pathToTicket)); public void LoadFile(byte[] ticket) { MemoryStream ticketFile = new MemoryStream(ticket); try { this.parseTicket((Stream) ticketFile); } catch { ticketFile.Dispose(); throw; } ticketFile.Dispose(); } public void LoadFile(Stream ticket) => this.parseTicket(ticket); public void Save(string savePath) => this.Save(savePath, false); public void Save(string savePath, bool fakeSign) { if (fakeSign) this.fakeSign = true; if (File.Exists(savePath)) File.Delete(savePath); using (FileStream writeStream = new FileStream(savePath, FileMode.Create)) this.writeToStream((Stream) writeStream); } public MemoryStream ToMemoryStream() => this.ToMemoryStream(false); public MemoryStream ToMemoryStream(bool fakeSign) { if (fakeSign) this.fakeSign = true; MemoryStream writeStream = new MemoryStream(); try { this.writeToStream((Stream) writeStream); } catch { writeStream.Dispose(); throw; } return writeStream; } public byte[] ToByteArray() => this.ToByteArray(false); public byte[] ToByteArray(bool fakeSign) { if (fakeSign) this.fakeSign = true; MemoryStream writeStream = new MemoryStream(); try { this.writeToStream((Stream) writeStream); } catch { writeStream.Dispose(); throw; } byte[] array = writeStream.ToArray(); writeStream.Dispose(); return array; } public void SetTitleKey(string newTitleKey) => this.SetTitleKey(newTitleKey.ToCharArray()); public void SetTitleKey(char[] newTitleKey) { if (newTitleKey.Length != 16) throw new Exception("The title key must be 16 characters long!"); for (int index = 0; index < 16; ++index) this.encryptedTitleKey[index] = (byte) newTitleKey[index]; this.decryptTitleKey(); this.titleKeyChanged = true; this.reDecrypt = true; this.newEncryptedTitleKey = this.encryptedTitleKey; } public void SetTitleKey(byte[] newTitleKey) { this.encryptedTitleKey = newTitleKey.Length == 16 ? newTitleKey : throw new Exception("The title key must be 16 characters long!"); this.decryptTitleKey(); this.titleKeyChanged = true; this.reDecrypt = true; this.newEncryptedTitleKey = newTitleKey; } public string GetUpperTitleID() { byte[] bytes = BitConverter.GetBytes(Shared.Swap((uint) this.titleId)); return new string(new char[4] { (char) bytes[0], (char) bytes[1], (char) bytes[2], (char) bytes[3] }); } private void writeToStream(Stream writeStream) { this.fireDebug("Writing Ticket..."); this.fireDebug(" Encrypting Title Key..."); this.encryptTitleKey(); this.fireDebug(" -> Decrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.decryptedTitleKey)); this.fireDebug(" -> Encrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.encryptedTitleKey)); if (this.fakeSign) { this.fireDebug(" Clearing Signature..."); this.signature = new byte[256]; } MemoryStream memoryStream = new MemoryStream(); memoryStream.Seek(0L, SeekOrigin.Begin); this.fireDebug(" Writing Signature Exponent... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.signatureExponent)), 0, 4); this.fireDebug(" Writing Signature... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.signature, 0, this.signature.Length); this.fireDebug(" Writing Padding... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.padding, 0, this.padding.Length); this.fireDebug(" Writing Issuer... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.issuer, 0, this.issuer.Length); this.fireDebug(" Writing Unknown... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.unknown, 0, this.unknown.Length); this.fireDebug(" Writing Title Key... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.encryptedTitleKey, 0, this.encryptedTitleKey.Length); this.fireDebug(" Writing Unknown2... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.WriteByte(this.unknown2); this.fireDebug(" Writing Ticket ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.ticketId)), 0, 8); this.fireDebug(" Writing Console ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.consoleId)), 0, 4); this.fireDebug(" Writing Title ID... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.titleId)), 0, 8); this.fireDebug(" Writing Unknwon3... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.unknown3)), 0, 2); this.fireDebug(" Writing NumOfDLC... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.numOfDlc)), 0, 2); this.fireDebug(" Writing Unknwon4... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.unknown4)), 0, 8); this.fireDebug(" Writing Padding2... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.WriteByte(this.padding2); this.fireDebug(" Writing Common Key Index... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.WriteByte(this.commonKeyIndex); this.fireDebug(" Writing Unknown5... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.unknown5, 0, this.unknown5.Length); this.fireDebug(" Writing Unknown6... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.unknown6, 0, this.unknown6.Length); this.fireDebug(" Writing Padding3... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.padding3)), 0, 2); this.fireDebug(" Writing Enable Time Limit... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.enableTimeLimit)), 0, 4); this.fireDebug(" Writing Time Limit... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(BitConverter.GetBytes(Shared.Swap(this.timeLimit)), 0, 4); this.fireDebug(" Writing Padding4... (Offset: 0x{0})", (object) memoryStream.Position.ToString("x8").ToUpper()); memoryStream.Write(this.padding4, 0, this.padding4.Length); byte[] array = memoryStream.ToArray(); memoryStream.Dispose(); if (this.fakeSign) { this.fireDebug(" Fakesigning Ticket..."); byte[] numArray = new byte[20]; SHA1 shA1 = SHA1.Create(); for (ushort index = 0; index < ushort.MaxValue; ++index) { byte[] bytes = BitConverter.GetBytes(index); array[498] = bytes[1]; array[499] = bytes[0]; if (shA1.ComputeHash(array)[0] == (byte) 0) { this.fireDebug(" -> Signed ({0})", (object) index); break; } if (index == (ushort) 65534) { this.fireDebug(" -> Signing Failed..."); throw new Exception("Fakesigning failed..."); } } shA1.Clear(); } writeStream.Seek(0L, SeekOrigin.Begin); writeStream.Write(array, 0, array.Length); this.fireDebug("Writing Ticket Finished..."); } private void parseTicket(Stream ticketFile) { this.fireDebug("Parsing Ticket..."); ticketFile.Seek(0L, SeekOrigin.Begin); byte[] buffer = new byte[8]; this.fireDebug(" Reading Signature Exponent... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 4); this.signatureExponent = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); this.fireDebug(" Reading Signature... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.signature, 0, this.signature.Length); this.fireDebug(" Reading Padding... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.padding, 0, this.padding.Length); this.fireDebug(" Reading Issuer... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.issuer, 0, this.issuer.Length); this.fireDebug(" Reading Unknown... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.unknown, 0, this.unknown.Length); this.fireDebug(" Reading Title Key... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.encryptedTitleKey, 0, this.encryptedTitleKey.Length); this.fireDebug(" Reading Unknown2... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); this.unknown2 = (byte) ticketFile.ReadByte(); this.fireDebug(" Reading Ticket ID.. (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 8); this.ticketId = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); this.fireDebug(" Reading Console ID... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 4); this.consoleId = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); this.fireDebug(" Reading Title ID... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 8); this.titleId = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); this.fireDebug(" Reading Unknown3... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); this.fireDebug(" Reading NumOfDLC... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 4); this.unknown3 = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); this.numOfDlc = Shared.Swap(BitConverter.ToUInt16(buffer, 2)); this.fireDebug(" Reading Unknown4... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 8); this.unknown4 = Shared.Swap(BitConverter.ToUInt64(buffer, 0)); this.fireDebug(" Reading Padding2... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); this.padding2 = (byte) ticketFile.ReadByte(); this.fireDebug(" Reading Common Key Index... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); this.commonKeyIndex = (byte) ticketFile.ReadByte(); this.newKeyIndex = this.commonKeyIndex; this.fireDebug(" Reading Unknown5... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.unknown5, 0, this.unknown5.Length); this.fireDebug(" Reading Unknown6... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.unknown6, 0, this.unknown6.Length); this.fireDebug(" Reading Padding3... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 2); this.padding3 = Shared.Swap(BitConverter.ToUInt16(buffer, 0)); this.fireDebug(" Reading Enable Time Limit... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); this.fireDebug(" Reading Time Limit... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(buffer, 0, 8); this.enableTimeLimit = Shared.Swap(BitConverter.ToUInt32(buffer, 0)); this.timeLimit = Shared.Swap(BitConverter.ToUInt32(buffer, 4)); this.fireDebug(" Reading Padding4... (Offset: 0x{0})", (object) ticketFile.Position.ToString("x8").ToUpper()); ticketFile.Read(this.padding4, 0, this.padding4.Length); this.fireDebug(" Decrypting Title Key..."); this.decryptTitleKey(); this.fireDebug(" -> Encrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.encryptedTitleKey)); this.fireDebug(" -> Decrypted Title Key: {0}", (object) Shared.ByteArrayToString(this.decryptedTitleKey)); this.fireDebug("Parsing Ticket Finished..."); } private void decryptTitleKey() { byte[] numArray = !this.dsitik ? (this.commonKeyIndex == (byte) 1 ? CommonKey.GetKoreanKey() : CommonKey.GetStandardKey()) : CommonKey.GetDSiKey(); byte[] bytes = BitConverter.GetBytes(Shared.Swap(this.titleId)); Array.Resize(ref bytes, 16); RijndaelManaged rijndaelManaged = new RijndaelManaged(); rijndaelManaged.Mode = CipherMode.CBC; rijndaelManaged.Padding = PaddingMode.None; rijndaelManaged.KeySize = 128; rijndaelManaged.BlockSize = 128; rijndaelManaged.Key = numArray; rijndaelManaged.IV = bytes; ICryptoTransform decryptor = rijndaelManaged.CreateDecryptor(); MemoryStream memoryStream = new MemoryStream(this.encryptedTitleKey); CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, decryptor, CryptoStreamMode.Read); cryptoStream.Read(this.decryptedTitleKey, 0, this.decryptedTitleKey.Length); cryptoStream.Dispose(); memoryStream.Dispose(); decryptor.Dispose(); rijndaelManaged.Clear(); } private void encryptTitleKey() { this.commonKeyIndex = this.newKeyIndex; byte[] numArray = !this.dsitik ? (this.commonKeyIndex == (byte) 1 ? CommonKey.GetKoreanKey() : CommonKey.GetStandardKey()) : CommonKey.GetDSiKey(); byte[] bytes = BitConverter.GetBytes(Shared.Swap(this.titleId)); Array.Resize(ref bytes, 16); RijndaelManaged rijndaelManaged = new RijndaelManaged(); rijndaelManaged.Mode = CipherMode.CBC; rijndaelManaged.Padding = PaddingMode.None; rijndaelManaged.KeySize = 128; rijndaelManaged.BlockSize = 128; rijndaelManaged.Key = numArray; rijndaelManaged.IV = bytes; ICryptoTransform encryptor = rijndaelManaged.CreateEncryptor(); MemoryStream memoryStream = new MemoryStream(this.decryptedTitleKey); CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, encryptor, CryptoStreamMode.Read); cryptoStream.Read(this.encryptedTitleKey, 0, this.encryptedTitleKey.Length); cryptoStream.Dispose(); memoryStream.Dispose(); encryptor.Dispose(); rijndaelManaged.Clear(); } private void reDecryptTitleKey() { this.encryptedTitleKey = this.newEncryptedTitleKey; this.decryptTitleKey(); } public event EventHandler Debug; private void fireDebug(string debugMessage, params object[] args) { EventHandler debug = this.Debug; if (debug == null) return; debug(new object(), new MessageEventArgs(string.Format(debugMessage, args))); } } }