Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use BCL CipherMode enum for AesCipher class #1564

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,15 +366,15 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy

Encryptions = new Dictionary<string, CipherInfo>
{
{ "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-ctr", new CipherInfo(128, (key, iv) => new AesCtrCipher(key, iv)) },
{ "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCtrCipher(key, iv)) },
{ "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCtrCipher(key, iv)) },
{ "[email protected]", new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
{ "[email protected]", new CipherInfo(256, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
{ "[email protected]", new CipherInfo(512, (key, iv) => new ChaCha20Poly1305Cipher(key, aadLength: 4), isAead: true) },
{ "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-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false)) },
{ "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false)) },
{ "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false)) },
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false)) },
};

Expand Down
12 changes: 6 additions & 6 deletions src/Renci.SshNet/PrivateKeyFile.OpenSSH.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,22 @@ public Key Parse()
cipherInfo = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false));
break;
case "aes128-cbc":
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false));
break;
case "aes192-cbc":
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false));
break;
case "aes256-cbc":
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: false));
break;
case "aes128-ctr":
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
cipherInfo = new CipherInfo(128, (key, iv) => new AesCtrCipher(key, iv));
break;
case "aes192-ctr":
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
cipherInfo = new CipherInfo(192, (key, iv) => new AesCtrCipher(key, iv));
break;
case "aes256-ctr":
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
cipherInfo = new CipherInfo(256, (key, iv) => new AesCtrCipher(key, iv));
break;
case "[email protected]":
cipherInfo = new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 0), isAead: true);
Expand Down
6 changes: 3 additions & 3 deletions src/Renci.SshNet/PrivateKeyFile.PKCS1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ public Key Parse()
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, CipherMode.CFB, pkcs7Padding: false));
break;
case "AES-128-CBC":
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: true));
break;
case "AES-192-CBC":
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: true));
break;
case "AES-256-CBC":
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, CipherMode.CBC, pkcs7Padding: true));
break;
default:
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key cipher \"{0}\" is not supported.", _cipherName));
Expand Down
4 changes: 3 additions & 1 deletion src/Renci.SshNet/PrivateKeyFile.PuTTY.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
using Renci.SshNet.Security;
using Renci.SshNet.Security.Cryptography.Ciphers;

using CipherMode = System.Security.Cryptography.CipherMode;

namespace Renci.SshNet
{
public partial class PrivateKeyFile
Expand Down Expand Up @@ -111,7 +113,7 @@ public Key Parse()
throw new SshException("PuTTY key file version " + _version + " is not supported");
}

using (var cipher = new AesCipher(cipherKey, cipherIV, AesCipherMode.CBC, pkcs7Padding: false))
using (var cipher = new AesCipher(cipherKey, cipherIV, CipherMode.CBC, pkcs7Padding: false))
{
privateKey = cipher.Decrypt(_data);
}
Expand Down
115 changes: 0 additions & 115 deletions src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs

This file was deleted.

13 changes: 4 additions & 9 deletions src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,27 @@ public sealed partial class AesCipher : BlockCipher, IDisposable
/// <param name="pkcs7Padding">Enable PKCS7 padding.</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Keysize is not valid for this algorithm.</exception>
public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false)
public AesCipher(byte[] key, byte[] iv, System.Security.Cryptography.CipherMode mode, bool pkcs7Padding = false)
: base(key, 16, mode: null, padding: null)
{
if (mode == AesCipherMode.OFB)
if (mode == System.Security.Cryptography.CipherMode.OFB)
{
// OFB is not supported on modern .NET
_impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new Pkcs7Padding() : null);
}
#if !NET6_0_OR_GREATER
else if (mode == AesCipherMode.CFB)
else if (mode == System.Security.Cryptography.CipherMode.CFB)
{
// CFB not supported on NetStandard 2.1
_impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new Pkcs7Padding() : null);
}
#endif
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,
mode,
pkcs7Padding ? PaddingMode.PKCS7 : PaddingMode.None);
}
}
Expand Down
26 changes: 0 additions & 26 deletions src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherMode.cs

This file was deleted.

109 changes: 109 additions & 0 deletions src/Renci.SshNet/Security/Cryptography/Ciphers/AesCtrCipher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System;
using System.Buffers.Binary;
using System.Numerics;
using System.Security.Cryptography;

namespace Renci.SshNet.Security.Cryptography.Ciphers
{
internal sealed class AesCtrCipher : BlockCipher, IDisposable
{
private readonly Aes _aes;
private readonly ICryptoTransform _encryptor;

private ulong _ivUpper; // The upper 64 bits of the IV
private ulong _ivLower; // The lower 64 bits of the IV

public AesCtrCipher(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();

_ivLower = BinaryPrimitives.ReadUInt64BigEndian(iv.AsSpan(8));
_ivUpper = BinaryPrimitives.ReadUInt64BigEndian(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 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 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;
}

// creates the Counter array filled with incrementing copies of IV
private void CTRCreateCounterArray(byte[] buffer)
{
for (var i = 0; i < buffer.Length; i += 16)
{
BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i + 8), _ivLower);
BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i), _ivUpper);

_ivLower += 1;
_ivUpper += (_ivLower == 0) ? 1UL : 0UL;
}
}

// XOR 2 arrays using Vector<byte>
private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length)
{
var i = 0;

var oneVectorFromEnd = length - Vector<byte>.Count;
for (; i <= oneVectorFromEnd; i += Vector<byte>.Count)
{
var v = new Vector<byte>(buffer, i) ^ new Vector<byte>(data, offset + i);
v.CopyTo(buffer, i);
}

for (; i < length; i++)
{
buffer[i] ^= data[offset + i];
}
}

public void Dispose()
{
_aes.Dispose();
_encryptor.Dispose();
}
}
}
Loading