-
-
Notifications
You must be signed in to change notification settings - Fork 931
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use hardware-accelerated AES CryptoServiceProvider (#865)
* Add FEATURE_AES_CSP to use hardware-accelerated AesCryptoServiceProvider Reduces CPU usage dramatically, allowing more performance on slower machines * Restructure, move most of the feature code to AesCipher.cs Fix padding for non-AES blockciphers Fix IV exception for non-AES blockciphers * Fix the AES Padding It looks like the legacy code doesn't correctly remove padding, so this code needs to do the same. * fix rebase issues restructure AES CSP code into its own class * Minor fixes * Rework based on suggestions * Move all changes to AesCypher.cs, as per Rob-Hague suggestion Remove FEATURE_AES_CSP conditional Fix OFB CipherMode * update AesCipherTest.cs generator * Fix continuous session encrypt/decrypt (preserve IV between calls when Padding is None) * Reduce CTR memory usage in Net 6+ Small performance increase in CTR buffer mode Cosmetic changes * Factor out the implementations and re-add the existing constructor * remove ctor; revert tests; remove unused _iv member * Reorder Encryption cipher preference list * Remove redundant AES tests Add tests for stream cipher state preservation * Refactor ArrayXOR() * Add test for IV overflow * Performance bump for AES CTR (thanks @robhague) * fix merge conflict * Move AesCipherMode enum to its own file --------- Co-authored-by: Pedro Fonseca <[email protected]> Co-authored-by: Rob Hague <[email protected]>
- Loading branch information
1 parent
18cf71b
commit 4ce18d3
Showing
12 changed files
with
740 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BlockImpl.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.