diff --git a/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/ARIA.cs b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/ARIA.cs index c96b364..af65692 100644 --- a/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/ARIA.cs +++ b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/ARIA.cs @@ -577,7 +577,7 @@ namespace WelsonJS.Cryptography /// /// /// - protected byte[] CreateKey(string key) + private byte[] CreateKey(string key) { SHA256 hasher = SHA256.Create(); byte[] hashData = hasher.ComputeHash(Encoding.Default.GetBytes(key)); @@ -587,7 +587,7 @@ namespace WelsonJS.Cryptography public byte[] Encrypt(byte[] data) { - byte[] indata = AnsiX923Padding.ApplyPadding(data, BLOCK_SIZE); + byte[] indata = AnsiX923Padding.AddPadding(data, BLOCK_SIZE); byte[] outdata = new byte[indata.Length]; for (int i = 0; i < indata.Length; i += BLOCK_SIZE) @@ -607,7 +607,7 @@ namespace WelsonJS.Cryptography engine.Decrypt(data, i, outdata, i); } - return AnsiX923Padding.RemovePadding(outdata, BLOCK_SIZE); + return AnsiX923Padding.RemovePadding(outdata, BLOCK_SIZE, true); } } } diff --git a/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/AnsiX923Padding.cs b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/AnsiX923Padding.cs index 063fc7c..86690d9 100644 --- a/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/AnsiX923Padding.cs +++ b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/AnsiX923Padding.cs @@ -19,6 +19,7 @@ * references: * - https://github.com/eGovFrame/egovframework.rte.root/blob/master/Foundation/egovframework.rte.fdl.crypto/src/main/java/egovframework/rte/fdl/cryptography/impl/aria/AnsiX923Padding.java * - ChatGPT prompt "AnsiX923Padding with C#" (chatgpt.com) + * - ChatGPT prompt "AnsiX923Padding with C#, Add a flag to decide how to handle possible errors when removing padding." (chatgpt.com) * * license: * GPLv3 or MS-RL(Microsoft Reciprocal License) @@ -31,12 +32,12 @@ namespace WelsonJS.Cryptography class AnsiX923Padding { /// - /// Applies ANSI X.923 padding to the input data to make it a multiple of the block size. + /// Add ANSI X.923 padding to the input data to make it a multiple of the block size. /// /// The data to be padded. /// The block size to pad to. /// Padded data with ANSI X.923 padding. - public static byte[] ApplyPadding(byte[] data, int blockSize) + public static byte[] AddPadding(byte[] data, int blockSize) { int paddingLength = blockSize - (data.Length % blockSize); @@ -62,19 +63,69 @@ namespace WelsonJS.Cryptography } /// - /// Removes the ANSI X.923 padding from the data. + /// Removes ANSI X.923 padding from the given data. /// - /// The padded data to remove padding from. - /// The block size used during padding. - /// Data without padding. - public static byte[] RemovePadding(byte[] data, int blockSize) + /// The input data, including padding. + /// The block size used for padding. + /// If true, ignores errors and attempts to process the input data as-is. + /// The unpadded data as a byte array. + /// Thrown if the input data or padding is invalid and ignoreErrors is false. + public static byte[] RemovePadding(byte[] data, int blockSize, bool ignoreErrors = false) { - // The last byte is the padding length + // Check for null or empty data + if (data == null || data.Length == 0) + { + if (ignoreErrors) + { + return new byte[] { }; + } + throw new ArgumentException("Input data cannot be null or empty."); + } + + // Ensure the data length is a multiple of the block size + if (data.Length % blockSize != 0) + { + if (ignoreErrors) + { + // Return the original data if errors are ignored + return data; + } + throw new ArgumentException("Input data length must be a multiple of the block size."); + } + + // Retrieve the padding length from the last byte int paddingLength = data[data.Length - 1]; - // Remove the padding + // Validate padding length + if (paddingLength <= 0 || paddingLength > blockSize) + { + if (ignoreErrors) + { + // Treat padding length as 0 and return the full data + return data; + } + throw new ArgumentException($"Invalid padding length: {paddingLength}. Must be between 1 and {blockSize}."); + } + + // Validate the padding region (last paddingLength - 1 bytes must be 0x00) + for (int i = data.Length - paddingLength; i < data.Length - 1; i++) + { + if (data[i] != 0x00) + { + if (ignoreErrors) + { + // Ignore invalid padding and return data up to the detected length + byte[] fallbackData = new byte[data.Length - paddingLength]; + Array.Copy(data, 0, fallbackData, 0, fallbackData.Length); + return fallbackData; + } + throw new ArgumentException("Invalid padding detected. Expected padding bytes to be 0x00."); + } + } + + // Extract unpadded data byte[] unpaddedData = new byte[data.Length - paddingLength]; - Array.Copy(data, unpaddedData, unpaddedData.Length); + Array.Copy(data, 0, unpaddedData, 0, unpaddedData.Length); return unpaddedData; } diff --git a/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/LEA.cs b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/LEA.cs index 2e5f0da..0492bb0 100644 --- a/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/LEA.cs +++ b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/LEA.cs @@ -30,6 +30,8 @@ * */ using System; +using System.Security.Cryptography; +using System.Text; namespace WelsonJS.Cryptography { @@ -279,5 +281,69 @@ namespace WelsonJS.Cryptography ENCRYPT, DECRYPT } + + public class ECB + { + private Mode mode; + private LEA engine; + private int blockSize; + + ECB(Mode mode, string key) + { + engine = new LEA(); + Init(mode, CreateKey(key)); + blockSize = engine.GetBlockSize(); + } + + public string GetAlgorithmName() + { + return engine.GetAlgorithmName() + "/ECB"; + } + + public void Init(Mode mode, byte[] mk) + { + this.mode = mode; + engine.Init(mode, mk); + } + + private byte[] CreateKey(string key) + { + SHA256 hasher = SHA256.Create(); + byte[] hashData = hasher.ComputeHash(Encoding.Default.GetBytes(key)); + + return hashData; + } + + public byte[] Encrypt(byte[] data) + { + if (this.mode != Mode.ENCRYPT) + throw new InvalidOperationException("Not initialized for encryption mode."); + + byte[] inputData = PKCS5Padding.AddPadding(data, blockSize); + byte[] outputData = new byte[inputData.Length]; + + for (int i = 0; i < inputData.Length; i += blockSize) + { + engine.ProcessBlock(inputData, i, outputData, i); + } + + return outputData; + } + + public byte[] Decrypt(byte[] data) + { + if (this.mode != Mode.DECRYPT) + throw new InvalidOperationException("Not initialized for decryption mode."); + + byte[] outputData = new byte[data.Length]; + + for (int i = 0; i < data.Length; i += blockSize) + { + engine.ProcessBlock(data, i, outputData, i); + } + + return PKCS5Padding.RemovePadding(outputData, blockSize, true); + } + } } } diff --git a/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/PKCS5Padding.cs b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/PKCS5Padding.cs new file mode 100644 index 0000000..cb16f8c --- /dev/null +++ b/WelsonJS.Toolkit/WelsonJS.Toolkit/Cryptography/PKCS5Padding.cs @@ -0,0 +1,105 @@ +/* + * WelsonJS.Toolkit: WelsonJS native component + * + * filename: + * PKCS5Padding.cs + * + * description: + * PKCS5Padding implementation + * + * website: + * - https://github.com/gnh1201/welsonjs + * - https://catswords.social/@catswords_oss + * - https://teams.live.com/l/community/FEACHncAhq8ldnojAI + * - https://discord.gg/XKG5CjtXEj + * + * authors: + * - Namhyeon Go (@gnh1201) + * + * references: + * - ChatGPT prompt "PKCS5Padding with C#" (chatgpt.com) + * - ChatGPT prompt "PKCS5Padding with C#, Add a flag to decide how to handle possible errors when removing padding." (chatgpt.com) + * + * license: + * GPLv3 or MS-RL(Microsoft Reciprocal License) + * + */ +using System; + +namespace WelsonJS.Cryptography +{ + public class PKCS5Padding + { + // Add padding to the data based on the block size. + public static byte[] AddPadding(byte[] data, int blockSize) + { + int paddingLength = blockSize - (data.Length % blockSize); + byte[] paddedData = new byte[data.Length + paddingLength]; + + // Copy original data into the padded array + Array.Copy(data, paddedData, data.Length); + + // Fill padding with the padding length (PKCS5) + for (int i = data.Length; i < paddedData.Length; i++) + { + paddedData[i] = (byte)paddingLength; + } + + return paddedData; + } + + // Remove padding based on the block size. + public static byte[] RemovePadding(byte[] data, int blockSize, bool ignoreErrors = false) + { + // If data length is 0, return empty array + if (data.Length == 0) + { + return new byte[] { }; + } + + // If data length is smaller than block size, treat it as unpadded + if (data.Length < blockSize) + { + return data; + } + + // Check if the last byte is valid padding (PKCS5) + int paddingLength = data[data.Length - 1]; + + // Validate padding length + if (paddingLength < 1 || paddingLength > blockSize) + { + if (!ignoreErrors) + { + throw new ArgumentException("Invalid padding length."); + } + else + { + return data; // Return data as is if error is ignored + } + } + + // Check if the padding is correct (i.e., all padding bytes must be equal to paddingLength) + for (int i = data.Length - paddingLength; i < data.Length; i++) + { + if (data[i] != paddingLength) + { + if (!ignoreErrors) + { + throw new ArgumentException("Invalid padding detected."); + } + else + { + return data; // Return data as is if error is ignored + } + } + } + + // Remove the padding + byte[] unpaddedData = new byte[data.Length - paddingLength]; + Array.Copy(data, unpaddedData, unpaddedData.Length); + + return unpaddedData; + } + } +} diff --git a/WelsonJS.Toolkit/WelsonJS.Toolkit/WelsonJS.Toolkit.csproj b/WelsonJS.Toolkit/WelsonJS.Toolkit/WelsonJS.Toolkit.csproj index 6f914f9..3dda594 100644 --- a/WelsonJS.Toolkit/WelsonJS.Toolkit/WelsonJS.Toolkit.csproj +++ b/WelsonJS.Toolkit/WelsonJS.Toolkit/WelsonJS.Toolkit.csproj @@ -93,6 +93,7 @@ +