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));
}
}
}
}