Reorganize cipher algorithm and security namespace

This commit is contained in:
olegkap_cp
2010-08-11 19:21:50 +00:00
parent 1be4fd1655
commit 775bb96b43
24 changed files with 281 additions and 88 deletions
@@ -45,6 +45,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Security\Algorithm.cs" />
<Compile Include="Security\Cipher.cs" />
<Compile Include="Security\CipherAES128.cs" />
<Compile Include="Security\CipherTripleDES.cs" />
<Compile Include="Security\Compression.cs" />
<Compile Include="Channels\ChannelSftp.cs" />
<Compile Include="Common\DataReceivedEventArgs.cs" />
@@ -86,14 +89,14 @@
<Compile Include="Messages\Sftp\StatusMessage.cs" />
<Compile Include="Messages\Sftp\VersionMessage.cs" />
<Compile Include="Messages\Sftp\WriteMessage.cs" />
<Compile Include="Services\PrivateKey.cs" />
<Compile Include="Security\PrivateKey.cs" />
<Compile Include="Security\KeyExchange.cs" />
<Compile Include="Security\KeyExchangeCompletedEventArgs.cs" />
<Compile Include="Security\KeyExchangeDiffieHellman.cs" />
<Compile Include="Security\KeyExchangeFailedEventArgs.cs" />
<Compile Include="Security\KeyExchangeSendMessageEventArgs.cs" />
<Compile Include="Services\PrivateKeyDsa.cs" />
<Compile Include="Services\PrivateKeyRsa.cs" />
<Compile Include="Security\PrivateKeyDsa.cs" />
<Compile Include="Security\PrivateKeyRsa.cs" />
<Compile Include="Security\Signature.cs" />
<Compile Include="Security\SignatureDss.cs" />
<Compile Include="Security\SignatureRsa.cs" />
@@ -1,4 +1,4 @@
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
public abstract class Algorithm
{
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Linq;
namespace Renci.SshClient.Security
{
internal abstract class Cipher
{
public abstract string Name { get; }
public abstract int BlockSize { get; }
public abstract int KeySize { get; }
protected byte[] Key { get; private set; }
protected byte[] Vector { get; private set; }
public virtual void Init(IEnumerable<byte> key, IEnumerable<byte> vector)
{
this.Key = key.ToArray();
this.Vector = vector.ToArray();
}
public abstract IEnumerable<byte> Encrypt(IEnumerable<byte> data);
public abstract IEnumerable<byte> Decrypt(IEnumerable<byte> data);
}
}
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace Renci.SshClient.Security
{
internal class CipherAES128 : Cipher
{
private SymmetricAlgorithm _algorithm;
private ICryptoTransform _encryptor;
private ICryptoTransform _decryptor;
public override string Name
{
get { return "aes128-cbc"; }
}
public override int KeySize
{
get
{
return this._algorithm.KeySize;
}
}
public override int BlockSize
{
get
{
return this._algorithm.BlockSize / 8;
}
}
public CipherAES128()
{
this._algorithm = new System.Security.Cryptography.RijndaelManaged();
this._algorithm.Mode = System.Security.Cryptography.CipherMode.CBC;
this._algorithm.Padding = System.Security.Cryptography.PaddingMode.None;
}
public override IEnumerable<byte> Encrypt(IEnumerable<byte> data)
{
if (this._encryptor == null)
{
this._encryptor = this._algorithm.CreateEncryptor(this.Key.Take(this.KeySize / 8).ToArray(), this.Vector.Take(this.BlockSize).ToArray());
}
var input = data.ToArray();
var output = new byte[input.Length];
var writtenBytes = this._encryptor.TransformBlock(input, 0, input.Length, output, 0);
if (writtenBytes < input.Length)
{
throw new InvalidOperationException("Encryption error.");
}
return output;
}
public override IEnumerable<byte> Decrypt(IEnumerable<byte> data)
{
if (this._decryptor == null)
{
this._decryptor = this._algorithm.CreateDecryptor(this.Key.Take(this.KeySize / 8).ToArray(), this.Vector.Take(this.BlockSize).ToArray());
}
var input = data.ToArray();
var output = new byte[input.Length];
var writtenBytes = this._decryptor.TransformBlock(input, 0, input.Length, output, 0);
if (writtenBytes < input.Length)
{
throw new InvalidOperationException("Encryption error.");
}
return output;
}
}
}
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace Renci.SshClient.Security
{
internal class CipherTripleDES : Cipher
{
private SymmetricAlgorithm _algorithm;
private ICryptoTransform _encryptor;
private ICryptoTransform _decryptor;
public override string Name
{
get { return "3des-cbc"; }
}
public override int KeySize
{
get
{
return this._algorithm.KeySize;
}
}
public override int BlockSize
{
get
{
return this._algorithm.BlockSize / 8;
}
}
public CipherTripleDES()
{
this._algorithm = new System.Security.Cryptography.TripleDESCryptoServiceProvider();
this._algorithm.Mode = System.Security.Cryptography.CipherMode.CBC;
this._algorithm.Padding = System.Security.Cryptography.PaddingMode.None;
}
public override IEnumerable<byte> Encrypt(IEnumerable<byte> data)
{
if (this._encryptor == null)
{
this._encryptor = this._algorithm.CreateEncryptor(this.Key.Take(this.KeySize / 8).ToArray(), this.Vector.Take(this.BlockSize).ToArray());
}
var input = data.ToArray();
var output = new byte[input.Length];
var writtenBytes = this._encryptor.TransformBlock(input, 0, input.Length, output, 0);
if (writtenBytes < input.Length)
{
throw new InvalidOperationException("Encryption error.");
}
return output;
}
public override IEnumerable<byte> Decrypt(IEnumerable<byte> data)
{
if (this._decryptor == null)
{
this._decryptor = this._algorithm.CreateDecryptor(this.Key.Take(this.KeySize / 8).ToArray(), this.Vector.Take(this.BlockSize).ToArray());
}
var input = data.ToArray();
var output = new byte[input.Length];
var writtenBytes = this._decryptor.TransformBlock(input, 0, input.Length, output, 0);
if (writtenBytes < input.Length)
{
throw new InvalidOperationException("Encryption error.");
}
return output;
}
}
}
@@ -1,4 +1,4 @@
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
public abstract class Compression : Algorithm
{
@@ -8,7 +8,7 @@ using Renci.SshClient.Common;
using Renci.SshClient.Messages;
using Renci.SshClient.Messages.Transport;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal abstract class KeyExchange : Algorithm
{
@@ -38,12 +38,12 @@ namespace Renci.SshClient.Algorithms
/// <summary>
/// Specifies negotiated algorithm to encrypt information when sent to the server
/// </summary>
private Func<SymmetricAlgorithm> _clientEncryptionAlgorithm;
private Func<Cipher> _clientCipher;
/// <summary>
/// Specifies negotiated algorithm to decrypt information from the server
/// </summary>
private Func<SymmetricAlgorithm> _serverDecryptionAlgorithm;
private Func<Cipher> _serverCipher;
/// <summary>
/// Specifies negotiated HMAC algorithm to use for client
@@ -75,9 +75,9 @@ namespace Renci.SshClient.Algorithms
public HMAC ClientMac { get; set; }
public ICryptoTransform Encryption { get; set; }
public Cipher ClientCipher { get; set; }
public ICryptoTransform Decryption { get; set; }
public Cipher ServerCipher { get; set; }
public Compression ServerDecompression { get; set; }
@@ -151,7 +151,7 @@ namespace Renci.SshClient.Algorithms
{
throw new InvalidOperationException("Client encryption algorithm not found");
}
this._clientEncryptionAlgorithm = Settings.Encryptions[clientEncryptionAlgorithmName];
this._clientCipher = Settings.Encryptions[clientEncryptionAlgorithmName];
// Determine encryption algorithm
var serverDecryptionAlgorithmName = (from a in message.EncryptionAlgorithmsServerToClient
@@ -162,7 +162,7 @@ namespace Renci.SshClient.Algorithms
{
throw new InvalidOperationException("Server decryption algorithm not found");
}
this._serverDecryptionAlgorithm = Settings.Encryptions[clientEncryptionAlgorithmName];
this._serverCipher = Settings.Encryptions[clientEncryptionAlgorithmName];
// Determine client hmac algorithm
var clientHmacAlgorithmName = (from a in message.MacAlgorithmsClientToSserver
@@ -196,41 +196,30 @@ namespace Renci.SshClient.Algorithms
this.Session.SessionId = this.ExchangeHash;
}
// Set encryption
ICryptoTransform encryption;
using (var clientAlgorithm = this._clientEncryptionAlgorithm())
{
// Calculate client to server initial IV
var clientValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', this.Session.SessionId));
// Initialize client cipher
var clientCipher = this._clientCipher();
// Calculate client to server initial IV
var clientVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', this.Session.SessionId));
// Calculate client to server encryption
var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', this.Session.SessionId));
// Calculate client to server encryption
var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', this.Session.SessionId));
clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, clientAlgorithm.KeySize / 8);
clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, clientCipher.KeySize / 8);
clientAlgorithm.Mode = System.Security.Cryptography.CipherMode.CBC;
clientAlgorithm.Padding = System.Security.Cryptography.PaddingMode.None;
clientCipher.Init(clientKey, clientVector);
encryption = clientAlgorithm.CreateEncryptor(clientKey.Take(clientAlgorithm.KeySize / 8).ToArray(), clientValue.Take(clientAlgorithm.BlockSize / 8).ToArray());
}
// Initilize server cipher
var serverCipher = this._serverCipher();
// Set decryption
ICryptoTransform decryption;
using (var serverAlgorithm = this._serverDecryptionAlgorithm())
{
// Calculate server to client initial IV
var serverValue = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', this.Session.SessionId));
// Calculate server to client initial IV
var serverVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', this.Session.SessionId));
// Calculate server to client encryption
var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', this.Session.SessionId));
// Calculate server to client encryption
var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', this.Session.SessionId));
serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, serverAlgorithm.KeySize / 8);
serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, serverCipher.KeySize / 8);
serverAlgorithm.Mode = System.Security.Cryptography.CipherMode.CBC;
serverAlgorithm.Padding = System.Security.Cryptography.PaddingMode.None;
decryption = serverAlgorithm.CreateDecryptor(serverKey.Take(serverAlgorithm.KeySize / 8).ToArray(), serverValue.Take(serverAlgorithm.BlockSize / 8).ToArray());
}
serverCipher.Init(serverKey, serverVector);
// Calculate client to server integrity
var MACc2s = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', this.Session.SessionId));
@@ -242,8 +231,8 @@ namespace Renci.SshClient.Algorithms
// TODO: Create compression and decompression objects if any
this.Decryption = decryption;
this.Encryption = encryption;
this.ServerCipher = serverCipher;
this.ClientCipher = clientCipher;
this.ServerDecompression = Compression.None;
this.ClientCompression = Compression.None;
this.ServerMac = serverMac;
@@ -1,6 +1,6 @@
using System;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal class KeyExchangeCompletedEventArgs : EventArgs
{
@@ -6,7 +6,7 @@ using Renci.SshClient.Common;
using Renci.SshClient.Messages;
using Renci.SshClient.Messages.Transport;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal class KeyExchangeDiffieHellman : KeyExchange
{
@@ -1,6 +1,6 @@
using System;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal class KeyExchangeFailedEventArgs : EventArgs
{
@@ -1,7 +1,7 @@
using System;
using Renci.SshClient.Messages;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal class KeyExchangeSendMessageEventArgs : EventArgs
{
@@ -1,5 +1,6 @@
using System.Collections.Generic;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal abstract class Signature : Algorithm
{
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal class SignatureDss : Signature
{
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace Renci.SshClient.Algorithms
namespace Renci.SshClient.Security
{
internal class SignatureRsa : Signature
{
@@ -1,6 +1,6 @@
using Renci.SshClient.Messages;
namespace Renci.SshClient.Security
namespace Renci.SshClient.Services
{
internal abstract class UserAuthentication
{
@@ -1,4 +1,4 @@
namespace Renci.SshClient.Security
namespace Renci.SshClient.Services
{
internal class UserAuthenticationHost : UserAuthentication
{
@@ -1,7 +1,7 @@
using Renci.SshClient.Messages;
using Renci.SshClient.Messages.Authentication;
namespace Renci.SshClient.Security
namespace Renci.SshClient.Services
{
internal class UserAuthenticationPassword : UserAuthentication
{
@@ -1,7 +1,7 @@
using Renci.SshClient.Messages;
using Renci.SshClient.Messages.Authentication;
namespace Renci.SshClient.Security
namespace Renci.SshClient.Services
{
internal class UserAuthenticationPublicKey : UserAuthentication
{
+13 -5
View File
@@ -8,12 +8,12 @@ using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Renci.SshClient.Algorithms;
using Renci.SshClient.Channels;
using Renci.SshClient.Common;
using Renci.SshClient.Messages;
using Renci.SshClient.Messages.Connection;
using Renci.SshClient.Messages.Transport;
using Renci.SshClient.Security;
using Renci.SshClient.Services;
namespace Renci.SshClient
@@ -82,9 +82,17 @@ namespace Renci.SshClient
protected HMAC ClientMac { get; private set; }
protected ICryptoTransform Encryption { get; private set; }
/// <summary>
/// Gets or sets the client cipher which used to encrypt messages sent to server.
/// </summary>
/// <value>The client cipher.</value>
protected Cipher ClientCipher { get; private set; }
protected ICryptoTransform Decryption { get; private set; }
/// <summary>
/// Gets or sets the server cipher which used to decrypt messages sent by server.
/// </summary>
/// <value>The server cipher.</value>
protected Cipher ServerCipher { get; private set; }
protected Compression ServerDecompression { get; private set; }
@@ -293,8 +301,8 @@ namespace Renci.SshClient
// Update encryption and decryption algorithm
this.ServerMac = this._keyExhcange.ServerMac;
this.ClientMac = this._keyExhcange.ClientMac;
this.Encryption = this._keyExhcange.Encryption;
this.Decryption = this._keyExhcange.Decryption;
this.ClientCipher = this._keyExhcange.ClientCipher;
this.ServerCipher = this._keyExhcange.ServerCipher;
this.ServerDecompression = this._keyExhcange.ServerDecompression;
this.ClientCompression = this._keyExhcange.ClientCompression;
+24 -20
View File
@@ -32,7 +32,7 @@ namespace Renci.SshClient
// Messages can be sent by different thread so we need to synchronize it
lock (_writeLock)
{
var paddingMultiplier = this.Encryption == null ? (byte)8 : (byte)this.Encryption.OutputBlockSize; // Should be recalculate base on cipher min lenght if sipher specified
var paddingMultiplier = this.ClientCipher == null ? (byte)8 : (byte)this.ClientCipher.BlockSize; // Should be recalculate base on cipher min lenght if sipher specified
// TODO: Maximum uncomporessed payload 32768
// TOOO: If compression specified then compress only payload
@@ -70,9 +70,10 @@ namespace Renci.SshClient
// Encrypt packet data
var encryptedData = packetData.ToList();
if (this.Encryption != null)
if (this.ClientCipher != null)
{
encryptedData = new List<byte>(this.Encrypt(packetData));
//encryptedData = new List<byte>(this.Encrypt(packetData));
encryptedData = new List<byte>(this.ClientCipher.Encrypt(packetData));
}
// Add message authentication code (MAC)
@@ -103,18 +104,20 @@ namespace Renci.SshClient
List<byte> decryptedData;
var blockSize = this.Decryption == null ? (byte)8 : (byte)this.Decryption.InputBlockSize;
//var blockSize = this.Decryption == null ? (byte)8 : (byte)this.Decryption.InputBlockSize;
var blockSize = this.ServerCipher == null ? (byte)8 : (byte)this.ServerCipher.BlockSize;
// Read packet lenght first
var data = new List<byte>(this.Read(blockSize));
if (this.Decryption == null)
if (this.ServerCipher == null)
{
decryptedData = data.ToList();
}
else
{
decryptedData = new List<byte>(this.Decrypt(data));
//decryptedData = new List<byte>(this.Decrypt(data));
decryptedData = new List<byte>(this.ServerCipher.Decrypt(data));
}
var packetLength = BitConverter.ToUInt32(decryptedData.Take(4).Reverse().ToArray(), 0);
@@ -130,13 +133,14 @@ namespace Renci.SshClient
{
data = new List<byte>(this.Read(blockSize));
if (this.Decryption == null)
if (this.ServerCipher == null)
{
decryptedData.AddRange(data);
}
else
{
decryptedData.AddRange(this.Decrypt(data));
//decryptedData.AddRange(this.Decrypt(data));
decryptedData.AddRange(this.ServerCipher.Decrypt(data));
}
bytesToRead -= blockSize;
}
@@ -181,18 +185,18 @@ namespace Renci.SshClient
return true;
}
private IEnumerable<byte> Encrypt(List<byte> data)
{
var temp = new byte[data.Count];
this.Encryption.TransformBlock(data.ToArray(), 0, data.Count, temp, 0);
return temp;
}
//private IEnumerable<byte> Encrypt(List<byte> data)
//{
// var temp = new byte[data.Count];
// this.Encryption.TransformBlock(data.ToArray(), 0, data.Count, temp, 0);
// return temp;
//}
private IEnumerable<byte> Decrypt(List<byte> data)
{
var temp = new byte[data.Count];
this.Decryption.TransformBlock(data.ToArray(), 0, data.Count, temp, 0);
return temp;
}
//private IEnumerable<byte> Decrypt(List<byte> data)
//{
// var temp = new byte[data.Count];
// this.Decryption.TransformBlock(data.ToArray(), 0, data.Count, temp, 0);
// return temp;
//}
}
}
+7 -11
View File
@@ -1,20 +1,19 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using Renci.SshClient.Algorithms;
using Renci.SshClient.Security;
namespace Renci.SshClient
{
internal static class Settings
{
public static IDictionary<string, Func<Session, KeyExchange>> KeyExchangeAlgorithms { get; private set; }
public static IDictionary<string, Func<SymmetricAlgorithm>> Encryptions { get; private set; }
public static IDictionary<string, Func<Cipher>> Encryptions { get; private set; }
public static IDictionary<string, Func<IEnumerable<byte>, HMAC>> HmacAlgorithms { get; private set; }
//public static IDictionary<string, Func<IEnumerable<byte>, IEnumerable<byte>, IEnumerable<byte>, bool>> HostKeyAlgorithms { get; private set; }
public static IDictionary<string, Func<IEnumerable<byte>, Signature>> HostKeyAlgorithms { get; private set; }
@@ -27,10 +26,10 @@ namespace Renci.SshClient
};
Settings.Encryptions = new Dictionary<string, Func<SymmetricAlgorithm>>()
Settings.Encryptions = new Dictionary<string, Func<Cipher>>()
{
{"3des-cbc", () => { return new System.Security.Cryptography.TripleDESCryptoServiceProvider();}},
//{"aes128-cbc", () => { return new System.Security.Cryptography.RijndaelManaged();}}, // TODO: Need to be tested, currently not working
{"3des-cbc", () => { return new CipherTripleDES();}},
//{"aes128-cbc", () => { return new CipherAES128();}}, // TODO: This cipher does not work
};
@@ -40,11 +39,8 @@ namespace Renci.SshClient
{"hmac-sha1", (key) => { return new System.Security.Cryptography.HMACSHA1(key.Take(20).ToArray());}},
};
//Settings.HostKeyAlgorithms = new Dictionary<string, Func<IEnumerable<byte>, IEnumerable<byte>, IEnumerable<byte>, bool>>()
Settings.HostKeyAlgorithms = new Dictionary<string, Func<IEnumerable<byte>, Signature>>()
{
//{"ssh-rsa", (hash, signature, hostKeyData) => { var s = new SignatureRsa(hostKeyData); return s.Validate(hash, signature);}},
//{"ssh-dsa", (hash, signature, hostKeyData) => { var s = new SignatureDss(hostKeyData); return s.Validate(hash, signature);}}, // TODO: Need to be tested
{"ssh-rsa", (hostKeyData) => { return new SignatureRsa(hostKeyData);}},
{"ssh-dsa", (hostKeyData) => { return new SignatureDss(hostKeyData);;}}, // TODO: Need to be tested
};