diff --git a/src/MaksIT.LTO.Backup/Application.cs b/src/MaksIT.LTO.Backup/Application.cs index 059c235..50f523e 100644 --- a/src/MaksIT.LTO.Backup/Application.cs +++ b/src/MaksIT.LTO.Backup/Application.cs @@ -244,27 +244,7 @@ public class Application { }); } - private static byte[] AddPadding(byte[] data, int blockSize) { - // Calculate the padding size - int paddingSize = blockSize - (data.Length % blockSize); - if (paddingSize == blockSize) { - paddingSize = 0; - } - // Create a new array with the original data plus padding - byte[] paddedData = new byte[data.Length + paddingSize + 1]; - Array.Copy(data, paddedData, data.Length); - - // Fill the padding with a specific value (e.g., 0x00) - for (int i = data.Length; i < paddedData.Length - 1; i++) { - paddedData[i] = 0x00; - } - - // Append the padding size at the end - paddedData[paddedData.Length - 1] = (byte)paddingSize; - - return paddedData; - } private void WriteFilesToTape(WorkingFolder workingFolder, string descriptorFilePath, uint blockSize) { PathAccessWrapper(workingFolder, (directoryPath) => { @@ -334,7 +314,7 @@ public class Application { var encryptedDescriptorData = AESGCMUtility.EncryptData(descriptorData, _secret); // add padding to the encrypted descriptor data - var paddedDescriptorData = AddPadding(encryptedDescriptorData, (int)blockSize); + var paddedDescriptorData = PaddingUtility.AddPadding(encryptedDescriptorData, (int)blockSize); // calculate the number of blocks needed var descriptorBlocks = (paddedDescriptorData.Length + blockSize - 1) / blockSize; @@ -381,55 +361,53 @@ public class Application { handler.SetPosition(TapeDeviceHandler.TAPE_SPACE_FILEMARKS, 0, 1); Thread.Sleep(2000); - var position = handler.GetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK); - if (position.Error != null) + var endOfBackupMarkerPosition = handler.GetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK); + if (endOfBackupMarkerPosition.Error != null || endOfBackupMarkerPosition.OffsetLow == null) return null; - var desctiptorBlocks = position.OffsetLow; + _logger.LogInformation($"End of backup marker position: {endOfBackupMarkerPosition.OffsetLow}"); + + var descriptorBlocks = endOfBackupMarkerPosition.OffsetLow; handler.SetPosition(TapeDeviceHandler.TAPE_SPACE_FILEMARKS, 0, 2); Thread.Sleep(2000); - position = handler.GetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK); - if (position.Error != null) + var endOfDescriptorMarkerPosition = handler.GetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK); + if (endOfDescriptorMarkerPosition.Error != null || endOfDescriptorMarkerPosition.OffsetLow == null) return null; - desctiptorBlocks = position.OffsetLow - desctiptorBlocks; + _logger.LogInformation($"End of descriptor marker position: {endOfDescriptorMarkerPosition.OffsetLow}"); + descriptorBlocks = endOfDescriptorMarkerPosition.OffsetLow - descriptorBlocks; - var padding = handler.ReadData(blockSize); + if (descriptorBlocks == null) + return null; - handler.SetPosition(TapeDeviceHandler.TAPE_SPACE_FILEMARKS, 0, 1); + _logger.LogInformation($"Descriptor blocks to read: {descriptorBlocks}"); + + handler.SetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK, 0, (long)(endOfDescriptorMarkerPosition.OffsetLow - descriptorBlocks)); Thread.Sleep(2000); - // read data from descriptorBlocks - var buffer = new List(); - for (var i = 0; i < desctiptorBlocks; i++) { - var data = handler.ReadData(blockSize); - buffer.AddRange(data); + descriptorBlocks -= 2; + + var paddedData = new byte[(descriptorBlocks.Value) * blockSize]; + var buffer = new byte[blockSize]; + + for (var i = 0; i < descriptorBlocks; i++) { + var bytesRead = handler.ReadData(buffer, 0, buffer.Length); + + // Copy the read data into the encryptedData array + Array.Copy(buffer, 0, paddedData, i * blockSize, buffer.Length); } - // Convert buffer to array - var paddedData = buffer.ToArray(); + // i need to remove padding from the data + var encryptedData = PaddingUtility.RemovePadding(paddedData, (int)blockSize); - // Retrieve the padding size from the last byte - int paddingSize = paddedData[^1]; - - // Calculate the length of the original data - int originalDataLength = paddedData.Length - paddingSize - 1; - - // Ensure the padding size is valid - if (paddingSize < 0 || paddingSize >= paddedData.Length || originalDataLength < 0) - return null; - - // Create a new array for the original data - var descriptorData = new byte[originalDataLength]; - Array.Copy(paddedData, descriptorData, originalDataLength); - - descriptorData = AESGCMUtility.DecryptData(descriptorData, _secret); + // decrypt the data + var decryptedData = AESGCMUtility.DecryptData(encryptedData, _secret); // Convert byte array to string and trim ending zeros - var json = Encoding.UTF8.GetString(descriptorData); + var json = Encoding.UTF8.GetString(decryptedData); try { var descriptor = JsonSerializer.Deserialize(json); @@ -514,7 +492,7 @@ public class Application { var encryptedDescriptorData = AESGCMUtility.EncryptData(File.ReadAllBytes(_descriptorFilePath), _secret); - var paddedDescriptorData = AddPadding(encryptedDescriptorData, (int)descriptor.BlockSize); + var paddedDescriptorData = PaddingUtility.AddPadding(encryptedDescriptorData, (int)descriptor.BlockSize); const ulong fileMarkBlocks = 2; diff --git a/src/MaksIT.LTO.Core/Crc32.cs b/src/MaksIT.LTO.Core/Crc32.cs new file mode 100644 index 0000000..4910ad2 --- /dev/null +++ b/src/MaksIT.LTO.Core/Crc32.cs @@ -0,0 +1,87 @@ +using System.Security.Cryptography; + + +namespace MaksIT.LTO.Core; + +public class Crc32 : HashAlgorithm { + public const uint DefaultPolynomial = 0xedb88320; + public const uint DefaultSeed = 0xffffffff; + + private static uint[]? defaultTable; + + private readonly uint seed; + private readonly uint[] table; + private uint hash; + + public Crc32() + : this(DefaultPolynomial, DefaultSeed) { + } + + public Crc32(uint polynomial, uint seed) { + table = InitializeTable(polynomial); + this.seed = hash = seed; + } + + public override void Initialize() { + hash = seed; + } + + protected override void HashCore(byte[] buffer, int start, int length) { + hash = CalculateHash(table, hash, buffer, start, length); + } + + protected override byte[] HashFinal() { + var hashBuffer = UInt32ToBigEndianBytes(~hash); + HashValue = hashBuffer; + return hashBuffer; + } + + public override int HashSize => 32; + + public static uint Compute(byte[] buffer) { + return Compute(DefaultPolynomial, DefaultSeed, buffer); + } + + public static uint Compute(uint seed, byte[] buffer) { + return Compute(DefaultPolynomial, seed, buffer); + } + + public static uint Compute(uint polynomial, uint seed, byte[] buffer) { + return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length); + } + + private static uint[] InitializeTable(uint polynomial) { + if (polynomial == DefaultPolynomial && defaultTable != null) + return defaultTable; + + var createTable = new uint[256]; + for (var i = 0; i < 256; i++) { + var entry = (uint)i; + for (var j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (entry >> 1) ^ polynomial; + else + entry >>= 1; + createTable[i] = entry; + } + + if (polynomial == DefaultPolynomial) + defaultTable = createTable; + + return createTable; + } + + private static uint CalculateHash(uint[] table, uint seed, byte[] buffer, int start, int size) { + var crc = seed; + for (var i = start; i < size - start; i++) + crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff]; + return crc; + } + + private static byte[] UInt32ToBigEndianBytes(uint x) => [ + (byte)((x >> 24) & 0xff), + (byte)((x >> 16) & 0xff), + (byte)((x >> 8) & 0xff), + (byte)(x & 0xff) + ]; +} diff --git a/src/MaksIT.LTO.Core/DriverManager.cs b/src/MaksIT.LTO.Core/DriverManager.cs deleted file mode 100644 index 0eabe5e..0000000 --- a/src/MaksIT.LTO.Core/DriverManager.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Diagnostics; - - -namespace MaksIT.LTO.Core; -public class DriverManager { - public static void RestartDriver(string deviceName) { - string script = $@" - $device = Get-PnpDevice -FriendlyName '{deviceName}' - Disable-PnpDevice -InstanceId $device.InstanceId -Confirm:$false - Start-Sleep -Seconds 5 - Enable-PnpDevice -InstanceId $device.InstanceId -Confirm:$false - "; - - ProcessStartInfo psi = new ProcessStartInfo { - FileName = "powershell.exe", - Arguments = $"-NoProfile -ExecutionPolicy Bypass -Command \"{script}\"", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - using (Process process = Process.Start(psi)) { - string output = process.StandardOutput.ReadToEnd(); - string error = process.StandardError.ReadToEnd(); - process.WaitForExit(); - - Console.WriteLine(output); - if (!string.IsNullOrEmpty(error)) { - Console.WriteLine($"Error: {error}"); - } - } - } -} diff --git a/src/MaksIT.LTO.Core/Utilities/ChecksumUtility.cs b/src/MaksIT.LTO.Core/Utilities/ChecksumUtility.cs index 861f30c..fa2f675 100644 --- a/src/MaksIT.LTO.Core/Utilities/ChecksumUtility.cs +++ b/src/MaksIT.LTO.Core/Utilities/ChecksumUtility.cs @@ -1,19 +1,16 @@ -using System.IO; -using System.Security.Cryptography; - -namespace MaksIT.LTO.Core.Helpers; +namespace MaksIT.LTO.Core.Helpers; public static class ChecksumUtility { public static string CalculateCRC32Checksum(byte[] data) { using var crc32 = new Crc32(); - byte[] hashBytes = crc32.ComputeHash(data); + var hashBytes = crc32.ComputeHash(data); return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); } public static string CalculateCRC32ChecksumFromFile(string filePath) { using var crc32 = new Crc32(); using var stream = File.OpenRead(filePath); - byte[] hashBytes = crc32.ComputeHash(stream); + var hashBytes = crc32.ComputeHash(stream); return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); } @@ -26,105 +23,23 @@ public static class ChecksumUtility { crc32.TransformBlock(buffer, 0, bytesRead, null, 0); } crc32.TransformFinalBlock(buffer, 0, 0); - byte[] hashBytes = crc32.Hash; - return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); + var hashBytes = crc32.Hash; + return BitConverter.ToString(hashBytes ?? Array.Empty()).Replace("-", "").ToLower(); } public static bool VerifyCRC32Checksum(byte[] data, string expectedChecksum) { - string calculatedChecksum = CalculateCRC32Checksum(data); + var calculatedChecksum = CalculateCRC32Checksum(data); return string.Equals(calculatedChecksum, expectedChecksum, StringComparison.OrdinalIgnoreCase); } public static bool VerifyCRC32ChecksumFromFile(string filePath, string expectedChecksum) { - string calculatedChecksum = CalculateCRC32ChecksumFromFile(filePath); + var calculatedChecksum = CalculateCRC32ChecksumFromFile(filePath); return string.Equals(calculatedChecksum, expectedChecksum, StringComparison.OrdinalIgnoreCase); } public static bool VerifyCRC32ChecksumFromFileInChunks(string filePath, string expectedChecksum, int chunkSize = 8192) { - string calculatedChecksum = CalculateCRC32ChecksumFromFileInChunks(filePath, chunkSize); + var calculatedChecksum = CalculateCRC32ChecksumFromFileInChunks(filePath, chunkSize); return string.Equals(calculatedChecksum, expectedChecksum, StringComparison.OrdinalIgnoreCase); } } -public class Crc32 : HashAlgorithm { - public const uint DefaultPolynomial = 0xedb88320; - public const uint DefaultSeed = 0xffffffff; - - private static uint[]? defaultTable; - - private readonly uint seed; - private readonly uint[] table; - private uint hash; - - public Crc32() - : this(DefaultPolynomial, DefaultSeed) { - } - - public Crc32(uint polynomial, uint seed) { - table = InitializeTable(polynomial); - this.seed = hash = seed; - } - - public override void Initialize() { - hash = seed; - } - - protected override void HashCore(byte[] buffer, int start, int length) { - hash = CalculateHash(table, hash, buffer, start, length); - } - - protected override byte[] HashFinal() { - var hashBuffer = UInt32ToBigEndianBytes(~hash); - HashValue = hashBuffer; - return hashBuffer; - } - - public override int HashSize => 32; - - public static uint Compute(byte[] buffer) { - return Compute(DefaultPolynomial, DefaultSeed, buffer); - } - - public static uint Compute(uint seed, byte[] buffer) { - return Compute(DefaultPolynomial, seed, buffer); - } - - public static uint Compute(uint polynomial, uint seed, byte[] buffer) { - return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length); - } - - private static uint[] InitializeTable(uint polynomial) { - if (polynomial == DefaultPolynomial && defaultTable != null) - return defaultTable; - - var createTable = new uint[256]; - for (var i = 0; i < 256; i++) { - var entry = (uint)i; - for (var j = 0; j < 8; j++) - if ((entry & 1) == 1) - entry = (entry >> 1) ^ polynomial; - else - entry >>= 1; - createTable[i] = entry; - } - - if (polynomial == DefaultPolynomial) - defaultTable = createTable; - - return createTable; - } - - private static uint CalculateHash(uint[] table, uint seed, byte[] buffer, int start, int size) { - var crc = seed; - for (var i = start; i < size - start; i++) - crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff]; - return crc; - } - - private static byte[] UInt32ToBigEndianBytes(uint x) => [ - (byte)((x >> 24) & 0xff), - (byte)((x >> 16) & 0xff), - (byte)((x >> 8) & 0xff), - (byte)(x & 0xff) - ]; -} diff --git a/src/MaksIT.LTO.Core/Utilities/PaddingUtility.cs b/src/MaksIT.LTO.Core/Utilities/PaddingUtility.cs new file mode 100644 index 0000000..81a932f --- /dev/null +++ b/src/MaksIT.LTO.Core/Utilities/PaddingUtility.cs @@ -0,0 +1,38 @@ +namespace MaksIT.LTO.Core.Utilities; + +public static class PaddingUtility { + + private const byte _specialByte = 0x80; + + public static byte[] AddPadding(byte[] data, int blockSize) { + var paddingSize = blockSize - (data.Length % blockSize); + if (paddingSize == blockSize) { + paddingSize = 0; // no padding needed if already aligned + } + + var paddedData = new byte[data.Length + paddingSize]; + Array.Copy(data, paddedData, data.Length); + + // fill the padding bytes with specialBytes + for (var i = data.Length; i < paddedData.Length; i++) { + paddedData[i] = _specialByte; + } + + return paddedData; + } + + public static byte[] RemovePadding(byte[] paddedData, int blockSize) { + var originalLength = paddedData.Length; + + // find the original length by checking for the padding byte 0x80 + while (originalLength > 0 && paddedData[originalLength - 1] == _specialByte) { + originalLength--; + } + + // create a new array to hold the unpadded data + var unpaddedData = new byte[originalLength]; + Array.Copy(paddedData, unpaddedData, originalLength); + + return unpaddedData; + } +} \ No newline at end of file