From c8291350885b2ddb3364278c7e161ad171c57b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Fri, 20 Aug 2021 01:13:43 +0100 Subject: Calculate crypt mode --- UVtools.Core/FileFormats/CTBEncryptedFile.cs | 316 ++++++++++++++------------- UVtools.WPF/ConsoleArguments.cs | 14 +- 2 files changed, 174 insertions(+), 156 deletions(-) diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index 34cd550..ad3a587 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -35,9 +35,15 @@ namespace UVtools.Core.FileFormats public const byte HASH_LENGTH = 32; public const uint LAYER_XOR_KEY = 0xEFBEADDE; - public static readonly byte[] Kong = new byte[0x20]; + public static readonly byte[] Kong = + { + 0xD0, 0x5B, 0x8E, 0x33, 0x71, 0xDE, 0x3D, 0x1A, 0xE5, 0x4F, 0x22, 0xDD, 0xDF, 0x5B, 0xFD, 0x94, + 0xAB, 0x5D, 0x64, 0x3A, 0x9D, 0x7E, 0xBF, 0xAF, 0x42, 0x03, 0xF3, 0x10, 0xD8, 0x52, 0x2A, 0xEA + }; - public static readonly byte[] CookieMonster = new byte[0x10]; + public static readonly byte[] CookieMonster = { + 0x0F, 0x01, 0x0A, 0x05, 0x05, 0x0B, 0x06, 0x07, 0x08, 0x06, 0x0A, 0x0C, 0x0C, 0x0D, 0x09, 0x0F + }; #endregion @@ -166,154 +172,6 @@ namespace UVtools.Core.FileFormats } } - public static void CryptFile(string filePath, bool encrypt) - { - using (MemoryStream decryptedStream = new MemoryStream(File.ReadAllBytes(filePath))) - { - BinaryWriter writer = new BinaryWriter(decryptedStream); - using MemoryStream ms = new MemoryStream(File.ReadAllBytes(filePath)); - BinaryReader reader = new BinaryReader(ms); - - /* magic */ - var magic = reader.ReadUInt32(); - if (magic != MAGIC_CBT_ENCRYPTED) - { - Console.Write("File does not appear to be an encrypted CTB. Aborting."); - return; - } - writer.Write(magic); - var headerLength = reader.ReadUInt32(); - writer.Write(headerLength); - var headerOffset = reader.ReadUInt32(); - writer.Write(headerOffset); - - /* pass through rest of data until encrypted header */ - var bytesToPassthru = headerOffset - ms.Position; - var temp = reader.ReadBytes((int)bytesToPassthru); - writer.Write(temp); - uint printerNameLength = 0; - - var originalHeader = reader.ReadBytes((int)headerLength); - - if (encrypt) - { - printerNameLength = BitExtensions.ToUIntLittleEndian(originalHeader, 164); - } - - /* decrypt header with recovered keys */ - var cryptedData = CryptExtensions.AesCryptBytes(originalHeader, Kong, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster); - writer.Write(cryptedData); - - /* get machine name length */ - if (!encrypt) - { - printerNameLength = BitExtensions.ToUIntLittleEndian(cryptedData, 164); - } - - /* pass through the next 2 dwords */ - writer.Write(reader.ReadUInt32()); - writer.Write(reader.ReadUInt32()); - - /* get offset and length of next section (not encrypted) */ - var nextOffset = reader.ReadUInt32(); - var nextLength = reader.ReadUInt32(); - - writer.Write(nextOffset); - writer.Write(nextLength); - - /* how many bytes from current position till the next offset */ - bytesToPassthru = nextOffset - ms.Position; - writer.Write(reader.ReadBytes((int)bytesToPassthru)); - - /* passthrough this whole block */ - writer.Write(reader.ReadBytes((int)nextLength)); - - /* pass throught the next 2 dwords */ - writer.Write(reader.ReadUInt32()); - writer.Write(reader.ReadUInt32()); - - /* get offset and length of next section (not encrypted) */ - nextOffset = reader.ReadUInt32(); - nextLength = reader.ReadUInt32(); - - writer.Write(nextOffset); - writer.Write(nextLength); - - /* how many bytes from current position till the next offset */ - bytesToPassthru = nextOffset - ms.Position; - writer.Write(reader.ReadBytes((int)bytesToPassthru)); - - /* passthrough this whole block */ - writer.Write(reader.ReadBytes((int)nextLength)); - - /* passes printer name and disclaimer */ - var x = reader.ReadBytes((int)(CTB_DISCLAIMER_SIZE + printerNameLength)); - writer.Write(x); - - /* we're at the layer offset table now */ - List layerOffsets = new List(); - var startOfTable = ms.Position; - ulong layerOffset = reader.ReadUInt64(); - ulong firstLayer = layerOffset; - while (ms.Position < (int)firstLayer) - { - layerOffsets.Add(layerOffset); - reader.ReadUInt64(); - layerOffset = reader.ReadUInt64(); - } - - ms.Position = (int)startOfTable; - - /* copy the rest of the file to the output memory stream, we'll decrypt layers next */ - writer.Write(reader.ReadBytes((int)(ms.Length - ms.Position))); - - byte[] cryptedFile = decryptedStream.ToArray(); - var layerCounter = 0; - foreach (var offset in layerOffsets) - { - layerCounter++; - ms.Position = (int)(offset + 0x10); - - ms.Position += 0x10; - var encryptedOffset = reader.ReadUInt32(); - var encryptedLength = reader.ReadUInt32(); - ms.Position -= 0x18; - - if (encryptedLength > 0) - { - Debug.WriteLine($"Layer {layerCounter} has {encryptedLength} bytes of encrypted data. (Layer data offset: {encryptedOffset})"); - var layerDataOffset = reader.ReadUInt32(); - - ms.Position = layerDataOffset + encryptedOffset; - - byte[] encryptedLayerData = reader.ReadBytes((int)encryptedLength); - - byte[] decryptedLayerData = CryptExtensions.AesCryptBytes(encryptedLayerData, Kong, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster); - - Array.Copy(decryptedLayerData, 0, cryptedFile, layerDataOffset + encryptedOffset, encryptedLength); - - - /* update encrypted markers in the layer header */ - byte[] empty = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; - Array.Copy(empty, 0, cryptedFile, layerDataOffset - 0x38, 8); - - } - else - { - Debug.WriteLine($"Layer {layerCounter} is not encrypted"); - } - } - - /* last 20 bytes are an encrypted sha256 */ - byte[] cipheredHash = cryptedFile[^0x20..]; - - byte[] plainHash = CryptExtensions.AesCryptBytes(cipheredHash, Kong, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster); - Array.Copy(plainHash, 0, cryptedFile, cryptedFile.Length - 0x20, 0x20); - - File.WriteAllBytes(filePath, cryptedFile); - } - } - public class LayerPointer { [FieldOrder(0)] public uint LayerOffset { get; set; } @@ -1204,7 +1062,7 @@ namespace UVtools.Core.FileFormats private void SanitizeProperties() { Settings.PerLayerSettings = LayerManager.AllLayersAreUsingGlobalParameters - ? (LayerManager.AnyLayerIsUsingTSMC ? PERLAYER_SETTINGS_DISALLOW_BUT_TSMC : PERLAYER_SETTINGS_DISALLOW) + ? PERLAYER_SETTINGS_DISALLOW : PERLAYER_SETTINGS_ALLOW; } @@ -1541,6 +1399,162 @@ namespace UVtools.Core.FileFormats } #endregion + #region Static Methods + public static void CryptFile(string filePath) + { + using var msReader = new MemoryStream(File.ReadAllBytes(filePath)); + using var msWriter = new MemoryStream(); + msReader.CopyTo(msWriter); + msWriter.Position = 0; + msReader.Position = 0; + var writer = new BinaryWriter(msWriter); + var reader = new BinaryReader(msReader); + + /* magic */ + var magic = reader.ReadUInt32(); + if (magic != MAGIC_CBT_ENCRYPTED) + { + Console.Write("File does not appear to be an encrypted CTB. Aborting."); + return; + } + + writer.Write(magic); + var headerLength = reader.ReadUInt32(); + writer.Write(headerLength); + var headerOffset = reader.ReadUInt32(); + writer.Write(headerOffset); + + var currentPos = msReader.Position; + msReader.Seek(headerOffset + 24, SeekOrigin.Begin); // Paddings of 0 + + + var encrypt = reader.ReadUInt32() == 0 && reader.ReadUInt32() == 0; // Both paddings must be zero so we know we need to encrypt! + msReader.Seek(currentPos, SeekOrigin.Begin); + + Console.Write($"Crypt mode: {(encrypt ? "Encrypting" : "Decrypting")}"); + + /* pass through rest of data until encrypted header */ + var bytesToPassthru = headerOffset - msReader.Position; + var temp = reader.ReadBytes((int)bytesToPassthru); + writer.Write(temp); + uint printerNameLength = 0; + + var originalHeader = reader.ReadBytes((int)headerLength); + + if (encrypt) + { + printerNameLength = BitExtensions.ToUIntLittleEndian(originalHeader, 164); + } + + /* decrypt header with recovered keys */ + var cryptedData = CryptExtensions.AesCryptBytes(originalHeader, Kong, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster); + writer.Write(cryptedData); + + /* get machine name length */ + if (!encrypt) + { + printerNameLength = BitExtensions.ToUIntLittleEndian(cryptedData, 164); + } + + /* pass through the next 2 dwords */ + writer.Write(reader.ReadUInt32()); + writer.Write(reader.ReadUInt32()); + + /* get offset and length of next section (not encrypted) */ + var nextOffset = reader.ReadUInt32(); + var nextLength = reader.ReadUInt32(); + + writer.Write(nextOffset); + writer.Write(nextLength); + + /* how many bytes from current position till the next offset */ + bytesToPassthru = nextOffset - msReader.Position; + writer.Write(reader.ReadBytes((int)bytesToPassthru)); + + /* passthrough this whole block */ + writer.Write(reader.ReadBytes((int)nextLength)); + + /* pass throught the next 2 dwords */ + writer.Write(reader.ReadUInt32()); + writer.Write(reader.ReadUInt32()); + + /* get offset and length of next section (not encrypted) */ + nextOffset = reader.ReadUInt32(); + nextLength = reader.ReadUInt32(); + + writer.Write(nextOffset); + writer.Write(nextLength); + /* how many bytes from current position till the next offset */ + bytesToPassthru = nextOffset - msReader.Position; + writer.Write(reader.ReadBytes((int)bytesToPassthru)); + + /* passthrough this whole block */ + writer.Write(reader.ReadBytes((int)nextLength)); + + /* passes printer name and disclaimer */ + var x = reader.ReadBytes((int)(CTB_DISCLAIMER_SIZE + printerNameLength)); + writer.Write(x); + + /* we're at the layer offset table now */ + var layerOffsets = new List(); + var startOfTable = msReader.Position; + ulong layerOffset = reader.ReadUInt64(); + ulong firstLayer = layerOffset; + while (msReader.Position < (int)firstLayer) + { + layerOffsets.Add(layerOffset); + reader.ReadUInt64(); + layerOffset = reader.ReadUInt64(); + } + + msReader.Position = (int)startOfTable; + + /* copy the rest of the file to the output memory stream, we'll decrypt layers next */ + writer.Write(reader.ReadBytes((int)(msReader.Length - msReader.Position))); + + byte[] cryptedFile = msWriter.ToArray(); + var layerCounter = 0; + foreach (var offset in layerOffsets) + { + layerCounter++; + msReader.Position = (int)(offset + 0x10); + + msReader.Position += 0x10; + var encryptedOffset = reader.ReadUInt32(); + var encryptedLength = reader.ReadUInt32(); + msReader.Position -= 0x18; + + if (encryptedLength > 0) + { + Debug.WriteLine($"Layer {layerCounter} has {encryptedLength} bytes of encrypted data. (Layer data offset: {encryptedOffset})"); + var layerDataOffset = reader.ReadUInt32(); + + msReader.Position = layerDataOffset + encryptedOffset; + + var encryptedLayerData = reader.ReadBytes((int)encryptedLength); + var decryptedLayerData = CryptExtensions.AesCryptBytes(encryptedLayerData, Kong, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster); + + Array.Copy(decryptedLayerData, 0, cryptedFile, layerDataOffset + encryptedOffset, encryptedLength); + + + /* update encrypted markers in the layer header */ + cryptedFile.AsSpan((int)(layerDataOffset - 0x38), 8).Fill(0); + } + else + { + Debug.WriteLine($"Layer {layerCounter} is not encrypted"); + } + } + + /* last 20 bytes are an encrypted sha256 */ + var cipheredHash = cryptedFile[^0x20..]; + + var plainHash = CryptExtensions.AesCryptBytes(cipheredHash, Kong, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster); + Array.Copy(plainHash, 0, cryptedFile, cryptedFile.Length - 0x20, 0x20); + + File.WriteAllBytes(filePath, cryptedFile); + } + #endregion } } diff --git a/UVtools.WPF/ConsoleArguments.cs b/UVtools.WPF/ConsoleArguments.cs index 50d9407..8ae50e9 100644 --- a/UVtools.WPF/ConsoleArguments.cs +++ b/UVtools.WPF/ConsoleArguments.cs @@ -141,12 +141,16 @@ namespace UVtools.WPF return true; } - if (args[0] is "--encrypt-ctb" or "--decrypt-ctb") + if (args[0] is "--crypt-ctb" or "--encrypt-ctb" or "--decrypt-ctb") { - bool isEncrypt = (args[0] is "--encrypt-ctb"); - - - CTBEncryptedFile.CryptFile(args[1], isEncrypt); + if (!File.Exists(args[1])) + { + Console.WriteLine($"Input file does not exists: {args[1]}"); + return true; + } + + CTBEncryptedFile.CryptFile(args[1]); + return true; } -- cgit v1.2.3