using System; namespace Renci.SshNet.Security.Cryptography.Ciphers { /// /// Implements 3DES cipher algorithm. /// public sealed class TripleDesCipher : DesCipher { private int[] _encryptionKey1; private int[] _encryptionKey2; private int[] _encryptionKey3; private int[] _decryptionKey1; private int[] _decryptionKey2; private int[] _decryptionKey3; /// /// Initializes a new instance of the class. /// /// The key. /// The mode. /// The padding. /// is null. public TripleDesCipher(byte[] key, CipherMode mode, CipherPadding padding) : base(key, mode, padding) { } /// /// 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 ((inputOffset + BlockSize) > inputBuffer.Length) { throw new IndexOutOfRangeException("input buffer too short"); } if ((outputOffset + BlockSize) > outputBuffer.Length) { throw new IndexOutOfRangeException("output buffer too short"); } if (_encryptionKey1 is null || _encryptionKey2 is null || _encryptionKey3 is null) { var part1 = new byte[8]; var part2 = new byte[8]; Buffer.BlockCopy(Key, 0, part1, 0, 8); Buffer.BlockCopy(Key, 8, part2, 0, 8); _encryptionKey1 = GenerateWorkingKey(encrypting: true, part1); _encryptionKey2 = GenerateWorkingKey(encrypting: false, part2); if (Key.Length == 24) { var part3 = new byte[8]; Buffer.BlockCopy(Key, 16, part3, 0, 8); _encryptionKey3 = GenerateWorkingKey(encrypting: true, part3); } else { _encryptionKey3 = _encryptionKey1; } } var temp = new byte[BlockSize]; DesFunc(_encryptionKey1, inputBuffer, inputOffset, temp, 0); DesFunc(_encryptionKey2, temp, 0, temp, 0); DesFunc(_encryptionKey3, temp, 0, outputBuffer, outputOffset); 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 ((inputOffset + BlockSize) > inputBuffer.Length) { throw new IndexOutOfRangeException("input buffer too short"); } if ((outputOffset + BlockSize) > outputBuffer.Length) { throw new IndexOutOfRangeException("output buffer too short"); } if (_decryptionKey1 is null || _decryptionKey2 is null || _decryptionKey3 is null) { var part1 = new byte[8]; var part2 = new byte[8]; Buffer.BlockCopy(Key, 0, part1, 0, 8); Buffer.BlockCopy(Key, 8, part2, 0, 8); _decryptionKey1 = GenerateWorkingKey(encrypting: false, part1); _decryptionKey2 = GenerateWorkingKey(encrypting: true, part2); if (Key.Length == 24) { var part3 = new byte[8]; Buffer.BlockCopy(Key, 16, part3, 0, 8); _decryptionKey3 = GenerateWorkingKey(encrypting: false, part3); } else { _decryptionKey3 = _decryptionKey1; } } var temp = new byte[BlockSize]; DesFunc(_decryptionKey3, inputBuffer, inputOffset, temp, 0); DesFunc(_decryptionKey2, temp, 0, temp, 0); DesFunc(_decryptionKey1, temp, 0, outputBuffer, outputOffset); return BlockSize; } /// /// Validates the key. /// protected override void ValidateKey() { var keySize = Key.Length * 8; if (keySize is not (128 or 128 + 64)) { throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); } } } }