From af5b858475fd83d942330fdff8233a2a7c1826e5 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Sun, 29 Aug 2021 18:25:26 +0200 Subject: [PATCH 01/19] Add FEATURE_AES_CSP to use hardware-accelerated AesCryptoServiceProvider Reduces CPU usage dramatically, allowing more performance on slower machines --- src/Renci.SshNet/Renci.SshNet.csproj | 2 +- .../Security/Cryptography/BlockCipher.cs | 102 +++++++++++------- .../Cryptography/Ciphers/CipherMode.cs | 80 +++++++++++++- .../Ciphers/Modes/CbcCipherMode.cs | 4 + .../Ciphers/Modes/CfbCipherMode.cs | 6 ++ .../Ciphers/Modes/CtrCipherMode.cs | 79 ++++++++++++++ .../Ciphers/Modes/EcbCipherMode.cs | 56 ++++++++++ .../Ciphers/Modes/OfbCipherMode.cs | 6 ++ 8 files changed, 293 insertions(+), 42 deletions(-) create mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 4ccf6c72e..860a6de8f 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -18,6 +18,6 @@ - FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP + FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP;FEATURE_AES_CSP diff --git a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs index b9f7dde58..19494b497 100644 --- a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs @@ -1,4 +1,5 @@ using System; +using csp = System.Security.Cryptography; using Renci.SshNet.Security.Cryptography.Ciphers; namespace Renci.SshNet.Security.Cryptography @@ -60,7 +61,20 @@ protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding _mode = mode; _padding = padding; +#if FEATURE_AES_CSP + // use AesCryptoServiceProvider which uses AES-NI (faster, less CPU usage) + csp.AesCryptoServiceProvider aesProvider = new csp.AesCryptoServiceProvider() + { + BlockSize = blockSize * 8, + KeySize = Key.Length * 8, + Mode = _mode.cspMode, + Padding = padding == null ? csp.PaddingMode.None : csp.PaddingMode.PKCS7, + Key = key + }; + _mode?.Init(this, aesProvider); +#else _mode?.Init(this); +#endif } /// @@ -74,34 +88,42 @@ protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding /// public override byte[] Encrypt(byte[] input, int offset, int length) { - if (length % _blockSize > 0) - { - if (_padding is null) - { - throw new ArgumentException("data"); - } - - var paddingLength = _blockSize - (length % _blockSize); - input = _padding.Pad(input, offset, length, paddingLength); - length += paddingLength; - offset = 0; - } - var output = new byte[length]; var writtenBytes = 0; - for (var i = 0; i < length / _blockSize; i++) +#if FEATURE_AES_CSP + if (_mode is not null && _mode.isCspAvailable) + writtenBytes = _mode.EncryptWithCSP(input, offset, output); + else { - if (_mode is null) +#endif + if (length % _blockSize > 0) { - writtenBytes += EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + if (_padding is null) + { + throw new ArgumentException("data"); + } + + var paddingLength = _blockSize - (length % _blockSize); + input = _padding.Pad(input, offset, length, paddingLength); + length += paddingLength; + offset = 0; } - else + + for (var i = 0; i < length / _blockSize; i++) { - writtenBytes += _mode.EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + if (_mode is null) + { + writtenBytes += EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + } + else + { + writtenBytes += _mode.EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + } } +#if FEATURE_AES_CSP } - +#endif if (writtenBytes < length) { throw new InvalidOperationException("Encryption error."); @@ -133,33 +155,37 @@ public override byte[] Decrypt(byte[] input) /// public override byte[] Decrypt(byte[] input, int offset, int length) { - if (length % _blockSize > 0) - { - if (_padding is null) - { - throw new ArgumentException("data"); - } - - input = _padding.Pad(_blockSize, input, offset, length); - offset = 0; - length = input.Length; - } - var output = new byte[length]; - var writtenBytes = 0; - for (var i = 0; i < length / _blockSize; i++) + +#if FEATURE_AES_CSP + if (_mode is not null && _mode.isCspAvailable) + writtenBytes = _mode.DecryptWithCSP(input, offset, output); + else { - if (_mode is null) +#endif + if (length % _blockSize > 0) { - writtenBytes += DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + if (_padding is null) + { + throw new ArgumentException("data"); + } + + input = _padding.Pad(_blockSize, input, offset, length); + offset = 0; + length = input.Length; } - else + + for (var i = 0; i < length / _blockSize; i++) { - writtenBytes += _mode.DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + if (_mode is null) + writtenBytes += DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + else + writtenBytes += _mode.DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } +#if FEATURE_AES_CSP } - +#endif if (writtenBytes < length) { throw new InvalidOperationException("Encryption error."); diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs index 490756aff..a5fda5d34 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs @@ -1,4 +1,6 @@ -using Renci.SshNet.Common; +using System; +using Renci.SshNet.Common; +using csp = System.Security.Cryptography; namespace Renci.SshNet.Security.Cryptography.Ciphers { @@ -17,7 +19,7 @@ public abstract class CipherMode /// /// Gets the IV vector. /// - protected byte[] IV; + internal byte[] IV; /// /// Holds block size of the cipher. @@ -32,6 +34,9 @@ public abstract class CipherMode /// The iv. protected CipherMode(byte[] iv) { + if (iv.Length < 16) + throw new ArgumentException("Invalid AES IV length"); + IV = iv; } @@ -70,6 +75,75 @@ internal void Init(BlockCipher cipher) /// /// The number of bytes decrypted. /// - public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); + public virtual int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + // By default, use the same EncryptBlock() function + // Modes that require a different implementation (non-symmetric) can override this function (CBC/CFB) + return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + +#if FEATURE_AES_CSP + // CryptoServiceProvider acceleration using AES-NI if supported + internal csp.ICryptoTransform aesDecryptor; + internal csp.ICryptoTransform aesEncryptor; + + // set to false when CSP is not available; falls back to legacy code + internal bool isCspAvailable = true; + + // corresponding CSP cipher mode + internal csp.CipherMode cspMode = csp.CipherMode.ECB; + + /// + /// If true, performs decryption using aesEncryptor + /// + protected bool cspDecryptAsEncrypt = true; + + /// + /// Initializes the specified cipher mode using CryptoServiceProvider acceleration + /// + /// The cipher. + /// The cryptoServiceProvider instance + internal void Init(BlockCipher cipher, csp.AesCryptoServiceProvider csp) + { + Init(cipher); + try + { + aesDecryptor = csp.CreateDecryptor(csp.Key, IV); + aesEncryptor = csp.CreateEncryptor(csp.Key, IV); + } + catch + { + // OFB/CFB might not be available on some versions of Windows - fallback to legacy code + isCspAvailable = false; + } + } + + /// + /// Encrypts the specified region of the input byte array using AesCryptoServiceProvider + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The output to which to write encrypted data. + /// The number of bytes encrypted + public virtual int EncryptWithCSP(byte[] data, int offset, byte[] output) + { + return aesEncryptor.TransformBlock(data, offset, output.Length, output, 0); + } + + /// + /// Decrypts the specified region of the input byte array using AesCryptoServiceProvider + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The output to which to write decrypted data. + /// The number of bytes decrypted + public virtual int DecryptWithCSP(byte[] data, int offset, byte[] output) + { + if (cspDecryptAsEncrypt) + return EncryptWithCSP(data, offset, output); + + return aesDecryptor.TransformBlock(data, offset, output.Length, output, 0); + } +#endif } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs index a2b9243d4..6ad19b271 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs @@ -15,6 +15,10 @@ public class CbcCipherMode : CipherMode public CbcCipherMode(byte[] iv) : base(iv) { +#if FEATURE_AES_CSP + cspMode = System.Security.Cryptography.CipherMode.CBC; + cspDecryptAsEncrypt = false; +#endif } /// diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs index 23a4bb2f7..08f09b2e9 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs @@ -18,6 +18,12 @@ public CfbCipherMode(byte[] iv) : base(iv) { _ivOutput = new byte[iv.Length]; + +#if FEATURE_AES_CSP + cspMode = System.Security.Cryptography.CipherMode.CFB; + // note: the legacy code also uses EncryptBlock() on the DecryptBlock() function + cspDecryptAsEncrypt = true; +#endif } /// diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs index a0ae5010b..1c512b5d3 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs @@ -10,6 +10,11 @@ public class CtrCipherMode : CipherMode { private readonly byte[] _ivOutput; +#if FEATURE_AES_CSP + // IV as uint[] for usage with CryptoServiceProvider + private uint[] _ivCSP; +#endif + /// /// Initializes a new instance of the class. /// @@ -18,6 +23,19 @@ public CtrCipherMode(byte[] iv) : base(iv) { _ivOutput = new byte[iv.Length]; + +#if FEATURE_AES_CSP + // CTR uses ECB plus an incrementing IV + cspMode = System.Security.Cryptography.CipherMode.ECB; + cspDecryptAsEncrypt = true; + + // convert the IV into an array of uint[4] for speed + _ivCSP = new uint[4]; + _ivCSP[0] = (uint)((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | (iv[3])); + _ivCSP[1] = (uint)((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | (iv[7])); + _ivCSP[2] = (uint)((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | (iv[11])); + _ivCSP[3] = (uint)((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | (iv[15])); +#endif } /// @@ -64,6 +82,67 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC return _blockSize; } +#if FEATURE_AES_CSP + /// + /// Encrypts the specified region of the input byte array using AesCryptoServiceProvider + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The output to which to write encrypted data. + /// The number of bytes encrypted + public override int EncryptWithCSP(byte[] data, int offset, byte[] output) + { + byte[] counter = CreateIVCounter(data, offset, output.Length); + int bytes = aesEncryptor.TransformBlock(counter, 0, output.Length, output, 0); + DataXorCounter(data, offset, output); + return bytes; + } + + // creates the Counter array filled with incrementing copies of IV + private byte[] CreateIVCounter(byte[] inputBuffer, int offset, int length) + { + // fill an array with IV, increment by 1 for each copy + uint[] counter = new uint[length / 4]; + for (int i = 0; i < counter.Length; i += 4) + { + // write IV to buffer (big endian) + counter[i] = (_ivCSP[0] << 24) | ((_ivCSP[0] << 8) & 0x00FF0000) | ((_ivCSP[0] >> 8) & 0x0000FF00) | (_ivCSP[0] >> 24); + counter[i + 1] = (_ivCSP[1] << 24) | ((_ivCSP[1] << 8) & 0x00FF0000) | ((_ivCSP[1] >> 8) & 0x0000FF00) | (_ivCSP[1] >> 24); + counter[i + 2] = (_ivCSP[2] << 24) | ((_ivCSP[2] << 8) & 0x00FF0000) | ((_ivCSP[2] >> 8) & 0x0000FF00) | (_ivCSP[2] >> 24); + counter[i + 3] = (_ivCSP[3] << 24) | ((_ivCSP[3] << 8) & 0x00FF0000) | ((_ivCSP[3] >> 8) & 0x0000FF00) | (_ivCSP[3] >> 24); + + // increment IV (little endian) + for (int j = 3; j >= 0 && ++_ivCSP[j] == 0; j--) ; + } + + // copy uint[] to byte[] + byte[] counterBytes = new byte[length]; + System.Buffer.BlockCopy(counter, 0, counterBytes, 0, length); + return counterBytes; + } + + // XORs the input data with the encrypted Counter array to produce the final output + private void DataXorCounter(byte[] data, int offset, byte[] output) + { + int length = output.Length; + + // original data + uint[] inwords = new uint[length / 4]; + System.Buffer.BlockCopy(data, offset, inwords, 0, length); + + // encrypted IV counter data + uint[] outwords = new uint[length / 4]; + System.Buffer.BlockCopy(output, 0, outwords, 0, length); + + // XOR encrypted Counter with input data (use uint arrays for speed) + for (int i = 0; i < outwords.Length; i++) + outwords[i] = outwords[i] ^ inwords[i]; + + // copy final data to output byte[] + System.Buffer.BlockCopy(outwords, 0, output, 0, length); + } +#endif + /// /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. /// diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs new file mode 100644 index 000000000..f10b32a01 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs @@ -0,0 +1,56 @@ +using System; +using System.Globalization; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +{ + /// + /// Implements ECB cipher mode + /// + public class EcbCipherMode : CipherMode + { + /// + /// Initializes a new instance of the class. + /// + /// The iv. + public EcbCipherMode(byte[] iv) + : base(iv) + { +#if FEATURE_AES_CSP + cspMode = System.Security.Cryptography.CipherMode.ECB; + cspDecryptAsEncrypt = false; +#endif + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + return Cipher.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + return Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + } +} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs index e87dc9d32..826e84a44 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs @@ -18,6 +18,12 @@ public OfbCipherMode(byte[] iv) : base(iv) { _ivOutput = new byte[iv.Length]; + +#if FEATURE_AES_CSP + cspMode = System.Security.Cryptography.CipherMode.OFB; + // note: the legacy code also uses EncryptBlock() on the DecryptBlock() function + cspDecryptAsEncrypt = true; +#endif } /// From 96226312ae9efd4fddf231e626d20a447bbf8769 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Mon, 30 Aug 2021 18:04:00 +0200 Subject: [PATCH 02/19] Restructure, move most of the feature code to AesCipher.cs Fix padding for non-AES blockciphers Fix IV exception for non-AES blockciphers --- .../Security/Cryptography/BlockCipher.cs | 97 +++----- .../Cryptography/Ciphers/AesCipher.cs | 218 +++++++++++++++++- .../Cryptography/Ciphers/CipherMode.cs | 80 +------ .../Ciphers/Modes/CbcCipherMode.cs | 4 - .../Ciphers/Modes/CfbCipherMode.cs | 6 - .../Ciphers/Modes/CtrCipherMode.cs | 79 ------- .../Ciphers/Modes/EcbCipherMode.cs | 56 ----- .../Ciphers/Modes/OfbCipherMode.cs | 8 +- 8 files changed, 252 insertions(+), 296 deletions(-) delete mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs diff --git a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs index 19494b497..9bfdb6c77 100644 --- a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs @@ -1,5 +1,4 @@ using System; -using csp = System.Security.Cryptography; using Renci.SshNet.Security.Cryptography.Ciphers; namespace Renci.SshNet.Security.Cryptography @@ -61,20 +60,7 @@ protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding _mode = mode; _padding = padding; -#if FEATURE_AES_CSP - // use AesCryptoServiceProvider which uses AES-NI (faster, less CPU usage) - csp.AesCryptoServiceProvider aesProvider = new csp.AesCryptoServiceProvider() - { - BlockSize = blockSize * 8, - KeySize = Key.Length * 8, - Mode = _mode.cspMode, - Padding = padding == null ? csp.PaddingMode.None : csp.PaddingMode.PKCS7, - Key = key - }; - _mode?.Init(this, aesProvider); -#else _mode?.Init(this); -#endif } /// @@ -88,42 +74,34 @@ protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding /// public override byte[] Encrypt(byte[] input, int offset, int length) { + if (length % _blockSize > 0) + { + if (_padding is null) + { + throw new ArgumentException("data"); + } + + var paddingLength = _blockSize - (length % _blockSize); + input = _padding.Pad(input, offset, length, paddingLength); + length += paddingLength; + offset = 0; + } + var output = new byte[length]; var writtenBytes = 0; -#if FEATURE_AES_CSP - if (_mode is not null && _mode.isCspAvailable) - writtenBytes = _mode.EncryptWithCSP(input, offset, output); - else + for (var i = 0; i < length / _blockSize; i++) { -#endif - if (length % _blockSize > 0) + if (_mode is null) { - if (_padding is null) - { - throw new ArgumentException("data"); - } - - var paddingLength = _blockSize - (length % _blockSize); - input = _padding.Pad(input, offset, length, paddingLength); - length += paddingLength; - offset = 0; + writtenBytes += EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } - - for (var i = 0; i < length / _blockSize; i++) + else { - if (_mode is null) - { - writtenBytes += EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); - } - else - { - writtenBytes += _mode.EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); - } + writtenBytes += _mode.EncryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } -#if FEATURE_AES_CSP } -#endif + if (writtenBytes < length) { throw new InvalidOperationException("Encryption error."); @@ -155,37 +133,32 @@ public override byte[] Decrypt(byte[] input) /// public override byte[] Decrypt(byte[] input, int offset, int length) { - var output = new byte[length]; - var writtenBytes = 0; - -#if FEATURE_AES_CSP - if (_mode is not null && _mode.isCspAvailable) - writtenBytes = _mode.DecryptWithCSP(input, offset, output); - else + if (length % _blockSize > 0) { -#endif - if (length % _blockSize > 0) - { if (_padding is null) - { - throw new ArgumentException("data"); - } + { + throw new ArgumentException("data"); + } input = _padding.Pad(_blockSize, input, offset, length); offset = 0; length = input.Length; - } + } - for (var i = 0; i < length / _blockSize; i++) + var output = new byte[length]; + var writtenBytes = 0; + for (var i = 0; i < length / _blockSize; i++) + { + if (_mode is null) { - if (_mode is null) - writtenBytes += DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); - else - writtenBytes += _mode.DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + writtenBytes += DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); + } + else + { + writtenBytes += _mode.DecryptBlock(input, offset + (i * _blockSize), _blockSize, output, i * _blockSize); } -#if FEATURE_AES_CSP } -#endif + if (writtenBytes < length) { throw new InvalidOperationException("Encryption error."); diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 517f2b463..48960a296 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -1,7 +1,7 @@ using System; using System.Globalization; - using Renci.SshNet.Common; +using csp = System.Security.Cryptography; namespace Renci.SshNet.Security.Cryptography.Ciphers { @@ -17,10 +17,15 @@ public sealed class AesCipher : BlockCipher private int _rounds; private uint[] _encryptionKey; private uint[] _decryptionKey; - private uint _c0; - private uint _c1; - private uint _c2; - private uint _c3; + private uint C0, C1, C2, C3; + +#if FEATURE_AES_CSP + private csp.ICryptoTransform aesDecryptor; + private csp.ICryptoTransform aesEncryptor; + private bool useCSP; // set to false when CSP is not available for a given mode; falls back to legacy code + private bool isCTRMode; + private uint[] _ctrIV; +#endif #region Static Definition Tables @@ -572,6 +577,11 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); } + +#if FEATURE_AES_CSP + // initialize AesCryptoServiceProvider which uses AES-NI (faster, less CPU usage) + useCSP = initCryptoServiceProvider(mode, padding); +#endif } /// @@ -666,6 +676,204 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC return BlockSize; } +#if FEATURE_AES_CSP + + /// + /// Encrypts the specified data using AesCryptoServiceProvider + /// + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . + /// Encrypted data + public override byte[] Encrypt(byte[] data, int offset, int length) + { + if (useCSP) + { + if (isCTRMode) + return CTREncryptDecrypt(data, offset, length); + else + { + if (length % BlockSize == 0) + { + byte[] output = new byte[length]; + aesEncryptor.TransformBlock(data, offset, length, output, 0); + return output; + } + else + { + // adds padding + byte[] output = aesEncryptor.TransformFinalBlock(data, offset, length); + return output; + } + } + } + else + return base.Encrypt(data, offset, length); + } + + /// + /// Decrypts the specified input using AesCryptoServiceProvider + /// + /// The input. + /// The zero-based offset in at which to begin decrypting. + /// The number of bytes to decrypt from . + /// + /// The decrypted data. + /// + public override byte[] Decrypt(byte[] data, int offset, int length) + { + if (useCSP) + { + if (isCTRMode) + return CTREncryptDecrypt(data, offset, length); + else + { + if (length % BlockSize == 0) + { + byte[] output = new byte[length]; + aesDecryptor.TransformBlock(data, offset, length, output, 0); + return output; + } + else + { + // handles padding + byte[] output = aesDecryptor.TransformFinalBlock(data, offset, length); + return output; + } + + + //byte[] ok = base.Decrypt(data, offset, length); + //for (int i = 0; i < a1.Length; i++) + // if (a1[i] != ok[i] || a1.Length != ok.Length) + // return null; + + //for (int i = 0; i < a1.Length; i++) + // if (a2[i] != ok[i] || a1.Length != ok.Length) + // return null; + + //return a1; + } + } + else + return base.Encrypt(data, offset, length); + } + + // initialize AesCryptoServiceProvider + private bool initCryptoServiceProvider(CipherMode mode, CipherPadding padding) + { + try + { + csp.PaddingMode cspPadding = padding == null ? csp.PaddingMode.None : csp.PaddingMode.PKCS7; // PKCS5 is same as PKCS7 + csp.CipherMode cspMode = 0; + isCTRMode = mode is Modes.CtrCipherMode; + + if (mode is Modes.CbcCipherMode) + cspMode = csp.CipherMode.CBC; + else if (isCTRMode) + cspMode = csp.CipherMode.ECB; // CTR uses ECB + else + return false; // OFB and CFB not supported, fallback to managed code + + // prepare IV array for CTR mode + if (isCTRMode) + _ctrIV = GetPackedIV(mode.IV); + + // create ICryptoTransform instances + var aesProvider = new csp.AesCryptoServiceProvider() + { + BlockSize = BlockSize * 8, + KeySize = Key.Length * 8, + Mode = cspMode, + Padding = cspPadding, + Key = Key, + IV = mode.IV, + }; + aesEncryptor = aesProvider.CreateEncryptor(Key, mode.IV); + aesDecryptor = aesProvider.CreateDecryptor(Key, mode.IV); + return true; + } + catch { } // fallback for unsupported key/iv/blocksize combinations + return false; + } + + // convert the IV into an array of uint[4] + private uint[] GetPackedIV(byte[] iv) + { + uint[] packedIV = new uint[4]; + packedIV[0] = (uint)((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | (iv[3])); + packedIV[1] = (uint)((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | (iv[7])); + packedIV[2] = (uint)((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | (iv[11])); + packedIV[3] = (uint)((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | (iv[15])); + return packedIV; + } + + // Perform AES-CTR encryption/decryption + private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) + { + int count = length / BlockSize; + if (length % BlockSize != 0) count++; + + byte[] counter = CTRCreateCounterArray(count); + byte[] aesCounter = aesEncryptor.TransformFinalBlock(counter, 0, counter.Length); + byte[] output = CTRArrayXOR(aesCounter, data, offset, length); + + return output; + } + + // creates the Counter array filled with incrementing copies of IV + private byte[] CTRCreateCounterArray(int blocks) + { + // fill an array with IV, increment by 1 for each copy + uint[] counter = new uint[blocks * 4]; + for (int i = 0; i < counter.Length; i += 4) + { + // write IV to buffer (big endian) + counter[i] = (_ctrIV[0] << 24) | ((_ctrIV[0] << 8) & 0x00FF0000) | ((_ctrIV[0] >> 8) & 0x0000FF00) | (_ctrIV[0] >> 24); + counter[i + 1] = (_ctrIV[1] << 24) | ((_ctrIV[1] << 8) & 0x00FF0000) | ((_ctrIV[1] >> 8) & 0x0000FF00) | (_ctrIV[1] >> 24); + counter[i + 2] = (_ctrIV[2] << 24) | ((_ctrIV[2] << 8) & 0x00FF0000) | ((_ctrIV[2] >> 8) & 0x0000FF00) | (_ctrIV[2] >> 24); + counter[i + 3] = (_ctrIV[3] << 24) | ((_ctrIV[3] << 8) & 0x00FF0000) | ((_ctrIV[3] >> 8) & 0x0000FF00) | (_ctrIV[3] >> 24); + + // increment IV (little endian) + for (int j = 3; j >= 0 && ++_ctrIV[j] == 0; j--) ; + } + + // copy uint[] to byte[] + byte[] counterBytes = new byte[blocks * 16]; + System.Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); + return counterBytes; + } + + // XORs the input data with the encrypted Counter array to produce the final output + // uses uint arrays for speed + private byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) + { + int words = length / 4; + if (length % 4 != 0) words++; + + // convert original data to words + uint[] datawords = new uint[words]; + System.Buffer.BlockCopy(data, offset, datawords, 0, length); + + // convert encrypted IV counter to words + uint[] counterwords = new uint[words]; + System.Buffer.BlockCopy(counter, 0, counterwords, 0, length); + + // XOR encrypted Counter with input data + for (int i = 0; i < words; i++) + counterwords[i] = counterwords[i] ^ datawords[i]; + + // copy uint[] to byte[] + byte[] output = counter; + System.Buffer.BlockCopy(counterwords, 0, output, 0, length); + + // adjust output for non-aligned lengths + if (output.Length > length) + Array.Resize(ref output, length); + + return output; + } +#endif + private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) { var KC = key.Length / 4; // key length in words diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs index a5fda5d34..e3a4a5f81 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs @@ -1,6 +1,4 @@ -using System; -using Renci.SshNet.Common; -using csp = System.Security.Cryptography; +using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography.Ciphers { @@ -19,7 +17,7 @@ public abstract class CipherMode /// /// Gets the IV vector. /// - internal byte[] IV; + internal byte[] IV {get; private set;} /// /// Holds block size of the cipher. @@ -34,9 +32,6 @@ public abstract class CipherMode /// The iv. protected CipherMode(byte[] iv) { - if (iv.Length < 16) - throw new ArgumentException("Invalid AES IV length"); - IV = iv; } @@ -75,75 +70,6 @@ internal void Init(BlockCipher cipher) /// /// The number of bytes decrypted. /// - public virtual int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - // By default, use the same EncryptBlock() function - // Modes that require a different implementation (non-symmetric) can override this function (CBC/CFB) - return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - -#if FEATURE_AES_CSP - // CryptoServiceProvider acceleration using AES-NI if supported - internal csp.ICryptoTransform aesDecryptor; - internal csp.ICryptoTransform aesEncryptor; - - // set to false when CSP is not available; falls back to legacy code - internal bool isCspAvailable = true; - - // corresponding CSP cipher mode - internal csp.CipherMode cspMode = csp.CipherMode.ECB; - - /// - /// If true, performs decryption using aesEncryptor - /// - protected bool cspDecryptAsEncrypt = true; - - /// - /// Initializes the specified cipher mode using CryptoServiceProvider acceleration - /// - /// The cipher. - /// The cryptoServiceProvider instance - internal void Init(BlockCipher cipher, csp.AesCryptoServiceProvider csp) - { - Init(cipher); - try - { - aesDecryptor = csp.CreateDecryptor(csp.Key, IV); - aesEncryptor = csp.CreateEncryptor(csp.Key, IV); - } - catch - { - // OFB/CFB might not be available on some versions of Windows - fallback to legacy code - isCspAvailable = false; - } - } - - /// - /// Encrypts the specified region of the input byte array using AesCryptoServiceProvider - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The output to which to write encrypted data. - /// The number of bytes encrypted - public virtual int EncryptWithCSP(byte[] data, int offset, byte[] output) - { - return aesEncryptor.TransformBlock(data, offset, output.Length, output, 0); - } - - /// - /// Decrypts the specified region of the input byte array using AesCryptoServiceProvider - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The output to which to write decrypted data. - /// The number of bytes decrypted - public virtual int DecryptWithCSP(byte[] data, int offset, byte[] output) - { - if (cspDecryptAsEncrypt) - return EncryptWithCSP(data, offset, output); - - return aesDecryptor.TransformBlock(data, offset, output.Length, output, 0); - } -#endif + public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs index 6ad19b271..a2b9243d4 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs @@ -15,10 +15,6 @@ public class CbcCipherMode : CipherMode public CbcCipherMode(byte[] iv) : base(iv) { -#if FEATURE_AES_CSP - cspMode = System.Security.Cryptography.CipherMode.CBC; - cspDecryptAsEncrypt = false; -#endif } /// diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs index 08f09b2e9..23a4bb2f7 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs @@ -18,12 +18,6 @@ public CfbCipherMode(byte[] iv) : base(iv) { _ivOutput = new byte[iv.Length]; - -#if FEATURE_AES_CSP - cspMode = System.Security.Cryptography.CipherMode.CFB; - // note: the legacy code also uses EncryptBlock() on the DecryptBlock() function - cspDecryptAsEncrypt = true; -#endif } /// diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs index 1c512b5d3..a0ae5010b 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs @@ -10,11 +10,6 @@ public class CtrCipherMode : CipherMode { private readonly byte[] _ivOutput; -#if FEATURE_AES_CSP - // IV as uint[] for usage with CryptoServiceProvider - private uint[] _ivCSP; -#endif - /// /// Initializes a new instance of the class. /// @@ -23,19 +18,6 @@ public CtrCipherMode(byte[] iv) : base(iv) { _ivOutput = new byte[iv.Length]; - -#if FEATURE_AES_CSP - // CTR uses ECB plus an incrementing IV - cspMode = System.Security.Cryptography.CipherMode.ECB; - cspDecryptAsEncrypt = true; - - // convert the IV into an array of uint[4] for speed - _ivCSP = new uint[4]; - _ivCSP[0] = (uint)((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | (iv[3])); - _ivCSP[1] = (uint)((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | (iv[7])); - _ivCSP[2] = (uint)((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | (iv[11])); - _ivCSP[3] = (uint)((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | (iv[15])); -#endif } /// @@ -82,67 +64,6 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC return _blockSize; } -#if FEATURE_AES_CSP - /// - /// Encrypts the specified region of the input byte array using AesCryptoServiceProvider - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The output to which to write encrypted data. - /// The number of bytes encrypted - public override int EncryptWithCSP(byte[] data, int offset, byte[] output) - { - byte[] counter = CreateIVCounter(data, offset, output.Length); - int bytes = aesEncryptor.TransformBlock(counter, 0, output.Length, output, 0); - DataXorCounter(data, offset, output); - return bytes; - } - - // creates the Counter array filled with incrementing copies of IV - private byte[] CreateIVCounter(byte[] inputBuffer, int offset, int length) - { - // fill an array with IV, increment by 1 for each copy - uint[] counter = new uint[length / 4]; - for (int i = 0; i < counter.Length; i += 4) - { - // write IV to buffer (big endian) - counter[i] = (_ivCSP[0] << 24) | ((_ivCSP[0] << 8) & 0x00FF0000) | ((_ivCSP[0] >> 8) & 0x0000FF00) | (_ivCSP[0] >> 24); - counter[i + 1] = (_ivCSP[1] << 24) | ((_ivCSP[1] << 8) & 0x00FF0000) | ((_ivCSP[1] >> 8) & 0x0000FF00) | (_ivCSP[1] >> 24); - counter[i + 2] = (_ivCSP[2] << 24) | ((_ivCSP[2] << 8) & 0x00FF0000) | ((_ivCSP[2] >> 8) & 0x0000FF00) | (_ivCSP[2] >> 24); - counter[i + 3] = (_ivCSP[3] << 24) | ((_ivCSP[3] << 8) & 0x00FF0000) | ((_ivCSP[3] >> 8) & 0x0000FF00) | (_ivCSP[3] >> 24); - - // increment IV (little endian) - for (int j = 3; j >= 0 && ++_ivCSP[j] == 0; j--) ; - } - - // copy uint[] to byte[] - byte[] counterBytes = new byte[length]; - System.Buffer.BlockCopy(counter, 0, counterBytes, 0, length); - return counterBytes; - } - - // XORs the input data with the encrypted Counter array to produce the final output - private void DataXorCounter(byte[] data, int offset, byte[] output) - { - int length = output.Length; - - // original data - uint[] inwords = new uint[length / 4]; - System.Buffer.BlockCopy(data, offset, inwords, 0, length); - - // encrypted IV counter data - uint[] outwords = new uint[length / 4]; - System.Buffer.BlockCopy(output, 0, outwords, 0, length); - - // XOR encrypted Counter with input data (use uint arrays for speed) - for (int i = 0; i < outwords.Length; i++) - outwords[i] = outwords[i] ^ inwords[i]; - - // copy final data to output byte[] - System.Buffer.BlockCopy(outwords, 0, output, 0, length); - } -#endif - /// /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. /// diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs deleted file mode 100644 index f10b32a01..000000000 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements ECB cipher mode - /// - public class EcbCipherMode : CipherMode - { - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public EcbCipherMode(byte[] iv) - : base(iv) - { -#if FEATURE_AES_CSP - cspMode = System.Security.Cryptography.CipherMode.ECB; - cspDecryptAsEncrypt = false; -#endif - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - return Cipher.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - return Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - } -} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs index 826e84a44..d3e2df2a6 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs @@ -18,12 +18,6 @@ public OfbCipherMode(byte[] iv) : base(iv) { _ivOutput = new byte[iv.Length]; - -#if FEATURE_AES_CSP - cspMode = System.Security.Cryptography.CipherMode.OFB; - // note: the legacy code also uses EncryptBlock() on the DecryptBlock() function - cspDecryptAsEncrypt = true; -#endif } /// @@ -81,6 +75,6 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } + } } From 75fa982c24e594625483c3eae9000edad1da1c00 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Mon, 30 Aug 2021 19:49:25 +0200 Subject: [PATCH 03/19] Fix the AES Padding It looks like the legacy code doesn't correctly remove padding, so this code needs to do the same. --- .../Cryptography/Ciphers/AesCipher.cs | 72 +++++++++---------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 48960a296..f92e7b438 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -25,6 +25,8 @@ public sealed class AesCipher : BlockCipher private bool useCSP; // set to false when CSP is not available for a given mode; falls back to legacy code private bool isCTRMode; private uint[] _ctrIV; + + CipherPadding _padding; #endif #region Static Definition Tables @@ -689,22 +691,24 @@ public override byte[] Encrypt(byte[] data, int offset, int length) { if (useCSP) { + if (length % BlockSize > 0) + { + if (_padding == null) + { + throw new ArgumentException("data"); + } + data = _padding.Pad(BlockSize, data, offset, length); + offset = 0; + length = data.Length; + } + if (isCTRMode) return CTREncryptDecrypt(data, offset, length); else { - if (length % BlockSize == 0) - { - byte[] output = new byte[length]; - aesEncryptor.TransformBlock(data, offset, length, output, 0); - return output; - } - else - { - // adds padding - byte[] output = aesEncryptor.TransformFinalBlock(data, offset, length); - return output; - } + byte[] output = new byte[length]; + aesEncryptor.TransformBlock(data, offset, length, output, 0); + return output; } } else @@ -724,34 +728,24 @@ public override byte[] Decrypt(byte[] data, int offset, int length) { if (useCSP) { + if (length % BlockSize > 0) + { + if (_padding == null) + { + throw new ArgumentException("data"); + } + data = _padding.Pad(BlockSize, data, offset, length); + offset = 0; + length = data.Length; + } + if (isCTRMode) return CTREncryptDecrypt(data, offset, length); else { - if (length % BlockSize == 0) - { - byte[] output = new byte[length]; - aesDecryptor.TransformBlock(data, offset, length, output, 0); - return output; - } - else - { - // handles padding - byte[] output = aesDecryptor.TransformFinalBlock(data, offset, length); - return output; - } - - - //byte[] ok = base.Decrypt(data, offset, length); - //for (int i = 0; i < a1.Length; i++) - // if (a1[i] != ok[i] || a1.Length != ok.Length) - // return null; - - //for (int i = 0; i < a1.Length; i++) - // if (a2[i] != ok[i] || a1.Length != ok.Length) - // return null; - - //return a1; + byte[] output = new byte[length]; + aesDecryptor.TransformBlock(data, offset, length, output, 0); + return output; } } else @@ -763,7 +757,11 @@ private bool initCryptoServiceProvider(CipherMode mode, CipherPadding padding) { try { - csp.PaddingMode cspPadding = padding == null ? csp.PaddingMode.None : csp.PaddingMode.PKCS7; // PKCS5 is same as PKCS7 + // use the provided CipherPadding object + _padding = padding; + csp.PaddingMode cspPadding = csp.PaddingMode.None; + + // set the Mode csp.CipherMode cspMode = 0; isCTRMode = mode is Modes.CtrCipherMode; From 5cc5d4a72096b3a083e7c20831e82579cb20ee5f Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Wed, 1 Nov 2023 18:03:58 +0100 Subject: [PATCH 04/19] fix rebase issues restructure AES CSP code into its own class --- src/Renci.SshNet/Renci.SshNet.csproj | 2 +- .../Cryptography/Ciphers/AesCipher.cs | 220 ++-------------- .../Cryptography/Ciphers/AesCipherCSP.cs | 241 ++++++++++++++++++ .../Ciphers/Modes/OfbCipherMode.cs | 2 +- 4 files changed, 271 insertions(+), 194 deletions(-) create mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 860a6de8f..16038d29b 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -6,7 +6,7 @@ - FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160 + FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160;FEATURE_AES_CSP diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index f92e7b438..a9bf9b80d 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using Renci.SshNet.Common; -using csp = System.Security.Cryptography; namespace Renci.SshNet.Security.Cryptography.Ciphers { @@ -14,20 +13,15 @@ public sealed class AesCipher : BlockCipher private const uint M2 = 0x7f7f7f7f; private const uint M3 = 0x0000001b; + private readonly AesCipherCSP _aesCSP; + private int _rounds; private uint[] _encryptionKey; private uint[] _decryptionKey; - private uint C0, C1, C2, C3; - -#if FEATURE_AES_CSP - private csp.ICryptoTransform aesDecryptor; - private csp.ICryptoTransform aesEncryptor; - private bool useCSP; // set to false when CSP is not available for a given mode; falls back to legacy code - private bool isCTRMode; - private uint[] _ctrIV; - - CipherPadding _padding; -#endif + private uint _c0; + private uint _c1; + private uint _c2; + private uint _c3; #region Static Definition Tables @@ -580,10 +574,8 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); } -#if FEATURE_AES_CSP // initialize AesCryptoServiceProvider which uses AES-NI (faster, less CPU usage) - useCSP = initCryptoServiceProvider(mode, padding); -#endif + _aesCSP = new AesCipherCSP(key, 16, mode, padding); } /// @@ -678,199 +670,43 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC return BlockSize; } -#if FEATURE_AES_CSP - /// - /// Encrypts the specified data using AesCryptoServiceProvider + /// Encrypts the specified data using CSP acceleration if enabled. /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . - /// Encrypted data - public override byte[] Encrypt(byte[] data, int offset, int length) + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . + /// + /// The encrypted data. + /// + public override byte[] Encrypt(byte[] input, int offset, int length) { - if (useCSP) + if (_aesCSP.IsCSPEnabled) { - if (length % BlockSize > 0) - { - if (_padding == null) - { - throw new ArgumentException("data"); - } - data = _padding.Pad(BlockSize, data, offset, length); - offset = 0; - length = data.Length; - } - - if (isCTRMode) - return CTREncryptDecrypt(data, offset, length); - else - { - byte[] output = new byte[length]; - aesEncryptor.TransformBlock(data, offset, length, output, 0); - return output; - } + return _aesCSP.Encrypt(input, offset, length); } - else - return base.Encrypt(data, offset, length); + + return base.Encrypt(input, offset, length); } /// - /// Decrypts the specified input using AesCryptoServiceProvider + /// Encrypts the specified data using CSP acceleration if enabled. /// - /// The input. - /// The zero-based offset in at which to begin decrypting. - /// The number of bytes to decrypt from . + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . /// - /// The decrypted data. + /// The encrypted data. /// - public override byte[] Decrypt(byte[] data, int offset, int length) + public override byte[] Decrypt(byte[] input, int offset, int length) { - if (useCSP) + if (_aesCSP.IsCSPEnabled) { - if (length % BlockSize > 0) - { - if (_padding == null) - { - throw new ArgumentException("data"); - } - data = _padding.Pad(BlockSize, data, offset, length); - offset = 0; - length = data.Length; - } - - if (isCTRMode) - return CTREncryptDecrypt(data, offset, length); - else - { - byte[] output = new byte[length]; - aesDecryptor.TransformBlock(data, offset, length, output, 0); - return output; - } + return _aesCSP.Decrypt(input, offset, length); } - else - return base.Encrypt(data, offset, length); - } - - // initialize AesCryptoServiceProvider - private bool initCryptoServiceProvider(CipherMode mode, CipherPadding padding) - { - try - { - // use the provided CipherPadding object - _padding = padding; - csp.PaddingMode cspPadding = csp.PaddingMode.None; - - // set the Mode - csp.CipherMode cspMode = 0; - isCTRMode = mode is Modes.CtrCipherMode; - - if (mode is Modes.CbcCipherMode) - cspMode = csp.CipherMode.CBC; - else if (isCTRMode) - cspMode = csp.CipherMode.ECB; // CTR uses ECB - else - return false; // OFB and CFB not supported, fallback to managed code - - // prepare IV array for CTR mode - if (isCTRMode) - _ctrIV = GetPackedIV(mode.IV); - - // create ICryptoTransform instances - var aesProvider = new csp.AesCryptoServiceProvider() - { - BlockSize = BlockSize * 8, - KeySize = Key.Length * 8, - Mode = cspMode, - Padding = cspPadding, - Key = Key, - IV = mode.IV, - }; - aesEncryptor = aesProvider.CreateEncryptor(Key, mode.IV); - aesDecryptor = aesProvider.CreateDecryptor(Key, mode.IV); - return true; - } - catch { } // fallback for unsupported key/iv/blocksize combinations - return false; - } - - // convert the IV into an array of uint[4] - private uint[] GetPackedIV(byte[] iv) - { - uint[] packedIV = new uint[4]; - packedIV[0] = (uint)((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | (iv[3])); - packedIV[1] = (uint)((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | (iv[7])); - packedIV[2] = (uint)((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | (iv[11])); - packedIV[3] = (uint)((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | (iv[15])); - return packedIV; - } - - // Perform AES-CTR encryption/decryption - private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) - { - int count = length / BlockSize; - if (length % BlockSize != 0) count++; - - byte[] counter = CTRCreateCounterArray(count); - byte[] aesCounter = aesEncryptor.TransformFinalBlock(counter, 0, counter.Length); - byte[] output = CTRArrayXOR(aesCounter, data, offset, length); - - return output; - } - - // creates the Counter array filled with incrementing copies of IV - private byte[] CTRCreateCounterArray(int blocks) - { - // fill an array with IV, increment by 1 for each copy - uint[] counter = new uint[blocks * 4]; - for (int i = 0; i < counter.Length; i += 4) - { - // write IV to buffer (big endian) - counter[i] = (_ctrIV[0] << 24) | ((_ctrIV[0] << 8) & 0x00FF0000) | ((_ctrIV[0] >> 8) & 0x0000FF00) | (_ctrIV[0] >> 24); - counter[i + 1] = (_ctrIV[1] << 24) | ((_ctrIV[1] << 8) & 0x00FF0000) | ((_ctrIV[1] >> 8) & 0x0000FF00) | (_ctrIV[1] >> 24); - counter[i + 2] = (_ctrIV[2] << 24) | ((_ctrIV[2] << 8) & 0x00FF0000) | ((_ctrIV[2] >> 8) & 0x0000FF00) | (_ctrIV[2] >> 24); - counter[i + 3] = (_ctrIV[3] << 24) | ((_ctrIV[3] << 8) & 0x00FF0000) | ((_ctrIV[3] >> 8) & 0x0000FF00) | (_ctrIV[3] >> 24); - - // increment IV (little endian) - for (int j = 3; j >= 0 && ++_ctrIV[j] == 0; j--) ; - } - - // copy uint[] to byte[] - byte[] counterBytes = new byte[blocks * 16]; - System.Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); - return counterBytes; - } - - // XORs the input data with the encrypted Counter array to produce the final output - // uses uint arrays for speed - private byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) - { - int words = length / 4; - if (length % 4 != 0) words++; - - // convert original data to words - uint[] datawords = new uint[words]; - System.Buffer.BlockCopy(data, offset, datawords, 0, length); - - // convert encrypted IV counter to words - uint[] counterwords = new uint[words]; - System.Buffer.BlockCopy(counter, 0, counterwords, 0, length); - - // XOR encrypted Counter with input data - for (int i = 0; i < words; i++) - counterwords[i] = counterwords[i] ^ datawords[i]; - - // copy uint[] to byte[] - byte[] output = counter; - System.Buffer.BlockCopy(counterwords, 0, output, 0, length); - - // adjust output for non-aligned lengths - if (output.Length > length) - Array.Resize(ref output, length); - return output; + return base.Encrypt(input, offset, length); } -#endif private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) { diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs new file mode 100644 index 000000000..72fc39f21 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs @@ -0,0 +1,241 @@ +using System; +using Renci.SshNet.Security.Cryptography.Ciphers.Modes; +using CSP = System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + // allow ECB in this class +#pragma warning disable CA5358 + + /// + /// AES cipher implementation using the accelerated AesCryptoServiceProvider + /// This CSP makes use of AES-NI instructions if available (faster, less CPU usage). + /// + public class AesCipherCSP + { + internal bool IsCSPEnabled { get; private set; } + + private readonly CipherPadding _padding; + private readonly int _blockSize; + private readonly bool _isCTRMode; + + private uint[] _ctrIV; + private CSP.ICryptoTransform _aesDecryptor; + private CSP.ICryptoTransform _aesEncryptor; + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The block size. + /// The mode. + /// The padding. + /// is . + /// Keysize is not valid for this algorithm. + public AesCipherCSP(byte[] key, int blockSize, CipherMode mode, CipherPadding padding) + { + _blockSize = blockSize; + _padding = padding; + _isCTRMode = mode is CtrCipherMode; + + if (mode is not CtrCipherMode and not CbcCipherMode) + { + // OFB and CFB not supported, let IsCSPEnabled = false + return; + } + +#if FEATURE_AES_CSP + // initialize AesCryptoServiceProvider + IsCSPEnabled = InitCryptoServiceProvider(key, mode.IV); +#endif + } + + /// + /// Encrypts the specified data using CSP acceleration if enabled. + /// + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . + /// + /// The encrypted data. + /// + public byte[] Encrypt(byte[] input, int offset, int length) + { + if (length % _blockSize > 0) + { + if (_padding is null) + { + throw new ArgumentException("input"); + } + + input = _padding.Pad(_blockSize, input, offset, length); + offset = 0; + length = input.Length; + } + + if (_isCTRMode) + { + return CTREncryptDecrypt(input, offset, length); + } + + var output = new byte[length]; + _ = _aesEncryptor.TransformBlock(input, offset, length, output, 0); + return output; + } + + /// + /// Encrypts the specified data using CSP acceleration if enabled. + /// + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . + /// + /// The encrypted data. + /// + public byte[] Decrypt(byte[] input, int offset, int length) + { + if (length % _blockSize > 0) + { + if (_padding is null) + { + throw new ArgumentException("input"); + } + + input = _padding.Pad(_blockSize, input, offset, length); + offset = 0; + length = input.Length; + } + + if (_isCTRMode) + { + return CTREncryptDecrypt(input, offset, length); + } + + var output = new byte[length]; + _ = _aesDecryptor.TransformBlock(input, offset, length, output, 0); + return output; + } + + // initialize AesCryptoServiceProvider + private bool InitCryptoServiceProvider(byte[] key, byte[] iv) + { + try + { + // prepare IV array for CTR mode + _ctrIV = GetPackedIV(iv); + + // create ICryptoTransform instances + using var aesProvider = CSP.Aes.Create(); + aesProvider.BlockSize = _blockSize * 8; + aesProvider.KeySize = key.Length * 8; + aesProvider.Mode = _isCTRMode ? CSP.CipherMode.ECB : CSP.CipherMode.CBC; // CTR uses ECB + aesProvider.Padding = CSP.PaddingMode.None; + aesProvider.Key = key; + aesProvider.IV = iv; + +#pragma warning disable CA5401 // allow specific IV in this context + _aesEncryptor = aesProvider.CreateEncryptor(key, iv); +#pragma warning restore CA5358 + _aesDecryptor = aesProvider.CreateDecryptor(key, iv); + + return true; + } + catch + { + // fallback for unsupported key/iv/blocksize combinations + } + + return false; + } + + // convert the IV into an array of uint[4] + private static uint[] GetPackedIV(byte[] iv) + { + var packedIV = new uint[4]; + packedIV[0] = (uint)((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | iv[3]); + packedIV[1] = (uint)((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | iv[7]); + packedIV[2] = (uint)((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | iv[11]); + packedIV[3] = (uint)((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | iv[15]); + return packedIV; + } + + // Perform AES-CTR encryption/decryption + private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) + { + var count = length / _blockSize; + if (length % _blockSize != 0) + { + count++; + } + + var counter = CTRCreateCounterArray(count); + var aesCounter = _aesEncryptor.TransformFinalBlock(counter, 0, counter.Length); + var output = CTRArrayXOR(aesCounter, data, offset, length); + + return output; + } + + // creates the Counter array filled with incrementing copies of IV + private byte[] CTRCreateCounterArray(int blocks) + { + // fill an array with IV, increment by 1 for each copy + var counter = new uint[blocks * 4]; + for (var i = 0; i < counter.Length; i += 4) + { + // write IV to buffer (big endian) + counter[i] = (_ctrIV[0] << 24) | ((_ctrIV[0] << 8) & 0x00FF0000) | ((_ctrIV[0] >> 8) & 0x0000FF00) | (_ctrIV[0] >> 24); + counter[i + 1] = (_ctrIV[1] << 24) | ((_ctrIV[1] << 8) & 0x00FF0000) | ((_ctrIV[1] >> 8) & 0x0000FF00) | (_ctrIV[1] >> 24); + counter[i + 2] = (_ctrIV[2] << 24) | ((_ctrIV[2] << 8) & 0x00FF0000) | ((_ctrIV[2] >> 8) & 0x0000FF00) | (_ctrIV[2] >> 24); + counter[i + 3] = (_ctrIV[3] << 24) | ((_ctrIV[3] << 8) & 0x00FF0000) | ((_ctrIV[3] >> 8) & 0x0000FF00) | (_ctrIV[3] >> 24); + + // increment IV (little endian) + for (var j = 3; j >= 0 && ++_ctrIV[j] == 0; j--) + { + // empty block + } + } + + // copy uint[] to byte[] + var counterBytes = new byte[blocks * 16]; + Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); + return counterBytes; + } + + // XORs the input data with the encrypted Counter array to produce the final output + // uses uint arrays for speed + private static byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) + { + var words = length / 4; + if (length % 4 != 0) + { + words++; + } + + // convert original data to words + var datawords = new uint[words]; + Buffer.BlockCopy(data, offset, datawords, 0, length); + + // convert encrypted IV counter to words + var counterwords = new uint[words]; + Buffer.BlockCopy(counter, 0, counterwords, 0, length); + + // XOR encrypted Counter with input data + for (var i = 0; i < words; i++) + { + counterwords[i] = counterwords[i] ^ datawords[i]; + } + + // copy uint[] to byte[] + var output = counter; + Buffer.BlockCopy(counterwords, 0, output, 0, length); + + // adjust output for non-aligned lengths + if (output.Length > length) + { + Array.Resize(ref output, length); + } + + return output; + } + } +} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs index d3e2df2a6..e87dc9d32 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs @@ -75,6 +75,6 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - + } } } From 086ad43550e3adaa98f418ffe0cb7fc11d4c771b Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Wed, 1 Nov 2023 18:21:16 +0100 Subject: [PATCH 05/19] Minor fixes --- src/Renci.SshNet/Security/Cryptography/BlockCipher.cs | 11 ++++++----- .../Security/Cryptography/Ciphers/AesCipher.cs | 2 +- .../Security/Cryptography/Ciphers/AesCipherCSP.cs | 5 ++--- .../Security/Cryptography/Ciphers/CipherMode.cs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs index 9bfdb6c77..b9f7dde58 100644 --- a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs @@ -76,7 +76,7 @@ public override byte[] Encrypt(byte[] input, int offset, int length) { if (length % _blockSize > 0) { - if (_padding is null) + if (_padding is null) { throw new ArgumentException("data"); } @@ -135,17 +135,18 @@ public override byte[] Decrypt(byte[] input, int offset, int length) { if (length % _blockSize > 0) { - if (_padding is null) + if (_padding is null) { throw new ArgumentException("data"); } - input = _padding.Pad(_blockSize, input, offset, length); - offset = 0; - length = input.Length; + input = _padding.Pad(_blockSize, input, offset, length); + offset = 0; + length = input.Length; } var output = new byte[length]; + var writtenBytes = 0; for (var i = 0; i < length / _blockSize; i++) { diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index a9bf9b80d..930a41a4a 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -705,7 +705,7 @@ public override byte[] Decrypt(byte[] input, int offset, int length) return _aesCSP.Decrypt(input, offset, length); } - return base.Encrypt(input, offset, length); + return base.Decrypt(input, offset, length); } private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs index 72fc39f21..0d3c03b13 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs @@ -4,9 +4,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { - // allow ECB in this class -#pragma warning disable CA5358 - /// /// AES cipher implementation using the accelerated AesCryptoServiceProvider /// This CSP makes use of AES-NI instructions if available (faster, less CPU usage). @@ -128,7 +125,9 @@ private bool InitCryptoServiceProvider(byte[] key, byte[] iv) using var aesProvider = CSP.Aes.Create(); aesProvider.BlockSize = _blockSize * 8; aesProvider.KeySize = key.Length * 8; +#pragma warning disable CA5358 // allow ECB aesProvider.Mode = _isCTRMode ? CSP.CipherMode.ECB : CSP.CipherMode.CBC; // CTR uses ECB +#pragma warning restore CA5358 aesProvider.Padding = CSP.PaddingMode.None; aesProvider.Key = key; aesProvider.IV = iv; diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs index e3a4a5f81..87deda9b5 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs @@ -17,7 +17,7 @@ public abstract class CipherMode /// /// Gets the IV vector. /// - internal byte[] IV {get; private set;} + internal byte[] IV; /// /// Holds block size of the cipher. From 39db35b96dc0854b1f14fc8145fc4321eac57223 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Mon, 6 Nov 2023 02:22:56 +0100 Subject: [PATCH 06/19] Rework based on suggestions --- src/Renci.SshNet/PrivateKeyFile.cs | 9 +- .../Security/Cryptography/BlockCipher.cs | 14 +- .../Security/Cryptography/Cipher.cs | 7 +- .../Cryptography/Ciphers/AesCipher.cs | 831 +----------------- .../Cryptography/Ciphers/AesCipherCSP.cs | 240 ----- .../Cryptography/Ciphers/CipherMode.cs | 258 +++++- .../Ciphers/Modes/CbcCipherMode.cs | 87 +- .../Ciphers/Modes/CfbCipherMode.cs | 67 +- .../Ciphers/Modes/CtrCipherMode.cs | 190 +++- .../Ciphers/Modes/EcbCipherMode.cs | 18 + .../Ciphers/Modes/OfbCipherMode.cs | 44 +- 11 files changed, 540 insertions(+), 1225 deletions(-) delete mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs create mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs index eb7b3767b..21c34cfe0 100644 --- a/src/Renci.SshNet/PrivateKeyFile.cs +++ b/src/Renci.SshNet/PrivateKeyFile.cs @@ -531,7 +531,14 @@ private static Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase) throw new SshException("Cipher '" + cipherName + "' is not supported for an OpenSSH key."); } - privateKeyBytes = cipher.Decrypt(privateKeyBytes); + try + { + privateKeyBytes = cipher.Decrypt(privateKeyBytes); + } + finally + { + cipher.Dispose(); + } } // validate private key length diff --git a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs index b9f7dde58..99bf624a6 100644 --- a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs @@ -60,7 +60,7 @@ protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding _mode = mode; _padding = padding; - _mode?.Init(this); + _mode?.Init(key, blockSize, padding); } /// @@ -110,18 +110,6 @@ public override byte[] Encrypt(byte[] input, int offset, int length) return output; } - /// - /// Decrypts the specified data. - /// - /// The data. - /// - /// The decrypted data. - /// - public override byte[] Decrypt(byte[] input) - { - return Decrypt(input, 0, input.Length); - } - /// /// Decrypts the specified input. /// diff --git a/src/Renci.SshNet/Security/Cryptography/Cipher.cs b/src/Renci.SshNet/Security/Cryptography/Cipher.cs index e624bbba3..5ce38581c 100644 --- a/src/Renci.SshNet/Security/Cryptography/Cipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Cipher.cs @@ -18,7 +18,7 @@ public abstract class Cipher /// /// The input. /// Encrypted data. - public byte[] Encrypt(byte[] input) + public virtual byte[] Encrypt(byte[] input) { return Encrypt(input, 0, input.Length); } @@ -41,7 +41,10 @@ public byte[] Encrypt(byte[] input) /// /// The decrypted data. /// - public abstract byte[] Decrypt(byte[] input); + public virtual byte[] Decrypt(byte[] input) + { + return Decrypt(input, 0, input.Length); + } /// /// Decrypts the specified input. diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 930a41a4a..984e4fdea 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -1,560 +1,16 @@ using System; using System.Globalization; -using Renci.SshNet.Common; + +using Renci.SshNet.Security.Cryptography.Ciphers.Modes; namespace Renci.SshNet.Security.Cryptography.Ciphers { /// /// AES cipher implementation. /// - public sealed class AesCipher : BlockCipher + public sealed class AesCipher : BlockCipher, IDisposable { - private const uint M1 = 0x80808080; - private const uint M2 = 0x7f7f7f7f; - private const uint M3 = 0x0000001b; - - private readonly AesCipherCSP _aesCSP; - - private int _rounds; - private uint[] _encryptionKey; - private uint[] _decryptionKey; - private uint _c0; - private uint _c1; - private uint _c2; - private uint _c3; - - #region Static Definition Tables - - private static readonly byte[] S = - { - 99, 124, 119, 123, 242, 107, 111, 197, - 48, 1, 103, 43, 254, 215, 171, 118, - 202, 130, 201, 125, 250, 89, 71, 240, - 173, 212, 162, 175, 156, 164, 114, 192, - 183, 253, 147, 38, 54, 63, 247, 204, - 52, 165, 229, 241, 113, 216, 49, 21, - 4, 199, 35, 195, 24, 150, 5, 154, - 7, 18, 128, 226, 235, 39, 178, 117, - 9, 131, 44, 26, 27, 110, 90, 160, - 82, 59, 214, 179, 41, 227, 47, 132, - 83, 209, 0, 237, 32, 252, 177, 91, - 106, 203, 190, 57, 74, 76, 88, 207, - 208, 239, 170, 251, 67, 77, 51, 133, - 69, 249, 2, 127, 80, 60, 159, 168, - 81, 163, 64, 143, 146, 157, 56, 245, - 188, 182, 218, 33, 16, 255, 243, 210, - 205, 12, 19, 236, 95, 151, 68, 23, - 196, 167, 126, 61, 100, 93, 25, 115, - 96, 129, 79, 220, 34, 42, 144, 136, - 70, 238, 184, 20, 222, 94, 11, 219, - 224, 50, 58, 10, 73, 6, 36, 92, - 194, 211, 172, 98, 145, 149, 228, 121, - 231, 200, 55, 109, 141, 213, 78, 169, - 108, 86, 244, 234, 101, 122, 174, 8, - 186, 120, 37, 46, 28, 166, 180, 198, - 232, 221, 116, 31, 75, 189, 139, 138, - 112, 62, 181, 102, 72, 3, 246, 14, - 97, 53, 87, 185, 134, 193, 29, 158, - 225, 248, 152, 17, 105, 217, 142, 148, - 155, 30, 135, 233, 206, 85, 40, 223, - 140, 161, 137, 13, 191, 230, 66, 104, - 65, 153, 45, 15, 176, 84, 187, 22 - }; - - // The inverse S-box - private static readonly byte[] Si = - { - 82, 9, 106, 213, 48, 54, 165, 56, - 191, 64, 163, 158, 129, 243, 215, 251, - 124, 227, 57, 130, 155, 47, 255, 135, - 52, 142, 67, 68, 196, 222, 233, 203, - 84, 123, 148, 50, 166, 194, 35, 61, - 238, 76, 149, 11, 66, 250, 195, 78, - 8, 46, 161, 102, 40, 217, 36, 178, - 118, 91, 162, 73, 109, 139, 209, 37, - 114, 248, 246, 100, 134, 104, 152, 22, - 212, 164, 92, 204, 93, 101, 182, 146, - 108, 112, 72, 80, 253, 237, 185, 218, - 94, 21, 70, 87, 167, 141, 157, 132, - 144, 216, 171, 0, 140, 188, 211, 10, - 247, 228, 88, 5, 184, 179, 69, 6, - 208, 44, 30, 143, 202, 63, 15, 2, - 193, 175, 189, 3, 1, 19, 138, 107, - 58, 145, 17, 65, 79, 103, 220, 234, - 151, 242, 207, 206, 240, 180, 230, 115, - 150, 172, 116, 34, 231, 173, 53, 133, - 226, 249, 55, 232, 28, 117, 223, 110, - 71, 241, 26, 113, 29, 41, 197, 137, - 111, 183, 98, 14, 170, 24, 190, 27, - 252, 86, 62, 75, 198, 210, 121, 32, - 154, 219, 192, 254, 120, 205, 90, 244, - 31, 221, 168, 51, 136, 7, 199, 49, - 177, 18, 16, 89, 39, 128, 236, 95, - 96, 81, 127, 169, 25, 181, 74, 13, - 45, 229, 122, 159, 147, 201, 156, 239, - 160, 224, 59, 77, 174, 42, 245, 176, - 200, 235, 187, 60, 131, 83, 153, 97, - 23, 43, 4, 126, 186, 119, 214, 38, - 225, 105, 20, 99, 85, 33, 12, 125 - }; - - // vector used in calculating key schedule (powers of x in GF(256)) - private static readonly byte[] Rcon = - { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, - 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 - }; - - // precomputation tables of calculations for rounds - private static readonly uint[] T0 = - { - 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, - 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, - 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, - 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, - 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, - 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, - 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, - 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, - 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, - 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, - 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, - 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, - 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, - 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, - 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, - 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, - 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, - 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, - 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, - 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, - 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, - 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, - 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, - 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, - 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, - 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, - 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, - 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, - 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, - 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, - 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, - 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, - 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, - 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, - 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, - 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, - 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, - 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, - 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, - 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, - 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, - 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, - 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, - 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, - 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, - 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, - 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, - 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, - 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, - 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, - 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, - 0x3a16162c - }; - - private static readonly uint[] T1 = - { - 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, - 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, - 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, - 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, - 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, - 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, - 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, - 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, - 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, - 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, - 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, - 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, - 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, - 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, - 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, - 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, - 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, - 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, - 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, - 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, - 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, - 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, - 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, - 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, - 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, - 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, - 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, - 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, - 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, - 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, - 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, - 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, - 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, - 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, - 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, - 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, - 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, - 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, - 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, - 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, - 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, - 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, - 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, - 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, - 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, - 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, - 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, - 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, - 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, - 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, - 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, - 0x16162c3a - }; - - private static readonly uint[] T2 = - { - 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, - 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, - 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, - 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, - 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, - 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, - 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, - 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, - 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, - 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, - 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, - 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, - 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, - 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, - 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, - 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, - 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, - 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, - 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, - 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, - 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, - 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, - 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, - 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, - 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, - 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, - 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, - 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, - 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, - 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, - 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, - 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, - 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, - 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, - 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, - 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, - 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, - 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, - 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, - 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, - 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, - 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, - 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, - 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, - 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, - 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, - 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, - 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, - 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, - 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, - 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, - 0x162c3a16 - }; - - private static readonly uint[] T3 = - { - 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, - 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, - 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, - 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, - 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, - 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, - 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, - 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, - 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, - 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, - 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, - 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, - 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, - 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, - 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, - 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, - 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, - 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, - 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, - 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, - 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, - 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, - 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, - 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, - 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, - 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, - 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, - 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, - 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, - 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, - 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, - 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, - 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, - 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, - 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, - 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, - 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, - 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, - 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, - 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, - 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, - 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, - 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, - 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, - 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, - 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, - 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, - 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, - 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, - 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, - 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, - 0x2c3a1616 - }; - - private static readonly uint[] Tinv0 = - { - 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, - 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, - 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, - 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, - 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, - 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, - 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, - 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, - 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, - 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, - 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, - 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, - 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, - 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, - 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, - 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, - 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, - 0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, - 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, - 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, - 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, - 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, - 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4, - 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, - 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, - 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, - 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, - 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, - 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, - 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, - 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, - 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, - 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, - 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850, - 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, - 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, - 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, - 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, - 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, - 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, - 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, - 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, - 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, - 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, - 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, - 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, - 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, - 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, - 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, - 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, - 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, - 0x4257b8d0 - }; - - private static readonly uint[] Tinv1 = - { - 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, - 0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, - 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, - 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, - 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, - 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, - 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, - 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, - 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, - 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, - 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, - 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, - 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, - 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, - 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, - 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, - 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, - 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, - 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, - 0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, - 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, - 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, - 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, - 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, - 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b, - 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, - 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, - 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, - 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, - 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, - 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, - 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, - 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, - 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, - 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, - 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, - 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, - 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, - 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4, - 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, - 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, - 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, - 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, - 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, - 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, - 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, - 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, - 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, - 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, - 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de, - 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, - 0x57b8d042 - }; - - private static readonly uint[] Tinv2 = - { - 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, - 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, - 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, - 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, - 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, - 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, - 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, - 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, - 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, - 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, - 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, - 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, - 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, - 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be, - 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, - 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, - 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, - 0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, - 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, - 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, - 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff, - 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, - 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296, - 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, - 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d, - 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, - 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b, - 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, - 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, - 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, - 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, - 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, - 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, - 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, - 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, - 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, - 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, - 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, - 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, - 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, - 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98, - 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, - 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, - 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, - 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, - 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, - 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, - 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, - 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, - 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3, - 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, - 0xb8d04257 - }; - - private static readonly uint[] Tinv3 = - { - 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, - 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, - 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, - 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, - 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f, - 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, - 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, - 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, - 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, - 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, - 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, - 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, - 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, - 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05, - 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, - 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, - 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, - 0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, - 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, - 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, - 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, - 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, - 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee, - 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, - 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09, - 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, - 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, - 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, - 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, - 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, - 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, - 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, - 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, - 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, - 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, - 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, - 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, - 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, - 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, - 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, - 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, - 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, - 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, - 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, - 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, - 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, - 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, - 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, - 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, - 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c, - 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, - 0xd04257b8 - }; - - #endregion + private readonly CipherMode _mode; /// /// Initializes a new instance of the class. @@ -574,8 +30,15 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); } - // initialize AesCryptoServiceProvider which uses AES-NI (faster, less CPU usage) - _aesCSP = new AesCipherCSP(key, 16, mode, padding); + // Creating a default ECB mode encryptor/decryptor to use when Ciphermode is null + // Normally the AES encryption/decryption happens on the CipherMode classes, but + // BlockCipher-derived classes also need to override EncryptBlock/DecryptBlock + _mode = mode; + if (_mode is null) + { + _mode = new EcbCipherMode(); + _mode.Init(key, 16, padding); + } } /// @@ -593,35 +56,7 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) /// or is too short. public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer is null) - { - throw new ArgumentNullException(nameof(inputBuffer)); - } - - if (outputBuffer is null) - { - throw new ArgumentNullException(nameof(outputBuffer)); - } - - if ((inputOffset + (32 / 2)) > inputBuffer.Length) - { - throw new ArgumentException("input buffer too short"); - } - - if ((outputOffset + (32 / 2)) > outputBuffer.Length) - { - throw new ArgumentException("output buffer too short"); - } - - _encryptionKey ??= GenerateWorkingKey(isEncryption: true, Key); - - UnPackBlock(inputBuffer, inputOffset); - - EncryptBlock(_encryptionKey); - - PackBlock(outputBuffer, outputOffset); - - return BlockSize; + return _mode.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } /// @@ -639,39 +74,11 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC /// or is too short. public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer is null) - { - throw new ArgumentNullException(nameof(inputBuffer)); - } - - if (outputBuffer is null) - { - throw new ArgumentNullException(nameof(outputBuffer)); - } - - if ((inputOffset + (32 / 2)) > inputBuffer.Length) - { - throw new ArgumentException("input buffer too short"); - } - - if ((outputOffset + (32 / 2)) > outputBuffer.Length) - { - throw new ArgumentException("output buffer too short"); - } - - _decryptionKey ??= GenerateWorkingKey(isEncryption: false, Key); - - UnPackBlock(inputBuffer, inputOffset); - - DecryptBlock(_decryptionKey); - - PackBlock(outputBuffer, outputOffset); - - return BlockSize; + return _mode.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } /// - /// Encrypts the specified data using CSP acceleration if enabled. + /// Encrypts the specified data. /// /// The data. /// The zero-based offset in at which to begin encrypting. @@ -681,211 +88,29 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC /// public override byte[] Encrypt(byte[] input, int offset, int length) { - if (_aesCSP.IsCSPEnabled) - { - return _aesCSP.Encrypt(input, offset, length); - } - - return base.Encrypt(input, offset, length); + return _mode.Encrypt(input, offset, length); } /// - /// Encrypts the specified data using CSP acceleration if enabled. + /// Decrypts the specified input. /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . + /// The input. + /// The zero-based offset in at which to begin decrypting. + /// The number of bytes to decrypt from . /// - /// The encrypted data. + /// The decrypted data. /// public override byte[] Decrypt(byte[] input, int offset, int length) { - if (_aesCSP.IsCSPEnabled) - { - return _aesCSP.Decrypt(input, offset, length); - } - - return base.Decrypt(input, offset, length); - } - - private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) - { - var KC = key.Length / 4; // key length in words - - if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length)) - { - throw new ArgumentException("Key length not 128/192/256 bits."); - } - - _rounds = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes - var W = new uint[(_rounds + 1) * 4]; // 4 words in a block - - /* - * Copy the key into the round key array. - */ - - var t = 0; - - for (var i = 0; i < key.Length; t++) - { - W[((t >> 2) * 4) + (t & 3)] = Pack.LittleEndianToUInt32(key, i); - i += 4; - } - - /* - * Calculate new values while not enough round key material is calculated. - */ - - var k = (_rounds + 1) << 2; - for (var i = KC; i < k; i++) - { - var temp = W[(((i - 1) >> 2) * 4) + ((i - 1) & 3)]; - if ((i % KC) == 0) - { - temp = SubWord(Shift(temp, 8)) ^ Rcon[(i / KC) - 1]; - } - else if ((KC > 6) && ((i % KC) == 4)) - { - temp = SubWord(temp); - } - - W[((i >> 2) * 4) + (i & 3)] = W[(((i - KC) >> 2) * 4) + ((i - KC) & 3)] ^ temp; - } - - if (!isEncryption) - { - for (var j = 1; j < _rounds; j++) - { - for (var i = 0; i < 4; i++) - { - W[(j * 4) + i] = InvMcol(W[(j * 4) + i]); - } - } - } - - return W; - } - - private static uint Shift(uint r, int shift) - { - return (r >> shift) | (r << (32 - shift)); - } - - private static uint FFmulX(uint x) - { - return ((x & M2) << 1) ^ (((x & M1) >> 7) * M3); + return _mode.Decrypt(input, offset, length); } - private static uint InvMcol(uint x) - { - var f2 = FFmulX(x); - var f4 = FFmulX(f2); - var f8 = FFmulX(f4); - var f9 = x ^ f8; - - return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); - } - - private static uint SubWord(uint x) - { - return (uint)S[x & 255] - | (((uint)S[(x >> 8) & 255]) << 8) - | (((uint)S[(x >> 16) & 255]) << 16) - | (((uint)S[(x >> 24) & 255]) << 24); - } - - private void UnPackBlock(byte[] bytes, int off) - { - _c0 = Pack.LittleEndianToUInt32(bytes, off); - _c1 = Pack.LittleEndianToUInt32(bytes, off + 4); - _c2 = Pack.LittleEndianToUInt32(bytes, off + 8); - _c3 = Pack.LittleEndianToUInt32(bytes, off + 12); - } - - private void PackBlock(byte[] bytes, int off) - { - Pack.UInt32ToLittleEndian(_c0, bytes, off); - Pack.UInt32ToLittleEndian(_c1, bytes, off + 4); - Pack.UInt32ToLittleEndian(_c2, bytes, off + 8); - Pack.UInt32ToLittleEndian(_c3, bytes, off + 12); - } - -#pragma warning disable SA1313 // Parameter names should begin with lower-case letter - private void EncryptBlock(uint[] KW) -#pragma warning restore SA1313 // Parameter names should begin with lower-case letter - { - int r; - uint r0, r1, r2, r3; - - _c0 ^= KW[(0 * 4) + 0]; - _c1 ^= KW[(0 * 4) + 1]; - _c2 ^= KW[(0 * 4) + 2]; - _c3 ^= KW[(0 * 4) + 3]; - - for (r = 1; r < _rounds - 1;) - { - r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[(r * 4) + 0]; - r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[(r * 4) + 1]; - r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[(r * 4) + 2]; - r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[(r++ * 4) + 3]; - _c0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[(r * 4) + 0]; - _c1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[(r * 4) + 1]; - _c2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[(r * 4) + 2]; - _c3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[(r++ * 4) + 3]; - } - - r0 = T0[_c0 & 255] ^ T1[(_c1 >> 8) & 255] ^ T2[(_c2 >> 16) & 255] ^ T3[_c3 >> 24] ^ KW[(r * 4) + 0]; - r1 = T0[_c1 & 255] ^ T1[(_c2 >> 8) & 255] ^ T2[(_c3 >> 16) & 255] ^ T3[_c0 >> 24] ^ KW[(r * 4) + 1]; - r2 = T0[_c2 & 255] ^ T1[(_c3 >> 8) & 255] ^ T2[(_c0 >> 16) & 255] ^ T3[_c1 >> 24] ^ KW[(r * 4) + 2]; - r3 = T0[_c3 & 255] ^ T1[(_c0 >> 8) & 255] ^ T2[(_c1 >> 16) & 255] ^ T3[_c2 >> 24] ^ KW[(r++ * 4) + 3]; - - /* - * The final round's table is a simple function of S so we don't use a whole other four tables for it. - */ - - _c0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[(r * 4) + 0]; - _c1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[(r * 4) + 1]; - _c2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[(r * 4) + 2]; - _c3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[(r * 4) + 3]; - } - -#pragma warning disable SA1313 // Parameter names should begin with lower-case letter - private void DecryptBlock(uint[] KW) -#pragma warning restore SA1313 // Parameter names should begin with lower-case letter + /// + /// Dispose the instance. + /// + public void Dispose() { - int r; - uint r0, r1, r2, r3; - - _c0 ^= KW[(_rounds * 4) + 0]; - _c1 ^= KW[(_rounds * 4) + 1]; - _c2 ^= KW[(_rounds * 4) + 2]; - _c3 ^= KW[(_rounds * 4) + 3]; - - for (r = _rounds - 1; r > 1;) - { - r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[(r * 4) + 0]; - r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[(r * 4) + 1]; - r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[(r * 4) + 2]; - r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[(r-- * 4) + 3]; - _c0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[(r * 4) + 0]; - _c1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[(r * 4) + 1]; - _c2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[(r * 4) + 2]; - _c3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[(r-- * 4) + 3]; - } - - r0 = Tinv0[_c0 & 255] ^ Tinv1[(_c3 >> 8) & 255] ^ Tinv2[(_c2 >> 16) & 255] ^ Tinv3[_c1 >> 24] ^ KW[(r * 4) + 0]; - r1 = Tinv0[_c1 & 255] ^ Tinv1[(_c0 >> 8) & 255] ^ Tinv2[(_c3 >> 16) & 255] ^ Tinv3[_c2 >> 24] ^ KW[(r * 4) + 1]; - r2 = Tinv0[_c2 & 255] ^ Tinv1[(_c1 >> 8) & 255] ^ Tinv2[(_c0 >> 16) & 255] ^ Tinv3[_c3 >> 24] ^ KW[(r * 4) + 2]; - r3 = Tinv0[_c3 & 255] ^ Tinv1[(_c2 >> 8) & 255] ^ Tinv2[(_c1 >> 16) & 255] ^ Tinv3[_c0 >> 24] ^ KW[(r * 4) + 3]; - - /* - * The final round's table is a simple function of Si so we don't use a whole other four tables for it. - */ - - _c0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[(0 * 4) + 0]; - _c1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[(0 * 4) + 1]; - _c2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[(0 * 4) + 2]; - _c3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[(0 * 4) + 3]; + _mode?.Dispose(); } } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs deleted file mode 100644 index 0d3c03b13..000000000 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherCSP.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using Renci.SshNet.Security.Cryptography.Ciphers.Modes; -using CSP = System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// AES cipher implementation using the accelerated AesCryptoServiceProvider - /// This CSP makes use of AES-NI instructions if available (faster, less CPU usage). - /// - public class AesCipherCSP - { - internal bool IsCSPEnabled { get; private set; } - - private readonly CipherPadding _padding; - private readonly int _blockSize; - private readonly bool _isCTRMode; - - private uint[] _ctrIV; - private CSP.ICryptoTransform _aesDecryptor; - private CSP.ICryptoTransform _aesEncryptor; - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The block size. - /// The mode. - /// The padding. - /// is . - /// Keysize is not valid for this algorithm. - public AesCipherCSP(byte[] key, int blockSize, CipherMode mode, CipherPadding padding) - { - _blockSize = blockSize; - _padding = padding; - _isCTRMode = mode is CtrCipherMode; - - if (mode is not CtrCipherMode and not CbcCipherMode) - { - // OFB and CFB not supported, let IsCSPEnabled = false - return; - } - -#if FEATURE_AES_CSP - // initialize AesCryptoServiceProvider - IsCSPEnabled = InitCryptoServiceProvider(key, mode.IV); -#endif - } - - /// - /// Encrypts the specified data using CSP acceleration if enabled. - /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . - /// - /// The encrypted data. - /// - public byte[] Encrypt(byte[] input, int offset, int length) - { - if (length % _blockSize > 0) - { - if (_padding is null) - { - throw new ArgumentException("input"); - } - - input = _padding.Pad(_blockSize, input, offset, length); - offset = 0; - length = input.Length; - } - - if (_isCTRMode) - { - return CTREncryptDecrypt(input, offset, length); - } - - var output = new byte[length]; - _ = _aesEncryptor.TransformBlock(input, offset, length, output, 0); - return output; - } - - /// - /// Encrypts the specified data using CSP acceleration if enabled. - /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . - /// - /// The encrypted data. - /// - public byte[] Decrypt(byte[] input, int offset, int length) - { - if (length % _blockSize > 0) - { - if (_padding is null) - { - throw new ArgumentException("input"); - } - - input = _padding.Pad(_blockSize, input, offset, length); - offset = 0; - length = input.Length; - } - - if (_isCTRMode) - { - return CTREncryptDecrypt(input, offset, length); - } - - var output = new byte[length]; - _ = _aesDecryptor.TransformBlock(input, offset, length, output, 0); - return output; - } - - // initialize AesCryptoServiceProvider - private bool InitCryptoServiceProvider(byte[] key, byte[] iv) - { - try - { - // prepare IV array for CTR mode - _ctrIV = GetPackedIV(iv); - - // create ICryptoTransform instances - using var aesProvider = CSP.Aes.Create(); - aesProvider.BlockSize = _blockSize * 8; - aesProvider.KeySize = key.Length * 8; -#pragma warning disable CA5358 // allow ECB - aesProvider.Mode = _isCTRMode ? CSP.CipherMode.ECB : CSP.CipherMode.CBC; // CTR uses ECB -#pragma warning restore CA5358 - aesProvider.Padding = CSP.PaddingMode.None; - aesProvider.Key = key; - aesProvider.IV = iv; - -#pragma warning disable CA5401 // allow specific IV in this context - _aesEncryptor = aesProvider.CreateEncryptor(key, iv); -#pragma warning restore CA5358 - _aesDecryptor = aesProvider.CreateDecryptor(key, iv); - - return true; - } - catch - { - // fallback for unsupported key/iv/blocksize combinations - } - - return false; - } - - // convert the IV into an array of uint[4] - private static uint[] GetPackedIV(byte[] iv) - { - var packedIV = new uint[4]; - packedIV[0] = (uint)((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | iv[3]); - packedIV[1] = (uint)((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | iv[7]); - packedIV[2] = (uint)((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | iv[11]); - packedIV[3] = (uint)((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | iv[15]); - return packedIV; - } - - // Perform AES-CTR encryption/decryption - private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) - { - var count = length / _blockSize; - if (length % _blockSize != 0) - { - count++; - } - - var counter = CTRCreateCounterArray(count); - var aesCounter = _aesEncryptor.TransformFinalBlock(counter, 0, counter.Length); - var output = CTRArrayXOR(aesCounter, data, offset, length); - - return output; - } - - // creates the Counter array filled with incrementing copies of IV - private byte[] CTRCreateCounterArray(int blocks) - { - // fill an array with IV, increment by 1 for each copy - var counter = new uint[blocks * 4]; - for (var i = 0; i < counter.Length; i += 4) - { - // write IV to buffer (big endian) - counter[i] = (_ctrIV[0] << 24) | ((_ctrIV[0] << 8) & 0x00FF0000) | ((_ctrIV[0] >> 8) & 0x0000FF00) | (_ctrIV[0] >> 24); - counter[i + 1] = (_ctrIV[1] << 24) | ((_ctrIV[1] << 8) & 0x00FF0000) | ((_ctrIV[1] >> 8) & 0x0000FF00) | (_ctrIV[1] >> 24); - counter[i + 2] = (_ctrIV[2] << 24) | ((_ctrIV[2] << 8) & 0x00FF0000) | ((_ctrIV[2] >> 8) & 0x0000FF00) | (_ctrIV[2] >> 24); - counter[i + 3] = (_ctrIV[3] << 24) | ((_ctrIV[3] << 8) & 0x00FF0000) | ((_ctrIV[3] >> 8) & 0x0000FF00) | (_ctrIV[3] >> 24); - - // increment IV (little endian) - for (var j = 3; j >= 0 && ++_ctrIV[j] == 0; j--) - { - // empty block - } - } - - // copy uint[] to byte[] - var counterBytes = new byte[blocks * 16]; - Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); - return counterBytes; - } - - // XORs the input data with the encrypted Counter array to produce the final output - // uses uint arrays for speed - private static byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) - { - var words = length / 4; - if (length % 4 != 0) - { - words++; - } - - // convert original data to words - var datawords = new uint[words]; - Buffer.BlockCopy(data, offset, datawords, 0, length); - - // convert encrypted IV counter to words - var counterwords = new uint[words]; - Buffer.BlockCopy(counter, 0, counterwords, 0, length); - - // XOR encrypted Counter with input data - for (var i = 0; i < words; i++) - { - counterwords[i] = counterwords[i] ^ datawords[i]; - } - - // copy uint[] to byte[] - var output = counter; - Buffer.BlockCopy(counterwords, 0, output, 0, length); - - // adjust output for non-aligned lengths - if (output.Length > length) - { - Array.Resize(ref output, length); - } - - return output; - } - } -} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs index 87deda9b5..dcc880e9c 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs @@ -1,49 +1,104 @@ -using Renci.SshNet.Common; +using System; +using System.Globalization; +using System.Security.Cryptography; + +using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography.Ciphers { /// /// Base class for cipher mode implementations. /// - public abstract class CipherMode + public abstract class CipherMode : IDisposable { -#pragma warning disable SA1401 // Fields should be private -#pragma warning disable SA1306 // Field names should begin with lower-case letter + private readonly System.Security.Cryptography.CipherMode _aesMode; + + private Aes _aes; + private ICryptoTransform _encryptor; + private ICryptoTransform _decryptor; + private CipherPadding _padding; + + /// + /// Gets a value indicating whether to process arrays in one go using CNG provider + /// Set to False to process arrays block by block. + /// + protected virtual bool SupportsMultipleBlocks + { + get { return true; } + } + + /// + /// Gets the AES Encryptor instance. + /// + protected ICryptoTransform Encryptor + { + get + { + if (_encryptor == null || !_encryptor.CanReuseTransform) + { + _encryptor = _aes.CreateEncryptor(); + } + + return _encryptor; + } + } + /// - /// Gets the cipher. + /// Gets the AES Decryptor instance. /// - protected BlockCipher Cipher; + protected ICryptoTransform Decryptor + { + get + { + if (_decryptor == null || !_decryptor.CanReuseTransform) + { + _decryptor = _aes.CreateDecryptor(); + } + + return _decryptor; + } + } /// /// Gets the IV vector. /// - internal byte[] IV; + protected internal byte[] IV { get; private set; } /// - /// Holds block size of the cipher. + /// Gets the block size of the cipher. /// - protected int _blockSize; -#pragma warning restore SA1306 // Field names should begin with lower-case letter -#pragma warning restore SA1401 // Fields should be private + protected int BlockSize { get; private set; } /// /// Initializes a new instance of the class. /// /// The iv. - protected CipherMode(byte[] iv) + /// The AES mode. + protected CipherMode(byte[] iv, System.Security.Cryptography.CipherMode aesMode) { IV = iv; + _aesMode = aesMode; } /// /// Initializes the specified cipher mode. /// - /// The cipher. - internal void Init(BlockCipher cipher) + /// The key. + /// The block size. + /// Enable PKCS7 padding. + internal void Init(byte[] key, int blocksize, CipherPadding padding) { - Cipher = cipher; - _blockSize = cipher.BlockSize; - IV = IV.Take(_blockSize); + BlockSize = blocksize; + IV = IV.Take(BlockSize); + _padding = padding; + + _aes = Aes.Create(); + _aes.BlockSize = BlockSize * 8; + _aes.FeedbackSize = BlockSize * 8; + _aes.Mode = _aesMode; + _aes.Padding = padding is null ? PaddingMode.None : PaddingMode.PKCS7; + _aes.Key = key; + _aes.IV = IV; } /// @@ -57,7 +112,27 @@ internal void Init(BlockCipher cipher) /// /// The number of bytes encrypted. /// - public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); + /// or is . + /// or is too short. + public virtual int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < BlockSize) + { + throw new ArgumentException("Invalid input buffer"); + } + + if (outputBuffer.Length - outputOffset < BlockSize) + { + throw new ArgumentException("Invalid output buffer"); + } + + if (inputCount != BlockSize) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); + } + + return Encryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } /// /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. @@ -70,6 +145,151 @@ internal void Init(BlockCipher cipher) /// /// The number of bytes decrypted. /// - public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); + /// or is . + /// or is too short. + public virtual int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < BlockSize) + { + throw new ArgumentException("Invalid input buffer"); + } + + if (outputBuffer.Length - outputOffset < BlockSize) + { + throw new ArgumentException("Invalid output buffer"); + } + + if (inputCount != BlockSize) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); + } + + return Decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + /// + /// Encrypts the specified data. + /// + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . + /// + /// The encrypted data. + /// + public virtual byte[] Encrypt(byte[] input, int offset, int length) + { + if (SupportsMultipleBlocks && Encryptor.CanTransformMultipleBlocks) + { + return Encryptor.TransformFinalBlock(input, offset, length); + } + + return EncryptArray(input, offset, length); + } + + /// + /// Decrypts the specified input. + /// + /// The input. + /// The zero-based offset in at which to begin decrypting. + /// The number of bytes to decrypt from . + /// + /// The decrypted data. + /// + public virtual byte[] Decrypt(byte[] input, int offset, int length) + { + if (SupportsMultipleBlocks && Decryptor.CanTransformMultipleBlocks) + { + return Decryptor.TransformFinalBlock(input, offset, length); + } + + return DecryptArray(input, offset, length); + } + + // Encrypts the data using EncryptBlock() on each block. + private byte[] EncryptArray(byte[] input, int offset, int length) + { + if (length % BlockSize > 0) + { + if (_padding is null) + { + throw new ArgumentException("data"); + } + + var paddingLength = BlockSize - (length % BlockSize); + input = _padding.Pad(input, offset, length, paddingLength); + length += paddingLength; + offset = 0; + } + + var output = new byte[length]; + var writtenBytes = 0; + + for (var i = 0; i < length / BlockSize; i++) + { + writtenBytes += EncryptBlock(input, offset + (i * BlockSize), BlockSize, output, i * BlockSize); + } + + if (writtenBytes < length) + { + throw new InvalidOperationException("Encryption error."); + } + + return output; + } + + // Decrypts the data using DecryptBlock() on each block. + private byte[] DecryptArray(byte[] input, int offset, int length) + { + if (length % BlockSize > 0) + { + if (_padding is null) + { + throw new ArgumentException("data"); + } + + input = _padding.Pad(BlockSize, input, offset, length); + offset = 0; + length = input.Length; + } + + var output = new byte[length]; + + var writtenBytes = 0; + for (var i = 0; i < length / BlockSize; i++) + { + writtenBytes += DecryptBlock(input, offset + (i * BlockSize), BlockSize, output, i * BlockSize); + } + + if (writtenBytes < length) + { + throw new InvalidOperationException("Encryption error."); + } + + return output; + } + + /// + /// Dispose the instance. + /// + /// Set to True to dispose of resouces. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _encryptor?.Dispose(); + _encryptor = null; + + _decryptor?.Dispose(); + _decryptor = null; + } + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs index a2b9243d4..753d17644 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs @@ -1,7 +1,4 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes { /// /// Implements CBC cipher mode. @@ -13,88 +10,8 @@ public class CbcCipherMode : CipherMode /// /// The iv. public CbcCipherMode(byte[] iv) - : base(iv) + : base(iv, System.Security.Cryptography.CipherMode.CBC) { } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < _blockSize) - { - throw new ArgumentException("Invalid input buffer"); - } - - if (outputBuffer.Length - outputOffset < _blockSize) - { - throw new ArgumentException("Invalid output buffer"); - } - - if (inputCount != _blockSize) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); - } - - for (var i = 0; i < _blockSize; i++) - { - IV[i] ^= inputBuffer[inputOffset + i]; - } - - _ = Cipher.EncryptBlock(IV, 0, inputCount, outputBuffer, outputOffset); - - Buffer.BlockCopy(outputBuffer, outputOffset, IV, 0, IV.Length); - - return _blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < _blockSize) - { - throw new ArgumentException("Invalid input buffer"); - } - - if (outputBuffer.Length - outputOffset < _blockSize) - { - throw new ArgumentException("Invalid output buffer"); - } - - if (inputCount != _blockSize) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); - } - - _ = Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - - for (var i = 0; i < _blockSize; i++) - { - outputBuffer[outputOffset + i] ^= IV[i]; - } - - Buffer.BlockCopy(inputBuffer, inputOffset, IV, 0, IV.Length); - - return _blockSize; - } } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs index 23a4bb2f7..6512da378 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs @@ -1,4 +1,7 @@ -using System; +#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts +#pragma warning disable IDE0005 // Using directive is unnecessary + +using System; using System.Globalization; namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes @@ -8,14 +11,37 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes /// public class CfbCipherMode : CipherMode { + // NET6 or above: The CNG supports CFB mode +#if NET6_0_OR_GREATER + + /// + /// Initializes a new instance of the class. + /// + /// The iv. + public CfbCipherMode(byte[] iv) + : base(iv, System.Security.Cryptography.CipherMode.CFB) + { + } + +#else // Others: The CNG does not support CFB mode, CFB implementated using ECB as base + private readonly byte[] _ivOutput; + /// + /// Gets a value indicating whether to process arrays in one go using CNG provider + /// Set to False to process arrays block by block. + /// + protected override bool SupportsMultipleBlocks + { + get { return false; } + } + /// /// Initializes a new instance of the class. /// /// The iv. public CfbCipherMode(byte[] iv) - : base(iv) + : base(iv, System.Security.Cryptography.CipherMode.ECB) { _ivOutput = new byte[iv.Length]; } @@ -33,32 +59,32 @@ public CfbCipherMode(byte[] iv) /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer.Length - inputOffset < _blockSize) + if (inputBuffer.Length - inputOffset < BlockSize) { throw new ArgumentException("Invalid input buffer"); } - if (outputBuffer.Length - outputOffset < _blockSize) + if (outputBuffer.Length - outputOffset < BlockSize) { throw new ArgumentException("Invalid output buffer"); } - if (inputCount != _blockSize) + if (inputCount != BlockSize) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); } - _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = base.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - for (var i = 0; i < _blockSize; i++) + for (var i = 0; i < BlockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - Buffer.BlockCopy(IV, _blockSize, IV, 0, IV.Length - _blockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, IV, IV.Length - _blockSize, _blockSize); + Buffer.BlockCopy(IV, BlockSize, IV, 0, IV.Length - BlockSize); + Buffer.BlockCopy(outputBuffer, outputOffset, IV, IV.Length - BlockSize, BlockSize); - return _blockSize; + return BlockSize; } /// @@ -74,32 +100,33 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC /// public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer.Length - inputOffset < _blockSize) + if (inputBuffer.Length - inputOffset < BlockSize) { throw new ArgumentException("Invalid input buffer"); } - if (outputBuffer.Length - outputOffset < _blockSize) + if (outputBuffer.Length - outputOffset < BlockSize) { throw new ArgumentException("Invalid output buffer"); } - if (inputCount != _blockSize) + if (inputCount != BlockSize) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); } - _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = base.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - Buffer.BlockCopy(IV, _blockSize, IV, 0, IV.Length - _blockSize); - Buffer.BlockCopy(inputBuffer, inputOffset, IV, IV.Length - _blockSize, _blockSize); + Buffer.BlockCopy(IV, BlockSize, IV, 0, IV.Length - BlockSize); + Buffer.BlockCopy(inputBuffer, inputOffset, IV, IV.Length - BlockSize, BlockSize); - for (var i = 0; i < _blockSize; i++) + for (var i = 0; i < BlockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - return _blockSize; + return BlockSize; } +#endif } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs index a0ae5010b..961cac1cd 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs @@ -1,5 +1,8 @@ -using System; -using System.Globalization; +#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts +#pragma warning disable IDE0005 // Using directive is unnecessary + +using System; +using System.Numerics; namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes { @@ -8,16 +11,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes /// public class CtrCipherMode : CipherMode { - private readonly byte[] _ivOutput; + private readonly uint[] _ivOutput; /// /// Initializes a new instance of the class. /// /// The iv. public CtrCipherMode(byte[] iv) - : base(iv) + : base(iv, System.Security.Cryptography.CipherMode.ECB) { - _ivOutput = new byte[iv.Length]; + _ivOutput = GetPackedIV(iv); } /// @@ -33,35 +36,10 @@ public CtrCipherMode(byte[] iv) /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer.Length - inputOffset < _blockSize) - { - throw new ArgumentException("Invalid input buffer"); - } - - if (outputBuffer.Length - outputOffset < _blockSize) - { - throw new ArgumentException("Invalid output buffer"); - } - - if (inputCount != _blockSize) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); - } + var buffer = CTREncryptDecrypt(inputBuffer, inputOffset, inputCount); + Buffer.BlockCopy(buffer, 0, outputBuffer, 0, buffer.Length); - _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - - for (var i = 0; i < _blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - var j = IV.Length; - while (--j >= 0 && ++IV[j] == 0) - { - // Intentionally empty block - } - - return _blockSize; + return buffer.Length; } /// @@ -79,5 +57,151 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC { return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } + + /// + /// Encrypts the specified data. + /// + /// The data. + /// The zero-based offset in at which to begin encrypting. + /// The number of bytes to encrypt from . + /// + /// The encrypted data. + /// + public override byte[] Encrypt(byte[] input, int offset, int length) + { + return CTREncryptDecrypt(input, offset, length); + } + + /// + /// Decrypts the specified input. + /// + /// The input. + /// The zero-based offset in at which to begin decrypting. + /// The number of bytes to decrypt from . + /// + /// The decrypted data. + /// + public override byte[] Decrypt(byte[] input, int offset, int length) + { + return CTREncryptDecrypt(input, offset, length); + } + + // convert the IV into an array of uint[4] + private static uint[] GetPackedIV(byte[] iv) + { + var packedIV = new uint[4]; + packedIV[0] = (uint) ((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | iv[3]); + packedIV[1] = (uint) ((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | iv[7]); + packedIV[2] = (uint) ((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | iv[11]); + packedIV[3] = (uint) ((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | iv[15]); + return packedIV; + } + + // Perform AES-CTR encryption/decryption + private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) + { + var count = length / BlockSize; + if (length % BlockSize != 0) + { + count++; + } + + var counter = CTRCreateCounterArray(count); + var aesCounter = Encryptor.TransformFinalBlock(counter, 0, counter.Length); + var output = CTRArrayXOR(aesCounter, data, offset, length); + + return output; + } + + // creates the Counter array filled with incrementing copies of IV + private byte[] CTRCreateCounterArray(int blocks) + { + // fill an array with IV, increment by 1 for each copy + var counter = new uint[blocks * 4]; + for (var i = 0; i < counter.Length; i += 4) + { + // write IV to buffer (big endian) + counter[i] = (_ivOutput[0] << 24) | ((_ivOutput[0] << 8) & 0x00FF0000) | ((_ivOutput[0] >> 8) & 0x0000FF00) | (_ivOutput[0] >> 24); + counter[i + 1] = (_ivOutput[1] << 24) | ((_ivOutput[1] << 8) & 0x00FF0000) | ((_ivOutput[1] >> 8) & 0x0000FF00) | (_ivOutput[1] >> 24); + counter[i + 2] = (_ivOutput[2] << 24) | ((_ivOutput[2] << 8) & 0x00FF0000) | ((_ivOutput[2] >> 8) & 0x0000FF00) | (_ivOutput[2] >> 24); + counter[i + 3] = (_ivOutput[3] << 24) | ((_ivOutput[3] << 8) & 0x00FF0000) | ((_ivOutput[3] >> 8) & 0x0000FF00) | (_ivOutput[3] >> 24); + + // increment IV (little endian) + for (var j = 3; j >= 0 && ++_ivOutput[j] == 0; j--) + { + // empty block + } + } + + // copy uint[] to byte[] + var counterBytes = new byte[blocks * 16]; + Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); + return counterBytes; + } + +#if NETSTANDARD2_1_OR_GREATER + + // XOR 2 arrays using Vector + private static byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) + { + for (var loopOffset = 0; length > 0; length -= Vector.Count) + { + var v = new Vector(counter, loopOffset) ^ new Vector(data, offset + loopOffset); + if (length >= Vector.Count) + { + v.CopyTo(counter, loopOffset); + loopOffset += Vector.Count; + } + else + { + for (var i = 0; i < length; i++) + { + counter[loopOffset++] = v[i]; + } + } + } + + return counter; + } + +#else // fallback to blockcopy and regular XOR + + // XORs the input data with the encrypted Counter array to produce the final output + // uses uint arrays for speed + private static byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) + { + var words = length / 4; + if (length % 4 != 0) + { + words++; + } + + // convert original data to words + var datawords = new uint[words]; + Buffer.BlockCopy(data, offset, datawords, 0, length); + + // convert encrypted IV counter to words + var counterwords = new uint[words]; + Buffer.BlockCopy(counter, 0, counterwords, 0, length); + + // XOR encrypted Counter with input data + for (var i = 0; i < words; i++) + { + counterwords[i] = counterwords[i] ^ datawords[i]; + } + + // copy uint[] to byte[] + var output = counter; + Buffer.BlockCopy(counterwords, 0, output, 0, length); + + // adjust output for non-aligned lengths + if (output.Length > length) + { + Array.Resize(ref output, length); + } + + return output; + } +#endif } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs new file mode 100644 index 000000000..48a77d716 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs @@ -0,0 +1,18 @@ +#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +{ + /// + /// Implements EBC cipher mode. + /// + public class EcbCipherMode : CipherMode + { + /// + /// Initializes a new instance of the class. + /// + public EcbCipherMode() + : base(new byte[16], System.Security.Cryptography.CipherMode.ECB) + { + } + } +} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs index df2ce60be..c9bce9f17 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs @@ -1,4 +1,7 @@ -using System; +#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts +#pragma warning disable IDE0005 // Using directive is unnecessary + +using System; using System.Globalization; namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes @@ -8,14 +11,36 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes /// public class OfbCipherMode : CipherMode { + // NetStandard 2.0: The CNG supports OFB mode +#if NETSTANDARD2_0 + /// + /// Initializes a new instance of the class. + /// + /// The iv. + public OfbCipherMode(byte[] iv) + : base(iv, System.Security.Cryptography.CipherMode.OFB) + { + } + +#else // NetStandard 2.1 and above: The CNG does not support OFB mode, OFB implementated using ECB as base + private readonly byte[] _ivOutput; + /// + /// Gets a value indicating whether to process arrays in one go using CNG provider + /// Set to False to process arrays block by block. + /// + protected override bool SupportsMultipleBlocks + { + get { return false; } + } + /// /// Initializes a new instance of the class. /// /// The iv. public OfbCipherMode(byte[] iv) - : base(iv) + : base(iv, System.Security.Cryptography.CipherMode.ECB) { _ivOutput = new byte[iv.Length]; } @@ -33,31 +58,31 @@ public OfbCipherMode(byte[] iv) /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer.Length - inputOffset < _blockSize) + if (inputBuffer.Length - inputOffset < BlockSize) { throw new ArgumentException("Invalid input buffer"); } - if (outputBuffer.Length - outputOffset < _blockSize) + if (outputBuffer.Length - outputOffset < BlockSize) { throw new ArgumentException("Invalid output buffer"); } - if (inputCount != _blockSize) + if (inputCount != BlockSize) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); } - _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = base.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); Buffer.BlockCopy(_ivOutput, 0, IV, 0, IV.Length); - for (var i = 0; i < _blockSize; i++) + for (var i = 0; i < BlockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - return _blockSize; + return BlockSize; } /// @@ -75,5 +100,6 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC { return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } +#endif } } From 63bfc766c80893029488a9bc8edbef4c4df0533e Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Tue, 7 Nov 2023 14:12:00 +0100 Subject: [PATCH 07/19] Move all changes to AesCypher.cs, as per Rob-Hague suggestion Remove FEATURE_AES_CSP conditional Fix OFB CipherMode --- src/Renci.SshNet/ConnectionInfo.cs | 12 +- src/Renci.SshNet/PrivateKeyFile.cs | 10 +- src/Renci.SshNet/Renci.SshNet.csproj | 4 +- .../Security/Cryptography/BlockCipher.cs | 14 +- .../Security/Cryptography/Cipher.cs | 7 +- .../Cryptography/Ciphers/AesCipher.cs | 380 ++++++++++- .../Cryptography/Ciphers/CipherMode.cs | 258 +------ .../Ciphers/Modes/CbcCipherMode.cs | 87 ++- .../Ciphers/Modes/CfbCipherMode.cs | 67 +- .../Ciphers/Modes/CtrCipherMode.cs | 190 +----- .../Ciphers/Modes/EcbCipherMode.cs | 18 - .../Ciphers/Modes/OfbCipherMode.cs | 46 +- .../Ciphers/AesCipherBenchmarks.cs | 47 +- .../Ciphers/AesCipherTest.Gen.cs.txt | 14 +- .../Cryptography/Ciphers/AesCipherTest.cs | 641 +++++++++--------- 15 files changed, 927 insertions(+), 868 deletions(-) delete mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 02e817b24..35012646d 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -360,11 +360,13 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy Encryptions = new Dictionary { - { "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, { "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) }, - { "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, - { "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, - { "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, + { "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, + { "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, + { "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, + { "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) }, + { "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) }, + { "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) }, { "blowfish-cbc", new CipherInfo(128, (key, iv) => new BlowfishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish192-cbc", new CipherInfo(192, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, @@ -374,8 +376,6 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy { "arcfour128", new CipherInfo(128, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: true)) }, { "arcfour256", new CipherInfo(256, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: true)) }, { "cast128-cbc", new CipherInfo(128, (key, iv) => new CastCipher(key, new CbcCipherMode(iv), padding: null)) }, - { "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, - { "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, }; HmacAlgorithms = new Dictionary diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs index 21c34cfe0..1373630cd 100644 --- a/src/Renci.SshNet/PrivateKeyFile.cs +++ b/src/Renci.SshNet/PrivateKeyFile.cs @@ -226,13 +226,13 @@ private void Open(Stream privateKey, string passPhrase) cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); break; case "AES-128-CBC": - cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); + cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true)); break; case "AES-192-CBC": - cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); + cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true)); break; case "AES-256-CBC": - cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); + cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true)); break; default: throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key cipher \"{0}\" is not supported.", cipherName)); @@ -522,10 +522,10 @@ private static Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase) switch (cipherName) { case "aes256-cbc": - cipher = new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); + cipher = new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false); break; case "aes256-ctr": - cipher = new AesCipher(key, new CtrCipherMode(iv), new PKCS7Padding()); + cipher = new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false); break; default: throw new SshException("Cipher '" + cipherName + "' is not supported for an OpenSSH key."); diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 16038d29b..4ccf6c72e 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -6,7 +6,7 @@ - FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160;FEATURE_AES_CSP + FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160 @@ -18,6 +18,6 @@ - FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP;FEATURE_AES_CSP + FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP diff --git a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs index 99bf624a6..b9f7dde58 100644 --- a/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/BlockCipher.cs @@ -60,7 +60,7 @@ protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding _mode = mode; _padding = padding; - _mode?.Init(key, blockSize, padding); + _mode?.Init(this); } /// @@ -110,6 +110,18 @@ public override byte[] Encrypt(byte[] input, int offset, int length) return output; } + /// + /// Decrypts the specified data. + /// + /// The data. + /// + /// The decrypted data. + /// + public override byte[] Decrypt(byte[] input) + { + return Decrypt(input, 0, input.Length); + } + /// /// Decrypts the specified input. /// diff --git a/src/Renci.SshNet/Security/Cryptography/Cipher.cs b/src/Renci.SshNet/Security/Cryptography/Cipher.cs index 5ce38581c..e624bbba3 100644 --- a/src/Renci.SshNet/Security/Cryptography/Cipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Cipher.cs @@ -18,7 +18,7 @@ public abstract class Cipher /// /// The input. /// Encrypted data. - public virtual byte[] Encrypt(byte[] input) + public byte[] Encrypt(byte[] input) { return Encrypt(input, 0, input.Length); } @@ -41,10 +41,7 @@ public virtual byte[] Encrypt(byte[] input) /// /// The decrypted data. /// - public virtual byte[] Decrypt(byte[] input) - { - return Decrypt(input, 0, input.Length); - } + public abstract byte[] Decrypt(byte[] input); /// /// Decrypts the specified input. diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 984e4fdea..3bf1235a2 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -1,27 +1,81 @@ -using System; +#pragma warning disable IDE0005 // Using directive is unnecessary. +#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts + +using System; using System.Globalization; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; +using Renci.SshNet.Security.Cryptography.Ciphers.Paddings; namespace Renci.SshNet.Security.Cryptography.Ciphers { + /// + /// Custom AES Cipher Mode, follows System.Security.Cryptography.CipherMode. + /// + public enum AesCipherMode + { + /// CBC Mode. + CBC = 1, + + /// ECB Mode. + ECB = 2, + + /// OFB Mode. + OFB = 3, + + /// CFB Mode. + CFB = 4, + + /// CTS Mode. + CTS = 5, + + /// CTR Mode. + CTR = 6 + } + /// /// AES cipher implementation. /// public sealed class AesCipher : BlockCipher, IDisposable { - private readonly CipherMode _mode; + private readonly AesCipherMode _aesMode; + private readonly CipherMode _blockMode; + + private readonly Aes _aes; + private ICryptoTransform _encryptor; + private ICryptoTransform _decryptor; + + private AesCipher _ecbHelper; + private uint[] _packedIV; + + /// + /// Initializes a new instance of the class in ECB mode. + /// + /// The key. + /// Enable PKCS7 padding. + /// is . + /// Keysize is not valid for this algorithm. + public AesCipher(byte[] key, bool pkcs7Padding = false) + : this(key, iv: null, AesCipherMode.ECB, pkcs7Padding) + { + } /// /// Initializes a new instance of the class. /// /// The key. /// The mode. - /// The padding. + /// The IV. + /// Enable PKCS7 padding. /// is . /// Keysize is not valid for this algorithm. - public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) + public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false) + : base(key, 16, mode: null, padding: pkcs7Padding ? new PKCS7Padding() : null) { var keySize = key.Length * 8; @@ -30,15 +84,67 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); } - // Creating a default ECB mode encryptor/decryptor to use when Ciphermode is null - // Normally the AES encryption/decryption happens on the CipherMode classes, but - // BlockCipher-derived classes also need to override EncryptBlock/DecryptBlock - _mode = mode; - if (_mode is null) + _aesMode = mode; + iv = iv?.Take(16) ?? new byte[16]; + var bclMode = GetBCLMode(mode, iv, out _blockMode); + + if (_blockMode is not null) + { + _ecbHelper = new AesCipher(key, pkcs7Padding: false); // no padding + _blockMode.Init(_ecbHelper); + } + + _aes = Aes.Create(); + _aes.BlockSize = 128; + _aes.FeedbackSize = 128; + _aes.Mode = bclMode; + _aes.Padding = pkcs7Padding ? PaddingMode.PKCS7 : PaddingMode.None; + _aes.Key = key; + _aes.IV = iv; + +#pragma warning disable S3329 // Cipher Block Chaining IVs should be unpredictable +#pragma warning disable CA5401 // Cipher Block Chaining IVs should be unpredictable + _encryptor = _aes.CreateEncryptor(); + _decryptor = _aes.CreateDecryptor(); + } + + // get the BCL-equivalent AES mode and optional legacy CipherMode + private System.Security.Cryptography.CipherMode GetBCLMode(AesCipherMode mode, byte[] iv, out CipherMode blockMode) + { + blockMode = null; + +#pragma warning disable IDE0010 // allow missing cases, fallback to ECB + switch (mode) { - _mode = new EcbCipherMode(); - _mode.Init(key, 16, padding); + case AesCipherMode.CBC: + return System.Security.Cryptography.CipherMode.CBC; + + case AesCipherMode.CTS: + return System.Security.Cryptography.CipherMode.CTS; + + // OFB is supported on 4.62 but has a faulty implementation, so always fallback to ECB + case AesCipherMode.OFB: + blockMode = new OfbCipherMode(iv); + break; + + // CFB not supported on NetStandard 2.1 + case AesCipherMode.CFB: +#if NET6_0_OR_GREATER + return System.Security.Cryptography.CipherMode.CFB; +#else + blockMode = new CfbCipherMode(iv); + break; +#endif + + // CTR not supported by the BCL + case AesCipherMode.CTR: + blockMode = new CtrCipherMode(iv); + _packedIV = GetPackedIV(iv); + break; } +#pragma warning restore IDE0010 // Add missing cases + + return System.Security.Cryptography.CipherMode.ECB; } /// @@ -56,7 +162,16 @@ public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) /// or is too short. public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - return _mode.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + // fallback: legacy block-by-block encryption + if (_blockMode is not null) + { + return _blockMode.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + CheckArgs(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + + // fast crypto using the BCL CryptoServiceProvider/CNG + return _encryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } /// @@ -74,7 +189,16 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC /// or is too short. public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - return _mode.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + // fallback: legacy block-by-block decryption + if (_blockMode is not null) + { + return _blockMode.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + CheckArgs(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + + // fast crypto using the BCL CryptoServiceProvider/CNG + return _decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } /// @@ -88,7 +212,19 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC /// public override byte[] Encrypt(byte[] input, int offset, int length) { - return _mode.Encrypt(input, offset, length); + if (_aesMode == AesCipherMode.CTR) + { + return CTREncryptDecrypt(input, offset, length); + } + + // fast crypto using the BCL CryptoServiceProvider/CNG + if (_blockMode is null && _encryptor.CanTransformMultipleBlocks) + { + return _encryptor.TransformFinalBlock(input, offset, length); + } + + // fallback: legacy block-by-block encryption + return base.Encrypt(input, offset, length); } /// @@ -102,15 +238,225 @@ public override byte[] Encrypt(byte[] input, int offset, int length) /// public override byte[] Decrypt(byte[] input, int offset, int length) { - return _mode.Decrypt(input, offset, length); + if (_aesMode == AesCipherMode.CTR) + { + return CTREncryptDecrypt(input, offset, length); + } + + // fast crypto using the BCL CryptoServiceProvider/CNG + if (_blockMode is null && _decryptor.CanTransformMultipleBlocks) + { + return _decryptor.TransformFinalBlock(input, offset, length); + } + + // fallback: legacy block-by-block decryption + return base.Decrypt(input, offset, length); } + private void CheckArgs(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < BlockSize) + { + throw new ArgumentException("Invalid input buffer"); + } + + if (outputBuffer.Length - outputOffset < BlockSize) + { + throw new ArgumentException("Invalid output buffer"); + } + + if (inputCount != BlockSize) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); + } + } + + // CTR implementation, optimized for large arrays + // This provides huge performance gain by avoiding a block-by-block encrpyt/decrypt loop + // AES-CTR is used by AWS SFTP Transfer Family + #region CTR + + // Perform AES-CTR encryption/decryption + private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) + { + var count = length / BlockSize; + if (length % BlockSize != 0) + { + count++; + } + + var counter = CTRCreateCounterArray(count); + var aesCounter = _encryptor.TransformFinalBlock(counter, 0, counter.Length); + var output = ArrayXOR(aesCounter, data, offset, length); + + return output; + } + +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER + + // creates the Counter array filled with incrementing copies of IV + private byte[] CTRCreateCounterArray(int blocks) + { + var bytes = new byte[blocks * 16]; + var counter = MemoryMarshal.Cast(bytes.AsSpan()); + + // fill array with IV, increment by 1 for each copy + for (var i = 0; i < counter.Length; i += 4) + { + // write IV to buffer (big endian) + counter[i] = _packedIV[0]; + counter[i + 1] = _packedIV[1]; + counter[i + 2] = _packedIV[2]; + counter[i + 3] = _packedIV[3]; + + // increment IV (little endian - swap, increment, swap back) + uint j = 3; + do + { + _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + } + while (_packedIV[j] == 0 && --j >= 0); + } + + return bytes; + } + + // XOR 2 arrays using Vector + private static byte[] ArrayXOR(byte[] counter, byte[] data, int offset, int length) + { + for (var loopOffset = 0; length > 0; length -= Vector.Count) + { + if (length >= Vector.Count) + { + var v = new Vector(counter, loopOffset) ^ new Vector(data, offset + loopOffset); + v.CopyTo(counter, loopOffset); + loopOffset += Vector.Count; + } + else + { + for (var i = 0; i < length; i++) + { + counter[loopOffset] ^= data[offset + loopOffset]; + loopOffset++; + } + } + } + + return counter; + } + +#else + // creates the Counter array filled with incrementing copies of IV + private byte[] CTRCreateCounterArray(int blocks) + { + // fill array with IV, increment by 1 for each copy + var counter = new uint[blocks * 4]; + for (var i = 0; i < counter.Length; i += 4) + { + // write IV to buffer (big endian) + counter[i] = _packedIV[0]; + counter[i + 1] = _packedIV[1]; + counter[i + 2] = _packedIV[2]; + counter[i + 3] = _packedIV[3]; + + // increment IV (little endian - swap, increment, swap back) + uint j = 3; + do + { + _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + } + while (_packedIV[j] == 0 && --j >= 0); + } + + // copy uint[] to byte[] + var counterBytes = new byte[blocks * 16]; + Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); + return counterBytes; + } + + // XOR 2 arrays using Uint[] and blockcopy + private static byte[] ArrayXOR(byte[] counter, byte[] data, int offset, int length) + { + var words = length / 4; + if (length % 4 != 0) + { + words++; + } + + // convert original data to words + var datawords = new uint[words]; + Buffer.BlockCopy(data, offset, datawords, 0, length); + + // convert encrypted IV counter to words + var counterwords = new uint[words]; + Buffer.BlockCopy(counter, 0, counterwords, 0, length); + + // XOR encrypted Counter with input data + for (var i = 0; i < words; i++) + { + counterwords[i] = counterwords[i] ^ datawords[i]; + } + + // copy uint[] to byte[] + var output = counter; + Buffer.BlockCopy(counterwords, 0, output, 0, length); + + // adjust output for non-aligned lengths + if (output.Length > length) + { + Array.Resize(ref output, length); + } + + return output; + } + +#endif + + // pack the IV into an array of uint[4] + private static uint[] GetPackedIV(byte[] iv) + { + var packedIV = new uint[4]; + packedIV[0] = BitConverter.ToUInt32(iv, 0); + packedIV[1] = BitConverter.ToUInt32(iv, 4); + packedIV[2] = BitConverter.ToUInt32(iv, 8); + packedIV[3] = BitConverter.ToUInt32(iv, 12); + + return packedIV; + } + + private static uint SwapEndianness(uint x) + { + x = (x >> 16) | (x << 16); + return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8); + } + + #endregion + /// /// Dispose the instance. /// + /// Set to True to dispose of resouces. + public void Dispose(bool disposing) + { + if (disposing) + { + _encryptor?.Dispose(); + _encryptor = null; + + _decryptor?.Dispose(); + _decryptor = null; + + _ecbHelper?.Dispose(); + _ecbHelper = null; + } + } + + /// public void Dispose() { - _mode?.Dispose(); + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); } } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs index dcc880e9c..490756aff 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs @@ -1,104 +1,49 @@ -using System; -using System.Globalization; -using System.Security.Cryptography; - -using Renci.SshNet.Common; +using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography.Ciphers { /// /// Base class for cipher mode implementations. /// - public abstract class CipherMode : IDisposable + public abstract class CipherMode { - private readonly System.Security.Cryptography.CipherMode _aesMode; - - private Aes _aes; - private ICryptoTransform _encryptor; - private ICryptoTransform _decryptor; - private CipherPadding _padding; - - /// - /// Gets a value indicating whether to process arrays in one go using CNG provider - /// Set to False to process arrays block by block. - /// - protected virtual bool SupportsMultipleBlocks - { - get { return true; } - } - - /// - /// Gets the AES Encryptor instance. - /// - protected ICryptoTransform Encryptor - { - get - { - if (_encryptor == null || !_encryptor.CanReuseTransform) - { - _encryptor = _aes.CreateEncryptor(); - } - - return _encryptor; - } - } - +#pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1306 // Field names should begin with lower-case letter /// - /// Gets the AES Decryptor instance. + /// Gets the cipher. /// - protected ICryptoTransform Decryptor - { - get - { - if (_decryptor == null || !_decryptor.CanReuseTransform) - { - _decryptor = _aes.CreateDecryptor(); - } - - return _decryptor; - } - } + protected BlockCipher Cipher; /// /// Gets the IV vector. /// - protected internal byte[] IV { get; private set; } + protected byte[] IV; /// - /// Gets the block size of the cipher. + /// Holds block size of the cipher. /// - protected int BlockSize { get; private set; } + protected int _blockSize; +#pragma warning restore SA1306 // Field names should begin with lower-case letter +#pragma warning restore SA1401 // Fields should be private /// /// Initializes a new instance of the class. /// /// The iv. - /// The AES mode. - protected CipherMode(byte[] iv, System.Security.Cryptography.CipherMode aesMode) + protected CipherMode(byte[] iv) { IV = iv; - _aesMode = aesMode; } /// /// Initializes the specified cipher mode. /// - /// The key. - /// The block size. - /// Enable PKCS7 padding. - internal void Init(byte[] key, int blocksize, CipherPadding padding) + /// The cipher. + internal void Init(BlockCipher cipher) { - BlockSize = blocksize; - IV = IV.Take(BlockSize); - _padding = padding; - - _aes = Aes.Create(); - _aes.BlockSize = BlockSize * 8; - _aes.FeedbackSize = BlockSize * 8; - _aes.Mode = _aesMode; - _aes.Padding = padding is null ? PaddingMode.None : PaddingMode.PKCS7; - _aes.Key = key; - _aes.IV = IV; + Cipher = cipher; + _blockSize = cipher.BlockSize; + IV = IV.Take(_blockSize); } /// @@ -112,27 +57,7 @@ internal void Init(byte[] key, int blocksize, CipherPadding padding) /// /// The number of bytes encrypted. /// - /// or is . - /// or is too short. - public virtual int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < BlockSize) - { - throw new ArgumentException("Invalid input buffer"); - } - - if (outputBuffer.Length - outputOffset < BlockSize) - { - throw new ArgumentException("Invalid output buffer"); - } - - if (inputCount != BlockSize) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); - } - - return Encryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } + public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); /// /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. @@ -145,151 +70,6 @@ public virtual int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCo /// /// The number of bytes decrypted. /// - /// or is . - /// or is too short. - public virtual int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < BlockSize) - { - throw new ArgumentException("Invalid input buffer"); - } - - if (outputBuffer.Length - outputOffset < BlockSize) - { - throw new ArgumentException("Invalid output buffer"); - } - - if (inputCount != BlockSize) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); - } - - return Decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - /// - /// Encrypts the specified data. - /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . - /// - /// The encrypted data. - /// - public virtual byte[] Encrypt(byte[] input, int offset, int length) - { - if (SupportsMultipleBlocks && Encryptor.CanTransformMultipleBlocks) - { - return Encryptor.TransformFinalBlock(input, offset, length); - } - - return EncryptArray(input, offset, length); - } - - /// - /// Decrypts the specified input. - /// - /// The input. - /// The zero-based offset in at which to begin decrypting. - /// The number of bytes to decrypt from . - /// - /// The decrypted data. - /// - public virtual byte[] Decrypt(byte[] input, int offset, int length) - { - if (SupportsMultipleBlocks && Decryptor.CanTransformMultipleBlocks) - { - return Decryptor.TransformFinalBlock(input, offset, length); - } - - return DecryptArray(input, offset, length); - } - - // Encrypts the data using EncryptBlock() on each block. - private byte[] EncryptArray(byte[] input, int offset, int length) - { - if (length % BlockSize > 0) - { - if (_padding is null) - { - throw new ArgumentException("data"); - } - - var paddingLength = BlockSize - (length % BlockSize); - input = _padding.Pad(input, offset, length, paddingLength); - length += paddingLength; - offset = 0; - } - - var output = new byte[length]; - var writtenBytes = 0; - - for (var i = 0; i < length / BlockSize; i++) - { - writtenBytes += EncryptBlock(input, offset + (i * BlockSize), BlockSize, output, i * BlockSize); - } - - if (writtenBytes < length) - { - throw new InvalidOperationException("Encryption error."); - } - - return output; - } - - // Decrypts the data using DecryptBlock() on each block. - private byte[] DecryptArray(byte[] input, int offset, int length) - { - if (length % BlockSize > 0) - { - if (_padding is null) - { - throw new ArgumentException("data"); - } - - input = _padding.Pad(BlockSize, input, offset, length); - offset = 0; - length = input.Length; - } - - var output = new byte[length]; - - var writtenBytes = 0; - for (var i = 0; i < length / BlockSize; i++) - { - writtenBytes += DecryptBlock(input, offset + (i * BlockSize), BlockSize, output, i * BlockSize); - } - - if (writtenBytes < length) - { - throw new InvalidOperationException("Encryption error."); - } - - return output; - } - - /// - /// Dispose the instance. - /// - /// Set to True to dispose of resouces. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _encryptor?.Dispose(); - _encryptor = null; - - _decryptor?.Dispose(); - _decryptor = null; - } - } - - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } + public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs index 753d17644..a2b9243d4 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs @@ -1,4 +1,7 @@ -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +using System; +using System.Globalization; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes { /// /// Implements CBC cipher mode. @@ -10,8 +13,88 @@ public class CbcCipherMode : CipherMode /// /// The iv. public CbcCipherMode(byte[] iv) - : base(iv, System.Security.Cryptography.CipherMode.CBC) + : base(iv) { } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < _blockSize) + { + throw new ArgumentException("Invalid input buffer"); + } + + if (outputBuffer.Length - outputOffset < _blockSize) + { + throw new ArgumentException("Invalid output buffer"); + } + + if (inputCount != _blockSize) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } + + for (var i = 0; i < _blockSize; i++) + { + IV[i] ^= inputBuffer[inputOffset + i]; + } + + _ = Cipher.EncryptBlock(IV, 0, inputCount, outputBuffer, outputOffset); + + Buffer.BlockCopy(outputBuffer, outputOffset, IV, 0, IV.Length); + + return _blockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < _blockSize) + { + throw new ArgumentException("Invalid input buffer"); + } + + if (outputBuffer.Length - outputOffset < _blockSize) + { + throw new ArgumentException("Invalid output buffer"); + } + + if (inputCount != _blockSize) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); + } + + _ = Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + + for (var i = 0; i < _blockSize; i++) + { + outputBuffer[outputOffset + i] ^= IV[i]; + } + + Buffer.BlockCopy(inputBuffer, inputOffset, IV, 0, IV.Length); + + return _blockSize; + } } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs index 6512da378..23a4bb2f7 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs @@ -1,7 +1,4 @@ -#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts -#pragma warning disable IDE0005 // Using directive is unnecessary - -using System; +using System; using System.Globalization; namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes @@ -11,37 +8,14 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes /// public class CfbCipherMode : CipherMode { - // NET6 or above: The CNG supports CFB mode -#if NET6_0_OR_GREATER - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public CfbCipherMode(byte[] iv) - : base(iv, System.Security.Cryptography.CipherMode.CFB) - { - } - -#else // Others: The CNG does not support CFB mode, CFB implementated using ECB as base - private readonly byte[] _ivOutput; - /// - /// Gets a value indicating whether to process arrays in one go using CNG provider - /// Set to False to process arrays block by block. - /// - protected override bool SupportsMultipleBlocks - { - get { return false; } - } - /// /// Initializes a new instance of the class. /// /// The iv. public CfbCipherMode(byte[] iv) - : base(iv, System.Security.Cryptography.CipherMode.ECB) + : base(iv) { _ivOutput = new byte[iv.Length]; } @@ -59,32 +33,32 @@ public CfbCipherMode(byte[] iv) /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer.Length - inputOffset < BlockSize) + if (inputBuffer.Length - inputOffset < _blockSize) { throw new ArgumentException("Invalid input buffer"); } - if (outputBuffer.Length - outputOffset < BlockSize) + if (outputBuffer.Length - outputOffset < _blockSize) { throw new ArgumentException("Invalid output buffer"); } - if (inputCount != BlockSize) + if (inputCount != _blockSize) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); } - _ = base.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - for (var i = 0; i < BlockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - Buffer.BlockCopy(IV, BlockSize, IV, 0, IV.Length - BlockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, IV, IV.Length - BlockSize, BlockSize); + Buffer.BlockCopy(IV, _blockSize, IV, 0, IV.Length - _blockSize); + Buffer.BlockCopy(outputBuffer, outputOffset, IV, IV.Length - _blockSize, _blockSize); - return BlockSize; + return _blockSize; } /// @@ -100,33 +74,32 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC /// public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer.Length - inputOffset < BlockSize) + if (inputBuffer.Length - inputOffset < _blockSize) { throw new ArgumentException("Invalid input buffer"); } - if (outputBuffer.Length - outputOffset < BlockSize) + if (outputBuffer.Length - outputOffset < _blockSize) { throw new ArgumentException("Invalid output buffer"); } - if (inputCount != BlockSize) + if (inputCount != _blockSize) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); } - _ = base.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - Buffer.BlockCopy(IV, BlockSize, IV, 0, IV.Length - BlockSize); - Buffer.BlockCopy(inputBuffer, inputOffset, IV, IV.Length - BlockSize, BlockSize); + Buffer.BlockCopy(IV, _blockSize, IV, 0, IV.Length - _blockSize); + Buffer.BlockCopy(inputBuffer, inputOffset, IV, IV.Length - _blockSize, _blockSize); - for (var i = 0; i < BlockSize; i++) + for (var i = 0; i < _blockSize; i++) { outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - return BlockSize; + return _blockSize; } -#endif } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs index 961cac1cd..a0ae5010b 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs @@ -1,8 +1,5 @@ -#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts -#pragma warning disable IDE0005 // Using directive is unnecessary - -using System; -using System.Numerics; +using System; +using System.Globalization; namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes { @@ -11,16 +8,16 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes /// public class CtrCipherMode : CipherMode { - private readonly uint[] _ivOutput; + private readonly byte[] _ivOutput; /// /// Initializes a new instance of the class. /// /// The iv. public CtrCipherMode(byte[] iv) - : base(iv, System.Security.Cryptography.CipherMode.ECB) + : base(iv) { - _ivOutput = GetPackedIV(iv); + _ivOutput = new byte[iv.Length]; } /// @@ -36,172 +33,51 @@ public CtrCipherMode(byte[] iv) /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - var buffer = CTREncryptDecrypt(inputBuffer, inputOffset, inputCount); - Buffer.BlockCopy(buffer, 0, outputBuffer, 0, buffer.Length); - - return buffer.Length; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - /// - /// Encrypts the specified data. - /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . - /// - /// The encrypted data. - /// - public override byte[] Encrypt(byte[] input, int offset, int length) - { - return CTREncryptDecrypt(input, offset, length); - } - - /// - /// Decrypts the specified input. - /// - /// The input. - /// The zero-based offset in at which to begin decrypting. - /// The number of bytes to decrypt from . - /// - /// The decrypted data. - /// - public override byte[] Decrypt(byte[] input, int offset, int length) - { - return CTREncryptDecrypt(input, offset, length); - } - - // convert the IV into an array of uint[4] - private static uint[] GetPackedIV(byte[] iv) - { - var packedIV = new uint[4]; - packedIV[0] = (uint) ((iv[0] << 24) | (iv[1] << 16) | (iv[2] << 8) | iv[3]); - packedIV[1] = (uint) ((iv[4] << 24) | (iv[5] << 16) | (iv[6] << 8) | iv[7]); - packedIV[2] = (uint) ((iv[8] << 24) | (iv[9] << 16) | (iv[10] << 8) | iv[11]); - packedIV[3] = (uint) ((iv[12] << 24) | (iv[13] << 16) | (iv[14] << 8) | iv[15]); - return packedIV; - } - - // Perform AES-CTR encryption/decryption - private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) - { - var count = length / BlockSize; - if (length % BlockSize != 0) + if (inputBuffer.Length - inputOffset < _blockSize) { - count++; + throw new ArgumentException("Invalid input buffer"); } - var counter = CTRCreateCounterArray(count); - var aesCounter = Encryptor.TransformFinalBlock(counter, 0, counter.Length); - var output = CTRArrayXOR(aesCounter, data, offset, length); - - return output; - } - - // creates the Counter array filled with incrementing copies of IV - private byte[] CTRCreateCounterArray(int blocks) - { - // fill an array with IV, increment by 1 for each copy - var counter = new uint[blocks * 4]; - for (var i = 0; i < counter.Length; i += 4) + if (outputBuffer.Length - outputOffset < _blockSize) { - // write IV to buffer (big endian) - counter[i] = (_ivOutput[0] << 24) | ((_ivOutput[0] << 8) & 0x00FF0000) | ((_ivOutput[0] >> 8) & 0x0000FF00) | (_ivOutput[0] >> 24); - counter[i + 1] = (_ivOutput[1] << 24) | ((_ivOutput[1] << 8) & 0x00FF0000) | ((_ivOutput[1] >> 8) & 0x0000FF00) | (_ivOutput[1] >> 24); - counter[i + 2] = (_ivOutput[2] << 24) | ((_ivOutput[2] << 8) & 0x00FF0000) | ((_ivOutput[2] >> 8) & 0x0000FF00) | (_ivOutput[2] >> 24); - counter[i + 3] = (_ivOutput[3] << 24) | ((_ivOutput[3] << 8) & 0x00FF0000) | ((_ivOutput[3] >> 8) & 0x0000FF00) | (_ivOutput[3] >> 24); - - // increment IV (little endian) - for (var j = 3; j >= 0 && ++_ivOutput[j] == 0; j--) - { - // empty block - } + throw new ArgumentException("Invalid output buffer"); } - // copy uint[] to byte[] - var counterBytes = new byte[blocks * 16]; - Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); - return counterBytes; - } - -#if NETSTANDARD2_1_OR_GREATER - - // XOR 2 arrays using Vector - private static byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) - { - for (var loopOffset = 0; length > 0; length -= Vector.Count) + if (inputCount != _blockSize) { - var v = new Vector(counter, loopOffset) ^ new Vector(data, offset + loopOffset); - if (length >= Vector.Count) - { - v.CopyTo(counter, loopOffset); - loopOffset += Vector.Count; - } - else - { - for (var i = 0; i < length; i++) - { - counter[loopOffset++] = v[i]; - } - } + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); } - return counter; - } - -#else // fallback to blockcopy and regular XOR + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); - // XORs the input data with the encrypted Counter array to produce the final output - // uses uint arrays for speed - private static byte[] CTRArrayXOR(byte[] counter, byte[] data, int offset, int length) - { - var words = length / 4; - if (length % 4 != 0) + for (var i = 0; i < _blockSize; i++) { - words++; + outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - // convert original data to words - var datawords = new uint[words]; - Buffer.BlockCopy(data, offset, datawords, 0, length); - - // convert encrypted IV counter to words - var counterwords = new uint[words]; - Buffer.BlockCopy(counter, 0, counterwords, 0, length); - - // XOR encrypted Counter with input data - for (var i = 0; i < words; i++) + var j = IV.Length; + while (--j >= 0 && ++IV[j] == 0) { - counterwords[i] = counterwords[i] ^ datawords[i]; + // Intentionally empty block } - // copy uint[] to byte[] - var output = counter; - Buffer.BlockCopy(counterwords, 0, output, 0, length); - - // adjust output for non-aligned lengths - if (output.Length > length) - { - Array.Resize(ref output, length); - } + return _blockSize; + } - return output; + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } -#endif } } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs deleted file mode 100644 index 48a77d716..000000000 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/EcbCipherMode.cs +++ /dev/null @@ -1,18 +0,0 @@ -#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements EBC cipher mode. - /// - public class EcbCipherMode : CipherMode - { - /// - /// Initializes a new instance of the class. - /// - public EcbCipherMode() - : base(new byte[16], System.Security.Cryptography.CipherMode.ECB) - { - } - } -} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs index c9bce9f17..70b6473d3 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs @@ -1,7 +1,4 @@ -#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts -#pragma warning disable IDE0005 // Using directive is unnecessary - -using System; +using System; using System.Globalization; namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes @@ -11,36 +8,14 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes /// public class OfbCipherMode : CipherMode { - // NetStandard 2.0: The CNG supports OFB mode -#if NETSTANDARD2_0 - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public OfbCipherMode(byte[] iv) - : base(iv, System.Security.Cryptography.CipherMode.OFB) - { - } - -#else // NetStandard 2.1 and above: The CNG does not support OFB mode, OFB implementated using ECB as base - private readonly byte[] _ivOutput; - /// - /// Gets a value indicating whether to process arrays in one go using CNG provider - /// Set to False to process arrays block by block. - /// - protected override bool SupportsMultipleBlocks - { - get { return false; } - } - /// /// Initializes a new instance of the class. /// /// The iv. public OfbCipherMode(byte[] iv) - : base(iv, System.Security.Cryptography.CipherMode.ECB) + : base(iv) { _ivOutput = new byte[iv.Length]; } @@ -58,31 +33,31 @@ public OfbCipherMode(byte[] iv) /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - if (inputBuffer.Length - inputOffset < BlockSize) + if (inputBuffer.Length - inputOffset < _blockSize) { throw new ArgumentException("Invalid input buffer"); } - if (outputBuffer.Length - outputOffset < BlockSize) + if (outputBuffer.Length - outputOffset < _blockSize) { throw new ArgumentException("Invalid output buffer"); } - if (inputCount != BlockSize) + if (inputCount != _blockSize) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", _blockSize)); } - _ = base.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); + _ = Cipher.EncryptBlock(IV, 0, IV.Length, _ivOutput, 0); Buffer.BlockCopy(_ivOutput, 0, IV, 0, IV.Length); - for (var i = 0; i < BlockSize; i++) + for (var i = 0; i < _blockSize; i++) { - outputBuffer[outputOffset + i] = (byte)(_ivOutput[i] ^ inputBuffer[inputOffset + i]); + outputBuffer[outputOffset + i] = (byte) (_ivOutput[i] ^ inputBuffer[inputOffset + i]); } - return BlockSize; + return _blockSize; } /// @@ -100,6 +75,5 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC { return EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } -#endif } } diff --git a/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs b/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs index ff414cc4a..bc0cdbf6c 100644 --- a/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs +++ b/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs @@ -1,6 +1,7 @@ -using BenchmarkDotNet.Attributes; +#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts + +using BenchmarkDotNet.Attributes; using Renci.SshNet.Security.Cryptography.Ciphers; -using Renci.SshNet.Security.Cryptography.Ciphers.Modes; namespace Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers { @@ -15,7 +16,7 @@ public AesCipherBenchmarks() { _key = new byte[32]; _iv = new byte[16]; - _data = new byte[256]; + _data = new byte[32 * 1024]; Random random = new(Seed: 12345); random.NextBytes(_key); @@ -26,13 +27,49 @@ public AesCipherBenchmarks() [Benchmark] public byte[] Encrypt_CBC() { - return new AesCipher(_key, new CbcCipherMode(_iv), null).Encrypt(_data); + return new AesCipher(_key, _iv, AesCipherMode.CBC, false).Encrypt(_data); } [Benchmark] public byte[] Decrypt_CBC() { - return new AesCipher(_key, new CbcCipherMode(_iv), null).Decrypt(_data); + return new AesCipher(_key, _iv, AesCipherMode.CBC, false).Decrypt(_data); + } + + [Benchmark] + public byte[] Encrypt_CFB() + { + return new AesCipher(_key, _iv, AesCipherMode.CFB, false).Encrypt(_data); + } + + [Benchmark] + public byte[] Decrypt_CFB() + { + return new AesCipher(_key, _iv, AesCipherMode.CFB, false).Decrypt(_data); + } + + [Benchmark] + public byte[] Encrypt_CTR() + { + return new AesCipher(_key, _iv, AesCipherMode.CTR, false).Encrypt(_data); + } + + [Benchmark] + public byte[] Decrypt_CTR() + { + return new AesCipher(_key, _iv, AesCipherMode.CTR, false).Decrypt(_data); + } + + [Benchmark] + public byte[] Encrypt_ECB() + { + return new AesCipher(_key).Encrypt(_data); + } + + [Benchmark] + public byte[] Decrypt_ECB() + { + return new AesCipher(_key).Decrypt(_data); } } } diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt index c0bda499b..390e85e71 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt @@ -6,11 +6,11 @@ Dictionary modes = new() { - ["ecb"] = ("mode: null", CipherMode.ECB), - ["cbc"] = ("new CbcCipherMode((byte[])iv.Clone())", CipherMode.CBC), - ["cfb"] = ("new CfbCipherMode((byte[])iv.Clone())", CipherMode.CFB), - ["ctr"] = ("new CtrCipherMode((byte[])iv.Clone())", null), - ["ofb"] = ("new OfbCipherMode((byte[])iv.Clone())", CipherMode.OFB), + ["ecb"] = ("iv: null, AesCipherMode.AES", CipherMode.ECB), + ["cbc"] = ("(byte[])iv.Clone(), AesCipherMode.CBC", CipherMode.CBC), + ["cfb"] = ("(byte[])iv.Clone(), AesCipherMode.CFB", CipherMode.CFB), + ["ctr"] = ("(byte[])iv.Clone(), AesCipherMode.CTR", null), + ["ofb"] = ("(byte[])iv.Clone(), AesCipherMode.OFB", CipherMode.OFB), }; Random random = new(123); @@ -90,7 +90,7 @@ foreach ((string mode, (string modeCode, CipherMode? bclMode)) in modes) tw.WriteLine($"// {openSslCmd} | hd"); // pipe to hexdump WriteBytes(expected); tw.WriteLine(); - tw.WriteLine($"var actual = new AesCipher(key, {modeCode}, padding: null).Encrypt(input);"); + tw.WriteLine($"var actual = new AesCipher(key, {modeCode}, pkcs7Padding: false).Encrypt(input);"); tw.WriteLine(); tw.WriteLine($"CollectionAssert.AreEqual(expected, actual);"); @@ -117,7 +117,7 @@ foreach ((string mode, (string modeCode, CipherMode? bclMode)) in modes) } tw.WriteLine(); - tw.WriteLine($"var decrypted = new AesCipher(key, {modeCode}, padding: null).Decrypt(actual);"); + tw.WriteLine($"var decrypted = new AesCipher(key, {modeCode}, pkcs7Padding: false).Decrypt(actual);"); tw.WriteLine(); tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted);"); diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index 1587481ea..ff7d978f2 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -1,7 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; -using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers @@ -19,7 +18,7 @@ public void Encrypt_Input_128_CBC() var key = new byte[] { 0xe4, 0x94, 0xf9, 0xb1, 0x00, 0x4f, 0x16, 0x2a, 0x80, 0x11, 0xea, 0x73, 0x0d, 0xb9, 0xbf, 0x64 }; var iv = new byte[] { 0x74, 0x8b, 0x4f, 0xe6, 0xc1, 0x29, 0xb3, 0x54, 0xec, 0x77, 0x92, 0xf3, 0x15, 0xa0, 0x41, 0xa8 }; var expected = new byte[] { 0x19, 0x7f, 0x80, 0xd8, 0xc9, 0x89, 0xc4, 0xa7, 0xc6, 0xc6, 0x3f, 0x9f, 0x1e, 0x00, 0x1f, 0x72, 0xa7, 0x5e, 0xde, 0x40, 0x88, 0xa2, 0x72, 0xf2, 0xed, 0x3f, 0x81, 0x45, 0xb6, 0xbd, 0x45, 0x87, 0x15, 0xa5, 0x10, 0x92, 0x4a, 0x37, 0x9e, 0xa9, 0x80, 0x1c, 0x14, 0x83, 0xa3, 0x39, 0x45, 0x28 }; - var testCipher = new AesCipher(key, new CbcCipherMode(iv), null); + var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false); var actual = testCipher.Encrypt(input); @@ -34,7 +33,7 @@ public void Encrypt_InputAndOffsetAndLength_128_CBC() var key = new byte[] { 0xe4, 0x94, 0xf9, 0xb1, 0x00, 0x4f, 0x16, 0x2a, 0x80, 0x11, 0xea, 0x73, 0x0d, 0xb9, 0xbf, 0x64 }; var iv = new byte[] { 0x74, 0x8b, 0x4f, 0xe6, 0xc1, 0x29, 0xb3, 0x54, 0xec, 0x77, 0x92, 0xf3, 0x15, 0xa0, 0x41, 0xa8 }; var expected = new byte[] { 0x19, 0x7f, 0x80, 0xd8, 0xc9, 0x89, 0xc4, 0xa7, 0xc6, 0xc6, 0x3f, 0x9f, 0x1e, 0x00, 0x1f, 0x72, 0xa7, 0x5e, 0xde, 0x40, 0x88, 0xa2, 0x72, 0xf2, 0xed, 0x3f, 0x81, 0x45, 0xb6, 0xbd, 0x45, 0x87, 0x15, 0xa5, 0x10, 0x92, 0x4a, 0x37, 0x9e, 0xa9, 0x80, 0x1c, 0x14, 0x83, 0xa3, 0x39, 0x45, 0x28 }; - var testCipher = new AesCipher(key, new CbcCipherMode(iv), null); + var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false); var actual = testCipher.Encrypt(input, 2, input.Length - 5); @@ -48,7 +47,7 @@ public void Encrypt_Input_128_CTR() var key = new byte[] { 0x17, 0x78, 0x56, 0xe1, 0x3e, 0xbd, 0x3e, 0x50, 0x1d, 0x79, 0x3f, 0x0f, 0x55, 0x37, 0x45, 0x54 }; var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var expected = new byte[] { 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d }; - var testCipher = new AesCipher(key, new CtrCipherMode(iv), null); + var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false); var actual = testCipher.Encrypt(input); @@ -62,7 +61,7 @@ public void Decrypt_Input_128_CTR() var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var input = new byte[] { 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d }; var expected = new byte[] { 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x73, 0x68, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x61, 0x75, 0x74, 0x68, 0xb0, 0x74, 0x21, 0x87, 0x16, 0xb9, 0x69, 0x48, 0x33, 0xce, 0xb3, 0xe7, 0xdc, 0x3f, 0x50, 0xdc, 0xcc, 0xd5, 0x27, 0xb7, 0xfe, 0x7a, 0x78, 0x22, 0xae, 0xc8 }; - var testCipher = new AesCipher(key, new CtrCipherMode(iv), null); + var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, false); var actual = testCipher.Decrypt(input); @@ -76,7 +75,7 @@ public void Decrypt_InputAndOffsetAndLength_128_CTR() var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var input = new byte[] { 0x0a, 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d, 0x0a, 0x05 }; var expected = new byte[] { 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x73, 0x68, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x61, 0x75, 0x74, 0x68, 0xb0, 0x74, 0x21, 0x87, 0x16, 0xb9, 0x69, 0x48, 0x33, 0xce, 0xb3, 0xe7, 0xdc, 0x3f, 0x50, 0xdc, 0xcc, 0xd5, 0x27, 0xb7, 0xfe, 0x7a, 0x78, 0x22, 0xae, 0xc8 }; - var testCipher = new AesCipher(key, new CtrCipherMode(iv), null); + var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, false); var actual = testCipher.Decrypt(input, 1, input.Length - 3); @@ -96,19 +95,19 @@ public void AES_ECB_128_Length16() { 0x96, 0x39, 0xec, 0x0d, 0xfc, 0x2d, 0xb2, 0x7c, 0xe9, 0x74, 0x8e, 0x5f, 0xb9, 0xf3, 0x99, 0xce, }; - + // echo -n -e '\x03\xe1\xe1\xaa\xa5\xbc\xa1\x9f\xba\x8c\x42\x05\x8b\x4a\xbf\x28' | openssl enc -e -aes-128-ecb -K 9639EC0DFC2DB27CE9748E5FB9F399CE -nopad | hd var expected = new byte[] { 0x9d, 0x55, 0x05, 0x4e, 0xe9, 0x50, 0xb5, 0x93, 0x50, 0x93, 0x69, 0x96, 0xa6, 0xdd, 0x1e, 0x15, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -124,20 +123,20 @@ public void AES_ECB_128_Length32() { 0x67, 0x02, 0x45, 0xc8, 0xb8, 0x64, 0x42, 0x17, 0xda, 0x85, 0x21, 0x3e, 0x5c, 0xa6, 0xee, 0xd4, }; - + // echo -n -e '\x1a\xf1\x3a\x35\x8c\xca\x3f\xd6\x2f\x65\xc1\x31\x2d\x41\xe5\xc7\xf3\x74\x23\x71\xed\x6d\x84\x79\x61\xd0\xf8\x6f\x7f\x0c\xcc\x86' | openssl enc -e -aes-128-ecb -K 670245C8B8644217DA85213E5CA6EED4 -nopad | hd var expected = new byte[] { 0x73, 0x67, 0xcc, 0x04, 0x46, 0xf5, 0x31, 0x9b, 0x64, 0x26, 0x32, 0xba, 0xa4, 0x18, 0x0d, 0x8a, 0xe3, 0x1c, 0x95, 0x44, 0x49, 0x9e, 0x4a, 0x17, 0x0e, 0x64, 0xd3, 0xe8, 0x5c, 0xe6, 0x9f, 0x83, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -155,7 +154,7 @@ public void AES_ECB_128_Length64() { 0xc7, 0x8d, 0x3a, 0x4c, 0xa2, 0xfb, 0xde, 0x1e, 0x49, 0x3e, 0xc1, 0x34, 0x86, 0x14, 0xc6, 0x2d, }; - + // echo -n -e '\x99\x3a\xc9\x2b\xfb\x1d\x0e\x8e\x31\x0c\x96\x68\x4c\x46\x1d\xbb\xe1\x23\xc8\x99\x59\x90\x47\xcb\x63\x99\x5b\xf7\x91\x87\x44\x09\x2e\xff\xa4\x21\xdc\xc3\xd9\x89\xd7\x24\x0a\x32\x05\x36\x60\x25\xa4\x17\xda\xaf\x08\xbe\xc9\x08\xf3\xfe\xc7\x61\xc2\x17\xfd\xaa' | openssl enc -e -aes-128-ecb -K C78D3A4CA2FBDE1E493EC1348614C62D -nopad | hd var expected = new byte[] { @@ -164,13 +163,13 @@ public void AES_ECB_128_Length64() 0xee, 0x64, 0xf2, 0xf4, 0xa3, 0x20, 0x54, 0x84, 0x7f, 0x8d, 0xe1, 0x6b, 0xf3, 0xd9, 0x7e, 0x34, 0x10, 0xe3, 0xe0, 0x30, 0xd3, 0x0e, 0xe3, 0x94, 0xd8, 0xf5, 0xb1, 0x44, 0xf8, 0x36, 0xfd, 0x0b, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -186,19 +185,19 @@ public void AES_ECB_192_Length16() 0x38, 0x99, 0x28, 0x8c, 0xc4, 0x84, 0xfd, 0x32, 0x8c, 0xca, 0x16, 0x06, 0xcc, 0x00, 0x22, 0xd2, 0x76, 0x00, 0x0d, 0x25, 0xa9, 0x4e, 0x31, 0x25, }; - + // echo -n -e '\x27\x60\x6b\x78\xfc\x13\x83\xa8\x38\xbb\x65\xca\xfd\x94\x82\xde' | openssl enc -e -aes-192-ecb -K 3899288CC484FD328CCA1606CC0022D276000D25A94E3125 -nopad | hd var expected = new byte[] { 0x1c, 0xd3, 0x91, 0xd8, 0xc3, 0xe0, 0x4d, 0x8e, 0x9e, 0x5c, 0xaf, 0xcc, 0x55, 0x65, 0x54, 0xb7, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -215,20 +214,20 @@ public void AES_ECB_192_Length32() 0x05, 0x6a, 0xc2, 0x70, 0x62, 0xff, 0x28, 0x34, 0xce, 0x08, 0x58, 0x9c, 0xe3, 0x76, 0x1b, 0xbb, 0x1a, 0xbc, 0xf9, 0x4c, 0x60, 0xe1, 0x5f, 0x57, }; - + // echo -n -e '\x63\x38\xec\x32\xfd\x7d\xdb\x38\x99\x93\x53\xfc\x86\x5d\x35\xe9\x68\x02\xda\x1a\x43\x0b\x02\x55\x57\x74\xed\x7d\x5a\xbf\x82\x3b' | openssl enc -e -aes-192-ecb -K 056AC27062FF2834CE08589CE3761BBB1ABCF94C60E15F57 -nopad | hd var expected = new byte[] { 0x02, 0x7c, 0x02, 0x3d, 0x80, 0x78, 0xbe, 0x53, 0x10, 0xb9, 0x1b, 0xbf, 0xb4, 0x2c, 0x16, 0xe7, 0x87, 0xe2, 0x91, 0x40, 0x31, 0x26, 0x67, 0xf6, 0xf7, 0x86, 0x73, 0x89, 0x0d, 0x35, 0x22, 0x6c, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -247,7 +246,7 @@ public void AES_ECB_192_Length64() 0x10, 0x0c, 0x69, 0x35, 0xc3, 0x1f, 0x8d, 0xe7, 0xc7, 0x6b, 0xa5, 0x2a, 0x6f, 0x46, 0x73, 0xe9, 0x6b, 0xb1, 0x8e, 0xac, 0xef, 0xf1, 0xcc, 0x78, }; - + // echo -n -e '\xda\xa5\x4b\x3b\xb3\x66\x71\xe0\x58\x31\x62\x9d\xc6\x36\xda\x23\x0b\x6b\x3b\xcb\x24\x9f\xa4\x6f\x29\x7e\x8b\xcb\x7f\xff\x21\x56\x34\x90\x72\xba\x95\x23\xa3\xcf\x25\xfa\x30\x5e\xfc\x40\x13\xda\x3d\xd3\x10\x2f\x89\xbc\x44\x3a\x01\xdb\x11\x34\xda\xa5\x60\x58' | openssl enc -e -aes-192-ecb -K 100C6935C31F8DE7C76BA52A6F4673E96BB18EACEFF1CC78 -nopad | hd var expected = new byte[] { @@ -256,13 +255,13 @@ public void AES_ECB_192_Length64() 0xc1, 0xe2, 0xf0, 0xbc, 0x01, 0xad, 0xb1, 0x15, 0xaf, 0x42, 0x6c, 0x08, 0xc8, 0xb3, 0x98, 0xf3, 0xcd, 0x20, 0xab, 0xbc, 0x59, 0xb2, 0xa5, 0x80, 0xf5, 0x8e, 0x53, 0xda, 0xb1, 0x39, 0x8f, 0xbc, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -278,19 +277,19 @@ public void AES_ECB_256_Length16() 0x3f, 0xf1, 0xe9, 0x8b, 0x65, 0xd3, 0xd5, 0x58, 0x77, 0x26, 0x91, 0x97, 0xf9, 0x84, 0x12, 0x8e, 0x9b, 0x71, 0x66, 0xc6, 0x8a, 0xaf, 0x61, 0x31, 0x6c, 0xff, 0x52, 0xea, 0xa5, 0xcb, 0x68, 0xe4, }; - + // echo -n -e '\x45\x29\x67\x1d\x16\x1a\xcb\xba\x67\x28\xc9\x28\x17\xb4\x69\x1e' | openssl enc -e -aes-256-ecb -K 3FF1E98B65D3D55877269197F984128E9B7166C68AAF61316CFF52EAA5CB68E4 -nopad | hd var expected = new byte[] { 0x6a, 0xd2, 0x73, 0x2b, 0x05, 0x2e, 0xdd, 0x74, 0x0c, 0x37, 0xf2, 0xcf, 0x8a, 0xef, 0x57, 0x8a, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -307,20 +306,20 @@ public void AES_ECB_256_Length32() 0x3e, 0x6e, 0xd3, 0x69, 0x3e, 0xc2, 0x96, 0xca, 0x9a, 0x20, 0x56, 0x3a, 0x6b, 0x50, 0xf0, 0x68, 0x5b, 0xfa, 0x32, 0xdc, 0x0a, 0xf6, 0x10, 0xea, 0xa0, 0x7c, 0xec, 0x58, 0x30, 0x19, 0x86, 0x1f, }; - + // echo -n -e '\x16\x3b\x8d\xa6\x4d\xa3\x94\x8f\x8f\xb8\x1f\x66\x81\xeb\xb3\xab\xbe\xac\x29\xca\xd3\x2b\x9a\x10\xba\xf4\x72\x7b\x09\x70\xa8\x38' | openssl enc -e -aes-256-ecb -K 3E6ED3693EC296CA9A20563A6B50F0685BFA32DC0AF610EAA07CEC583019861F -nopad | hd var expected = new byte[] { 0xb3, 0x37, 0x5d, 0x78, 0xf5, 0x99, 0x69, 0xad, 0x7e, 0xf9, 0x0f, 0xb7, 0x00, 0x8b, 0x99, 0x0f, 0x59, 0x0b, 0x9c, 0x7a, 0xf2, 0xb6, 0x34, 0x0d, 0xc9, 0xdd, 0x15, 0x6e, 0x75, 0xe7, 0xc6, 0x82, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -339,7 +338,7 @@ public void AES_ECB_256_Length64() 0xd4, 0x87, 0xea, 0x53, 0xe8, 0x73, 0x87, 0x22, 0x56, 0xe6, 0xcd, 0x47, 0x29, 0x23, 0x91, 0xe3, 0x0f, 0xee, 0xe7, 0x16, 0x43, 0x76, 0x0c, 0xb7, 0x41, 0x2f, 0x6e, 0xeb, 0xf6, 0xd8, 0x3e, 0x35, }; - + // echo -n -e '\x03\x09\x1f\x0e\x3e\xcb\x2e\x47\x5e\xe9\xc8\xc2\xd5\x3e\x9a\x80\x9a\x37\x2a\x85\x28\xdd\x51\x11\x8d\x36\xc6\xab\xc6\x5c\x14\x41\xd7\x82\x55\x26\xf9\x77\xe0\x44\xb7\xe0\xb4\x2d\x80\xaa\x26\xd7\xc4\xaf\x19\x9e\x34\x20\x41\x25\xb8\x0d\x81\x08\x05\x82\x81\x01' | openssl enc -e -aes-256-ecb -K D487EA53E873872256E6CD47292391E30FEEE71643760CB7412F6EEBF6D83E35 -nopad | hd var expected = new byte[] { @@ -348,13 +347,13 @@ public void AES_ECB_256_Length64() 0x06, 0x36, 0x7b, 0x61, 0x48, 0x62, 0x76, 0xfb, 0x58, 0x3e, 0x08, 0x51, 0xf6, 0xf8, 0xfa, 0xd2, 0x63, 0xd2, 0x7d, 0x7a, 0xfc, 0xdb, 0x11, 0x08, 0x70, 0x73, 0x61, 0xe0, 0xfb, 0x93, 0xa6, 0xf9, }; - - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - + + var actual = new AesCipher(key).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -373,19 +372,19 @@ public void AES_CBC_128_Length16() { 0x32, 0xda, 0x6f, 0x58, 0xe0, 0x28, 0x99, 0xf5, 0xf5, 0xfa, 0x7e, 0x8c, 0xc1, 0x35, 0x4c, 0x8d, }; - + // echo -n -e '\x7c\x9e\xf8\x16\x9b\x6a\xbe\x5e\x7a\x33\x11\xb9\x04\x9b\x2c\x7d' | openssl enc -e -aes-128-cbc -K A798E775CA98233C0096ED4C2DBE6447 -iv 32DA6F58E02899F5F5FA7E8CC1354C8D -nopad | hd var expected = new byte[] { 0x49, 0x0e, 0xa9, 0x6f, 0x55, 0xb3, 0x57, 0xdf, 0x7c, 0x18, 0x77, 0x0c, 0xca, 0x46, 0x0d, 0x83, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -405,20 +404,20 @@ public void AES_CBC_128_Length32() { 0xca, 0xc2, 0xbd, 0xf7, 0xae, 0x21, 0x62, 0xf5, 0x2e, 0x28, 0xbb, 0x1f, 0x06, 0xfa, 0xca, 0xe4, }; - + // echo -n -e '\xca\xcb\xa6\xb9\x12\x87\xca\xe3\x7a\xbb\x16\x04\x7c\x71\x30\xbc\xce\xc9\x86\x2a\x2b\xd4\x9c\x7e\xfe\xf2\x80\xcf\x19\x96\x7b\xca' | openssl enc -e -aes-128-cbc -K 4A60826217AA35AB108BDD2512957883 -iv CAC2BDF7AE2162F52E28BB1F06FACAE4 -nopad | hd var expected = new byte[] { 0x55, 0xf4, 0x06, 0x4c, 0xdf, 0x4e, 0xf0, 0x12, 0xce, 0x45, 0x53, 0xdd, 0x9e, 0x12, 0x62, 0x61, 0x2d, 0x87, 0x42, 0x20, 0xf1, 0x0b, 0x78, 0x96, 0xd5, 0x7c, 0xeb, 0xa2, 0x7f, 0x4b, 0x5a, 0xff, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -440,7 +439,7 @@ public void AES_CBC_128_Length64() { 0x8e, 0x7d, 0x33, 0x9e, 0x6f, 0x9b, 0x21, 0x4f, 0xee, 0x2a, 0x96, 0x4a, 0x3e, 0x32, 0x63, 0x68, }; - + // echo -n -e '\x3f\x4b\xb9\x1c\xef\xcd\xa4\x23\x94\xdb\x1a\x9f\xf7\x77\x6c\x69\x79\xfc\x05\x57\xd9\x84\x1c\x29\xfe\x8c\x34\xef\xef\x15\xa4\x15\xc1\xf9\xe5\xc6\xdb\x5c\x94\xfc\x1d\x99\x63\xd3\x06\xc2\xfe\xb7\xbb\x51\xa6\x09\xf4\x72\x0a\xbb\x2f\x90\x1e\x62\x99\xb5\x34\x7e' | openssl enc -e -aes-128-cbc -K 3604DEFD91A68D1D680839402148223C -iv 8E7D339E6F9B214FEE2A964A3E326368 -nopad | hd var expected = new byte[] { @@ -449,13 +448,13 @@ public void AES_CBC_128_Length64() 0x64, 0xd5, 0x0e, 0xc2, 0x47, 0xce, 0x2a, 0x40, 0x47, 0x12, 0x05, 0xde, 0x19, 0xbd, 0x23, 0x76, 0x3d, 0x61, 0x9e, 0x0d, 0x54, 0x7f, 0xe1, 0xc4, 0x78, 0xf2, 0x04, 0x00, 0x68, 0xa9, 0x9b, 0x32, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -475,19 +474,19 @@ public void AES_CBC_192_Length16() { 0x57, 0x1e, 0xda, 0xe6, 0xf9, 0x35, 0x16, 0x23, 0x91, 0xaf, 0xdb, 0x5c, 0x5e, 0x47, 0xe7, 0xcf, }; - + // echo -n -e '\x65\xe4\x9c\x01\xe4\x00\x26\x15\xc3\x88\xa1\xeb\x38\xca\x99\xe6' | openssl enc -e -aes-192-cbc -K 6EE2D41C81960F9BE38E0F660F43DF36A5D1DA3CAC20578D -iv 571EDAE6F935162391AFDB5C5E47E7CF -nopad | hd var expected = new byte[] { 0xe1, 0x2f, 0x71, 0xad, 0x59, 0xae, 0xa7, 0xe3, 0xd3, 0x23, 0x43, 0x81, 0x31, 0xc2, 0xe5, 0xd9, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -508,20 +507,20 @@ public void AES_CBC_192_Length32() { 0x5f, 0x6f, 0xdc, 0x06, 0xea, 0xa5, 0x18, 0x27, 0x92, 0xe8, 0x7e, 0xe4, 0xf4, 0x8e, 0x4c, 0x87, }; - + // echo -n -e '\xd5\x00\x1e\x55\xf1\xbf\x05\x80\xa9\x6a\x46\x67\xef\x5c\x3a\x4e\x8a\x46\xc5\x63\xbb\x28\xa1\xae\x78\xeb\xd4\x5f\x67\x82\xd8\x5e' | openssl enc -e -aes-192-cbc -K E90B67AB02029B9718593C8EEEAE3334758DD217828413AC -iv 5F6FDC06EAA5182792E87EE4F48E4C87 -nopad | hd var expected = new byte[] { 0x21, 0x2c, 0x43, 0x64, 0x48, 0x20, 0xe9, 0xfd, 0xe9, 0x15, 0x27, 0x4d, 0x35, 0x8f, 0xf8, 0x42, 0x07, 0xf2, 0x98, 0x41, 0xbb, 0x58, 0x3d, 0xe5, 0xcf, 0x56, 0xf5, 0x4b, 0x33, 0xf7, 0xa0, 0x9a, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -544,7 +543,7 @@ public void AES_CBC_192_Length64() { 0xe9, 0x55, 0xd3, 0x62, 0x90, 0xea, 0x36, 0xf4, 0x77, 0xe6, 0xea, 0xb7, 0xa4, 0x10, 0x7c, 0x85, }; - + // echo -n -e '\xab\x2d\x4a\x61\xeb\x12\xc0\xca\xb7\xa0\xea\xda\xb0\xc0\xdb\x65\xf8\xbb\x4c\x92\x26\x95\xac\x72\x41\x15\xfc\x06\x30\x4f\x3f\xe6\x40\x4a\x6b\x54\x39\xb1\xc0\x4c\xaf\x11\x4e\x4a\xbb\x3e\x76\xd2\x0c\x18\xeb\x39\x42\xb9\x61\x15\x81\xd7\x20\xd6\x16\xba\x9a\x67' | openssl enc -e -aes-192-cbc -K 60049A6655872C46FAFFE3144762B7039F29F9186306A386 -iv E955D36290EA36F477E6EAB7A4107C85 -nopad | hd var expected = new byte[] { @@ -553,13 +552,13 @@ public void AES_CBC_192_Length64() 0x7f, 0x91, 0x8f, 0xed, 0xa9, 0x88, 0x72, 0x0c, 0x9c, 0x55, 0x13, 0x87, 0x34, 0x17, 0x51, 0xd3, 0xda, 0xba, 0xde, 0xb2, 0x7d, 0xbc, 0x71, 0xf8, 0x9b, 0xaa, 0x93, 0x52, 0xf4, 0x26, 0x3c, 0x6f, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -579,19 +578,19 @@ public void AES_CBC_256_Length16() { 0xe2, 0x90, 0x56, 0x90, 0x93, 0x7d, 0xd2, 0x22, 0xef, 0x2d, 0x7a, 0xe7, 0xb0, 0x6e, 0xa7, 0x1f, }; - + // echo -n -e '\xec\xa5\x3e\x43\xd6\x4d\xce\x1f\x1f\x1d\x37\xec\xc0\x82\x03\x5a' | openssl enc -e -aes-256-cbc -K 60137CFFB3C9B510C9EE9C6077005F8EAC732BBEC760B09C87B44273B34934F5 -iv E2905690937DD222EF2D7AE7B06EA71F -nopad | hd var expected = new byte[] { 0xe7, 0xa5, 0x53, 0xd7, 0x28, 0x4c, 0x16, 0x4e, 0xfc, 0xa2, 0xa8, 0x86, 0xfc, 0xcb, 0x71, 0x61, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -612,20 +611,20 @@ public void AES_CBC_256_Length32() { 0xf4, 0x5f, 0xf1, 0x64, 0x8d, 0x52, 0x75, 0xd3, 0x08, 0xe0, 0xea, 0x54, 0xa1, 0x48, 0x29, 0xcd, }; - + // echo -n -e '\xbe\xa8\x3f\x4d\x56\x45\x92\x00\x63\xe0\x78\xfe\x87\x42\x5d\x7f\xba\xa7\x7d\xe7\xaa\xce\xfb\x2f\xa1\x09\xcf\x99\xe5\xc8\xec\x18' | openssl enc -e -aes-256-cbc -K 0D22B40A09E69E9DFD552DB205D39AADD0FA2D08F0BF75F0AC10AB4C76F81A9B -iv F45FF1648D5275D308E0EA54A14829CD -nopad | hd var expected = new byte[] { 0x4f, 0x29, 0xa7, 0xd7, 0xbc, 0x51, 0x95, 0xc5, 0x4c, 0x79, 0x1c, 0xde, 0xad, 0xc8, 0xe0, 0xfd, 0x6a, 0xfb, 0x4a, 0x8b, 0xc8, 0x25, 0x87, 0x5c, 0x9b, 0x47, 0xf5, 0x3f, 0x42, 0xf5, 0xc6, 0x08, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -648,7 +647,7 @@ public void AES_CBC_256_Length64() { 0x2a, 0x2b, 0x52, 0xf9, 0x69, 0x3c, 0x42, 0xe7, 0x0f, 0x0c, 0x7f, 0xde, 0x12, 0xad, 0xb9, 0xab, }; - + // echo -n -e '\x6e\x21\xc9\xeb\x9a\xe3\x28\x4e\xad\xc4\x5e\xf9\x3a\x52\x26\x04\x3b\x91\xbd\xa6\xe2\x36\x1f\x7d\x85\x59\xff\x0f\xd5\x21\x4e\x63\xe7\xde\xdd\x54\x2f\x2f\x00\x11\x36\xa3\xb7\xc8\xf4\x7c\x98\xb6\xb9\xe5\x18\x0f\x8b\x82\xeb\x38\x02\x4b\x65\x40\xe3\x19\x78\x8b' | openssl enc -e -aes-256-cbc -K 76FA125C74D2D50C90AC703E7E578809C14855F0083FD864F2A452E3C9C02D1D -iv 2A2B52F9693C42E70F0C7FDE12ADB9AB -nopad | hd var expected = new byte[] { @@ -657,13 +656,13 @@ public void AES_CBC_256_Length64() 0x54, 0x3e, 0xb6, 0x23, 0x0a, 0x82, 0x7d, 0x3f, 0xbf, 0x88, 0xd1, 0x05, 0x0d, 0x10, 0x10, 0x59, 0x08, 0x19, 0x66, 0x47, 0xe7, 0xd9, 0x1d, 0x1c, 0x42, 0xdc, 0x97, 0x9c, 0xf0, 0x9a, 0x14, 0x34, }; - - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -682,19 +681,19 @@ public void AES_CFB_128_Length16() { 0xea, 0xa7, 0x20, 0x6c, 0x40, 0x92, 0x59, 0xa2, 0xa8, 0x1b, 0xd7, 0xbc, 0xd1, 0x72, 0x67, 0x1d, }; - + // echo -n -e '\xc2\x5c\x7f\x9b\xc3\x88\x83\x37\x22\xad\x6a\xcf\x7f\xf1\x42\xd0' | openssl enc -e -aes-128-cfb -K 7F531353048F9F84066EE0FCBFFA5144 -iv EAA7206C409259A2A81BD7BCD172671D -nopad | hd var expected = new byte[] { 0x76, 0xd2, 0x2b, 0x69, 0xa6, 0xdf, 0x3b, 0x4d, 0x4a, 0x52, 0x8a, 0x7a, 0x54, 0x9d, 0xbe, 0x55, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -714,20 +713,20 @@ public void AES_CFB_128_Length32() { 0xa4, 0xaf, 0x60, 0xab, 0x8d, 0x8e, 0x4c, 0xf3, 0x1f, 0x71, 0xc6, 0x27, 0xbb, 0xbe, 0x7c, 0xda, }; - + // echo -n -e '\xc9\xae\x5e\xbf\x99\x66\xa6\x33\xbd\xfa\x94\x55\xa8\x87\x77\x28\x5b\x25\xe4\xd8\xd1\xd6\x8f\xed\xf9\x71\xab\xe8\xb7\xe2\xb3\x94' | openssl enc -e -aes-128-cfb -K 06C232119B92AB8400ECAE46FE04F921 -iv A4AF60AB8D8E4CF31F71C627BBBE7CDA -nopad | hd var expected = new byte[] { 0x62, 0x67, 0x2b, 0xa7, 0x0b, 0xd1, 0xbc, 0x2f, 0x55, 0xaa, 0x71, 0x53, 0x7a, 0x68, 0xe5, 0x46, 0x18, 0xc5, 0xf7, 0x41, 0x78, 0x5f, 0x38, 0x6b, 0x4d, 0x04, 0x00, 0x3b, 0x61, 0x8c, 0xaf, 0xe7, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -749,7 +748,7 @@ public void AES_CFB_128_Length64() { 0x73, 0x1b, 0xa7, 0xfa, 0xd4, 0x97, 0xc8, 0xfb, 0x7b, 0xbf, 0x05, 0xe0, 0xa4, 0xb6, 0xca, 0xbf, }; - + // echo -n -e '\x47\xff\x4e\xe5\x54\x65\x8d\xc9\x7a\x60\xd7\xe4\x27\x49\xef\xf4\x78\x89\x44\x07\x82\x07\x06\x77\x76\x3e\xf1\x29\xcc\x84\xc8\x42\x70\xd3\xff\xfe\xb6\x13\xcc\x3e\x22\x96\x31\x2d\xb6\x67\xcb\xd6\x82\xd1\xaf\x31\x79\x74\x58\x3f\xf9\xd6\x6f\x16\x73\x63\xfc\xf6' | openssl enc -e -aes-128-cfb -K 979573544AF03D64BD05E5EBD5D8C00E -iv 731BA7FAD497C8FB7BBF05E0A4B6CABF -nopad | hd var expected = new byte[] { @@ -758,13 +757,13 @@ public void AES_CFB_128_Length64() 0x44, 0xe6, 0xce, 0x49, 0xe6, 0x8f, 0x35, 0x27, 0x26, 0x21, 0x04, 0xee, 0x52, 0x44, 0x40, 0x80, 0xf7, 0x49, 0xbc, 0xbf, 0xcb, 0x5c, 0xfa, 0x12, 0xcb, 0xcc, 0x38, 0x71, 0x68, 0xd6, 0xe9, 0x64, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -784,19 +783,19 @@ public void AES_CFB_192_Length16() { 0x90, 0x15, 0x66, 0x89, 0x23, 0x54, 0x6c, 0x0f, 0x55, 0xe4, 0xca, 0x43, 0x12, 0x72, 0x02, 0x98, }; - + // echo -n -e '\x9a\x3b\x96\x21\xf3\x77\x8b\x91\x94\x4a\x73\x74\x8f\x6c\x6a\x20' | openssl enc -e -aes-192-cfb -K 1561A357BC022100CC78D98AEB5DC0073A26519A429F1AFB -iv 9015668923546C0F55E4CA4312720298 -nopad | hd var expected = new byte[] { 0x4f, 0x9b, 0xdf, 0x72, 0x2d, 0x10, 0x1b, 0xb9, 0xa1, 0xe1, 0x06, 0xba, 0xbc, 0xc5, 0xfe, 0x13, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -817,20 +816,20 @@ public void AES_CFB_192_Length32() { 0xb9, 0xdc, 0x70, 0xd4, 0xcb, 0x9f, 0xa3, 0x0d, 0x77, 0x72, 0x45, 0x61, 0x50, 0x31, 0x2c, 0xa8, }; - + // echo -n -e '\x1a\x96\x54\x7b\x9e\x01\xa6\x36\x8a\x6c\x3a\x69\x1a\xcf\xdd\x76\x46\xa7\xc7\xa7\x9b\x97\xdc\x78\x0b\xca\x35\x06\x93\x7c\xf4\xc7' | openssl enc -e -aes-192-cfb -K 23B97FAC4A9E5D8E6F2FFFB61903F4850753FC6BAB5BFC83 -iv B9DC70D4CB9FA30D7772456150312CA8 -nopad | hd var expected = new byte[] { 0xc0, 0xdf, 0x63, 0xb5, 0x17, 0x40, 0xd4, 0xa7, 0x73, 0x40, 0xc5, 0x21, 0xa5, 0xea, 0x63, 0xdf, 0x72, 0xcf, 0x57, 0x7f, 0xf9, 0x5d, 0xfe, 0xb1, 0x36, 0x9a, 0x1d, 0x02, 0x0d, 0x4b, 0x8f, 0x35, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -853,7 +852,7 @@ public void AES_CFB_192_Length64() { 0xd9, 0x60, 0xfc, 0xbb, 0xb1, 0x44, 0xab, 0xc6, 0x1e, 0xbb, 0xa0, 0x77, 0x4b, 0x5f, 0x87, 0xac, }; - + // echo -n -e '\x4a\x38\x37\x6b\x98\x26\x5e\x08\xd5\xb0\xff\x3f\x80\x88\x1c\xc8\xbc\xfc\xf3\x6d\x2d\x89\xc3\xcf\x8c\xf1\x3e\xa7\xbe\x93\x34\xd6\x27\x53\x21\x72\x23\x90\xeb\x93\x7d\x68\xfe\x1b\xa0\x63\x8d\xee\x56\x7c\xa4\x54\x3d\xbe\x7a\xc0\x75\x68\xdf\xa6\xe7\xb7\x49\x42' | openssl enc -e -aes-192-cfb -K 7B28182D67AAA52C1160F0C58AA72F28644F5041EEE09868 -iv D960FCBBB144ABC61EBBA0774B5F87AC -nopad | hd var expected = new byte[] { @@ -862,13 +861,13 @@ public void AES_CFB_192_Length64() 0xec, 0x58, 0x90, 0x0d, 0xe0, 0x32, 0xb6, 0xa4, 0xfd, 0x6f, 0xac, 0xdb, 0x40, 0x63, 0xe9, 0x28, 0x69, 0x90, 0x2a, 0xf9, 0xf4, 0xe8, 0xcc, 0xa5, 0x2b, 0xdd, 0x9c, 0xbc, 0x44, 0xcd, 0x1e, 0x5b, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -888,19 +887,19 @@ public void AES_CFB_256_Length16() { 0x67, 0x06, 0xe7, 0x9c, 0x0b, 0x80, 0xfe, 0xed, 0xfd, 0x75, 0x28, 0xa4, 0x0d, 0x67, 0xc6, 0x80, }; - + // echo -n -e '\xd7\x78\x42\xdd\xce\xb5\x36\xcd\x38\xb7\x42\x97\x66\x08\x53\x9a' | openssl enc -e -aes-256-cfb -K 90AF5406ADE97ACCB429A7CE07B7DC04C8A469769EBB9A242A2E82FA01145F16 -iv 6706E79C0B80FEEDFD7528A40D67C680 -nopad | hd var expected = new byte[] { 0xf0, 0xfa, 0x95, 0x5c, 0xfc, 0x3f, 0xbe, 0xe5, 0x4b, 0x55, 0x57, 0xad, 0x93, 0x63, 0x36, 0x07, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -921,20 +920,20 @@ public void AES_CFB_256_Length32() { 0x1a, 0x1a, 0x16, 0x65, 0x60, 0x07, 0x5a, 0x2e, 0x19, 0xdc, 0xf7, 0xbe, 0xb9, 0x1d, 0xa4, 0x26, }; - + // echo -n -e '\xda\x3e\xcf\xc2\x9e\xdd\xfc\xd4\x15\x30\xdc\x7f\x67\x80\xcb\xa0\xca\x91\x66\x01\xd0\x40\xf8\x47\xa5\x7b\x78\x28\x93\xf5\x16\xc2' | openssl enc -e -aes-256-cfb -K 680120C3459C778A091286DBA37F867DAA88D97C01C4B09945871C2365D3411F -iv 1A1A166560075A2E19DCF7BEB91DA426 -nopad | hd var expected = new byte[] { 0x94, 0x65, 0xf5, 0x19, 0xe9, 0xc8, 0xc6, 0xd0, 0x0d, 0x81, 0x4e, 0x13, 0xb8, 0x37, 0x2b, 0x92, 0xc2, 0xc1, 0x54, 0x9c, 0xfd, 0xf9, 0x43, 0xd0, 0xdc, 0xa7, 0x20, 0x68, 0x3e, 0xc3, 0x8f, 0x3c, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -957,7 +956,7 @@ public void AES_CFB_256_Length64() { 0x9a, 0xb4, 0x33, 0x33, 0xc2, 0x25, 0x9f, 0xfd, 0xe2, 0x52, 0xee, 0x1c, 0xeb, 0xc6, 0xc7, 0x99, }; - + // echo -n -e '\xf5\xfa\x7d\x0a\x1c\x99\xc0\xa4\x51\x86\x7e\xbe\x7f\x54\x24\x35\xd1\x67\xc1\x89\x68\x20\x1d\xa2\x2d\xab\x63\x25\xcc\xf1\xe0\x27\xe3\xf6\x2d\x6a\x56\x36\x03\x81\x59\x72\x13\xd9\x89\x9c\xae\xc5\xb7\xc1\xec\x52\x5c\x1a\xbd\xd4\xdd\xda\xdd\x70\x35\x9b\xd7\x5f' | openssl enc -e -aes-256-cfb -K A656DA8926BADF9A633F2FF60C431990FC9D6D0A048DCBC838588D7B59924BBE -iv 9AB43333C2259FFDE252EE1CEBC6C799 -nopad | hd var expected = new byte[] { @@ -966,13 +965,13 @@ public void AES_CFB_256_Length64() 0xa1, 0xd6, 0x8a, 0xc4, 0xbc, 0xe1, 0xca, 0x0a, 0xaa, 0xa8, 0xea, 0x4f, 0x7c, 0xd2, 0xd2, 0xc4, 0xf6, 0xd4, 0x06, 0xef, 0x04, 0xf1, 0xe5, 0x53, 0x54, 0xd5, 0x80, 0xc2, 0x96, 0x6b, 0xc7, 0x07, }; - - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -991,19 +990,19 @@ public void AES_CTR_128_Length16() { 0x91, 0xf3, 0xba, 0x0b, 0x1e, 0xb2, 0x8f, 0xce, 0x59, 0x1b, 0xa8, 0xaf, 0xd4, 0xd1, 0xd0, 0x7e, }; - + // echo -n -e '\xc1\x4d\x74\x98\x2e\xcc\x5a\x18\x8a\x12\x50\xcd\x2c\x63\x41\xd0' | openssl enc -e -aes-128-ctr -K F4715B580FE5CED7FD7028B29EAEDC71 -iv 91F3BA0B1EB28FCE591BA8AFD4D1D07E -nopad | hd var expected = new byte[] { 0xe4, 0x03, 0x8f, 0x2a, 0xdd, 0x9d, 0xf6, 0x87, 0xf6, 0x29, 0xee, 0x27, 0x4c, 0xf3, 0xba, 0x82, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1023,20 +1022,20 @@ public void AES_CTR_128_Length32() { 0xa1, 0xcc, 0x79, 0xf6, 0x95, 0x97, 0xd4, 0xdb, 0x6b, 0xe6, 0x99, 0xdd, 0x70, 0x95, 0x9e, 0x60, }; - + // echo -n -e '\x11\x1e\x28\x7a\x6a\x6f\x89\xdb\x7f\x9d\x9a\xbd\xa3\xa8\x79\xdc\x36\xde\x3c\x38\xa9\x35\xb2\x41\xe1\x8d\xff\xf4\x3d\x1e\x02\x2c' | openssl enc -e -aes-128-ctr -K A0AAA180866107216ADE8C8017D12AB1 -iv A1CC79F69597D4DB6BE699DD70959E60 -nopad | hd var expected = new byte[] { 0xa9, 0x27, 0xa5, 0xbd, 0x73, 0x59, 0xe3, 0x69, 0x79, 0x89, 0x62, 0xe8, 0x4c, 0x7d, 0x75, 0xcd, 0x9c, 0xb2, 0x30, 0x94, 0xdc, 0x88, 0xfa, 0x39, 0x05, 0x0c, 0x26, 0x25, 0x28, 0x6a, 0x9b, 0x4e, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1058,7 +1057,7 @@ public void AES_CTR_128_Length64() { 0x92, 0xdb, 0xe4, 0x3e, 0xaf, 0x8f, 0x92, 0x13, 0x71, 0x56, 0xd1, 0x9f, 0x0f, 0x68, 0xc3, 0xc1, }; - + // echo -n -e '\x9b\x6e\x1d\xf8\x07\xf9\x55\xd4\xd7\x1a\xce\xca\xa8\x31\x29\x0f\x63\x4d\x52\x71\xa5\x0c\x96\x08\xd6\xc5\x14\xa0\xc8\x29\xb1\xd5\x40\x2c\xe5\xa9\xb4\x31\xa9\xa8\x76\xa5\x1e\x7a\xc8\x09\x32\x39\xbc\x89\x7a\x22\x42\x2c\xba\x8e\xd7\x15\x22\x41\xe4\xb5\x0b\xad' | openssl enc -e -aes-128-ctr -K 69F98A7C4B805B31A4AAFAFFED1C3FCC -iv 92DBE43EAF8F92137156D19F0F68C3C1 -nopad | hd var expected = new byte[] { @@ -1067,13 +1066,13 @@ public void AES_CTR_128_Length64() 0x1d, 0x18, 0x55, 0xfc, 0x56, 0x59, 0xaf, 0x0b, 0x2b, 0x80, 0x87, 0x0c, 0x87, 0x45, 0xb0, 0xe2, 0xec, 0x47, 0x81, 0x82, 0x89, 0x24, 0x76, 0xe2, 0x20, 0x6a, 0x99, 0xe2, 0xa7, 0x5a, 0xb0, 0x40, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1093,19 +1092,19 @@ public void AES_CTR_192_Length16() { 0xec, 0xa8, 0x10, 0x66, 0x10, 0xfb, 0xe1, 0xb6, 0xb5, 0x15, 0xca, 0xb9, 0xb9, 0xba, 0xf0, 0xcd, }; - + // echo -n -e '\x9a\x70\x11\xcf\x7f\xb6\xee\x3b\x2e\x48\x7e\x97\x32\xbb\xa1\xbb' | openssl enc -e -aes-192-ctr -K D556AF09D0CCFEDA66760AF5AFBC223BE639657D0A704CDC -iv ECA8106610FBE1B6B515CAB9B9BAF0CD -nopad | hd var expected = new byte[] { 0xc4, 0x4e, 0x81, 0x32, 0xe6, 0x6d, 0x0a, 0x78, 0x49, 0xe5, 0x64, 0x6c, 0xe6, 0xc2, 0x91, 0xc9, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1126,20 +1125,20 @@ public void AES_CTR_192_Length32() { 0x6d, 0xfd, 0x74, 0x57, 0xb9, 0xf2, 0x80, 0xbd, 0xbf, 0x85, 0xb0, 0xbd, 0x19, 0xdd, 0x5d, 0xc6, }; - + // echo -n -e '\x72\x37\x68\x09\xab\xf9\x8c\x72\x26\x42\xb1\xf9\x55\x24\xb1\x64\x09\xd2\x1c\x28\xbb\x97\xc9\x6b\x94\x54\x3f\x9a\xf2\x69\x82\x2b' | openssl enc -e -aes-192-ctr -K 48970AD3074330F31C9D40CE49E860916465AFE69EC812DB -iv 6DFD7457B9F280BDBF85B0BD19DD5DC6 -nopad | hd var expected = new byte[] { 0xfa, 0x30, 0x3f, 0x12, 0x3c, 0x7a, 0xa8, 0x1e, 0xfd, 0xaa, 0x17, 0x71, 0xbd, 0x01, 0xeb, 0xac, 0x85, 0xcd, 0x88, 0xa8, 0x25, 0xc8, 0xbd, 0xf8, 0xc3, 0xa9, 0x74, 0x36, 0x82, 0x19, 0xfc, 0xb3, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1162,7 +1161,7 @@ public void AES_CTR_192_Length64() { 0xf1, 0x7a, 0x87, 0xdb, 0xf3, 0xb0, 0x86, 0x7e, 0x52, 0x13, 0xd4, 0x0c, 0x6f, 0x34, 0xca, 0xe0, }; - + // echo -n -e '\xa2\x28\x0b\x1e\x56\xfb\x21\xac\xf3\xae\x35\x8c\xb9\x9c\x8d\x80\x85\x2f\x66\x09\xce\xd8\x3a\x2a\x1d\x82\x0e\xc4\x37\xa3\x77\x86\x07\xe9\x43\x75\xbc\xf3\x84\x72\xdb\xc8\x63\x0b\xbc\xf3\x03\x23\xf7\x30\x38\xea\x77\x53\xf7\xc9\xee\xe0\x00\xd4\xec\x5d\x75\x50' | openssl enc -e -aes-192-ctr -K 36AF84CF5817C391AAF32D06742E6E297EEBCC066B8D0FB4 -iv F17A87DBF3B0867E5213D40C6F34CAE0 -nopad | hd var expected = new byte[] { @@ -1171,13 +1170,13 @@ public void AES_CTR_192_Length64() 0xba, 0x65, 0x2d, 0xe3, 0x7b, 0x60, 0x9c, 0x64, 0x4e, 0xcc, 0x32, 0xb5, 0x38, 0xa4, 0xed, 0x69, 0x2d, 0x26, 0x4a, 0x22, 0x97, 0x7a, 0x94, 0x5e, 0xb0, 0xb2, 0x3d, 0x42, 0x2b, 0x4a, 0x5e, 0x5d, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1197,19 +1196,19 @@ public void AES_CTR_256_Length16() { 0x2e, 0x2b, 0x6d, 0x9e, 0x56, 0xeb, 0x50, 0x85, 0x07, 0x45, 0x16, 0x76, 0x3d, 0xf3, 0x64, 0x11, }; - + // echo -n -e '\x6d\xa6\x3f\x83\x25\xf1\x54\xbf\x72\xd7\x55\x00\x90\x6f\xe5\xa9' | openssl enc -e -aes-256-ctr -K 9FD0DEDE8FE79EFA6DAFB3615A61BA4A21EC98C44D8B8E0025C8691B5B85EEE3 -iv 2E2B6D9E56EB5085074516763DF36411 -nopad | hd var expected = new byte[] { 0xa6, 0x46, 0x19, 0x9d, 0x3e, 0xa5, 0x53, 0xc8, 0xd9, 0xb3, 0x46, 0xbc, 0x0b, 0x3e, 0x47, 0xf4, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1230,20 +1229,20 @@ public void AES_CTR_256_Length32() { 0x1a, 0x87, 0x62, 0x25, 0x84, 0x4e, 0x41, 0x76, 0xc3, 0x24, 0x5f, 0x9b, 0xbe, 0x7c, 0x02, 0x11, }; - + // echo -n -e '\x1d\x0a\xdf\xa4\xd6\x20\x5c\x14\x41\xdd\xb9\xc6\x7e\x83\x9f\xe7\xc0\xd0\x32\x2f\xf4\x1b\xf4\x35\x9b\x13\xbd\x08\x74\x18\xc2\x32' | openssl enc -e -aes-256-ctr -K 6458FE51A5490C0DCF585D78328A0784A52FB56DC0351C0115AA09C36353A028 -iv 1A876225844E4176C3245F9BBE7C0211 -nopad | hd var expected = new byte[] { 0x72, 0xb6, 0xb0, 0x78, 0xae, 0x36, 0xaa, 0x9e, 0x6b, 0xb7, 0x63, 0x7a, 0x77, 0x68, 0x8b, 0x42, 0x1d, 0x7e, 0xe7, 0xe7, 0xa0, 0xae, 0x31, 0x9b, 0xb3, 0x21, 0xb8, 0x0c, 0x47, 0x3e, 0xaf, 0xdd, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1266,7 +1265,7 @@ public void AES_CTR_256_Length64() { 0xdb, 0x2f, 0xcf, 0x6f, 0xf2, 0xed, 0xe7, 0xfb, 0x59, 0x86, 0x1b, 0x85, 0xc1, 0xf5, 0x32, 0xc2, }; - + // echo -n -e '\x0b\x38\x62\x45\x62\x55\x71\x2e\x3b\xfc\x3b\xfb\x40\x49\xaa\x7b\xb8\x34\x5d\xab\x27\xe1\xff\x57\xed\x3e\xa9\x9b\xd5\x80\x43\x98\xa7\xf7\xb7\x2a\xf0\x5a\xc6\xc4\x15\x34\xea\x88\x12\x46\x36\x79\x7a\xe4\xe3\x89\x1e\x57\xe9\x29\x39\x0b\x58\x23\xac\xd6\x58\xba' | openssl enc -e -aes-256-ctr -K B9A25348927F8B5D6E9896F3F77744A6082F20F19DB97A500E8EF1E502A2183E -iv DB2FCF6FF2EDE7FB59861B85C1F532C2 -nopad | hd var expected = new byte[] { @@ -1275,13 +1274,13 @@ public void AES_CTR_256_Length64() 0x15, 0xcc, 0x98, 0xc9, 0xe4, 0x6f, 0x29, 0x13, 0xf9, 0x61, 0x33, 0x77, 0x6b, 0x43, 0x44, 0xde, 0x92, 0xb0, 0x7b, 0x7a, 0x77, 0x65, 0xf0, 0xcc, 0xbd, 0xe4, 0x41, 0xea, 0x9e, 0xfd, 0xdf, 0x41, }; - - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1300,19 +1299,19 @@ public void AES_OFB_128_Length16() { 0x5c, 0x2f, 0x1d, 0x50, 0x86, 0x9c, 0x89, 0x74, 0x11, 0xd0, 0x46, 0xef, 0xb2, 0xe3, 0x6d, 0xb3, }; - + // echo -n -e '\xc7\xb1\x1b\x7c\xb5\x66\x1d\xff\x28\x03\x3a\x03\x8d\xa6\x5b\xcc' | openssl enc -e -aes-128-ofb -K 805718C8A7D4B31B482598169EF48E19 -iv 5C2F1D50869C897411D046EFB2E36DB3 -nopad | hd var expected = new byte[] { 0xb0, 0x65, 0x77, 0x03, 0xb4, 0x54, 0x82, 0x92, 0x05, 0x82, 0x93, 0x1f, 0x8d, 0x7b, 0xb6, 0xf0, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1332,20 +1331,20 @@ public void AES_OFB_128_Length32() { 0x6f, 0x12, 0x7a, 0x91, 0x3b, 0x0f, 0x2b, 0x20, 0x0a, 0x21, 0x9c, 0x39, 0xb2, 0x43, 0x64, 0x39, }; - + // echo -n -e '\x2a\x4f\x05\x69\xdd\x69\x1a\xf2\xfe\xff\x34\x8f\xcd\x06\x60\x34\x74\x21\xa7\x5d\x88\x0a\x45\xe4\xcd\xa3\xb7\xd7\x8e\xc4\x68\x64' | openssl enc -e -aes-128-ofb -K B8E5EC4EEE243BF2152B528667F9A70A -iv 6F127A913B0F2B200A219C39B2436439 -nopad | hd var expected = new byte[] { 0x11, 0x2d, 0xdf, 0xcf, 0x49, 0xc9, 0xd8, 0x0a, 0x7d, 0xd3, 0x2f, 0xf5, 0xc5, 0xec, 0x7e, 0xc9, 0x11, 0xb9, 0xd6, 0x67, 0x6c, 0xe7, 0xaa, 0x09, 0x93, 0xe3, 0x5f, 0xed, 0x38, 0x46, 0x37, 0xd2, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1367,7 +1366,7 @@ public void AES_OFB_128_Length64() { 0x61, 0xf8, 0x28, 0xc1, 0xc4, 0x39, 0xf7, 0xdf, 0x28, 0x2f, 0xef, 0xf2, 0x91, 0x9f, 0x90, 0x54, }; - + // echo -n -e '\x97\xd0\xd7\xe8\x1a\x11\x45\x4f\xe5\xb5\x48\x5c\xb7\xbe\x7c\xd4\xfc\xac\x68\x7b\x49\xd7\x28\xa8\xba\xcb\x44\xcd\x88\x01\x3f\xd2\xc7\x19\xef\x97\x21\xbe\xef\x5d\xcc\x2b\xac\x86\xc7\xce\x69\x4b\xa4\xc7\x3d\x05\xda\xe8\xf0\xc0\xa7\x2f\x2d\x4f\xcd\x77\xc6\xe3' | openssl enc -e -aes-128-ofb -K 7576949ECEE5B23DBD0AAE1E2BA2E1EB -iv 61F828C1C439F7DF282FEFF2919F9054 -nopad | hd var expected = new byte[] { @@ -1376,13 +1375,13 @@ public void AES_OFB_128_Length64() 0x5e, 0xb0, 0xdb, 0x23, 0xc7, 0x33, 0x2b, 0x06, 0x0d, 0x01, 0x1e, 0x9b, 0xb8, 0xf1, 0xde, 0x27, 0xda, 0xad, 0x1b, 0xa5, 0x20, 0x67, 0xd2, 0xa6, 0x18, 0x26, 0x30, 0x43, 0x2f, 0xa2, 0x66, 0x0b, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1402,19 +1401,19 @@ public void AES_OFB_192_Length16() { 0x7b, 0x69, 0xac, 0xc3, 0xf1, 0x26, 0xa5, 0x56, 0x9a, 0xe9, 0xa4, 0x4f, 0xb1, 0xbc, 0x05, 0x5e, }; - + // echo -n -e '\x64\xc8\x10\x50\x3a\xcb\x7d\xbf\x14\x00\x48\xd0\x39\xd2\x94\x05' | openssl enc -e -aes-192-ofb -K 4D41EDD44F051F3C7EB5759EF5C0AB1D7959BA629190B196 -iv 7B69ACC3F126A5569AE9A44FB1BC055E -nopad | hd var expected = new byte[] { 0x79, 0x41, 0x28, 0xc9, 0x3b, 0x89, 0x6f, 0x69, 0x92, 0xb0, 0x3e, 0x38, 0x11, 0x2c, 0xe5, 0xd8, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1435,20 +1434,20 @@ public void AES_OFB_192_Length32() { 0x89, 0x79, 0x75, 0x36, 0xda, 0xbd, 0x39, 0xf8, 0xbe, 0x98, 0x8c, 0xbc, 0x79, 0xb6, 0xff, 0x64, }; - + // echo -n -e '\xa9\xd4\xd2\x85\x55\xde\xc9\x54\x54\x2a\x56\xe0\x17\x32\x74\xbd\x90\x57\x58\xe5\x59\x5b\x4a\x58\x0f\x1f\x04\x0b\x1b\x5c\x6b\xbd' | openssl enc -e -aes-192-ofb -K 545BB9BDBE2C419C9F576EC6D0C53E6875E6BF5A631F054D -iv 89797536DABD39F8BE988CBC79B6FF64 -nopad | hd var expected = new byte[] { 0x23, 0x79, 0x22, 0x9c, 0xa4, 0xfe, 0xc4, 0xf4, 0xd9, 0xc7, 0x4f, 0x63, 0x01, 0x54, 0xca, 0xe6, 0xe8, 0xe8, 0x8e, 0x1a, 0xa6, 0x25, 0xa5, 0x65, 0x0d, 0x5a, 0xe2, 0x9c, 0xd2, 0x7e, 0x06, 0x14, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1471,7 +1470,7 @@ public void AES_OFB_192_Length64() { 0xae, 0x39, 0xad, 0x74, 0x21, 0xea, 0x87, 0xa1, 0x18, 0xf6, 0x91, 0x50, 0xb7, 0x18, 0xe1, 0x8a, }; - + // echo -n -e '\x15\xbc\x46\x01\xed\x84\x87\x4a\xe0\x9c\x96\x34\x9d\x11\x5a\x34\x56\x6b\x33\x44\xb7\x0b\xc2\xe1\x1e\x76\x07\x37\x39\x82\xee\xbe\xe7\x5b\x44\xa7\xd9\x03\x60\x04\xf1\x2a\x55\x3e\x27\x04\x5a\xad\x3e\x57\x65\x0d\x83\xbb\xac\x0a\xf9\x64\xe2\x76\x7d\x50\x11\x5e' | openssl enc -e -aes-192-ofb -K ADD74D42CCB35A520B2E528CB584B91A1C59F9E11CE03B2C -iv AE39AD7421EA87A118F69150B718E18A -nopad | hd var expected = new byte[] { @@ -1480,13 +1479,13 @@ public void AES_OFB_192_Length64() 0xdf, 0x14, 0x8b, 0xe4, 0xa9, 0x74, 0x3b, 0xc2, 0x50, 0xc3, 0x23, 0xfe, 0xc6, 0x27, 0x0d, 0x74, 0xc7, 0xd0, 0x21, 0x0a, 0x40, 0x1d, 0x32, 0x32, 0x88, 0x86, 0x40, 0xa9, 0x4c, 0x59, 0x9c, 0xb4, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1506,19 +1505,19 @@ public void AES_OFB_256_Length16() { 0xb4, 0xc4, 0xb4, 0x96, 0x84, 0x4e, 0x84, 0x16, 0xe1, 0xe1, 0xad, 0xb7, 0xac, 0x95, 0x82, 0x41, }; - + // echo -n -e '\xfd\x13\x02\x54\x68\x20\x55\x75\xab\xc8\x5a\x23\x57\x30\x42\xcc' | openssl enc -e -aes-256-ofb -K 9C20949206E63121A6B173BFF2696303786EE408DEE6C38DE437C9588F644AE8 -iv B4C4B496844E8416E1E1ADB7AC958241 -nopad | hd var expected = new byte[] { 0x98, 0x85, 0x21, 0xeb, 0x42, 0x0c, 0x8b, 0xb3, 0xab, 0x64, 0x78, 0xe5, 0x67, 0xdd, 0xee, 0x36, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1539,20 +1538,20 @@ public void AES_OFB_256_Length32() { 0x24, 0x5f, 0x19, 0x95, 0xe5, 0x58, 0x89, 0x06, 0xef, 0x90, 0x57, 0xb6, 0x94, 0x02, 0x89, 0x32, }; - + // echo -n -e '\x65\x56\xab\xe9\x4c\x39\xed\xb5\x5c\x06\xae\xce\x1d\xd8\x91\x42\x67\x8b\x0b\x2e\xb5\xcd\x7f\x29\xe9\xcd\x26\xfd\x39\x0c\xe1\x4e' | openssl enc -e -aes-256-ofb -K B487F339B9609C7AD6FE397DA0B6F9094F6B50208A548C97D681FF7E12F07B50 -iv 245F1995E5588906EF9057B694028932 -nopad | hd var expected = new byte[] { 0x46, 0xe6, 0x18, 0x3c, 0x18, 0xf9, 0x6d, 0x4f, 0xc2, 0x75, 0x89, 0xea, 0x0d, 0xc9, 0x9a, 0x4c, 0x39, 0x54, 0x2e, 0x9f, 0x81, 0x49, 0xd3, 0x6b, 0x58, 0x20, 0x03, 0x21, 0x8d, 0x41, 0x9a, 0x42, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } @@ -1575,7 +1574,7 @@ public void AES_OFB_256_Length64() { 0x55, 0x74, 0x31, 0x68, 0x12, 0x10, 0x5d, 0xb4, 0xcd, 0x5e, 0x56, 0xb5, 0xa1, 0xb1, 0xa6, 0x5f, }; - + // echo -n -e '\xd4\x1d\xad\xce\xbc\xc0\xc4\x60\xfb\x5b\x62\x37\x61\x1d\x68\xe6\x82\xe8\x58\x41\x9d\x63\x23\xf7\xe1\x49\x31\xfa\xfd\xd5\x03\xd4\xf8\xcd\xaa\xf4\x43\xad\x93\x64\x9b\xb8\x9a\x89\xf6\x51\xa5\xd1\x28\x71\x34\xab\xa9\x47\x95\x70\xf9\xb5\xec\x72\x8f\xc9\x63\x26' | openssl enc -e -aes-256-ofb -K 578D3F947373A3D554F4A6E4C99A018FA460D18BA1582BB03739FA8DC121D5D1 -iv 5574316812105DB4CD5E56B5A1B1A65F -nopad | hd var expected = new byte[] { @@ -1584,13 +1583,13 @@ public void AES_OFB_256_Length64() 0x5e, 0xa5, 0x16, 0x3f, 0x9b, 0x18, 0x86, 0x4e, 0x94, 0xe2, 0x60, 0x70, 0x1f, 0x39, 0xa9, 0x4d, 0x7a, 0x3a, 0x43, 0xa6, 0x8f, 0x48, 0xfe, 0x6e, 0x64, 0xf6, 0x01, 0x0d, 0xdf, 0x9d, 0x34, 0xee, }; - - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - + + var actual = new AesCipher(key, (byte[]) iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - + + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted); } } From 5ef1df6e8a413fe1c214f07b766637efbe1c16e7 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Tue, 7 Nov 2023 16:18:06 +0100 Subject: [PATCH 08/19] update AesCipherTest.cs generator --- .../Ciphers/AesCipherTest.Gen.cs.txt | 2 +- .../Cryptography/Ciphers/AesCipherTest.cs | 150 +++++++++--------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt index 390e85e71..3b3bfa65d 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt @@ -6,7 +6,7 @@ Dictionary modes = new() { - ["ecb"] = ("iv: null, AesCipherMode.AES", CipherMode.ECB), + ["ecb"] = ("iv: null, AesCipherMode.ECB", CipherMode.ECB), ["cbc"] = ("(byte[])iv.Clone(), AesCipherMode.CBC", CipherMode.CBC), ["cfb"] = ("(byte[])iv.Clone(), AesCipherMode.CFB", CipherMode.CFB), ["ctr"] = ("(byte[])iv.Clone(), AesCipherMode.CTR", null), diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index ff7d978f2..753ecc3cc 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -18,7 +18,7 @@ public void Encrypt_Input_128_CBC() var key = new byte[] { 0xe4, 0x94, 0xf9, 0xb1, 0x00, 0x4f, 0x16, 0x2a, 0x80, 0x11, 0xea, 0x73, 0x0d, 0xb9, 0xbf, 0x64 }; var iv = new byte[] { 0x74, 0x8b, 0x4f, 0xe6, 0xc1, 0x29, 0xb3, 0x54, 0xec, 0x77, 0x92, 0xf3, 0x15, 0xa0, 0x41, 0xa8 }; var expected = new byte[] { 0x19, 0x7f, 0x80, 0xd8, 0xc9, 0x89, 0xc4, 0xa7, 0xc6, 0xc6, 0x3f, 0x9f, 0x1e, 0x00, 0x1f, 0x72, 0xa7, 0x5e, 0xde, 0x40, 0x88, 0xa2, 0x72, 0xf2, 0xed, 0x3f, 0x81, 0x45, 0xb6, 0xbd, 0x45, 0x87, 0x15, 0xa5, 0x10, 0x92, 0x4a, 0x37, 0x9e, 0xa9, 0x80, 0x1c, 0x14, 0x83, 0xa3, 0x39, 0x45, 0x28 }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false); + var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false); var actual = testCipher.Encrypt(input); @@ -33,7 +33,7 @@ public void Encrypt_InputAndOffsetAndLength_128_CBC() var key = new byte[] { 0xe4, 0x94, 0xf9, 0xb1, 0x00, 0x4f, 0x16, 0x2a, 0x80, 0x11, 0xea, 0x73, 0x0d, 0xb9, 0xbf, 0x64 }; var iv = new byte[] { 0x74, 0x8b, 0x4f, 0xe6, 0xc1, 0x29, 0xb3, 0x54, 0xec, 0x77, 0x92, 0xf3, 0x15, 0xa0, 0x41, 0xa8 }; var expected = new byte[] { 0x19, 0x7f, 0x80, 0xd8, 0xc9, 0x89, 0xc4, 0xa7, 0xc6, 0xc6, 0x3f, 0x9f, 0x1e, 0x00, 0x1f, 0x72, 0xa7, 0x5e, 0xde, 0x40, 0x88, 0xa2, 0x72, 0xf2, 0xed, 0x3f, 0x81, 0x45, 0xb6, 0xbd, 0x45, 0x87, 0x15, 0xa5, 0x10, 0x92, 0x4a, 0x37, 0x9e, 0xa9, 0x80, 0x1c, 0x14, 0x83, 0xa3, 0x39, 0x45, 0x28 }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false); + var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false); var actual = testCipher.Encrypt(input, 2, input.Length - 5); @@ -47,7 +47,7 @@ public void Encrypt_Input_128_CTR() var key = new byte[] { 0x17, 0x78, 0x56, 0xe1, 0x3e, 0xbd, 0x3e, 0x50, 0x1d, 0x79, 0x3f, 0x0f, 0x55, 0x37, 0x45, 0x54 }; var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var expected = new byte[] { 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false); + var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false); var actual = testCipher.Encrypt(input); @@ -61,7 +61,7 @@ public void Decrypt_Input_128_CTR() var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var input = new byte[] { 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d }; var expected = new byte[] { 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x73, 0x68, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x61, 0x75, 0x74, 0x68, 0xb0, 0x74, 0x21, 0x87, 0x16, 0xb9, 0x69, 0x48, 0x33, 0xce, 0xb3, 0xe7, 0xdc, 0x3f, 0x50, 0xdc, 0xcc, 0xd5, 0x27, 0xb7, 0xfe, 0x7a, 0x78, 0x22, 0xae, 0xc8 }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, false); + var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, false); var actual = testCipher.Decrypt(input); @@ -75,7 +75,7 @@ public void Decrypt_InputAndOffsetAndLength_128_CTR() var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var input = new byte[] { 0x0a, 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d, 0x0a, 0x05 }; var expected = new byte[] { 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x73, 0x68, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x61, 0x75, 0x74, 0x68, 0xb0, 0x74, 0x21, 0x87, 0x16, 0xb9, 0x69, 0x48, 0x33, 0xce, 0xb3, 0xe7, 0xdc, 0x3f, 0x50, 0xdc, 0xcc, 0xd5, 0x27, 0xb7, 0xfe, 0x7a, 0x78, 0x22, 0xae, 0xc8 }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, false); + var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, false); var actual = testCipher.Decrypt(input, 1, input.Length - 3); @@ -379,11 +379,11 @@ public void AES_CBC_128_Length16() 0x49, 0x0e, 0xa9, 0x6f, 0x55, 0xb3, 0x57, 0xdf, 0x7c, 0x18, 0x77, 0x0c, 0xca, 0x46, 0x0d, 0x83, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -412,11 +412,11 @@ public void AES_CBC_128_Length32() 0x2d, 0x87, 0x42, 0x20, 0xf1, 0x0b, 0x78, 0x96, 0xd5, 0x7c, 0xeb, 0xa2, 0x7f, 0x4b, 0x5a, 0xff, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -449,11 +449,11 @@ public void AES_CBC_128_Length64() 0x3d, 0x61, 0x9e, 0x0d, 0x54, 0x7f, 0xe1, 0xc4, 0x78, 0xf2, 0x04, 0x00, 0x68, 0xa9, 0x9b, 0x32, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -481,11 +481,11 @@ public void AES_CBC_192_Length16() 0xe1, 0x2f, 0x71, 0xad, 0x59, 0xae, 0xa7, 0xe3, 0xd3, 0x23, 0x43, 0x81, 0x31, 0xc2, 0xe5, 0xd9, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -515,11 +515,11 @@ public void AES_CBC_192_Length32() 0x07, 0xf2, 0x98, 0x41, 0xbb, 0x58, 0x3d, 0xe5, 0xcf, 0x56, 0xf5, 0x4b, 0x33, 0xf7, 0xa0, 0x9a, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -553,11 +553,11 @@ public void AES_CBC_192_Length64() 0xda, 0xba, 0xde, 0xb2, 0x7d, 0xbc, 0x71, 0xf8, 0x9b, 0xaa, 0x93, 0x52, 0xf4, 0x26, 0x3c, 0x6f, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -585,11 +585,11 @@ public void AES_CBC_256_Length16() 0xe7, 0xa5, 0x53, 0xd7, 0x28, 0x4c, 0x16, 0x4e, 0xfc, 0xa2, 0xa8, 0x86, 0xfc, 0xcb, 0x71, 0x61, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -619,11 +619,11 @@ public void AES_CBC_256_Length32() 0x6a, 0xfb, 0x4a, 0x8b, 0xc8, 0x25, 0x87, 0x5c, 0x9b, 0x47, 0xf5, 0x3f, 0x42, 0xf5, 0xc6, 0x08, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -657,11 +657,11 @@ public void AES_CBC_256_Length64() 0x08, 0x19, 0x66, 0x47, 0xe7, 0xd9, 0x1d, 0x1c, 0x42, 0xdc, 0x97, 0x9c, 0xf0, 0x9a, 0x14, 0x34, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -688,11 +688,11 @@ public void AES_CFB_128_Length16() 0x76, 0xd2, 0x2b, 0x69, 0xa6, 0xdf, 0x3b, 0x4d, 0x4a, 0x52, 0x8a, 0x7a, 0x54, 0x9d, 0xbe, 0x55, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -721,11 +721,11 @@ public void AES_CFB_128_Length32() 0x18, 0xc5, 0xf7, 0x41, 0x78, 0x5f, 0x38, 0x6b, 0x4d, 0x04, 0x00, 0x3b, 0x61, 0x8c, 0xaf, 0xe7, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -758,11 +758,11 @@ public void AES_CFB_128_Length64() 0xf7, 0x49, 0xbc, 0xbf, 0xcb, 0x5c, 0xfa, 0x12, 0xcb, 0xcc, 0x38, 0x71, 0x68, 0xd6, 0xe9, 0x64, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -790,11 +790,11 @@ public void AES_CFB_192_Length16() 0x4f, 0x9b, 0xdf, 0x72, 0x2d, 0x10, 0x1b, 0xb9, 0xa1, 0xe1, 0x06, 0xba, 0xbc, 0xc5, 0xfe, 0x13, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -824,11 +824,11 @@ public void AES_CFB_192_Length32() 0x72, 0xcf, 0x57, 0x7f, 0xf9, 0x5d, 0xfe, 0xb1, 0x36, 0x9a, 0x1d, 0x02, 0x0d, 0x4b, 0x8f, 0x35, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -862,11 +862,11 @@ public void AES_CFB_192_Length64() 0x69, 0x90, 0x2a, 0xf9, 0xf4, 0xe8, 0xcc, 0xa5, 0x2b, 0xdd, 0x9c, 0xbc, 0x44, 0xcd, 0x1e, 0x5b, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -894,11 +894,11 @@ public void AES_CFB_256_Length16() 0xf0, 0xfa, 0x95, 0x5c, 0xfc, 0x3f, 0xbe, 0xe5, 0x4b, 0x55, 0x57, 0xad, 0x93, 0x63, 0x36, 0x07, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -928,11 +928,11 @@ public void AES_CFB_256_Length32() 0xc2, 0xc1, 0x54, 0x9c, 0xfd, 0xf9, 0x43, 0xd0, 0xdc, 0xa7, 0x20, 0x68, 0x3e, 0xc3, 0x8f, 0x3c, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -966,11 +966,11 @@ public void AES_CFB_256_Length64() 0xf6, 0xd4, 0x06, 0xef, 0x04, 0xf1, 0xe5, 0x53, 0x54, 0xd5, 0x80, 0xc2, 0x96, 0x6b, 0xc7, 0x07, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -997,11 +997,11 @@ public void AES_CTR_128_Length16() 0xe4, 0x03, 0x8f, 0x2a, 0xdd, 0x9d, 0xf6, 0x87, 0xf6, 0x29, 0xee, 0x27, 0x4c, 0xf3, 0xba, 0x82, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1030,11 +1030,11 @@ public void AES_CTR_128_Length32() 0x9c, 0xb2, 0x30, 0x94, 0xdc, 0x88, 0xfa, 0x39, 0x05, 0x0c, 0x26, 0x25, 0x28, 0x6a, 0x9b, 0x4e, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1067,11 +1067,11 @@ public void AES_CTR_128_Length64() 0xec, 0x47, 0x81, 0x82, 0x89, 0x24, 0x76, 0xe2, 0x20, 0x6a, 0x99, 0xe2, 0xa7, 0x5a, 0xb0, 0x40, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1099,11 +1099,11 @@ public void AES_CTR_192_Length16() 0xc4, 0x4e, 0x81, 0x32, 0xe6, 0x6d, 0x0a, 0x78, 0x49, 0xe5, 0x64, 0x6c, 0xe6, 0xc2, 0x91, 0xc9, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1133,11 +1133,11 @@ public void AES_CTR_192_Length32() 0x85, 0xcd, 0x88, 0xa8, 0x25, 0xc8, 0xbd, 0xf8, 0xc3, 0xa9, 0x74, 0x36, 0x82, 0x19, 0xfc, 0xb3, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1171,11 +1171,11 @@ public void AES_CTR_192_Length64() 0x2d, 0x26, 0x4a, 0x22, 0x97, 0x7a, 0x94, 0x5e, 0xb0, 0xb2, 0x3d, 0x42, 0x2b, 0x4a, 0x5e, 0x5d, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1203,11 +1203,11 @@ public void AES_CTR_256_Length16() 0xa6, 0x46, 0x19, 0x9d, 0x3e, 0xa5, 0x53, 0xc8, 0xd9, 0xb3, 0x46, 0xbc, 0x0b, 0x3e, 0x47, 0xf4, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1237,11 +1237,11 @@ public void AES_CTR_256_Length32() 0x1d, 0x7e, 0xe7, 0xe7, 0xa0, 0xae, 0x31, 0x9b, 0xb3, 0x21, 0xb8, 0x0c, 0x47, 0x3e, 0xaf, 0xdd, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1275,11 +1275,11 @@ public void AES_CTR_256_Length64() 0x92, 0xb0, 0x7b, 0x7a, 0x77, 0x65, 0xf0, 0xcc, 0xbd, 0xe4, 0x41, 0xea, 0x9e, 0xfd, 0xdf, 0x41, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1306,11 +1306,11 @@ public void AES_OFB_128_Length16() 0xb0, 0x65, 0x77, 0x03, 0xb4, 0x54, 0x82, 0x92, 0x05, 0x82, 0x93, 0x1f, 0x8d, 0x7b, 0xb6, 0xf0, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1339,11 +1339,11 @@ public void AES_OFB_128_Length32() 0x11, 0xb9, 0xd6, 0x67, 0x6c, 0xe7, 0xaa, 0x09, 0x93, 0xe3, 0x5f, 0xed, 0x38, 0x46, 0x37, 0xd2, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1376,11 +1376,11 @@ public void AES_OFB_128_Length64() 0xda, 0xad, 0x1b, 0xa5, 0x20, 0x67, 0xd2, 0xa6, 0x18, 0x26, 0x30, 0x43, 0x2f, 0xa2, 0x66, 0x0b, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1408,11 +1408,11 @@ public void AES_OFB_192_Length16() 0x79, 0x41, 0x28, 0xc9, 0x3b, 0x89, 0x6f, 0x69, 0x92, 0xb0, 0x3e, 0x38, 0x11, 0x2c, 0xe5, 0xd8, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1442,11 +1442,11 @@ public void AES_OFB_192_Length32() 0xe8, 0xe8, 0x8e, 0x1a, 0xa6, 0x25, 0xa5, 0x65, 0x0d, 0x5a, 0xe2, 0x9c, 0xd2, 0x7e, 0x06, 0x14, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1480,11 +1480,11 @@ public void AES_OFB_192_Length64() 0xc7, 0xd0, 0x21, 0x0a, 0x40, 0x1d, 0x32, 0x32, 0x88, 0x86, 0x40, 0xa9, 0x4c, 0x59, 0x9c, 0xb4, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1512,11 +1512,11 @@ public void AES_OFB_256_Length16() 0x98, 0x85, 0x21, 0xeb, 0x42, 0x0c, 0x8b, 0xb3, 0xab, 0x64, 0x78, 0xe5, 0x67, 0xdd, 0xee, 0x36, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } @@ -1546,11 +1546,11 @@ public void AES_OFB_256_Length32() 0x39, 0x54, 0x2e, 0x9f, 0x81, 0x49, 0xd3, 0x6b, 0x58, 0x20, 0x03, 0x21, 0x8d, 0x41, 0x9a, 0x42, }; - var actual = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - var decrypted = new AesCipher(key, (byte[])iv.Clone(),AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); } From ac2c36dba9effe337833b00284d3fcccbb827146 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Thu, 9 Nov 2023 18:16:28 +0100 Subject: [PATCH 09/19] Fix continuous session encrypt/decrypt (preserve IV between calls when Padding is None) --- .../Cryptography/Ciphers/AesCipher.cs | 33 ++++++++++++++----- .../Cryptography/Ciphers/AesCipherTest.cs | 4 +-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 3bf1235a2..49759a141 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -90,7 +90,7 @@ public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = if (_blockMode is not null) { - _ecbHelper = new AesCipher(key, pkcs7Padding: false); // no padding + _ecbHelper = new AesCipher(key, pkcs7Padding: false); // ECB with no padding _blockMode.Init(_ecbHelper); } @@ -220,6 +220,13 @@ public override byte[] Encrypt(byte[] input, int offset, int length) // fast crypto using the BCL CryptoServiceProvider/CNG if (_blockMode is null && _encryptor.CanTransformMultipleBlocks) { + if (_aes.Padding == PaddingMode.None) + { + var output = new byte[length]; + _ = _encryptor.TransformBlock(input, offset, length, output, 0); + return output; + } + return _encryptor.TransformFinalBlock(input, offset, length); } @@ -246,6 +253,13 @@ public override byte[] Decrypt(byte[] input, int offset, int length) // fast crypto using the BCL CryptoServiceProvider/CNG if (_blockMode is null && _decryptor.CanTransformMultipleBlocks) { + if (_aes.Padding == PaddingMode.None) + { + var output = new byte[length]; + _ = _decryptor.TransformBlock(input, offset, length, output, 0); + return output; + } + return _decryptor.TransformFinalBlock(input, offset, length); } @@ -286,8 +300,15 @@ private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) } var counter = CTRCreateCounterArray(count); - var aesCounter = _encryptor.TransformFinalBlock(counter, 0, counter.Length); - var output = ArrayXOR(aesCounter, data, offset, length); + var ouput = new byte[counter.Length]; + _ = _encryptor.TransformBlock(counter, 0, counter.Length, ouput, 0); + var output = ArrayXOR(ouput, data, offset, length); + + // adjust output for non-blocksized lengths + if (output.Length > length) + { + Array.Resize(ref output, length); + } return output; } @@ -401,12 +422,6 @@ private static byte[] ArrayXOR(byte[] counter, byte[] data, int offset, int leng var output = counter; Buffer.BlockCopy(counterwords, 0, output, 0, length); - // adjust output for non-aligned lengths - if (output.Length > length) - { - Array.Resize(ref output, length); - } - return output; } diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index 753ecc3cc..b990ce5a3 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -61,7 +61,7 @@ public void Decrypt_Input_128_CTR() var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var input = new byte[] { 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d }; var expected = new byte[] { 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x73, 0x68, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x61, 0x75, 0x74, 0x68, 0xb0, 0x74, 0x21, 0x87, 0x16, 0xb9, 0x69, 0x48, 0x33, 0xce, 0xb3, 0xe7, 0xdc, 0x3f, 0x50, 0xdc, 0xcc, 0xd5, 0x27, 0xb7, 0xfe, 0x7a, 0x78, 0x22, 0xae, 0xc8 }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, false); + var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false); var actual = testCipher.Decrypt(input); @@ -75,7 +75,7 @@ public void Decrypt_InputAndOffsetAndLength_128_CTR() var iv = new byte[] { 0xe6, 0x65, 0x36, 0x0d, 0xdd, 0xd7, 0x50, 0xc3, 0x48, 0xdb, 0x48, 0x07, 0xa1, 0x30, 0xd2, 0x38 }; var input = new byte[] { 0x0a, 0xca, 0xfb, 0x1c, 0x49, 0xbf, 0x82, 0x2a, 0xbb, 0x1c, 0x52, 0xc7, 0x86, 0x22, 0x8a, 0xe5, 0xa4, 0xf3, 0xda, 0x4e, 0x1c, 0x3a, 0x87, 0x41, 0x1c, 0xd2, 0x6e, 0x76, 0xdc, 0xc2, 0xe9, 0xc2, 0x0e, 0xf5, 0xc7, 0xbd, 0x12, 0x85, 0xfa, 0x0e, 0xda, 0xee, 0x50, 0xd7, 0xfd, 0x81, 0x34, 0x25, 0x6d, 0x0a, 0x05 }; var expected = new byte[] { 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x73, 0x68, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x61, 0x75, 0x74, 0x68, 0xb0, 0x74, 0x21, 0x87, 0x16, 0xb9, 0x69, 0x48, 0x33, 0xce, 0xb3, 0xe7, 0xdc, 0x3f, 0x50, 0xdc, 0xcc, 0xd5, 0x27, 0xb7, 0xfe, 0x7a, 0x78, 0x22, 0xae, 0xc8 }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, false); + var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false); var actual = testCipher.Decrypt(input, 1, input.Length - 3); From ada6ccbcc20e1e26463fbe22004556c9f884da57 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Fri, 10 Nov 2023 14:20:10 +0100 Subject: [PATCH 10/19] Reduce CTR memory usage in Net 6+ Small performance increase in CTR buffer mode Cosmetic changes --- .../Cryptography/Ciphers/AesCipher.cs | 110 ++++++++++-------- 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 49759a141..805d8caeb 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -43,6 +43,8 @@ public enum AesCipherMode /// public sealed class AesCipher : BlockCipher, IDisposable { + private const int BLOCKSIZE = 16; + private readonly AesCipherMode _aesMode; private readonly CipherMode _blockMode; @@ -75,7 +77,7 @@ public AesCipher(byte[] key, bool pkcs7Padding = false) /// is . /// Keysize is not valid for this algorithm. public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false) - : base(key, 16, mode: null, padding: pkcs7Padding ? new PKCS7Padding() : null) + : base(key, BLOCKSIZE, mode: null, padding: pkcs7Padding ? new PKCS7Padding() : null) { var keySize = key.Length * 8; @@ -85,7 +87,7 @@ public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = } _aesMode = mode; - iv = iv?.Take(16) ?? new byte[16]; + iv = iv?.Take(BLOCKSIZE) ?? new byte[BLOCKSIZE]; var bclMode = GetBCLMode(mode, iv, out _blockMode); if (_blockMode is not null) @@ -95,8 +97,8 @@ public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = } _aes = Aes.Create(); - _aes.BlockSize = 128; - _aes.FeedbackSize = 128; + _aes.BlockSize = BLOCKSIZE * 8; + _aes.FeedbackSize = BLOCKSIZE * 8; _aes.Mode = bclMode; _aes.Padding = pkcs7Padding ? PaddingMode.PKCS7 : PaddingMode.None; _aes.Key = key; @@ -299,80 +301,84 @@ private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) count++; } - var counter = CTRCreateCounterArray(count); - var ouput = new byte[counter.Length]; - _ = _encryptor.TransformBlock(counter, 0, counter.Length, ouput, 0); - var output = ArrayXOR(ouput, data, offset, length); + var buffer = new byte[count * BlockSize]; + CTRCreateCounterArray(buffer); + _ = _encryptor.TransformBlock(buffer, 0, buffer.Length, buffer, 0); + ArrayXOR(buffer, data, offset, length); // adjust output for non-blocksized lengths - if (output.Length > length) + if (buffer.Length > length) { - Array.Resize(ref output, length); + Array.Resize(ref buffer, length); } - return output; + return buffer; } #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER // creates the Counter array filled with incrementing copies of IV - private byte[] CTRCreateCounterArray(int blocks) + private void CTRCreateCounterArray(byte[] buffer) { - var bytes = new byte[blocks * 16]; - var counter = MemoryMarshal.Cast(bytes.AsSpan()); + var counter = MemoryMarshal.Cast(buffer.AsSpan()); // fill array with IV, increment by 1 for each copy - for (var i = 0; i < counter.Length; i += 4) + var len = counter.Length; + for (var i = 0; i < len; i += 4) { - // write IV to buffer (big endian) counter[i] = _packedIV[0]; counter[i + 1] = _packedIV[1]; counter[i + 2] = _packedIV[2]; counter[i + 3] = _packedIV[3]; - // increment IV (little endian - swap, increment, swap back) - uint j = 3; - do + // increment IV (little endian) + if (_packedIV[3] < 0xFF000000u) + { + _packedIV[3] += 0x01000000u; + } + else { - _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + uint j = 3; + do + { + _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + } + while (_packedIV[j] == 0 && --j >= 0); } - while (_packedIV[j] == 0 && --j >= 0); } - - return bytes; } // XOR 2 arrays using Vector - private static byte[] ArrayXOR(byte[] counter, byte[] data, int offset, int length) + private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) { - for (var loopOffset = 0; length > 0; length -= Vector.Count) + var vectorSize = Vector.Count; + for (var loopOffset = 0; length > 0; length -= vectorSize) { - if (length >= Vector.Count) + if (length >= vectorSize) { - var v = new Vector(counter, loopOffset) ^ new Vector(data, offset + loopOffset); - v.CopyTo(counter, loopOffset); - loopOffset += Vector.Count; + var v = new Vector(buffer, loopOffset) ^ new Vector(data, offset + loopOffset); + v.CopyTo(buffer, loopOffset); + loopOffset += vectorSize; } else { for (var i = 0; i < length; i++) { - counter[loopOffset] ^= data[offset + loopOffset]; + buffer[loopOffset] ^= data[offset + loopOffset]; loopOffset++; } } } - - return counter; } #else // creates the Counter array filled with incrementing copies of IV - private byte[] CTRCreateCounterArray(int blocks) + private void CTRCreateCounterArray(byte[] buffer) { // fill array with IV, increment by 1 for each copy - var counter = new uint[blocks * 4]; - for (var i = 0; i < counter.Length; i += 4) + var words = buffer.Length / 4; + var counter = new uint[words]; + for (var i = 0; i < words; i += 4) { // write IV to buffer (big endian) counter[i] = _packedIV[0]; @@ -380,23 +386,28 @@ private byte[] CTRCreateCounterArray(int blocks) counter[i + 2] = _packedIV[2]; counter[i + 3] = _packedIV[3]; - // increment IV (little endian - swap, increment, swap back) - uint j = 3; - do + // increment IV (little endian) + if (_packedIV[3] < 0xFF000000u) { - _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + _packedIV[3] += 0x01000000u; + } + else + { + uint j = 3; + do + { + _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + } + while (_packedIV[j] == 0 && --j >= 0); } - while (_packedIV[j] == 0 && --j >= 0); } // copy uint[] to byte[] - var counterBytes = new byte[blocks * 16]; - Buffer.BlockCopy(counter, 0, counterBytes, 0, counterBytes.Length); - return counterBytes; + Buffer.BlockCopy(counter, 0, buffer, 0, buffer.Length); } // XOR 2 arrays using Uint[] and blockcopy - private static byte[] ArrayXOR(byte[] counter, byte[] data, int offset, int length) + private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) { var words = length / 4; if (length % 4 != 0) @@ -409,20 +420,17 @@ private static byte[] ArrayXOR(byte[] counter, byte[] data, int offset, int leng Buffer.BlockCopy(data, offset, datawords, 0, length); // convert encrypted IV counter to words - var counterwords = new uint[words]; - Buffer.BlockCopy(counter, 0, counterwords, 0, length); + var bufferwords = new uint[words]; + Buffer.BlockCopy(buffer, 0, bufferwords, 0, length); // XOR encrypted Counter with input data for (var i = 0; i < words; i++) { - counterwords[i] = counterwords[i] ^ datawords[i]; + bufferwords[i] = bufferwords[i] ^ datawords[i]; } // copy uint[] to byte[] - var output = counter; - Buffer.BlockCopy(counterwords, 0, output, 0, length); - - return output; + Buffer.BlockCopy(bufferwords, 0, buffer, 0, length); } #endif From 799a5c5ebcf6283fb433c1652e108ac68ee362d9 Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Sun, 12 Nov 2023 15:32:37 +0100 Subject: [PATCH 11/19] Factor out the implementations and re-add the existing constructor --- .editorconfig | 15 + .../Cryptography/Ciphers/AesCipher.BclImpl.cs | 108 +++ .../Ciphers/AesCipher.BlockImpl.cs | 54 ++ .../Cryptography/Ciphers/AesCipher.CtrImpl.cs | 232 +++++ .../Cryptography/Ciphers/AesCipher.cs | 426 +-------- .../Ciphers/AesCipherBenchmarks.cs | 8 +- .../Ciphers/AesCipherTest.Gen.cs.txt | 26 +- .../Cryptography/Ciphers/AesCipherTest.cs | 811 +++++++++++------- 8 files changed, 962 insertions(+), 718 deletions(-) create mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs create mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BlockImpl.cs create mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs diff --git a/.editorconfig b/.editorconfig index 944a6bf07..c0b0596db 100644 --- a/.editorconfig +++ b/.editorconfig @@ -291,6 +291,9 @@ dotnet_diagnostic.SA1520.severity = none # We do not use file headers. dotnet_diagnostic.SA1633.severity = none +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + # SA1648: must be used with inheriting class # # This rule is disabled by default, hence we need to explicitly enable it. @@ -555,6 +558,18 @@ dotnet_code_quality.CA1859.api_surface = all # This is similar to, but less powerful than, MA0015. dotnet_diagnostic.CA2208.severity = none +# CA5358: Do Not Use Unsafe Cipher Modes / Review cipher mode usage with cryptography experts +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca5358 +# +# We use ECB mode as the basis for other modes (e.g. CTR) +dotnet_diagnostic.CA5358.severity = none + +# CA5401: Do not use CreateEncryptor with non-default IV +# https://learn.microsoft.com/en-gb/dotnet/fundamentals/code-analysis/quality-rules/ca5401 +# +# We need to specify the IV. +dotnet_diagnostic.CA5401.severity = none + #### Roslyn IDE analyser rules #### # IDE0032: Use auto-implemented property diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs new file mode 100644 index 000000000..0941489f5 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs @@ -0,0 +1,108 @@ +using System; +using System.Security.Cryptography; + +using Renci.SshNet.Common; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + public partial class AesCipher + { + private sealed class BclImpl : BlockCipher, IDisposable + { + private readonly Aes _aes; + private readonly ICryptoTransform _encryptor; + private readonly ICryptoTransform _decryptor; + + public BclImpl( + byte[] key, + byte[] iv, + System.Security.Cryptography.CipherMode cipherMode, + PaddingMode paddingMode) + : base(key, 16, mode: null, padding: null) + { + var aes = Aes.Create(); + aes.Key = key; + + if (cipherMode != System.Security.Cryptography.CipherMode.ECB) + { + if (iv is null) + { + throw new ArgumentNullException(nameof(iv)); + } + + aes.IV = iv.Take(16); + } + + aes.Mode = cipherMode; + aes.Padding = paddingMode; + aes.FeedbackSize = 128; // We use CFB128 + _aes = aes; + _encryptor = aes.CreateEncryptor(); + _decryptor = aes.CreateDecryptor(); + } + + public override byte[] Encrypt(byte[] input, int offset, int length) + { + if (_aes.Padding != PaddingMode.None) + { + // If padding has been specified, call TransformFinalBlock to apply + // the padding and reset the state. + return _encryptor.TransformFinalBlock(input, offset, length); + } + + // Otherwise, (the most important case) assume this instance is + // used for one direction of an SSH connection, whereby the + // encrypted data in all packets are considered a single data + // stream i.e. we do not want to reset the state between calls to Encrypt. + var output = new byte[length]; + _ = _encryptor.TransformBlock(input, offset, length, output, 0); + return output; + } + + public override byte[] Decrypt(byte[] input, int offset, int length) + { + if (_aes.Padding != PaddingMode.None) + { + // If padding has been specified, call TransformFinalBlock to apply + // the padding and reset the state. + return _decryptor.TransformFinalBlock(input, offset, length); + } + + // Otherwise, (the most important case) assume this instance is + // used for one direction of an SSH connection, whereby the + // encrypted data in all packets are considered a single data + // stream i.e. we do not want to reset the state between calls to Decrypt. + var output = new byte[length]; + _ = _decryptor.TransformBlock(input, offset, length, output, 0); + return output; + } + + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + throw new NotImplementedException($"Invalid usage of {nameof(EncryptBlock)}."); + } + + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}."); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + _aes.Dispose(); + _encryptor.Dispose(); + _decryptor.Dispose(); + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + } +} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BlockImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BlockImpl.cs new file mode 100644 index 000000000..ff261d767 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BlockImpl.cs @@ -0,0 +1,54 @@ +using System; +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + public partial class AesCipher + { + private sealed class BlockImpl : BlockCipher, IDisposable + { + private readonly Aes _aes; + private readonly ICryptoTransform _encryptor; + private readonly ICryptoTransform _decryptor; + + public BlockImpl(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 16, mode, padding) + { + var aes = Aes.Create(); + aes.Key = key; + aes.Mode = System.Security.Cryptography.CipherMode.ECB; + aes.Padding = PaddingMode.None; + _aes = aes; + _encryptor = aes.CreateEncryptor(); + _decryptor = aes.CreateDecryptor(); + } + + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + return _encryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + return _decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + _aes.Dispose(); + _encryptor.Dispose(); + _decryptor.Dispose(); + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + } +} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs new file mode 100644 index 000000000..f21fa81d9 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs @@ -0,0 +1,232 @@ +using System; +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER +using System.Numerics; +using System.Runtime.InteropServices; +#endif +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + public partial class AesCipher + { + private sealed class CtrImpl : BlockCipher, IDisposable + { + private readonly Aes _aes; + private readonly byte[] _iv; + private readonly uint[] _packedIV; + private readonly ICryptoTransform _encryptor; + + public CtrImpl( + byte[] key, + byte[] iv) + : base(key, 16, mode: null, padding: null) + { + var aes = Aes.Create(); + aes.Key = key; + aes.Mode = System.Security.Cryptography.CipherMode.ECB; + aes.Padding = PaddingMode.None; + _aes = aes; + _encryptor = aes.CreateEncryptor(); + + _iv = iv; + _packedIV = GetPackedIV(iv); + } + + public override byte[] Encrypt(byte[] input, int offset, int length) + { + return CTREncryptDecrypt(input, offset, length); + } + + public override byte[] Decrypt(byte[] input, int offset, int length) + { + return CTREncryptDecrypt(input, offset, length); + } + + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}."); + } + + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + throw new NotImplementedException($"Invalid usage of {nameof(EncryptBlock)}."); + } + + private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) + { + var count = length / BlockSize; + if (length % BlockSize != 0) + { + count++; + } + + var buffer = new byte[count * BlockSize]; + CTRCreateCounterArray(buffer); + _ = _encryptor.TransformBlock(buffer, 0, buffer.Length, buffer, 0); + ArrayXOR(buffer, data, offset, length); + + // adjust output for non-blocksized lengths + if (buffer.Length > length) + { + Array.Resize(ref buffer, length); + } + + return buffer; + } + +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER + + // creates the Counter array filled with incrementing copies of IV + private void CTRCreateCounterArray(byte[] buffer) + { + var counter = MemoryMarshal.Cast(buffer.AsSpan()); + + // fill array with IV, increment by 1 for each copy + var len = counter.Length; + for (var i = 0; i < len; i += 4) + { + counter[i] = _packedIV[0]; + counter[i + 1] = _packedIV[1]; + counter[i + 2] = _packedIV[2]; + counter[i + 3] = _packedIV[3]; + + // increment IV (little endian) + if (_packedIV[3] < 0xFF000000u) + { + _packedIV[3] += 0x01000000u; + } + else + { + uint j = 3; + do + { + _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + } + while (_packedIV[j] == 0 && --j >= 0); + } + } + } + + // XOR 2 arrays using Vector + private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) + { + var vectorSize = Vector.Count; + for (var loopOffset = 0; length > 0; length -= vectorSize) + { + if (length >= vectorSize) + { + var v = new Vector(buffer, loopOffset) ^ new Vector(data, offset + loopOffset); + v.CopyTo(buffer, loopOffset); + loopOffset += vectorSize; + } + else + { + for (var i = 0; i < length; i++) + { + buffer[loopOffset] ^= data[offset + loopOffset]; + loopOffset++; + } + } + } + } + +#else + // creates the Counter array filled with incrementing copies of IV + private void CTRCreateCounterArray(byte[] buffer) + { + // fill array with IV, increment by 1 for each copy + var words = buffer.Length / 4; + var counter = new uint[words]; + for (var i = 0; i < words; i += 4) + { + // write IV to buffer (big endian) + counter[i] = _packedIV[0]; + counter[i + 1] = _packedIV[1]; + counter[i + 2] = _packedIV[2]; + counter[i + 3] = _packedIV[3]; + + // increment IV (little endian) + if (_packedIV[3] < 0xFF000000u) + { + _packedIV[3] += 0x01000000u; + } + else + { + uint j = 3; + do + { + _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); + } + while (_packedIV[j] == 0 && --j >= 0); + } + } + + // copy uint[] to byte[] + Buffer.BlockCopy(counter, 0, buffer, 0, buffer.Length); + } + + // XOR 2 arrays using Uint[] and blockcopy + private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) + { + var words = length / 4; + if (length % 4 != 0) + { + words++; + } + + // convert original data to words + var datawords = new uint[words]; + Buffer.BlockCopy(data, offset, datawords, 0, length); + + // convert encrypted IV counter to words + var bufferwords = new uint[words]; + Buffer.BlockCopy(buffer, 0, bufferwords, 0, length); + + // XOR encrypted Counter with input data + for (var i = 0; i < words; i++) + { + bufferwords[i] = bufferwords[i] ^ datawords[i]; + } + + // copy uint[] to byte[] + Buffer.BlockCopy(bufferwords, 0, buffer, 0, length); + } + +#endif + + // pack the IV into an array of uint[4] + private static uint[] GetPackedIV(byte[] iv) + { + var packedIV = new uint[4]; + packedIV[0] = BitConverter.ToUInt32(iv, 0); + packedIV[1] = BitConverter.ToUInt32(iv, 4); + packedIV[2] = BitConverter.ToUInt32(iv, 8); + packedIV[3] = BitConverter.ToUInt32(iv, 12); + + return packedIV; + } + + private static uint SwapEndianness(uint x) + { + x = (x >> 16) | (x << 16); + return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + _aes.Dispose(); + _encryptor.Dispose(); + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + } +} diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 805d8caeb..885bfba60 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -1,14 +1,6 @@ -#pragma warning disable IDE0005 // Using directive is unnecessary. -#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts - -using System; -using System.Globalization; -using System.Numerics; -using System.Runtime.InteropServices; +using System; using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Security.Cryptography.Ciphers.Paddings; @@ -41,30 +33,22 @@ public enum AesCipherMode /// /// AES cipher implementation. /// - public sealed class AesCipher : BlockCipher, IDisposable + public sealed partial class AesCipher : BlockCipher, IDisposable { - private const int BLOCKSIZE = 16; - - private readonly AesCipherMode _aesMode; - private readonly CipherMode _blockMode; - - private readonly Aes _aes; - private ICryptoTransform _encryptor; - private ICryptoTransform _decryptor; - - private AesCipher _ecbHelper; - private uint[] _packedIV; + private readonly BlockCipher _impl; /// - /// Initializes a new instance of the class in ECB mode. + /// Initializes a new instance of the class. /// /// The key. - /// Enable PKCS7 padding. + /// The mode. + /// The padding. /// is . /// Keysize is not valid for this algorithm. - public AesCipher(byte[] key, bool pkcs7Padding = false) - : this(key, iv: null, AesCipherMode.ECB, pkcs7Padding) + public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 16, mode, padding) { + _impl = new BlockImpl(key, mode, padding); } /// @@ -77,400 +61,68 @@ public AesCipher(byte[] key, bool pkcs7Padding = false) /// is . /// Keysize is not valid for this algorithm. public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false) - : base(key, BLOCKSIZE, mode: null, padding: pkcs7Padding ? new PKCS7Padding() : null) + : base(key, 16, mode: null, padding: null) { - var keySize = key.Length * 8; - - if (keySize is not (256 or 192 or 128)) + if (mode == AesCipherMode.OFB) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); + // OFB is not supported on modern .NET + _impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null); } - - _aesMode = mode; - iv = iv?.Take(BLOCKSIZE) ?? new byte[BLOCKSIZE]; - var bclMode = GetBCLMode(mode, iv, out _blockMode); - - if (_blockMode is not null) - { - _ecbHelper = new AesCipher(key, pkcs7Padding: false); // ECB with no padding - _blockMode.Init(_ecbHelper); - } - - _aes = Aes.Create(); - _aes.BlockSize = BLOCKSIZE * 8; - _aes.FeedbackSize = BLOCKSIZE * 8; - _aes.Mode = bclMode; - _aes.Padding = pkcs7Padding ? PaddingMode.PKCS7 : PaddingMode.None; - _aes.Key = key; - _aes.IV = iv; - -#pragma warning disable S3329 // Cipher Block Chaining IVs should be unpredictable -#pragma warning disable CA5401 // Cipher Block Chaining IVs should be unpredictable - _encryptor = _aes.CreateEncryptor(); - _decryptor = _aes.CreateDecryptor(); - } - - // get the BCL-equivalent AES mode and optional legacy CipherMode - private System.Security.Cryptography.CipherMode GetBCLMode(AesCipherMode mode, byte[] iv, out CipherMode blockMode) - { - blockMode = null; - -#pragma warning disable IDE0010 // allow missing cases, fallback to ECB - switch (mode) +#if !NET6_0_OR_GREATER + else if (mode == AesCipherMode.CFB) { - case AesCipherMode.CBC: - return System.Security.Cryptography.CipherMode.CBC; - - case AesCipherMode.CTS: - return System.Security.Cryptography.CipherMode.CTS; - - // OFB is supported on 4.62 but has a faulty implementation, so always fallback to ECB - case AesCipherMode.OFB: - blockMode = new OfbCipherMode(iv); - break; - // CFB not supported on NetStandard 2.1 - case AesCipherMode.CFB: -#if NET6_0_OR_GREATER - return System.Security.Cryptography.CipherMode.CFB; -#else - blockMode = new CfbCipherMode(iv); - break; + _impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null); + } #endif - - // CTR not supported by the BCL - case AesCipherMode.CTR: - blockMode = new CtrCipherMode(iv); - _packedIV = GetPackedIV(iv); - break; + else if (mode == AesCipherMode.CTR) + { + // CTR not supported by the BCL, use an optimized implementation + _impl = new CtrImpl(key, iv); + } + else + { + _impl = new BclImpl( + key, + iv, + (System.Security.Cryptography.CipherMode) mode, + pkcs7Padding ? PaddingMode.PKCS7 : PaddingMode.None); } -#pragma warning restore IDE0010 // Add missing cases - - return System.Security.Cryptography.CipherMode.ECB; } - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - /// or is . - /// or is too short. + /// public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - // fallback: legacy block-by-block encryption - if (_blockMode is not null) - { - return _blockMode.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - CheckArgs(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - - // fast crypto using the BCL CryptoServiceProvider/CNG - return _encryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + return _impl.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - /// or is . - /// or is too short. + /// public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - // fallback: legacy block-by-block decryption - if (_blockMode is not null) - { - return _blockMode.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - CheckArgs(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - - // fast crypto using the BCL CryptoServiceProvider/CNG - return _decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + return _impl.EncryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } - /// - /// Encrypts the specified data. - /// - /// The data. - /// The zero-based offset in at which to begin encrypting. - /// The number of bytes to encrypt from . - /// - /// The encrypted data. - /// + /// public override byte[] Encrypt(byte[] input, int offset, int length) { - if (_aesMode == AesCipherMode.CTR) - { - return CTREncryptDecrypt(input, offset, length); - } - - // fast crypto using the BCL CryptoServiceProvider/CNG - if (_blockMode is null && _encryptor.CanTransformMultipleBlocks) - { - if (_aes.Padding == PaddingMode.None) - { - var output = new byte[length]; - _ = _encryptor.TransformBlock(input, offset, length, output, 0); - return output; - } - - return _encryptor.TransformFinalBlock(input, offset, length); - } - - // fallback: legacy block-by-block encryption - return base.Encrypt(input, offset, length); + return _impl.Encrypt(input, offset, length); } - /// - /// Decrypts the specified input. - /// - /// The input. - /// The zero-based offset in at which to begin decrypting. - /// The number of bytes to decrypt from . - /// - /// The decrypted data. - /// + /// public override byte[] Decrypt(byte[] input, int offset, int length) { - if (_aesMode == AesCipherMode.CTR) - { - return CTREncryptDecrypt(input, offset, length); - } - - // fast crypto using the BCL CryptoServiceProvider/CNG - if (_blockMode is null && _decryptor.CanTransformMultipleBlocks) - { - if (_aes.Padding == PaddingMode.None) - { - var output = new byte[length]; - _ = _decryptor.TransformBlock(input, offset, length, output, 0); - return output; - } - - return _decryptor.TransformFinalBlock(input, offset, length); - } - - // fallback: legacy block-by-block decryption - return base.Decrypt(input, offset, length); - } - - private void CheckArgs(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < BlockSize) - { - throw new ArgumentException("Invalid input buffer"); - } - - if (outputBuffer.Length - outputOffset < BlockSize) - { - throw new ArgumentException("Invalid output buffer"); - } - - if (inputCount != BlockSize) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", BlockSize)); - } - } - - // CTR implementation, optimized for large arrays - // This provides huge performance gain by avoiding a block-by-block encrpyt/decrypt loop - // AES-CTR is used by AWS SFTP Transfer Family - #region CTR - - // Perform AES-CTR encryption/decryption - private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) - { - var count = length / BlockSize; - if (length % BlockSize != 0) - { - count++; - } - - var buffer = new byte[count * BlockSize]; - CTRCreateCounterArray(buffer); - _ = _encryptor.TransformBlock(buffer, 0, buffer.Length, buffer, 0); - ArrayXOR(buffer, data, offset, length); - - // adjust output for non-blocksized lengths - if (buffer.Length > length) - { - Array.Resize(ref buffer, length); - } - - return buffer; - } - -#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER - - // creates the Counter array filled with incrementing copies of IV - private void CTRCreateCounterArray(byte[] buffer) - { - var counter = MemoryMarshal.Cast(buffer.AsSpan()); - - // fill array with IV, increment by 1 for each copy - var len = counter.Length; - for (var i = 0; i < len; i += 4) - { - counter[i] = _packedIV[0]; - counter[i + 1] = _packedIV[1]; - counter[i + 2] = _packedIV[2]; - counter[i + 3] = _packedIV[3]; - - // increment IV (little endian) - if (_packedIV[3] < 0xFF000000u) - { - _packedIV[3] += 0x01000000u; - } - else - { - uint j = 3; - do - { - _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); - } - while (_packedIV[j] == 0 && --j >= 0); - } - } + return _impl.Decrypt(input, offset, length); } - // XOR 2 arrays using Vector - private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) - { - var vectorSize = Vector.Count; - for (var loopOffset = 0; length > 0; length -= vectorSize) - { - if (length >= vectorSize) - { - var v = new Vector(buffer, loopOffset) ^ new Vector(data, offset + loopOffset); - v.CopyTo(buffer, loopOffset); - loopOffset += vectorSize; - } - else - { - for (var i = 0; i < length; i++) - { - buffer[loopOffset] ^= data[offset + loopOffset]; - loopOffset++; - } - } - } - } - -#else - // creates the Counter array filled with incrementing copies of IV - private void CTRCreateCounterArray(byte[] buffer) - { - // fill array with IV, increment by 1 for each copy - var words = buffer.Length / 4; - var counter = new uint[words]; - for (var i = 0; i < words; i += 4) - { - // write IV to buffer (big endian) - counter[i] = _packedIV[0]; - counter[i + 1] = _packedIV[1]; - counter[i + 2] = _packedIV[2]; - counter[i + 3] = _packedIV[3]; - - // increment IV (little endian) - if (_packedIV[3] < 0xFF000000u) - { - _packedIV[3] += 0x01000000u; - } - else - { - uint j = 3; - do - { - _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); - } - while (_packedIV[j] == 0 && --j >= 0); - } - } - - // copy uint[] to byte[] - Buffer.BlockCopy(counter, 0, buffer, 0, buffer.Length); - } - - // XOR 2 arrays using Uint[] and blockcopy - private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) - { - var words = length / 4; - if (length % 4 != 0) - { - words++; - } - - // convert original data to words - var datawords = new uint[words]; - Buffer.BlockCopy(data, offset, datawords, 0, length); - - // convert encrypted IV counter to words - var bufferwords = new uint[words]; - Buffer.BlockCopy(buffer, 0, bufferwords, 0, length); - - // XOR encrypted Counter with input data - for (var i = 0; i < words; i++) - { - bufferwords[i] = bufferwords[i] ^ datawords[i]; - } - - // copy uint[] to byte[] - Buffer.BlockCopy(bufferwords, 0, buffer, 0, length); - } - -#endif - - // pack the IV into an array of uint[4] - private static uint[] GetPackedIV(byte[] iv) - { - var packedIV = new uint[4]; - packedIV[0] = BitConverter.ToUInt32(iv, 0); - packedIV[1] = BitConverter.ToUInt32(iv, 4); - packedIV[2] = BitConverter.ToUInt32(iv, 8); - packedIV[3] = BitConverter.ToUInt32(iv, 12); - - return packedIV; - } - - private static uint SwapEndianness(uint x) - { - x = (x >> 16) | (x << 16); - return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8); - } - - #endregion - /// /// Dispose the instance. /// /// Set to True to dispose of resouces. public void Dispose(bool disposing) { - if (disposing) + if (disposing && _impl is IDisposable disposableImpl) { - _encryptor?.Dispose(); - _encryptor = null; - - _decryptor?.Dispose(); - _decryptor = null; - - _ecbHelper?.Dispose(); - _ecbHelper = null; + disposableImpl.Dispose(); } } diff --git a/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs b/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs index bc0cdbf6c..f24559c5b 100644 --- a/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs +++ b/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts - -using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; using Renci.SshNet.Security.Cryptography.Ciphers; namespace Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers @@ -63,13 +61,13 @@ public byte[] Decrypt_CTR() [Benchmark] public byte[] Encrypt_ECB() { - return new AesCipher(_key).Encrypt(_data); + return new AesCipher(_key, null, AesCipherMode.ECB, false).Encrypt(_data); } [Benchmark] public byte[] Decrypt_ECB() { - return new AesCipher(_key).Decrypt(_data); + return new AesCipher(_key, null, AesCipherMode.ECB, false).Decrypt(_data); } } } diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt index 3b3bfa65d..144047c0e 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt @@ -4,20 +4,20 @@ // expected encrypted values, and also verifies those values against the .NET // BCL implementation as an extra validation before generating the tests. -Dictionary modes = new() +Dictionary modes = new() { - ["ecb"] = ("iv: null, AesCipherMode.ECB", CipherMode.ECB), - ["cbc"] = ("(byte[])iv.Clone(), AesCipherMode.CBC", CipherMode.CBC), - ["cfb"] = ("(byte[])iv.Clone(), AesCipherMode.CFB", CipherMode.CFB), - ["ctr"] = ("(byte[])iv.Clone(), AesCipherMode.CTR", null), - ["ofb"] = ("(byte[])iv.Clone(), AesCipherMode.OFB", CipherMode.OFB), + ["ecb"] = ("mode: null", "iv: null, AesCipherMode.ECB", CipherMode.ECB), + ["cbc"] = ("new CbcCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.CBC", CipherMode.CBC), + ["cfb"] = ("new CfbCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.CFB", CipherMode.CFB), + ["ctr"] = ("new CtrCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.CTR", null), + ["ofb"] = ("new OfbCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.OFB", CipherMode.OFB), }; Random random = new(123); using IndentedTextWriter tw = new(Console.Out); -foreach ((string mode, (string modeCode, CipherMode? bclMode)) in modes) +foreach ((string mode, (string ctor1Code, string ctor2Code, CipherMode? bclMode)) in modes) { foreach (int keySize in new int[] { 128, 192, 256 }) { @@ -90,8 +90,7 @@ foreach ((string mode, (string modeCode, CipherMode? bclMode)) in modes) tw.WriteLine($"// {openSslCmd} | hd"); // pipe to hexdump WriteBytes(expected); tw.WriteLine(); - tw.WriteLine($"var actual = new AesCipher(key, {modeCode}, pkcs7Padding: false).Encrypt(input);"); - tw.WriteLine(); + tw.WriteLine($"var actual = new AesCipher(key, {ctor1Code}, padding: null).Encrypt(input);"); tw.WriteLine($"CollectionAssert.AreEqual(expected, actual);"); if (bclMode is not null and not CipherMode.OFB) @@ -117,9 +116,14 @@ foreach ((string mode, (string modeCode, CipherMode? bclMode)) in modes) } tw.WriteLine(); - tw.WriteLine($"var decrypted = new AesCipher(key, {modeCode}, pkcs7Padding: false).Decrypt(actual);"); - tw.WriteLine(); + tw.WriteLine($"var decrypted = new AesCipher(key, {ctor1Code}, padding: null).Decrypt(actual);"); tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted);"); + tw.WriteLine(); + tw.WriteLine($"var actual2 = new AesCipher(key, {ctor2Code}, pkcs7Padding: false).Encrypt(input);"); + tw.WriteLine($"CollectionAssert.AreEqual(expected, actual2);"); + tw.WriteLine(); + tw.WriteLine($"var decrypted2 = new AesCipher(key, {ctor2Code}, pkcs7Padding: false).Decrypt(actual);"); + tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted2);"); tw.Indent--; tw.WriteLine("}"); diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index b990ce5a3..44df816e0 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; +using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers @@ -95,20 +96,24 @@ public void AES_ECB_128_Length16() { 0x96, 0x39, 0xec, 0x0d, 0xfc, 0x2d, 0xb2, 0x7c, 0xe9, 0x74, 0x8e, 0x5f, 0xb9, 0xf3, 0x99, 0xce, }; - + // echo -n -e '\x03\xe1\xe1\xaa\xa5\xbc\xa1\x9f\xba\x8c\x42\x05\x8b\x4a\xbf\x28' | openssl enc -e -aes-128-ecb -K 9639EC0DFC2DB27CE9748E5FB9F399CE -nopad | hd var expected = new byte[] { 0x9d, 0x55, 0x05, 0x4e, 0xe9, 0x50, 0xb5, 0x93, 0x50, 0x93, 0x69, 0x96, 0xa6, 0xdd, 0x1e, 0x15, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -123,21 +128,25 @@ public void AES_ECB_128_Length32() { 0x67, 0x02, 0x45, 0xc8, 0xb8, 0x64, 0x42, 0x17, 0xda, 0x85, 0x21, 0x3e, 0x5c, 0xa6, 0xee, 0xd4, }; - + // echo -n -e '\x1a\xf1\x3a\x35\x8c\xca\x3f\xd6\x2f\x65\xc1\x31\x2d\x41\xe5\xc7\xf3\x74\x23\x71\xed\x6d\x84\x79\x61\xd0\xf8\x6f\x7f\x0c\xcc\x86' | openssl enc -e -aes-128-ecb -K 670245C8B8644217DA85213E5CA6EED4 -nopad | hd var expected = new byte[] { 0x73, 0x67, 0xcc, 0x04, 0x46, 0xf5, 0x31, 0x9b, 0x64, 0x26, 0x32, 0xba, 0xa4, 0x18, 0x0d, 0x8a, 0xe3, 0x1c, 0x95, 0x44, 0x49, 0x9e, 0x4a, 0x17, 0x0e, 0x64, 0xd3, 0xe8, 0x5c, 0xe6, 0x9f, 0x83, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -154,7 +163,7 @@ public void AES_ECB_128_Length64() { 0xc7, 0x8d, 0x3a, 0x4c, 0xa2, 0xfb, 0xde, 0x1e, 0x49, 0x3e, 0xc1, 0x34, 0x86, 0x14, 0xc6, 0x2d, }; - + // echo -n -e '\x99\x3a\xc9\x2b\xfb\x1d\x0e\x8e\x31\x0c\x96\x68\x4c\x46\x1d\xbb\xe1\x23\xc8\x99\x59\x90\x47\xcb\x63\x99\x5b\xf7\x91\x87\x44\x09\x2e\xff\xa4\x21\xdc\xc3\xd9\x89\xd7\x24\x0a\x32\x05\x36\x60\x25\xa4\x17\xda\xaf\x08\xbe\xc9\x08\xf3\xfe\xc7\x61\xc2\x17\xfd\xaa' | openssl enc -e -aes-128-ecb -K C78D3A4CA2FBDE1E493EC1348614C62D -nopad | hd var expected = new byte[] { @@ -163,14 +172,18 @@ public void AES_ECB_128_Length64() 0xee, 0x64, 0xf2, 0xf4, 0xa3, 0x20, 0x54, 0x84, 0x7f, 0x8d, 0xe1, 0x6b, 0xf3, 0xd9, 0x7e, 0x34, 0x10, 0xe3, 0xe0, 0x30, 0xd3, 0x0e, 0xe3, 0x94, 0xd8, 0xf5, 0xb1, 0x44, 0xf8, 0x36, 0xfd, 0x0b, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -185,20 +198,24 @@ public void AES_ECB_192_Length16() 0x38, 0x99, 0x28, 0x8c, 0xc4, 0x84, 0xfd, 0x32, 0x8c, 0xca, 0x16, 0x06, 0xcc, 0x00, 0x22, 0xd2, 0x76, 0x00, 0x0d, 0x25, 0xa9, 0x4e, 0x31, 0x25, }; - + // echo -n -e '\x27\x60\x6b\x78\xfc\x13\x83\xa8\x38\xbb\x65\xca\xfd\x94\x82\xde' | openssl enc -e -aes-192-ecb -K 3899288CC484FD328CCA1606CC0022D276000D25A94E3125 -nopad | hd var expected = new byte[] { 0x1c, 0xd3, 0x91, 0xd8, 0xc3, 0xe0, 0x4d, 0x8e, 0x9e, 0x5c, 0xaf, 0xcc, 0x55, 0x65, 0x54, 0xb7, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -214,21 +231,25 @@ public void AES_ECB_192_Length32() 0x05, 0x6a, 0xc2, 0x70, 0x62, 0xff, 0x28, 0x34, 0xce, 0x08, 0x58, 0x9c, 0xe3, 0x76, 0x1b, 0xbb, 0x1a, 0xbc, 0xf9, 0x4c, 0x60, 0xe1, 0x5f, 0x57, }; - + // echo -n -e '\x63\x38\xec\x32\xfd\x7d\xdb\x38\x99\x93\x53\xfc\x86\x5d\x35\xe9\x68\x02\xda\x1a\x43\x0b\x02\x55\x57\x74\xed\x7d\x5a\xbf\x82\x3b' | openssl enc -e -aes-192-ecb -K 056AC27062FF2834CE08589CE3761BBB1ABCF94C60E15F57 -nopad | hd var expected = new byte[] { 0x02, 0x7c, 0x02, 0x3d, 0x80, 0x78, 0xbe, 0x53, 0x10, 0xb9, 0x1b, 0xbf, 0xb4, 0x2c, 0x16, 0xe7, 0x87, 0xe2, 0x91, 0x40, 0x31, 0x26, 0x67, 0xf6, 0xf7, 0x86, 0x73, 0x89, 0x0d, 0x35, 0x22, 0x6c, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -246,7 +267,7 @@ public void AES_ECB_192_Length64() 0x10, 0x0c, 0x69, 0x35, 0xc3, 0x1f, 0x8d, 0xe7, 0xc7, 0x6b, 0xa5, 0x2a, 0x6f, 0x46, 0x73, 0xe9, 0x6b, 0xb1, 0x8e, 0xac, 0xef, 0xf1, 0xcc, 0x78, }; - + // echo -n -e '\xda\xa5\x4b\x3b\xb3\x66\x71\xe0\x58\x31\x62\x9d\xc6\x36\xda\x23\x0b\x6b\x3b\xcb\x24\x9f\xa4\x6f\x29\x7e\x8b\xcb\x7f\xff\x21\x56\x34\x90\x72\xba\x95\x23\xa3\xcf\x25\xfa\x30\x5e\xfc\x40\x13\xda\x3d\xd3\x10\x2f\x89\xbc\x44\x3a\x01\xdb\x11\x34\xda\xa5\x60\x58' | openssl enc -e -aes-192-ecb -K 100C6935C31F8DE7C76BA52A6F4673E96BB18EACEFF1CC78 -nopad | hd var expected = new byte[] { @@ -255,14 +276,18 @@ public void AES_ECB_192_Length64() 0xc1, 0xe2, 0xf0, 0xbc, 0x01, 0xad, 0xb1, 0x15, 0xaf, 0x42, 0x6c, 0x08, 0xc8, 0xb3, 0x98, 0xf3, 0xcd, 0x20, 0xab, 0xbc, 0x59, 0xb2, 0xa5, 0x80, 0xf5, 0x8e, 0x53, 0xda, 0xb1, 0x39, 0x8f, 0xbc, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -277,20 +302,24 @@ public void AES_ECB_256_Length16() 0x3f, 0xf1, 0xe9, 0x8b, 0x65, 0xd3, 0xd5, 0x58, 0x77, 0x26, 0x91, 0x97, 0xf9, 0x84, 0x12, 0x8e, 0x9b, 0x71, 0x66, 0xc6, 0x8a, 0xaf, 0x61, 0x31, 0x6c, 0xff, 0x52, 0xea, 0xa5, 0xcb, 0x68, 0xe4, }; - + // echo -n -e '\x45\x29\x67\x1d\x16\x1a\xcb\xba\x67\x28\xc9\x28\x17\xb4\x69\x1e' | openssl enc -e -aes-256-ecb -K 3FF1E98B65D3D55877269197F984128E9B7166C68AAF61316CFF52EAA5CB68E4 -nopad | hd var expected = new byte[] { 0x6a, 0xd2, 0x73, 0x2b, 0x05, 0x2e, 0xdd, 0x74, 0x0c, 0x37, 0xf2, 0xcf, 0x8a, 0xef, 0x57, 0x8a, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -306,21 +335,25 @@ public void AES_ECB_256_Length32() 0x3e, 0x6e, 0xd3, 0x69, 0x3e, 0xc2, 0x96, 0xca, 0x9a, 0x20, 0x56, 0x3a, 0x6b, 0x50, 0xf0, 0x68, 0x5b, 0xfa, 0x32, 0xdc, 0x0a, 0xf6, 0x10, 0xea, 0xa0, 0x7c, 0xec, 0x58, 0x30, 0x19, 0x86, 0x1f, }; - + // echo -n -e '\x16\x3b\x8d\xa6\x4d\xa3\x94\x8f\x8f\xb8\x1f\x66\x81\xeb\xb3\xab\xbe\xac\x29\xca\xd3\x2b\x9a\x10\xba\xf4\x72\x7b\x09\x70\xa8\x38' | openssl enc -e -aes-256-ecb -K 3E6ED3693EC296CA9A20563A6B50F0685BFA32DC0AF610EAA07CEC583019861F -nopad | hd var expected = new byte[] { 0xb3, 0x37, 0x5d, 0x78, 0xf5, 0x99, 0x69, 0xad, 0x7e, 0xf9, 0x0f, 0xb7, 0x00, 0x8b, 0x99, 0x0f, 0x59, 0x0b, 0x9c, 0x7a, 0xf2, 0xb6, 0x34, 0x0d, 0xc9, 0xdd, 0x15, 0x6e, 0x75, 0xe7, 0xc6, 0x82, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -338,7 +371,7 @@ public void AES_ECB_256_Length64() 0xd4, 0x87, 0xea, 0x53, 0xe8, 0x73, 0x87, 0x22, 0x56, 0xe6, 0xcd, 0x47, 0x29, 0x23, 0x91, 0xe3, 0x0f, 0xee, 0xe7, 0x16, 0x43, 0x76, 0x0c, 0xb7, 0x41, 0x2f, 0x6e, 0xeb, 0xf6, 0xd8, 0x3e, 0x35, }; - + // echo -n -e '\x03\x09\x1f\x0e\x3e\xcb\x2e\x47\x5e\xe9\xc8\xc2\xd5\x3e\x9a\x80\x9a\x37\x2a\x85\x28\xdd\x51\x11\x8d\x36\xc6\xab\xc6\x5c\x14\x41\xd7\x82\x55\x26\xf9\x77\xe0\x44\xb7\xe0\xb4\x2d\x80\xaa\x26\xd7\xc4\xaf\x19\x9e\x34\x20\x41\x25\xb8\x0d\x81\x08\x05\x82\x81\x01' | openssl enc -e -aes-256-ecb -K D487EA53E873872256E6CD47292391E30FEEE71643760CB7412F6EEBF6D83E35 -nopad | hd var expected = new byte[] { @@ -347,14 +380,18 @@ public void AES_ECB_256_Length64() 0x06, 0x36, 0x7b, 0x61, 0x48, 0x62, 0x76, 0xfb, 0x58, 0x3e, 0x08, 0x51, 0xf6, 0xf8, 0xfa, 0xd2, 0x63, 0xd2, 0x7d, 0x7a, 0xfc, 0xdb, 0x11, 0x08, 0x70, 0x73, 0x61, 0xe0, 0xfb, 0x93, 0xa6, 0xf9, }; - - var actual = new AesCipher(key).Encrypt(input); - + + var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key).Decrypt(actual); - + + var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -372,20 +409,24 @@ public void AES_CBC_128_Length16() { 0x32, 0xda, 0x6f, 0x58, 0xe0, 0x28, 0x99, 0xf5, 0xf5, 0xfa, 0x7e, 0x8c, 0xc1, 0x35, 0x4c, 0x8d, }; - + // echo -n -e '\x7c\x9e\xf8\x16\x9b\x6a\xbe\x5e\x7a\x33\x11\xb9\x04\x9b\x2c\x7d' | openssl enc -e -aes-128-cbc -K A798E775CA98233C0096ED4C2DBE6447 -iv 32DA6F58E02899F5F5FA7E8CC1354C8D -nopad | hd var expected = new byte[] { 0x49, 0x0e, 0xa9, 0x6f, 0x55, 0xb3, 0x57, 0xdf, 0x7c, 0x18, 0x77, 0x0c, 0xca, 0x46, 0x0d, 0x83, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -404,21 +445,25 @@ public void AES_CBC_128_Length32() { 0xca, 0xc2, 0xbd, 0xf7, 0xae, 0x21, 0x62, 0xf5, 0x2e, 0x28, 0xbb, 0x1f, 0x06, 0xfa, 0xca, 0xe4, }; - + // echo -n -e '\xca\xcb\xa6\xb9\x12\x87\xca\xe3\x7a\xbb\x16\x04\x7c\x71\x30\xbc\xce\xc9\x86\x2a\x2b\xd4\x9c\x7e\xfe\xf2\x80\xcf\x19\x96\x7b\xca' | openssl enc -e -aes-128-cbc -K 4A60826217AA35AB108BDD2512957883 -iv CAC2BDF7AE2162F52E28BB1F06FACAE4 -nopad | hd var expected = new byte[] { 0x55, 0xf4, 0x06, 0x4c, 0xdf, 0x4e, 0xf0, 0x12, 0xce, 0x45, 0x53, 0xdd, 0x9e, 0x12, 0x62, 0x61, 0x2d, 0x87, 0x42, 0x20, 0xf1, 0x0b, 0x78, 0x96, 0xd5, 0x7c, 0xeb, 0xa2, 0x7f, 0x4b, 0x5a, 0xff, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -439,7 +484,7 @@ public void AES_CBC_128_Length64() { 0x8e, 0x7d, 0x33, 0x9e, 0x6f, 0x9b, 0x21, 0x4f, 0xee, 0x2a, 0x96, 0x4a, 0x3e, 0x32, 0x63, 0x68, }; - + // echo -n -e '\x3f\x4b\xb9\x1c\xef\xcd\xa4\x23\x94\xdb\x1a\x9f\xf7\x77\x6c\x69\x79\xfc\x05\x57\xd9\x84\x1c\x29\xfe\x8c\x34\xef\xef\x15\xa4\x15\xc1\xf9\xe5\xc6\xdb\x5c\x94\xfc\x1d\x99\x63\xd3\x06\xc2\xfe\xb7\xbb\x51\xa6\x09\xf4\x72\x0a\xbb\x2f\x90\x1e\x62\x99\xb5\x34\x7e' | openssl enc -e -aes-128-cbc -K 3604DEFD91A68D1D680839402148223C -iv 8E7D339E6F9B214FEE2A964A3E326368 -nopad | hd var expected = new byte[] { @@ -448,14 +493,18 @@ public void AES_CBC_128_Length64() 0x64, 0xd5, 0x0e, 0xc2, 0x47, 0xce, 0x2a, 0x40, 0x47, 0x12, 0x05, 0xde, 0x19, 0xbd, 0x23, 0x76, 0x3d, 0x61, 0x9e, 0x0d, 0x54, 0x7f, 0xe1, 0xc4, 0x78, 0xf2, 0x04, 0x00, 0x68, 0xa9, 0x9b, 0x32, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -474,20 +523,24 @@ public void AES_CBC_192_Length16() { 0x57, 0x1e, 0xda, 0xe6, 0xf9, 0x35, 0x16, 0x23, 0x91, 0xaf, 0xdb, 0x5c, 0x5e, 0x47, 0xe7, 0xcf, }; - + // echo -n -e '\x65\xe4\x9c\x01\xe4\x00\x26\x15\xc3\x88\xa1\xeb\x38\xca\x99\xe6' | openssl enc -e -aes-192-cbc -K 6EE2D41C81960F9BE38E0F660F43DF36A5D1DA3CAC20578D -iv 571EDAE6F935162391AFDB5C5E47E7CF -nopad | hd var expected = new byte[] { 0xe1, 0x2f, 0x71, 0xad, 0x59, 0xae, 0xa7, 0xe3, 0xd3, 0x23, 0x43, 0x81, 0x31, 0xc2, 0xe5, 0xd9, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -507,21 +560,25 @@ public void AES_CBC_192_Length32() { 0x5f, 0x6f, 0xdc, 0x06, 0xea, 0xa5, 0x18, 0x27, 0x92, 0xe8, 0x7e, 0xe4, 0xf4, 0x8e, 0x4c, 0x87, }; - + // echo -n -e '\xd5\x00\x1e\x55\xf1\xbf\x05\x80\xa9\x6a\x46\x67\xef\x5c\x3a\x4e\x8a\x46\xc5\x63\xbb\x28\xa1\xae\x78\xeb\xd4\x5f\x67\x82\xd8\x5e' | openssl enc -e -aes-192-cbc -K E90B67AB02029B9718593C8EEEAE3334758DD217828413AC -iv 5F6FDC06EAA5182792E87EE4F48E4C87 -nopad | hd var expected = new byte[] { 0x21, 0x2c, 0x43, 0x64, 0x48, 0x20, 0xe9, 0xfd, 0xe9, 0x15, 0x27, 0x4d, 0x35, 0x8f, 0xf8, 0x42, 0x07, 0xf2, 0x98, 0x41, 0xbb, 0x58, 0x3d, 0xe5, 0xcf, 0x56, 0xf5, 0x4b, 0x33, 0xf7, 0xa0, 0x9a, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -543,7 +600,7 @@ public void AES_CBC_192_Length64() { 0xe9, 0x55, 0xd3, 0x62, 0x90, 0xea, 0x36, 0xf4, 0x77, 0xe6, 0xea, 0xb7, 0xa4, 0x10, 0x7c, 0x85, }; - + // echo -n -e '\xab\x2d\x4a\x61\xeb\x12\xc0\xca\xb7\xa0\xea\xda\xb0\xc0\xdb\x65\xf8\xbb\x4c\x92\x26\x95\xac\x72\x41\x15\xfc\x06\x30\x4f\x3f\xe6\x40\x4a\x6b\x54\x39\xb1\xc0\x4c\xaf\x11\x4e\x4a\xbb\x3e\x76\xd2\x0c\x18\xeb\x39\x42\xb9\x61\x15\x81\xd7\x20\xd6\x16\xba\x9a\x67' | openssl enc -e -aes-192-cbc -K 60049A6655872C46FAFFE3144762B7039F29F9186306A386 -iv E955D36290EA36F477E6EAB7A4107C85 -nopad | hd var expected = new byte[] { @@ -552,14 +609,18 @@ public void AES_CBC_192_Length64() 0x7f, 0x91, 0x8f, 0xed, 0xa9, 0x88, 0x72, 0x0c, 0x9c, 0x55, 0x13, 0x87, 0x34, 0x17, 0x51, 0xd3, 0xda, 0xba, 0xde, 0xb2, 0x7d, 0xbc, 0x71, 0xf8, 0x9b, 0xaa, 0x93, 0x52, 0xf4, 0x26, 0x3c, 0x6f, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -578,20 +639,24 @@ public void AES_CBC_256_Length16() { 0xe2, 0x90, 0x56, 0x90, 0x93, 0x7d, 0xd2, 0x22, 0xef, 0x2d, 0x7a, 0xe7, 0xb0, 0x6e, 0xa7, 0x1f, }; - + // echo -n -e '\xec\xa5\x3e\x43\xd6\x4d\xce\x1f\x1f\x1d\x37\xec\xc0\x82\x03\x5a' | openssl enc -e -aes-256-cbc -K 60137CFFB3C9B510C9EE9C6077005F8EAC732BBEC760B09C87B44273B34934F5 -iv E2905690937DD222EF2D7AE7B06EA71F -nopad | hd var expected = new byte[] { 0xe7, 0xa5, 0x53, 0xd7, 0x28, 0x4c, 0x16, 0x4e, 0xfc, 0xa2, 0xa8, 0x86, 0xfc, 0xcb, 0x71, 0x61, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -611,21 +676,25 @@ public void AES_CBC_256_Length32() { 0xf4, 0x5f, 0xf1, 0x64, 0x8d, 0x52, 0x75, 0xd3, 0x08, 0xe0, 0xea, 0x54, 0xa1, 0x48, 0x29, 0xcd, }; - + // echo -n -e '\xbe\xa8\x3f\x4d\x56\x45\x92\x00\x63\xe0\x78\xfe\x87\x42\x5d\x7f\xba\xa7\x7d\xe7\xaa\xce\xfb\x2f\xa1\x09\xcf\x99\xe5\xc8\xec\x18' | openssl enc -e -aes-256-cbc -K 0D22B40A09E69E9DFD552DB205D39AADD0FA2D08F0BF75F0AC10AB4C76F81A9B -iv F45FF1648D5275D308E0EA54A14829CD -nopad | hd var expected = new byte[] { 0x4f, 0x29, 0xa7, 0xd7, 0xbc, 0x51, 0x95, 0xc5, 0x4c, 0x79, 0x1c, 0xde, 0xad, 0xc8, 0xe0, 0xfd, 0x6a, 0xfb, 0x4a, 0x8b, 0xc8, 0x25, 0x87, 0x5c, 0x9b, 0x47, 0xf5, 0x3f, 0x42, 0xf5, 0xc6, 0x08, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -647,7 +716,7 @@ public void AES_CBC_256_Length64() { 0x2a, 0x2b, 0x52, 0xf9, 0x69, 0x3c, 0x42, 0xe7, 0x0f, 0x0c, 0x7f, 0xde, 0x12, 0xad, 0xb9, 0xab, }; - + // echo -n -e '\x6e\x21\xc9\xeb\x9a\xe3\x28\x4e\xad\xc4\x5e\xf9\x3a\x52\x26\x04\x3b\x91\xbd\xa6\xe2\x36\x1f\x7d\x85\x59\xff\x0f\xd5\x21\x4e\x63\xe7\xde\xdd\x54\x2f\x2f\x00\x11\x36\xa3\xb7\xc8\xf4\x7c\x98\xb6\xb9\xe5\x18\x0f\x8b\x82\xeb\x38\x02\x4b\x65\x40\xe3\x19\x78\x8b' | openssl enc -e -aes-256-cbc -K 76FA125C74D2D50C90AC703E7E578809C14855F0083FD864F2A452E3C9C02D1D -iv 2A2B52F9693C42E70F0C7FDE12ADB9AB -nopad | hd var expected = new byte[] { @@ -656,14 +725,18 @@ public void AES_CBC_256_Length64() 0x54, 0x3e, 0xb6, 0x23, 0x0a, 0x82, 0x7d, 0x3f, 0xbf, 0x88, 0xd1, 0x05, 0x0d, 0x10, 0x10, 0x59, 0x08, 0x19, 0x66, 0x47, 0xe7, 0xd9, 0x1d, 0x1c, 0x42, 0xdc, 0x97, 0x9c, 0xf0, 0x9a, 0x14, 0x34, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -681,20 +754,24 @@ public void AES_CFB_128_Length16() { 0xea, 0xa7, 0x20, 0x6c, 0x40, 0x92, 0x59, 0xa2, 0xa8, 0x1b, 0xd7, 0xbc, 0xd1, 0x72, 0x67, 0x1d, }; - + // echo -n -e '\xc2\x5c\x7f\x9b\xc3\x88\x83\x37\x22\xad\x6a\xcf\x7f\xf1\x42\xd0' | openssl enc -e -aes-128-cfb -K 7F531353048F9F84066EE0FCBFFA5144 -iv EAA7206C409259A2A81BD7BCD172671D -nopad | hd var expected = new byte[] { 0x76, 0xd2, 0x2b, 0x69, 0xa6, 0xdf, 0x3b, 0x4d, 0x4a, 0x52, 0x8a, 0x7a, 0x54, 0x9d, 0xbe, 0x55, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -713,21 +790,25 @@ public void AES_CFB_128_Length32() { 0xa4, 0xaf, 0x60, 0xab, 0x8d, 0x8e, 0x4c, 0xf3, 0x1f, 0x71, 0xc6, 0x27, 0xbb, 0xbe, 0x7c, 0xda, }; - + // echo -n -e '\xc9\xae\x5e\xbf\x99\x66\xa6\x33\xbd\xfa\x94\x55\xa8\x87\x77\x28\x5b\x25\xe4\xd8\xd1\xd6\x8f\xed\xf9\x71\xab\xe8\xb7\xe2\xb3\x94' | openssl enc -e -aes-128-cfb -K 06C232119B92AB8400ECAE46FE04F921 -iv A4AF60AB8D8E4CF31F71C627BBBE7CDA -nopad | hd var expected = new byte[] { 0x62, 0x67, 0x2b, 0xa7, 0x0b, 0xd1, 0xbc, 0x2f, 0x55, 0xaa, 0x71, 0x53, 0x7a, 0x68, 0xe5, 0x46, 0x18, 0xc5, 0xf7, 0x41, 0x78, 0x5f, 0x38, 0x6b, 0x4d, 0x04, 0x00, 0x3b, 0x61, 0x8c, 0xaf, 0xe7, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -748,7 +829,7 @@ public void AES_CFB_128_Length64() { 0x73, 0x1b, 0xa7, 0xfa, 0xd4, 0x97, 0xc8, 0xfb, 0x7b, 0xbf, 0x05, 0xe0, 0xa4, 0xb6, 0xca, 0xbf, }; - + // echo -n -e '\x47\xff\x4e\xe5\x54\x65\x8d\xc9\x7a\x60\xd7\xe4\x27\x49\xef\xf4\x78\x89\x44\x07\x82\x07\x06\x77\x76\x3e\xf1\x29\xcc\x84\xc8\x42\x70\xd3\xff\xfe\xb6\x13\xcc\x3e\x22\x96\x31\x2d\xb6\x67\xcb\xd6\x82\xd1\xaf\x31\x79\x74\x58\x3f\xf9\xd6\x6f\x16\x73\x63\xfc\xf6' | openssl enc -e -aes-128-cfb -K 979573544AF03D64BD05E5EBD5D8C00E -iv 731BA7FAD497C8FB7BBF05E0A4B6CABF -nopad | hd var expected = new byte[] { @@ -757,14 +838,18 @@ public void AES_CFB_128_Length64() 0x44, 0xe6, 0xce, 0x49, 0xe6, 0x8f, 0x35, 0x27, 0x26, 0x21, 0x04, 0xee, 0x52, 0x44, 0x40, 0x80, 0xf7, 0x49, 0xbc, 0xbf, 0xcb, 0x5c, 0xfa, 0x12, 0xcb, 0xcc, 0x38, 0x71, 0x68, 0xd6, 0xe9, 0x64, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -783,20 +868,24 @@ public void AES_CFB_192_Length16() { 0x90, 0x15, 0x66, 0x89, 0x23, 0x54, 0x6c, 0x0f, 0x55, 0xe4, 0xca, 0x43, 0x12, 0x72, 0x02, 0x98, }; - + // echo -n -e '\x9a\x3b\x96\x21\xf3\x77\x8b\x91\x94\x4a\x73\x74\x8f\x6c\x6a\x20' | openssl enc -e -aes-192-cfb -K 1561A357BC022100CC78D98AEB5DC0073A26519A429F1AFB -iv 9015668923546C0F55E4CA4312720298 -nopad | hd var expected = new byte[] { 0x4f, 0x9b, 0xdf, 0x72, 0x2d, 0x10, 0x1b, 0xb9, 0xa1, 0xe1, 0x06, 0xba, 0xbc, 0xc5, 0xfe, 0x13, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -816,21 +905,25 @@ public void AES_CFB_192_Length32() { 0xb9, 0xdc, 0x70, 0xd4, 0xcb, 0x9f, 0xa3, 0x0d, 0x77, 0x72, 0x45, 0x61, 0x50, 0x31, 0x2c, 0xa8, }; - + // echo -n -e '\x1a\x96\x54\x7b\x9e\x01\xa6\x36\x8a\x6c\x3a\x69\x1a\xcf\xdd\x76\x46\xa7\xc7\xa7\x9b\x97\xdc\x78\x0b\xca\x35\x06\x93\x7c\xf4\xc7' | openssl enc -e -aes-192-cfb -K 23B97FAC4A9E5D8E6F2FFFB61903F4850753FC6BAB5BFC83 -iv B9DC70D4CB9FA30D7772456150312CA8 -nopad | hd var expected = new byte[] { 0xc0, 0xdf, 0x63, 0xb5, 0x17, 0x40, 0xd4, 0xa7, 0x73, 0x40, 0xc5, 0x21, 0xa5, 0xea, 0x63, 0xdf, 0x72, 0xcf, 0x57, 0x7f, 0xf9, 0x5d, 0xfe, 0xb1, 0x36, 0x9a, 0x1d, 0x02, 0x0d, 0x4b, 0x8f, 0x35, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -852,7 +945,7 @@ public void AES_CFB_192_Length64() { 0xd9, 0x60, 0xfc, 0xbb, 0xb1, 0x44, 0xab, 0xc6, 0x1e, 0xbb, 0xa0, 0x77, 0x4b, 0x5f, 0x87, 0xac, }; - + // echo -n -e '\x4a\x38\x37\x6b\x98\x26\x5e\x08\xd5\xb0\xff\x3f\x80\x88\x1c\xc8\xbc\xfc\xf3\x6d\x2d\x89\xc3\xcf\x8c\xf1\x3e\xa7\xbe\x93\x34\xd6\x27\x53\x21\x72\x23\x90\xeb\x93\x7d\x68\xfe\x1b\xa0\x63\x8d\xee\x56\x7c\xa4\x54\x3d\xbe\x7a\xc0\x75\x68\xdf\xa6\xe7\xb7\x49\x42' | openssl enc -e -aes-192-cfb -K 7B28182D67AAA52C1160F0C58AA72F28644F5041EEE09868 -iv D960FCBBB144ABC61EBBA0774B5F87AC -nopad | hd var expected = new byte[] { @@ -861,14 +954,18 @@ public void AES_CFB_192_Length64() 0xec, 0x58, 0x90, 0x0d, 0xe0, 0x32, 0xb6, 0xa4, 0xfd, 0x6f, 0xac, 0xdb, 0x40, 0x63, 0xe9, 0x28, 0x69, 0x90, 0x2a, 0xf9, 0xf4, 0xe8, 0xcc, 0xa5, 0x2b, 0xdd, 0x9c, 0xbc, 0x44, 0xcd, 0x1e, 0x5b, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -887,20 +984,24 @@ public void AES_CFB_256_Length16() { 0x67, 0x06, 0xe7, 0x9c, 0x0b, 0x80, 0xfe, 0xed, 0xfd, 0x75, 0x28, 0xa4, 0x0d, 0x67, 0xc6, 0x80, }; - + // echo -n -e '\xd7\x78\x42\xdd\xce\xb5\x36\xcd\x38\xb7\x42\x97\x66\x08\x53\x9a' | openssl enc -e -aes-256-cfb -K 90AF5406ADE97ACCB429A7CE07B7DC04C8A469769EBB9A242A2E82FA01145F16 -iv 6706E79C0B80FEEDFD7528A40D67C680 -nopad | hd var expected = new byte[] { 0xf0, 0xfa, 0x95, 0x5c, 0xfc, 0x3f, 0xbe, 0xe5, 0x4b, 0x55, 0x57, 0xad, 0x93, 0x63, 0x36, 0x07, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -920,21 +1021,25 @@ public void AES_CFB_256_Length32() { 0x1a, 0x1a, 0x16, 0x65, 0x60, 0x07, 0x5a, 0x2e, 0x19, 0xdc, 0xf7, 0xbe, 0xb9, 0x1d, 0xa4, 0x26, }; - + // echo -n -e '\xda\x3e\xcf\xc2\x9e\xdd\xfc\xd4\x15\x30\xdc\x7f\x67\x80\xcb\xa0\xca\x91\x66\x01\xd0\x40\xf8\x47\xa5\x7b\x78\x28\x93\xf5\x16\xc2' | openssl enc -e -aes-256-cfb -K 680120C3459C778A091286DBA37F867DAA88D97C01C4B09945871C2365D3411F -iv 1A1A166560075A2E19DCF7BEB91DA426 -nopad | hd var expected = new byte[] { 0x94, 0x65, 0xf5, 0x19, 0xe9, 0xc8, 0xc6, 0xd0, 0x0d, 0x81, 0x4e, 0x13, 0xb8, 0x37, 0x2b, 0x92, 0xc2, 0xc1, 0x54, 0x9c, 0xfd, 0xf9, 0x43, 0xd0, 0xdc, 0xa7, 0x20, 0x68, 0x3e, 0xc3, 0x8f, 0x3c, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -956,7 +1061,7 @@ public void AES_CFB_256_Length64() { 0x9a, 0xb4, 0x33, 0x33, 0xc2, 0x25, 0x9f, 0xfd, 0xe2, 0x52, 0xee, 0x1c, 0xeb, 0xc6, 0xc7, 0x99, }; - + // echo -n -e '\xf5\xfa\x7d\x0a\x1c\x99\xc0\xa4\x51\x86\x7e\xbe\x7f\x54\x24\x35\xd1\x67\xc1\x89\x68\x20\x1d\xa2\x2d\xab\x63\x25\xcc\xf1\xe0\x27\xe3\xf6\x2d\x6a\x56\x36\x03\x81\x59\x72\x13\xd9\x89\x9c\xae\xc5\xb7\xc1\xec\x52\x5c\x1a\xbd\xd4\xdd\xda\xdd\x70\x35\x9b\xd7\x5f' | openssl enc -e -aes-256-cfb -K A656DA8926BADF9A633F2FF60C431990FC9D6D0A048DCBC838588D7B59924BBE -iv 9AB43333C2259FFDE252EE1CEBC6C799 -nopad | hd var expected = new byte[] { @@ -965,14 +1070,18 @@ public void AES_CFB_256_Length64() 0xa1, 0xd6, 0x8a, 0xc4, 0xbc, 0xe1, 0xca, 0x0a, 0xaa, 0xa8, 0xea, 0x4f, 0x7c, 0xd2, 0xd2, 0xc4, 0xf6, 0xd4, 0x06, 0xef, 0x04, 0xf1, 0xe5, 0x53, 0x54, 0xd5, 0x80, 0xc2, 0x96, 0x6b, 0xc7, 0x07, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -990,20 +1099,24 @@ public void AES_CTR_128_Length16() { 0x91, 0xf3, 0xba, 0x0b, 0x1e, 0xb2, 0x8f, 0xce, 0x59, 0x1b, 0xa8, 0xaf, 0xd4, 0xd1, 0xd0, 0x7e, }; - + // echo -n -e '\xc1\x4d\x74\x98\x2e\xcc\x5a\x18\x8a\x12\x50\xcd\x2c\x63\x41\xd0' | openssl enc -e -aes-128-ctr -K F4715B580FE5CED7FD7028B29EAEDC71 -iv 91F3BA0B1EB28FCE591BA8AFD4D1D07E -nopad | hd var expected = new byte[] { 0xe4, 0x03, 0x8f, 0x2a, 0xdd, 0x9d, 0xf6, 0x87, 0xf6, 0x29, 0xee, 0x27, 0x4c, 0xf3, 0xba, 0x82, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1022,21 +1135,25 @@ public void AES_CTR_128_Length32() { 0xa1, 0xcc, 0x79, 0xf6, 0x95, 0x97, 0xd4, 0xdb, 0x6b, 0xe6, 0x99, 0xdd, 0x70, 0x95, 0x9e, 0x60, }; - + // echo -n -e '\x11\x1e\x28\x7a\x6a\x6f\x89\xdb\x7f\x9d\x9a\xbd\xa3\xa8\x79\xdc\x36\xde\x3c\x38\xa9\x35\xb2\x41\xe1\x8d\xff\xf4\x3d\x1e\x02\x2c' | openssl enc -e -aes-128-ctr -K A0AAA180866107216ADE8C8017D12AB1 -iv A1CC79F69597D4DB6BE699DD70959E60 -nopad | hd var expected = new byte[] { 0xa9, 0x27, 0xa5, 0xbd, 0x73, 0x59, 0xe3, 0x69, 0x79, 0x89, 0x62, 0xe8, 0x4c, 0x7d, 0x75, 0xcd, 0x9c, 0xb2, 0x30, 0x94, 0xdc, 0x88, 0xfa, 0x39, 0x05, 0x0c, 0x26, 0x25, 0x28, 0x6a, 0x9b, 0x4e, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1057,7 +1174,7 @@ public void AES_CTR_128_Length64() { 0x92, 0xdb, 0xe4, 0x3e, 0xaf, 0x8f, 0x92, 0x13, 0x71, 0x56, 0xd1, 0x9f, 0x0f, 0x68, 0xc3, 0xc1, }; - + // echo -n -e '\x9b\x6e\x1d\xf8\x07\xf9\x55\xd4\xd7\x1a\xce\xca\xa8\x31\x29\x0f\x63\x4d\x52\x71\xa5\x0c\x96\x08\xd6\xc5\x14\xa0\xc8\x29\xb1\xd5\x40\x2c\xe5\xa9\xb4\x31\xa9\xa8\x76\xa5\x1e\x7a\xc8\x09\x32\x39\xbc\x89\x7a\x22\x42\x2c\xba\x8e\xd7\x15\x22\x41\xe4\xb5\x0b\xad' | openssl enc -e -aes-128-ctr -K 69F98A7C4B805B31A4AAFAFFED1C3FCC -iv 92DBE43EAF8F92137156D19F0F68C3C1 -nopad | hd var expected = new byte[] { @@ -1066,14 +1183,18 @@ public void AES_CTR_128_Length64() 0x1d, 0x18, 0x55, 0xfc, 0x56, 0x59, 0xaf, 0x0b, 0x2b, 0x80, 0x87, 0x0c, 0x87, 0x45, 0xb0, 0xe2, 0xec, 0x47, 0x81, 0x82, 0x89, 0x24, 0x76, 0xe2, 0x20, 0x6a, 0x99, 0xe2, 0xa7, 0x5a, 0xb0, 0x40, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1092,20 +1213,24 @@ public void AES_CTR_192_Length16() { 0xec, 0xa8, 0x10, 0x66, 0x10, 0xfb, 0xe1, 0xb6, 0xb5, 0x15, 0xca, 0xb9, 0xb9, 0xba, 0xf0, 0xcd, }; - + // echo -n -e '\x9a\x70\x11\xcf\x7f\xb6\xee\x3b\x2e\x48\x7e\x97\x32\xbb\xa1\xbb' | openssl enc -e -aes-192-ctr -K D556AF09D0CCFEDA66760AF5AFBC223BE639657D0A704CDC -iv ECA8106610FBE1B6B515CAB9B9BAF0CD -nopad | hd var expected = new byte[] { 0xc4, 0x4e, 0x81, 0x32, 0xe6, 0x6d, 0x0a, 0x78, 0x49, 0xe5, 0x64, 0x6c, 0xe6, 0xc2, 0x91, 0xc9, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1125,21 +1250,25 @@ public void AES_CTR_192_Length32() { 0x6d, 0xfd, 0x74, 0x57, 0xb9, 0xf2, 0x80, 0xbd, 0xbf, 0x85, 0xb0, 0xbd, 0x19, 0xdd, 0x5d, 0xc6, }; - + // echo -n -e '\x72\x37\x68\x09\xab\xf9\x8c\x72\x26\x42\xb1\xf9\x55\x24\xb1\x64\x09\xd2\x1c\x28\xbb\x97\xc9\x6b\x94\x54\x3f\x9a\xf2\x69\x82\x2b' | openssl enc -e -aes-192-ctr -K 48970AD3074330F31C9D40CE49E860916465AFE69EC812DB -iv 6DFD7457B9F280BDBF85B0BD19DD5DC6 -nopad | hd var expected = new byte[] { 0xfa, 0x30, 0x3f, 0x12, 0x3c, 0x7a, 0xa8, 0x1e, 0xfd, 0xaa, 0x17, 0x71, 0xbd, 0x01, 0xeb, 0xac, 0x85, 0xcd, 0x88, 0xa8, 0x25, 0xc8, 0xbd, 0xf8, 0xc3, 0xa9, 0x74, 0x36, 0x82, 0x19, 0xfc, 0xb3, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1161,7 +1290,7 @@ public void AES_CTR_192_Length64() { 0xf1, 0x7a, 0x87, 0xdb, 0xf3, 0xb0, 0x86, 0x7e, 0x52, 0x13, 0xd4, 0x0c, 0x6f, 0x34, 0xca, 0xe0, }; - + // echo -n -e '\xa2\x28\x0b\x1e\x56\xfb\x21\xac\xf3\xae\x35\x8c\xb9\x9c\x8d\x80\x85\x2f\x66\x09\xce\xd8\x3a\x2a\x1d\x82\x0e\xc4\x37\xa3\x77\x86\x07\xe9\x43\x75\xbc\xf3\x84\x72\xdb\xc8\x63\x0b\xbc\xf3\x03\x23\xf7\x30\x38\xea\x77\x53\xf7\xc9\xee\xe0\x00\xd4\xec\x5d\x75\x50' | openssl enc -e -aes-192-ctr -K 36AF84CF5817C391AAF32D06742E6E297EEBCC066B8D0FB4 -iv F17A87DBF3B0867E5213D40C6F34CAE0 -nopad | hd var expected = new byte[] { @@ -1170,14 +1299,18 @@ public void AES_CTR_192_Length64() 0xba, 0x65, 0x2d, 0xe3, 0x7b, 0x60, 0x9c, 0x64, 0x4e, 0xcc, 0x32, 0xb5, 0x38, 0xa4, 0xed, 0x69, 0x2d, 0x26, 0x4a, 0x22, 0x97, 0x7a, 0x94, 0x5e, 0xb0, 0xb2, 0x3d, 0x42, 0x2b, 0x4a, 0x5e, 0x5d, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1196,20 +1329,24 @@ public void AES_CTR_256_Length16() { 0x2e, 0x2b, 0x6d, 0x9e, 0x56, 0xeb, 0x50, 0x85, 0x07, 0x45, 0x16, 0x76, 0x3d, 0xf3, 0x64, 0x11, }; - + // echo -n -e '\x6d\xa6\x3f\x83\x25\xf1\x54\xbf\x72\xd7\x55\x00\x90\x6f\xe5\xa9' | openssl enc -e -aes-256-ctr -K 9FD0DEDE8FE79EFA6DAFB3615A61BA4A21EC98C44D8B8E0025C8691B5B85EEE3 -iv 2E2B6D9E56EB5085074516763DF36411 -nopad | hd var expected = new byte[] { 0xa6, 0x46, 0x19, 0x9d, 0x3e, 0xa5, 0x53, 0xc8, 0xd9, 0xb3, 0x46, 0xbc, 0x0b, 0x3e, 0x47, 0xf4, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1229,21 +1366,25 @@ public void AES_CTR_256_Length32() { 0x1a, 0x87, 0x62, 0x25, 0x84, 0x4e, 0x41, 0x76, 0xc3, 0x24, 0x5f, 0x9b, 0xbe, 0x7c, 0x02, 0x11, }; - + // echo -n -e '\x1d\x0a\xdf\xa4\xd6\x20\x5c\x14\x41\xdd\xb9\xc6\x7e\x83\x9f\xe7\xc0\xd0\x32\x2f\xf4\x1b\xf4\x35\x9b\x13\xbd\x08\x74\x18\xc2\x32' | openssl enc -e -aes-256-ctr -K 6458FE51A5490C0DCF585D78328A0784A52FB56DC0351C0115AA09C36353A028 -iv 1A876225844E4176C3245F9BBE7C0211 -nopad | hd var expected = new byte[] { 0x72, 0xb6, 0xb0, 0x78, 0xae, 0x36, 0xaa, 0x9e, 0x6b, 0xb7, 0x63, 0x7a, 0x77, 0x68, 0x8b, 0x42, 0x1d, 0x7e, 0xe7, 0xe7, 0xa0, 0xae, 0x31, 0x9b, 0xb3, 0x21, 0xb8, 0x0c, 0x47, 0x3e, 0xaf, 0xdd, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1265,7 +1406,7 @@ public void AES_CTR_256_Length64() { 0xdb, 0x2f, 0xcf, 0x6f, 0xf2, 0xed, 0xe7, 0xfb, 0x59, 0x86, 0x1b, 0x85, 0xc1, 0xf5, 0x32, 0xc2, }; - + // echo -n -e '\x0b\x38\x62\x45\x62\x55\x71\x2e\x3b\xfc\x3b\xfb\x40\x49\xaa\x7b\xb8\x34\x5d\xab\x27\xe1\xff\x57\xed\x3e\xa9\x9b\xd5\x80\x43\x98\xa7\xf7\xb7\x2a\xf0\x5a\xc6\xc4\x15\x34\xea\x88\x12\x46\x36\x79\x7a\xe4\xe3\x89\x1e\x57\xe9\x29\x39\x0b\x58\x23\xac\xd6\x58\xba' | openssl enc -e -aes-256-ctr -K B9A25348927F8B5D6E9896F3F77744A6082F20F19DB97A500E8EF1E502A2183E -iv DB2FCF6FF2EDE7FB59861B85C1F532C2 -nopad | hd var expected = new byte[] { @@ -1274,14 +1415,18 @@ public void AES_CTR_256_Length64() 0x15, 0xcc, 0x98, 0xc9, 0xe4, 0x6f, 0x29, 0x13, 0xf9, 0x61, 0x33, 0x77, 0x6b, 0x43, 0x44, 0xde, 0x92, 0xb0, 0x7b, 0x7a, 0x77, 0x65, 0xf0, 0xcc, 0xbd, 0xe4, 0x41, 0xea, 0x9e, 0xfd, 0xdf, 0x41, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1299,20 +1444,24 @@ public void AES_OFB_128_Length16() { 0x5c, 0x2f, 0x1d, 0x50, 0x86, 0x9c, 0x89, 0x74, 0x11, 0xd0, 0x46, 0xef, 0xb2, 0xe3, 0x6d, 0xb3, }; - + // echo -n -e '\xc7\xb1\x1b\x7c\xb5\x66\x1d\xff\x28\x03\x3a\x03\x8d\xa6\x5b\xcc' | openssl enc -e -aes-128-ofb -K 805718C8A7D4B31B482598169EF48E19 -iv 5C2F1D50869C897411D046EFB2E36DB3 -nopad | hd var expected = new byte[] { 0xb0, 0x65, 0x77, 0x03, 0xb4, 0x54, 0x82, 0x92, 0x05, 0x82, 0x93, 0x1f, 0x8d, 0x7b, 0xb6, 0xf0, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1331,21 +1480,25 @@ public void AES_OFB_128_Length32() { 0x6f, 0x12, 0x7a, 0x91, 0x3b, 0x0f, 0x2b, 0x20, 0x0a, 0x21, 0x9c, 0x39, 0xb2, 0x43, 0x64, 0x39, }; - + // echo -n -e '\x2a\x4f\x05\x69\xdd\x69\x1a\xf2\xfe\xff\x34\x8f\xcd\x06\x60\x34\x74\x21\xa7\x5d\x88\x0a\x45\xe4\xcd\xa3\xb7\xd7\x8e\xc4\x68\x64' | openssl enc -e -aes-128-ofb -K B8E5EC4EEE243BF2152B528667F9A70A -iv 6F127A913B0F2B200A219C39B2436439 -nopad | hd var expected = new byte[] { 0x11, 0x2d, 0xdf, 0xcf, 0x49, 0xc9, 0xd8, 0x0a, 0x7d, 0xd3, 0x2f, 0xf5, 0xc5, 0xec, 0x7e, 0xc9, 0x11, 0xb9, 0xd6, 0x67, 0x6c, 0xe7, 0xaa, 0x09, 0x93, 0xe3, 0x5f, 0xed, 0x38, 0x46, 0x37, 0xd2, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1366,7 +1519,7 @@ public void AES_OFB_128_Length64() { 0x61, 0xf8, 0x28, 0xc1, 0xc4, 0x39, 0xf7, 0xdf, 0x28, 0x2f, 0xef, 0xf2, 0x91, 0x9f, 0x90, 0x54, }; - + // echo -n -e '\x97\xd0\xd7\xe8\x1a\x11\x45\x4f\xe5\xb5\x48\x5c\xb7\xbe\x7c\xd4\xfc\xac\x68\x7b\x49\xd7\x28\xa8\xba\xcb\x44\xcd\x88\x01\x3f\xd2\xc7\x19\xef\x97\x21\xbe\xef\x5d\xcc\x2b\xac\x86\xc7\xce\x69\x4b\xa4\xc7\x3d\x05\xda\xe8\xf0\xc0\xa7\x2f\x2d\x4f\xcd\x77\xc6\xe3' | openssl enc -e -aes-128-ofb -K 7576949ECEE5B23DBD0AAE1E2BA2E1EB -iv 61F828C1C439F7DF282FEFF2919F9054 -nopad | hd var expected = new byte[] { @@ -1375,14 +1528,18 @@ public void AES_OFB_128_Length64() 0x5e, 0xb0, 0xdb, 0x23, 0xc7, 0x33, 0x2b, 0x06, 0x0d, 0x01, 0x1e, 0x9b, 0xb8, 0xf1, 0xde, 0x27, 0xda, 0xad, 0x1b, 0xa5, 0x20, 0x67, 0xd2, 0xa6, 0x18, 0x26, 0x30, 0x43, 0x2f, 0xa2, 0x66, 0x0b, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1401,20 +1558,24 @@ public void AES_OFB_192_Length16() { 0x7b, 0x69, 0xac, 0xc3, 0xf1, 0x26, 0xa5, 0x56, 0x9a, 0xe9, 0xa4, 0x4f, 0xb1, 0xbc, 0x05, 0x5e, }; - + // echo -n -e '\x64\xc8\x10\x50\x3a\xcb\x7d\xbf\x14\x00\x48\xd0\x39\xd2\x94\x05' | openssl enc -e -aes-192-ofb -K 4D41EDD44F051F3C7EB5759EF5C0AB1D7959BA629190B196 -iv 7B69ACC3F126A5569AE9A44FB1BC055E -nopad | hd var expected = new byte[] { 0x79, 0x41, 0x28, 0xc9, 0x3b, 0x89, 0x6f, 0x69, 0x92, 0xb0, 0x3e, 0x38, 0x11, 0x2c, 0xe5, 0xd8, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1434,21 +1595,25 @@ public void AES_OFB_192_Length32() { 0x89, 0x79, 0x75, 0x36, 0xda, 0xbd, 0x39, 0xf8, 0xbe, 0x98, 0x8c, 0xbc, 0x79, 0xb6, 0xff, 0x64, }; - + // echo -n -e '\xa9\xd4\xd2\x85\x55\xde\xc9\x54\x54\x2a\x56\xe0\x17\x32\x74\xbd\x90\x57\x58\xe5\x59\x5b\x4a\x58\x0f\x1f\x04\x0b\x1b\x5c\x6b\xbd' | openssl enc -e -aes-192-ofb -K 545BB9BDBE2C419C9F576EC6D0C53E6875E6BF5A631F054D -iv 89797536DABD39F8BE988CBC79B6FF64 -nopad | hd var expected = new byte[] { 0x23, 0x79, 0x22, 0x9c, 0xa4, 0xfe, 0xc4, 0xf4, 0xd9, 0xc7, 0x4f, 0x63, 0x01, 0x54, 0xca, 0xe6, 0xe8, 0xe8, 0x8e, 0x1a, 0xa6, 0x25, 0xa5, 0x65, 0x0d, 0x5a, 0xe2, 0x9c, 0xd2, 0x7e, 0x06, 0x14, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1470,7 +1635,7 @@ public void AES_OFB_192_Length64() { 0xae, 0x39, 0xad, 0x74, 0x21, 0xea, 0x87, 0xa1, 0x18, 0xf6, 0x91, 0x50, 0xb7, 0x18, 0xe1, 0x8a, }; - + // echo -n -e '\x15\xbc\x46\x01\xed\x84\x87\x4a\xe0\x9c\x96\x34\x9d\x11\x5a\x34\x56\x6b\x33\x44\xb7\x0b\xc2\xe1\x1e\x76\x07\x37\x39\x82\xee\xbe\xe7\x5b\x44\xa7\xd9\x03\x60\x04\xf1\x2a\x55\x3e\x27\x04\x5a\xad\x3e\x57\x65\x0d\x83\xbb\xac\x0a\xf9\x64\xe2\x76\x7d\x50\x11\x5e' | openssl enc -e -aes-192-ofb -K ADD74D42CCB35A520B2E528CB584B91A1C59F9E11CE03B2C -iv AE39AD7421EA87A118F69150B718E18A -nopad | hd var expected = new byte[] { @@ -1479,14 +1644,18 @@ public void AES_OFB_192_Length64() 0xdf, 0x14, 0x8b, 0xe4, 0xa9, 0x74, 0x3b, 0xc2, 0x50, 0xc3, 0x23, 0xfe, 0xc6, 0x27, 0x0d, 0x74, 0xc7, 0xd0, 0x21, 0x0a, 0x40, 0x1d, 0x32, 0x32, 0x88, 0x86, 0x40, 0xa9, 0x4c, 0x59, 0x9c, 0xb4, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1505,20 +1674,24 @@ public void AES_OFB_256_Length16() { 0xb4, 0xc4, 0xb4, 0x96, 0x84, 0x4e, 0x84, 0x16, 0xe1, 0xe1, 0xad, 0xb7, 0xac, 0x95, 0x82, 0x41, }; - + // echo -n -e '\xfd\x13\x02\x54\x68\x20\x55\x75\xab\xc8\x5a\x23\x57\x30\x42\xcc' | openssl enc -e -aes-256-ofb -K 9C20949206E63121A6B173BFF2696303786EE408DEE6C38DE437C9588F644AE8 -iv B4C4B496844E8416E1E1ADB7AC958241 -nopad | hd var expected = new byte[] { 0x98, 0x85, 0x21, 0xeb, 0x42, 0x0c, 0x8b, 0xb3, 0xab, 0x64, 0x78, 0xe5, 0x67, 0xdd, 0xee, 0x36, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1538,21 +1711,25 @@ public void AES_OFB_256_Length32() { 0x24, 0x5f, 0x19, 0x95, 0xe5, 0x58, 0x89, 0x06, 0xef, 0x90, 0x57, 0xb6, 0x94, 0x02, 0x89, 0x32, }; - + // echo -n -e '\x65\x56\xab\xe9\x4c\x39\xed\xb5\x5c\x06\xae\xce\x1d\xd8\x91\x42\x67\x8b\x0b\x2e\xb5\xcd\x7f\x29\xe9\xcd\x26\xfd\x39\x0c\xe1\x4e' | openssl enc -e -aes-256-ofb -K B487F339B9609C7AD6FE397DA0B6F9094F6B50208A548C97D681FF7E12F07B50 -iv 245F1995E5588906EF9057B694028932 -nopad | hd var expected = new byte[] { 0x46, 0xe6, 0x18, 0x3c, 0x18, 0xf9, 0x6d, 0x4f, 0xc2, 0x75, 0x89, 0xea, 0x0d, 0xc9, 0x9a, 0x4c, 0x39, 0x54, 0x2e, 0x9f, 0x81, 0x49, 0xd3, 0x6b, 0x58, 0x20, 0x03, 0x21, 0x8d, 0x41, 0x9a, 0x42, }; - - var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } [TestMethod] @@ -1574,7 +1751,7 @@ public void AES_OFB_256_Length64() { 0x55, 0x74, 0x31, 0x68, 0x12, 0x10, 0x5d, 0xb4, 0xcd, 0x5e, 0x56, 0xb5, 0xa1, 0xb1, 0xa6, 0x5f, }; - + // echo -n -e '\xd4\x1d\xad\xce\xbc\xc0\xc4\x60\xfb\x5b\x62\x37\x61\x1d\x68\xe6\x82\xe8\x58\x41\x9d\x63\x23\xf7\xe1\x49\x31\xfa\xfd\xd5\x03\xd4\xf8\xcd\xaa\xf4\x43\xad\x93\x64\x9b\xb8\x9a\x89\xf6\x51\xa5\xd1\x28\x71\x34\xab\xa9\x47\x95\x70\xf9\xb5\xec\x72\x8f\xc9\x63\x26' | openssl enc -e -aes-256-ofb -K 578D3F947373A3D554F4A6E4C99A018FA460D18BA1582BB03739FA8DC121D5D1 -iv 5574316812105DB4CD5E56B5A1B1A65F -nopad | hd var expected = new byte[] { @@ -1583,14 +1760,18 @@ public void AES_OFB_256_Length64() 0x5e, 0xa5, 0x16, 0x3f, 0x9b, 0x18, 0x86, 0x4e, 0x94, 0xe2, 0x60, 0x70, 0x1f, 0x39, 0xa9, 0x4d, 0x7a, 0x3a, 0x43, 0xa6, 0x8f, 0x48, 0xfe, 0x6e, 0x64, 0xf6, 0x01, 0x0d, 0xdf, 0x9d, 0x34, 0xee, }; - - var actual = new AesCipher(key, (byte[]) iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - + + var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); CollectionAssert.AreEqual(expected, actual); - - var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - + + var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); CollectionAssert.AreEqual(input, decrypted); + + var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); + CollectionAssert.AreEqual(expected, actual2); + + var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); + CollectionAssert.AreEqual(input, decrypted2); } } } From 36cea676402f4ffa88660115f5da424e18611a36 Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Mon, 13 Nov 2023 19:20:20 +0100 Subject: [PATCH 12/19] remove ctor; revert tests; remove unused _iv member --- .../Cryptography/Ciphers/AesCipher.CtrImpl.cs | 2 - .../Cryptography/Ciphers/AesCipher.cs | 14 - .../Ciphers/AesCipherTest.Gen.cs.txt | 26 +- .../Cryptography/Ciphers/AesCipherTest.cs | 542 ++++++------------ 4 files changed, 192 insertions(+), 392 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs index f21fa81d9..6aae37160 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs @@ -12,7 +12,6 @@ public partial class AesCipher private sealed class CtrImpl : BlockCipher, IDisposable { private readonly Aes _aes; - private readonly byte[] _iv; private readonly uint[] _packedIV; private readonly ICryptoTransform _encryptor; @@ -28,7 +27,6 @@ public CtrImpl( _aes = aes; _encryptor = aes.CreateEncryptor(); - _iv = iv; _packedIV = GetPackedIV(iv); } diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 885bfba60..f33ecc0c8 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -37,20 +37,6 @@ public sealed partial class AesCipher : BlockCipher, IDisposable { private readonly BlockCipher _impl; - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is . - /// Keysize is not valid for this algorithm. - public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) - { - _impl = new BlockImpl(key, mode, padding); - } - /// /// Initializes a new instance of the class. /// diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt index 144047c0e..3b3bfa65d 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt @@ -4,20 +4,20 @@ // expected encrypted values, and also verifies those values against the .NET // BCL implementation as an extra validation before generating the tests. -Dictionary modes = new() +Dictionary modes = new() { - ["ecb"] = ("mode: null", "iv: null, AesCipherMode.ECB", CipherMode.ECB), - ["cbc"] = ("new CbcCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.CBC", CipherMode.CBC), - ["cfb"] = ("new CfbCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.CFB", CipherMode.CFB), - ["ctr"] = ("new CtrCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.CTR", null), - ["ofb"] = ("new OfbCipherMode((byte[])iv.Clone())", "(byte[])iv.Clone(), AesCipherMode.OFB", CipherMode.OFB), + ["ecb"] = ("iv: null, AesCipherMode.ECB", CipherMode.ECB), + ["cbc"] = ("(byte[])iv.Clone(), AesCipherMode.CBC", CipherMode.CBC), + ["cfb"] = ("(byte[])iv.Clone(), AesCipherMode.CFB", CipherMode.CFB), + ["ctr"] = ("(byte[])iv.Clone(), AesCipherMode.CTR", null), + ["ofb"] = ("(byte[])iv.Clone(), AesCipherMode.OFB", CipherMode.OFB), }; Random random = new(123); using IndentedTextWriter tw = new(Console.Out); -foreach ((string mode, (string ctor1Code, string ctor2Code, CipherMode? bclMode)) in modes) +foreach ((string mode, (string modeCode, CipherMode? bclMode)) in modes) { foreach (int keySize in new int[] { 128, 192, 256 }) { @@ -90,7 +90,8 @@ foreach ((string mode, (string ctor1Code, string ctor2Code, CipherMode? bclMode) tw.WriteLine($"// {openSslCmd} | hd"); // pipe to hexdump WriteBytes(expected); tw.WriteLine(); - tw.WriteLine($"var actual = new AesCipher(key, {ctor1Code}, padding: null).Encrypt(input);"); + tw.WriteLine($"var actual = new AesCipher(key, {modeCode}, pkcs7Padding: false).Encrypt(input);"); + tw.WriteLine(); tw.WriteLine($"CollectionAssert.AreEqual(expected, actual);"); if (bclMode is not null and not CipherMode.OFB) @@ -116,14 +117,9 @@ foreach ((string mode, (string ctor1Code, string ctor2Code, CipherMode? bclMode) } tw.WriteLine(); - tw.WriteLine($"var decrypted = new AesCipher(key, {ctor1Code}, padding: null).Decrypt(actual);"); - tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted);"); - tw.WriteLine(); - tw.WriteLine($"var actual2 = new AesCipher(key, {ctor2Code}, pkcs7Padding: false).Encrypt(input);"); - tw.WriteLine($"CollectionAssert.AreEqual(expected, actual2);"); + tw.WriteLine($"var decrypted = new AesCipher(key, {modeCode}, pkcs7Padding: false).Decrypt(actual);"); tw.WriteLine(); - tw.WriteLine($"var decrypted2 = new AesCipher(key, {ctor2Code}, pkcs7Padding: false).Decrypt(actual);"); - tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted2);"); + tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted);"); tw.Indent--; tw.WriteLine("}"); diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index 44df816e0..261db93c7 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -1,7 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; -using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Tests.Common; namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers @@ -103,17 +102,13 @@ public void AES_ECB_128_Length16() 0x9d, 0x55, 0x05, 0x4e, 0xe9, 0x50, 0xb5, 0x93, 0x50, 0x93, 0x69, 0x96, 0xa6, 0xdd, 0x1e, 0x15, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -136,17 +131,13 @@ public void AES_ECB_128_Length32() 0xe3, 0x1c, 0x95, 0x44, 0x49, 0x9e, 0x4a, 0x17, 0x0e, 0x64, 0xd3, 0xe8, 0x5c, 0xe6, 0x9f, 0x83, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -173,17 +164,13 @@ public void AES_ECB_128_Length64() 0x10, 0xe3, 0xe0, 0x30, 0xd3, 0x0e, 0xe3, 0x94, 0xd8, 0xf5, 0xb1, 0x44, 0xf8, 0x36, 0xfd, 0x0b, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -205,17 +192,13 @@ public void AES_ECB_192_Length16() 0x1c, 0xd3, 0x91, 0xd8, 0xc3, 0xe0, 0x4d, 0x8e, 0x9e, 0x5c, 0xaf, 0xcc, 0x55, 0x65, 0x54, 0xb7, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -239,17 +222,13 @@ public void AES_ECB_192_Length32() 0x87, 0xe2, 0x91, 0x40, 0x31, 0x26, 0x67, 0xf6, 0xf7, 0x86, 0x73, 0x89, 0x0d, 0x35, 0x22, 0x6c, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -277,17 +256,13 @@ public void AES_ECB_192_Length64() 0xcd, 0x20, 0xab, 0xbc, 0x59, 0xb2, 0xa5, 0x80, 0xf5, 0x8e, 0x53, 0xda, 0xb1, 0x39, 0x8f, 0xbc, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -309,17 +284,13 @@ public void AES_ECB_256_Length16() 0x6a, 0xd2, 0x73, 0x2b, 0x05, 0x2e, 0xdd, 0x74, 0x0c, 0x37, 0xf2, 0xcf, 0x8a, 0xef, 0x57, 0x8a, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -343,17 +314,13 @@ public void AES_ECB_256_Length32() 0x59, 0x0b, 0x9c, 0x7a, 0xf2, 0xb6, 0x34, 0x0d, 0xc9, 0xdd, 0x15, 0x6e, 0x75, 0xe7, 0xc6, 0x82, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -381,17 +348,13 @@ public void AES_ECB_256_Length64() 0x63, 0xd2, 0x7d, 0x7a, 0xfc, 0xdb, 0x11, 0x08, 0x70, 0x73, 0x61, 0xe0, 0xfb, 0x93, 0xa6, 0xf9, }; - var actual = new AesCipher(key, mode: null, padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, mode: null, padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, iv: null, AesCipherMode.ECB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -416,17 +379,13 @@ public void AES_CBC_128_Length16() 0x49, 0x0e, 0xa9, 0x6f, 0x55, 0xb3, 0x57, 0xdf, 0x7c, 0x18, 0x77, 0x0c, 0xca, 0x46, 0x0d, 0x83, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -453,17 +412,13 @@ public void AES_CBC_128_Length32() 0x2d, 0x87, 0x42, 0x20, 0xf1, 0x0b, 0x78, 0x96, 0xd5, 0x7c, 0xeb, 0xa2, 0x7f, 0x4b, 0x5a, 0xff, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -494,17 +449,13 @@ public void AES_CBC_128_Length64() 0x3d, 0x61, 0x9e, 0x0d, 0x54, 0x7f, 0xe1, 0xc4, 0x78, 0xf2, 0x04, 0x00, 0x68, 0xa9, 0x9b, 0x32, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -530,17 +481,13 @@ public void AES_CBC_192_Length16() 0xe1, 0x2f, 0x71, 0xad, 0x59, 0xae, 0xa7, 0xe3, 0xd3, 0x23, 0x43, 0x81, 0x31, 0xc2, 0xe5, 0xd9, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -568,17 +515,13 @@ public void AES_CBC_192_Length32() 0x07, 0xf2, 0x98, 0x41, 0xbb, 0x58, 0x3d, 0xe5, 0xcf, 0x56, 0xf5, 0x4b, 0x33, 0xf7, 0xa0, 0x9a, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -610,17 +553,13 @@ public void AES_CBC_192_Length64() 0xda, 0xba, 0xde, 0xb2, 0x7d, 0xbc, 0x71, 0xf8, 0x9b, 0xaa, 0x93, 0x52, 0xf4, 0x26, 0x3c, 0x6f, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -646,17 +585,13 @@ public void AES_CBC_256_Length16() 0xe7, 0xa5, 0x53, 0xd7, 0x28, 0x4c, 0x16, 0x4e, 0xfc, 0xa2, 0xa8, 0x86, 0xfc, 0xcb, 0x71, 0x61, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -684,17 +619,13 @@ public void AES_CBC_256_Length32() 0x6a, 0xfb, 0x4a, 0x8b, 0xc8, 0x25, 0x87, 0x5c, 0x9b, 0x47, 0xf5, 0x3f, 0x42, 0xf5, 0xc6, 0x08, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -726,17 +657,13 @@ public void AES_CBC_256_Length64() 0x08, 0x19, 0x66, 0x47, 0xe7, 0xd9, 0x1d, 0x1c, 0x42, 0xdc, 0x97, 0x9c, 0xf0, 0x9a, 0x14, 0x34, }; - var actual = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CbcCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -761,17 +688,13 @@ public void AES_CFB_128_Length16() 0x76, 0xd2, 0x2b, 0x69, 0xa6, 0xdf, 0x3b, 0x4d, 0x4a, 0x52, 0x8a, 0x7a, 0x54, 0x9d, 0xbe, 0x55, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -798,17 +721,13 @@ public void AES_CFB_128_Length32() 0x18, 0xc5, 0xf7, 0x41, 0x78, 0x5f, 0x38, 0x6b, 0x4d, 0x04, 0x00, 0x3b, 0x61, 0x8c, 0xaf, 0xe7, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -839,17 +758,13 @@ public void AES_CFB_128_Length64() 0xf7, 0x49, 0xbc, 0xbf, 0xcb, 0x5c, 0xfa, 0x12, 0xcb, 0xcc, 0x38, 0x71, 0x68, 0xd6, 0xe9, 0x64, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -875,17 +790,13 @@ public void AES_CFB_192_Length16() 0x4f, 0x9b, 0xdf, 0x72, 0x2d, 0x10, 0x1b, 0xb9, 0xa1, 0xe1, 0x06, 0xba, 0xbc, 0xc5, 0xfe, 0x13, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -913,17 +824,13 @@ public void AES_CFB_192_Length32() 0x72, 0xcf, 0x57, 0x7f, 0xf9, 0x5d, 0xfe, 0xb1, 0x36, 0x9a, 0x1d, 0x02, 0x0d, 0x4b, 0x8f, 0x35, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -955,17 +862,13 @@ public void AES_CFB_192_Length64() 0x69, 0x90, 0x2a, 0xf9, 0xf4, 0xe8, 0xcc, 0xa5, 0x2b, 0xdd, 0x9c, 0xbc, 0x44, 0xcd, 0x1e, 0x5b, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -991,17 +894,13 @@ public void AES_CFB_256_Length16() 0xf0, 0xfa, 0x95, 0x5c, 0xfc, 0x3f, 0xbe, 0xe5, 0x4b, 0x55, 0x57, 0xad, 0x93, 0x63, 0x36, 0x07, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1029,17 +928,13 @@ public void AES_CFB_256_Length32() 0xc2, 0xc1, 0x54, 0x9c, 0xfd, 0xf9, 0x43, 0xd0, 0xdc, 0xa7, 0x20, 0x68, 0x3e, 0xc3, 0x8f, 0x3c, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1071,17 +966,13 @@ public void AES_CFB_256_Length64() 0xf6, 0xd4, 0x06, 0xef, 0x04, 0xf1, 0xe5, 0x53, 0x54, 0xd5, 0x80, 0xc2, 0x96, 0x6b, 0xc7, 0x07, }; - var actual = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1106,17 +997,13 @@ public void AES_CTR_128_Length16() 0xe4, 0x03, 0x8f, 0x2a, 0xdd, 0x9d, 0xf6, 0x87, 0xf6, 0x29, 0xee, 0x27, 0x4c, 0xf3, 0xba, 0x82, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1143,17 +1030,13 @@ public void AES_CTR_128_Length32() 0x9c, 0xb2, 0x30, 0x94, 0xdc, 0x88, 0xfa, 0x39, 0x05, 0x0c, 0x26, 0x25, 0x28, 0x6a, 0x9b, 0x4e, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1184,17 +1067,13 @@ public void AES_CTR_128_Length64() 0xec, 0x47, 0x81, 0x82, 0x89, 0x24, 0x76, 0xe2, 0x20, 0x6a, 0x99, 0xe2, 0xa7, 0x5a, 0xb0, 0x40, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1220,17 +1099,13 @@ public void AES_CTR_192_Length16() 0xc4, 0x4e, 0x81, 0x32, 0xe6, 0x6d, 0x0a, 0x78, 0x49, 0xe5, 0x64, 0x6c, 0xe6, 0xc2, 0x91, 0xc9, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1258,17 +1133,13 @@ public void AES_CTR_192_Length32() 0x85, 0xcd, 0x88, 0xa8, 0x25, 0xc8, 0xbd, 0xf8, 0xc3, 0xa9, 0x74, 0x36, 0x82, 0x19, 0xfc, 0xb3, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1300,17 +1171,13 @@ public void AES_CTR_192_Length64() 0x2d, 0x26, 0x4a, 0x22, 0x97, 0x7a, 0x94, 0x5e, 0xb0, 0xb2, 0x3d, 0x42, 0x2b, 0x4a, 0x5e, 0x5d, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1336,17 +1203,13 @@ public void AES_CTR_256_Length16() 0xa6, 0x46, 0x19, 0x9d, 0x3e, 0xa5, 0x53, 0xc8, 0xd9, 0xb3, 0x46, 0xbc, 0x0b, 0x3e, 0x47, 0xf4, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1374,17 +1237,13 @@ public void AES_CTR_256_Length32() 0x1d, 0x7e, 0xe7, 0xe7, 0xa0, 0xae, 0x31, 0x9b, 0xb3, 0x21, 0xb8, 0x0c, 0x47, 0x3e, 0xaf, 0xdd, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1416,17 +1275,13 @@ public void AES_CTR_256_Length64() 0x92, 0xb0, 0x7b, 0x7a, 0x77, 0x65, 0xf0, 0xcc, 0xbd, 0xe4, 0x41, 0xea, 0x9e, 0xfd, 0xdf, 0x41, }; - var actual = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new CtrCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1451,17 +1306,13 @@ public void AES_OFB_128_Length16() 0xb0, 0x65, 0x77, 0x03, 0xb4, 0x54, 0x82, 0x92, 0x05, 0x82, 0x93, 0x1f, 0x8d, 0x7b, 0xb6, 0xf0, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1488,17 +1339,13 @@ public void AES_OFB_128_Length32() 0x11, 0xb9, 0xd6, 0x67, 0x6c, 0xe7, 0xaa, 0x09, 0x93, 0xe3, 0x5f, 0xed, 0x38, 0x46, 0x37, 0xd2, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1529,17 +1376,13 @@ public void AES_OFB_128_Length64() 0xda, 0xad, 0x1b, 0xa5, 0x20, 0x67, 0xd2, 0xa6, 0x18, 0x26, 0x30, 0x43, 0x2f, 0xa2, 0x66, 0x0b, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1565,17 +1408,13 @@ public void AES_OFB_192_Length16() 0x79, 0x41, 0x28, 0xc9, 0x3b, 0x89, 0x6f, 0x69, 0x92, 0xb0, 0x3e, 0x38, 0x11, 0x2c, 0xe5, 0xd8, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1603,17 +1442,13 @@ public void AES_OFB_192_Length32() 0xe8, 0xe8, 0x8e, 0x1a, 0xa6, 0x25, 0xa5, 0x65, 0x0d, 0x5a, 0xe2, 0x9c, 0xd2, 0x7e, 0x06, 0x14, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1645,17 +1480,13 @@ public void AES_OFB_192_Length64() 0xc7, 0xd0, 0x21, 0x0a, 0x40, 0x1d, 0x32, 0x32, 0x88, 0x86, 0x40, 0xa9, 0x4c, 0x59, 0x9c, 0xb4, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1681,17 +1512,13 @@ public void AES_OFB_256_Length16() 0x98, 0x85, 0x21, 0xeb, 0x42, 0x0c, 0x8b, 0xb3, 0xab, 0x64, 0x78, 0xe5, 0x67, 0xdd, 0xee, 0x36, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1719,17 +1546,13 @@ public void AES_OFB_256_Length32() 0x39, 0x54, 0x2e, 0x9f, 0x81, 0x49, 0xd3, 0x6b, 0x58, 0x20, 0x03, 0x21, 0x8d, 0x41, 0x9a, 0x42, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } [TestMethod] @@ -1761,17 +1584,14 @@ public void AES_OFB_256_Length64() 0x7a, 0x3a, 0x43, 0xa6, 0x8f, 0x48, 0xfe, 0x6e, 0x64, 0xf6, 0x01, 0x0d, 0xdf, 0x9d, 0x34, 0xee, }; - var actual = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Encrypt(input); - CollectionAssert.AreEqual(expected, actual); + var actual = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - var decrypted = new AesCipher(key, new OfbCipherMode((byte[])iv.Clone()), padding: null).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted); + CollectionAssert.AreEqual(expected, actual); - var actual2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Encrypt(input); - CollectionAssert.AreEqual(expected, actual2); + var decrypted = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - var decrypted2 = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.OFB, pkcs7Padding: false).Decrypt(actual); - CollectionAssert.AreEqual(input, decrypted2); + CollectionAssert.AreEqual(input, decrypted); } + } } From 84df6e4061620c0f9faddaf78912657aa98cf0a0 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Mon, 20 Nov 2023 17:56:15 +0100 Subject: [PATCH 13/19] Reorder Encryption cipher preference list --- src/Renci.SshNet/ConnectionInfo.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 35012646d..6ffbf5979 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -360,13 +360,13 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy Encryptions = new Dictionary { - { "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) }, - { "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, - { "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, - { "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, { "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) }, { "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) }, { "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) }, + { "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, + { "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, + { "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) }, + { "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) }, { "blowfish-cbc", new CipherInfo(128, (key, iv) => new BlowfishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish192-cbc", new CipherInfo(192, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, From c056fbcf7db4391280d9b7fd2d189df26fbb35da Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Mon, 20 Nov 2023 18:27:45 +0100 Subject: [PATCH 14/19] Remove redundant AES tests Add tests for stream cipher state preservation --- .../Cryptography/Ciphers/AesCipherTest.cs | 78 ++++++++++++++++--- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index 261db93c7..64de72d18 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -1,4 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; + +using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Tests.Common; @@ -12,17 +14,75 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography.Ciphers public class AesCipherTest : TestBase { [TestMethod] - public void Encrypt_Input_128_CBC() + public void AES_CTR_Encrypt_Should_Preserve_Cipher_Stream_State() { - var input = new byte[] { 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x73, 0x68, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x61, 0x75, 0x74, 0x68, 0x30, 0x9e, 0xe0, 0x9c, 0x12, 0xee, 0x3a, 0x30, 0x03, 0x52, 0x1c, 0x1a, 0xe7, 0x3e, 0x0b, 0x9a, 0xcf, 0x9a, 0x57, 0x42, 0x0b, 0x4f, 0x4a, 0x15, 0xa0, 0xf5 }; - var key = new byte[] { 0xe4, 0x94, 0xf9, 0xb1, 0x00, 0x4f, 0x16, 0x2a, 0x80, 0x11, 0xea, 0x73, 0x0d, 0xb9, 0xbf, 0x64 }; - var iv = new byte[] { 0x74, 0x8b, 0x4f, 0xe6, 0xc1, 0x29, 0xb3, 0x54, 0xec, 0x77, 0x92, 0xf3, 0x15, 0xa0, 0x41, 0xa8 }; - var expected = new byte[] { 0x19, 0x7f, 0x80, 0xd8, 0xc9, 0x89, 0xc4, 0xa7, 0xc6, 0xc6, 0x3f, 0x9f, 0x1e, 0x00, 0x1f, 0x72, 0xa7, 0x5e, 0xde, 0x40, 0x88, 0xa2, 0x72, 0xf2, 0xed, 0x3f, 0x81, 0x45, 0xb6, 0xbd, 0x45, 0x87, 0x15, 0xa5, 0x10, 0x92, 0x4a, 0x37, 0x9e, 0xa9, 0x80, 0x1c, 0x14, 0x83, 0xa3, 0x39, 0x45, 0x28 }; - var testCipher = new AesCipher(key, (byte[])iv.Clone(), AesCipherMode.CBC, pkcs7Padding: false); + var input = new byte[] + { + 0x9b, 0x6e, 0x1d, 0xf8, 0x07, 0xf9, 0x55, 0xd4, 0xd7, 0x1a, 0xce, 0xca, 0xa8, 0x31, 0x29, 0x0f, + 0x63, 0x4d, 0x52, 0x71, 0xa5, 0x0c, 0x96, 0x08, 0xd6, 0xc5, 0x14, 0xa0, 0xc8, 0x29, 0xb1, 0xd5, + 0x40, 0x2c, 0xe5, 0xa9, 0xb4, 0x31, 0xa9, 0xa8, 0x76, 0xa5, 0x1e, 0x7a, 0xc8, 0x09, 0x32, 0x39, + 0xbc, 0x89, 0x7a, 0x22, 0x42, 0x2c, 0xba, 0x8e, 0xd7, 0x15, 0x22, 0x41, 0xe4, 0xb5, 0x0b, 0xad, + }; + var key = new byte[] + { + 0x69, 0xf9, 0x8a, 0x7c, 0x4b, 0x80, 0x5b, 0x31, 0xa4, 0xaa, 0xfa, 0xff, 0xed, 0x1c, 0x3f, 0xcc, + }; + var iv = new byte[] + { + 0x92, 0xdb, 0xe4, 0x3e, 0xaf, 0x8f, 0x92, 0x13, 0x71, 0x56, 0xd1, 0x9f, 0x0f, 0x68, 0xc3, 0xc1, + }; - var actual = testCipher.Encrypt(input); + // echo -n -e '\x9b\x6e\x1d\xf8\x07\xf9\x55\xd4\xd7\x1a\xce\xca\xa8\x31\x29\x0f\x63\x4d\x52\x71\xa5\x0c\x96\x08\xd6\xc5\x14\xa0\xc8\x29\xb1\xd5\x40\x2c\xe5\xa9\xb4\x31\xa9\xa8\x76\xa5\x1e\x7a\xc8\x09\x32\x39\xbc\x89\x7a\x22\x42\x2c\xba\x8e\xd7\x15\x22\x41\xe4\xb5\x0b\xad' | openssl enc -e -aes-128-ctr -K 69F98A7C4B805B31A4AAFAFFED1C3FCC -iv 92DBE43EAF8F92137156D19F0F68C3C1 -nopad | hd + var expected = new byte[] + { + 0xc0, 0x69, 0x4b, 0xdb, 0xb2, 0x0c, 0x22, 0x82, 0xf0, 0x85, 0x77, 0x3e, 0xd9, 0x5a, 0xe7, 0x9e, + 0xb0, 0x34, 0xe8, 0x95, 0x8e, 0x93, 0x0a, 0xcf, 0xa4, 0x26, 0xd3, 0x80, 0x12, 0xcc, 0x06, 0x38, + 0x1d, 0x18, 0x55, 0xfc, 0x56, 0x59, 0xaf, 0x0b, 0x2b, 0x80, 0x87, 0x0c, 0x87, 0x45, 0xb0, 0xe2, + 0xec, 0x47, 0x81, 0x82, 0x89, 0x24, 0x76, 0xe2, 0x20, 0x6a, 0x99, 0xe2, 0xa7, 0x5a, 0xb0, 0x40, + }; - Assert.IsTrue(actual.IsEqualTo(expected)); + var cipher = new AesCipher(key, (byte[]) iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false); + var actual1 = cipher.Encrypt(input.Take(32)); + var actual2 = cipher.Encrypt(input.Take(32, 32)); + + CollectionAssert.AreEqual(expected.Take(32), actual1); + CollectionAssert.AreEqual(expected.Take(32, 32), actual2); + } + + [TestMethod] + public void AES_CTR_Decrypt_Should_Preserve_Cipher_Stream_State() + { + var input = new byte[] + { + 0xc0, 0x69, 0x4b, 0xdb, 0xb2, 0x0c, 0x22, 0x82, 0xf0, 0x85, 0x77, 0x3e, 0xd9, 0x5a, 0xe7, 0x9e, + 0xb0, 0x34, 0xe8, 0x95, 0x8e, 0x93, 0x0a, 0xcf, 0xa4, 0x26, 0xd3, 0x80, 0x12, 0xcc, 0x06, 0x38, + 0x1d, 0x18, 0x55, 0xfc, 0x56, 0x59, 0xaf, 0x0b, 0x2b, 0x80, 0x87, 0x0c, 0x87, 0x45, 0xb0, 0xe2, + 0xec, 0x47, 0x81, 0x82, 0x89, 0x24, 0x76, 0xe2, 0x20, 0x6a, 0x99, 0xe2, 0xa7, 0x5a, 0xb0, 0x40, + }; + var key = new byte[] + { + 0x69, 0xf9, 0x8a, 0x7c, 0x4b, 0x80, 0x5b, 0x31, 0xa4, 0xaa, 0xfa, 0xff, 0xed, 0x1c, 0x3f, 0xcc, + }; + var iv = new byte[] + { + 0x92, 0xdb, 0xe4, 0x3e, 0xaf, 0x8f, 0x92, 0x13, 0x71, 0x56, 0xd1, 0x9f, 0x0f, 0x68, 0xc3, 0xc1, + }; + + // echo -n -e '\x9b\x6e\x1d\xf8\x07\xf9\x55\xd4\xd7\x1a\xce\xca\xa8\x31\x29\x0f\x63\x4d\x52\x71\xa5\x0c\x96\x08\xd6\xc5\x14\xa0\xc8\x29\xb1\xd5\x40\x2c\xe5\xa9\xb4\x31\xa9\xa8\x76\xa5\x1e\x7a\xc8\x09\x32\x39\xbc\x89\x7a\x22\x42\x2c\xba\x8e\xd7\x15\x22\x41\xe4\xb5\x0b\xad' | openssl enc -e -aes-128-ctr -K 69F98A7C4B805B31A4AAFAFFED1C3FCC -iv 92DBE43EAF8F92137156D19F0F68C3C1 -nopad | hd + var expected = new byte[] + { + 0x9b, 0x6e, 0x1d, 0xf8, 0x07, 0xf9, 0x55, 0xd4, 0xd7, 0x1a, 0xce, 0xca, 0xa8, 0x31, 0x29, 0x0f, + 0x63, 0x4d, 0x52, 0x71, 0xa5, 0x0c, 0x96, 0x08, 0xd6, 0xc5, 0x14, 0xa0, 0xc8, 0x29, 0xb1, 0xd5, + 0x40, 0x2c, 0xe5, 0xa9, 0xb4, 0x31, 0xa9, 0xa8, 0x76, 0xa5, 0x1e, 0x7a, 0xc8, 0x09, 0x32, 0x39, + 0xbc, 0x89, 0x7a, 0x22, 0x42, 0x2c, 0xba, 0x8e, 0xd7, 0x15, 0x22, 0x41, 0xe4, 0xb5, 0x0b, 0xad, + }; + + var cipher = new AesCipher(key, (byte[]) iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false); + var actual1 = cipher.Decrypt(input.Take(32)); + var actual2 = cipher.Decrypt(input.Take(32, 32)); + + CollectionAssert.AreEqual(expected.Take(32), actual1); + CollectionAssert.AreEqual(expected.Take(32, 32), actual2); } [TestMethod] From 1902ed7e9ad3e22a8628f79f277fdc4ad0aa13d7 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Mon, 20 Nov 2023 20:55:24 +0100 Subject: [PATCH 15/19] Refactor ArrayXOR() --- .../Cryptography/Ciphers/AesCipher.CtrImpl.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs index 6aae37160..e0894ad3d 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs @@ -108,23 +108,18 @@ private void CTRCreateCounterArray(byte[] buffer) // XOR 2 arrays using Vector private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) { - var vectorSize = Vector.Count; - for (var loopOffset = 0; length > 0; length -= vectorSize) + var i = 0; + + var oneVectorFromEnd = length - Vector.Count; + for (; i <= oneVectorFromEnd; i += Vector.Count) { - if (length >= vectorSize) - { - var v = new Vector(buffer, loopOffset) ^ new Vector(data, offset + loopOffset); - v.CopyTo(buffer, loopOffset); - loopOffset += vectorSize; - } - else - { - for (var i = 0; i < length; i++) - { - buffer[loopOffset] ^= data[offset + loopOffset]; - loopOffset++; - } - } + var v = new Vector(buffer, i) ^ new Vector(data, offset + i); + v.CopyTo(buffer, i); + } + + for (; i < length; i++) + { + buffer[i] ^= data[offset + i]; } } From 6529a6383a24023fb900f9d922d7591cf3bc66b1 Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Tue, 21 Nov 2023 20:52:59 +0100 Subject: [PATCH 16/19] Add test for IV overflow --- .../Cryptography/Ciphers/AesCipher.CtrImpl.cs | 4 +- .../Cryptography/Ciphers/AesCipherTest.cs | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs index e0894ad3d..b472015fa 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs @@ -95,7 +95,7 @@ private void CTRCreateCounterArray(byte[] buffer) } else { - uint j = 3; + var j = 3; do { _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); @@ -145,7 +145,7 @@ private void CTRCreateCounterArray(byte[] buffer) } else { - uint j = 3; + var j = 3; do { _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs index 64de72d18..e1a0e9744 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.cs @@ -85,6 +85,45 @@ public void AES_CTR_Decrypt_Should_Preserve_Cipher_Stream_State() CollectionAssert.AreEqual(expected.Take(32, 32), actual2); } + [TestMethod] + public void AES_CTR_IV_Overflow() + { + var input = new byte[] + { + 0x03, 0xe1, 0xe1, 0xaa, 0xa5, 0xbc, 0xa1, 0x9f, 0xba, 0x8c, 0x42, 0x05, 0x8b, 0x4a, 0xbf, 0x28, + 0x96, 0x39, 0xec, 0x0d, 0xfc, 0x2d, 0xb2, 0x7c, 0xe9, 0x74, 0x8e, 0x5f, 0xb9, 0xf3, 0x99, 0xce, + 0xe1, 0x1a, 0x5c, 0x51, 0xa3, 0x1d, 0xd7, 0x1b, 0x15, 0x8c, 0xad, 0xa6, 0xaf, 0x63, 0x0d, 0x8c, + 0x1a, 0xf1, 0x3a, 0x35, 0x8c, 0xca, 0x3f, 0xd6, 0x2f, 0x65, 0xc1, 0x31, 0x2d, 0x41, 0xe5, 0xc7, + }; + var key = new byte[] + { + 0xf3, 0x74, 0x23, 0x71, 0xed, 0x6d, 0x84, 0x79, 0x61, 0xd0, 0xf8, 0x6f, 0x7f, 0x0c, 0xcc, 0x86, + 0x67, 0x02, 0x45, 0xc8, 0xb8, 0x64, 0x42, 0x17, 0xda, 0x85, 0x21, 0x3e, 0x5c, 0xa6, 0xee, 0xd4, + }; + var iv = new byte[] + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + // echo -n -e '\x03\xe1\xe1\xaa\xa5\xbc\xa1\x9f\xba\x8c\x42\x05\x8b\x4a\xbf\x28\x96\x39\xec\x0d\xfc\x2d\xb2\x7c\xe9\x74\x8e\x5f\xb9\xf3\x99\xce\xe1\x1a\x5c\x51\xa3\x1d\xd7\x1b\x15\x8c\xad\xa6\xaf\x63\x0d\x8c\x1a\xf1\x3a\x35\x8c\xca\x3f\xd6\x2f\x65\xc1\x31\x2d\x41\xe5\xc7' | openssl enc -e -aes-256-ctr -K F3742371ED6D847961D0F86F7F0CCC86670245C8B8644217DA85213E5CA6EED4 -iv FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -nopad | hd + var expected = new byte[] + { + 0x4e, 0xfa, 0xa2, 0x01, 0x5c, 0x5f, 0x3a, 0x55, 0x33, 0x53, 0x51, 0xda, 0xdf, 0xa5, 0xab, 0x7e, + 0x57, 0x93, 0x2d, 0xe6, 0x99, 0x4a, 0x58, 0x56, 0xcb, 0x19, 0x9f, 0x88, 0xe1, 0xa8, 0x7b, 0xcd, + 0x3d, 0x8e, 0xdd, 0x10, 0xc8, 0xb3, 0x60, 0xb0, 0x2b, 0xaf, 0x92, 0xfe, 0x39, 0x47, 0x35, 0xcc, + 0xfd, 0x34, 0xc5, 0x81, 0xfa, 0xb9, 0xe3, 0xc4, 0x10, 0xed, 0x06, 0x6e, 0x91, 0x5e, 0xfc, 0x47, + }; + + var actual = new AesCipher(key, (byte[]) iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Encrypt(input); + + CollectionAssert.AreEqual(expected, actual); + + var decrypted = new AesCipher(key, (byte[]) iv.Clone(), AesCipherMode.CTR, pkcs7Padding: false).Decrypt(actual); + + CollectionAssert.AreEqual(input, decrypted); + } + + [TestMethod] public void Encrypt_InputAndOffsetAndLength_128_CBC() { From 2cedd261172fc1bf86cd343142aed0b179bac0a0 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Tue, 21 Nov 2023 22:09:06 +0100 Subject: [PATCH 17/19] Performance bump for AES CTR (thanks @robhague) --- .../Cryptography/Ciphers/AesCipher.CtrImpl.cs | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs index b472015fa..93954c9f1 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs @@ -1,7 +1,7 @@ using System; #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER +using System.Buffers.Binary; using System.Numerics; -using System.Runtime.InteropServices; #endif using System.Security.Cryptography; @@ -12,9 +12,17 @@ public partial class AesCipher private sealed class CtrImpl : BlockCipher, IDisposable { private readonly Aes _aes; - private readonly uint[] _packedIV; + private readonly ICryptoTransform _encryptor; +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER + private ulong _ivUpper; // The upper 64 bits of the IV + private ulong _ivLower; // The lower 64 bits of the IV +#else + // The same on netfx + private readonly uint[] _packedIV; +#endif + public CtrImpl( byte[] key, byte[] iv) @@ -27,7 +35,12 @@ public CtrImpl( _aes = aes; _encryptor = aes.CreateEncryptor(); +#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER + _ivLower = BinaryPrimitives.ReadUInt64BigEndian(iv.AsSpan(8)); + _ivUpper = BinaryPrimitives.ReadUInt64BigEndian(iv); +#else _packedIV = GetPackedIV(iv); +#endif } public override byte[] Encrypt(byte[] input, int offset, int length) @@ -74,20 +87,18 @@ private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER - // creates the Counter array filled with incrementing copies of IV + // creates the Counter array filled with incrementing copies of IV private void CTRCreateCounterArray(byte[] buffer) { - var counter = MemoryMarshal.Cast(buffer.AsSpan()); - - // fill array with IV, increment by 1 for each copy - var len = counter.Length; - for (var i = 0; i < len; i += 4) + for (var i = 0; i < buffer.Length; i += 16) { - counter[i] = _packedIV[0]; - counter[i + 1] = _packedIV[1]; - counter[i + 2] = _packedIV[2]; - counter[i + 3] = _packedIV[3]; + BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i + 8), _ivLower); + BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i), _ivUpper); +<<<<<<< HEAD + _ivLower += 1; + _ivUpper += (_ivLower == 0) ? 1UL : 0UL; +======= // increment IV (little endian) if (_packedIV[3] < 0xFF000000u) { @@ -102,6 +113,7 @@ private void CTRCreateCounterArray(byte[] buffer) } while (_packedIV[j] == 0 && --j >= 0); } +>>>>>>> 6529a6383a24023fb900f9d922d7591cf3bc66b1 } } @@ -185,8 +197,6 @@ private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) Buffer.BlockCopy(bufferwords, 0, buffer, 0, length); } -#endif - // pack the IV into an array of uint[4] private static uint[] GetPackedIV(byte[] iv) { @@ -204,6 +214,7 @@ private static uint SwapEndianness(uint x) x = (x >> 16) | (x << 16); return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8); } +#endif private void Dispose(bool disposing) { From 15de75e8d33989a8cad7ff75066c9d293bf1dd43 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Tue, 21 Nov 2023 22:55:43 +0100 Subject: [PATCH 18/19] fix merge conflict --- .../Cryptography/Ciphers/AesCipher.CtrImpl.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs index 93954c9f1..dacaf4a79 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs @@ -95,25 +95,8 @@ private void CTRCreateCounterArray(byte[] buffer) BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i + 8), _ivLower); BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i), _ivUpper); -<<<<<<< HEAD _ivLower += 1; _ivUpper += (_ivLower == 0) ? 1UL : 0UL; -======= - // increment IV (little endian) - if (_packedIV[3] < 0xFF000000u) - { - _packedIV[3] += 0x01000000u; - } - else - { - var j = 3; - do - { - _packedIV[j] = SwapEndianness(SwapEndianness(_packedIV[j]) + 1); - } - while (_packedIV[j] == 0 && --j >= 0); - } ->>>>>>> 6529a6383a24023fb900f9d922d7591cf3bc66b1 } } From 554c2d6ad10d9a91d50647aa201967e21ed5ac78 Mon Sep 17 00:00:00 2001 From: Pedro Fonseca Date: Tue, 28 Nov 2023 12:06:50 +0100 Subject: [PATCH 19/19] Move AesCipherMode enum to its own file --- .../Cryptography/Ciphers/AesCipher.cs | 24 ----------------- .../Cryptography/Ciphers/AesCipherMode.cs | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherMode.cs diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index 6ff726d92..447e1aee3 100644 --- a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -6,30 +6,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers { - /// - /// Custom AES Cipher Mode, follows System.Security.Cryptography.CipherMode. - /// - public enum AesCipherMode - { - /// CBC Mode. - CBC = 1, - - /// ECB Mode. - ECB = 2, - - /// OFB Mode. - OFB = 3, - - /// CFB Mode. - CFB = 4, - - /// CTS Mode. - CTS = 5, - - /// CTR Mode. - CTR = 6 - } - /// /// AES cipher implementation. /// diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherMode.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherMode.cs new file mode 100644 index 000000000..51ebfdd14 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherMode.cs @@ -0,0 +1,26 @@ +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Custom AES Cipher Mode, follows System.Security.Cryptography.CipherMode. + /// + public enum AesCipherMode + { + /// CBC Mode. + CBC = 1, + + /// ECB Mode. + ECB = 2, + + /// OFB Mode. + OFB = 3, + + /// CFB Mode. + CFB = 4, + + /// CTS Mode. + CTS = 5, + + /// CTR Mode. + CTR = 6 + } +}