-
-
Notifications
You must be signed in to change notification settings - Fork 968
Support AesGcm cipher #1364
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
Support AesGcm cipher #1364
Changes from 8 commits
8b5c819
4d160ca
7546a5a
270fb96
7a6b334
1059b68
fe26b65
1b92a6a
7377cbe
cdbe822
65ad16a
3336d3d
37a16bf
fdc8f49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -388,6 +388,10 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy | |
| { "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)) }, | ||
| #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_0_OR_GREATER | ||
| { "[email protected]", new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv)) }, | ||
| { "[email protected]", new CipherInfo(256, (key, iv) => new AesGcmCipher(key, iv)) }, | ||
| #endif | ||
| { "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) }, | ||
| { "blowfish-cbc", new CipherInfo(128, (key, iv) => new BlowfishCipher(key, new CbcCipherMode(iv), padding: null)) }, | ||
| { "twofish-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| namespace Renci.SshNet.Security.Cryptography | ||
| { | ||
| /// <summary> | ||
| /// Represents algorithm for Authenticated Encryption with Associated data. | ||
| /// </summary> | ||
| public abstract class AeadCipher : SymmetricCipher | ||
| { | ||
| /// <summary> | ||
| /// Gets the size of the block in bytes. | ||
| /// </summary> | ||
| /// <value> | ||
| /// The size of the block in bytes. | ||
| /// </value> | ||
| private readonly byte _blockSize; | ||
|
|
||
| /// <summary> | ||
| /// Gets the tag size in bytes. | ||
| /// </summary> | ||
| public int TagSize { get; } | ||
|
|
||
| /// <inheritdoc/> | ||
| public override byte MinimumSize | ||
| { | ||
| get { return _blockSize; } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="AeadCipher"/> class. | ||
| /// </summary> | ||
| /// <param name="key">The key.</param> | ||
| /// <param name="tagSize">The tag size in bytes.</param> | ||
| protected AeadCipher(byte[] key, int tagSize) | ||
| : base(key) | ||
| { | ||
| _blockSize = 16; | ||
| TagSize = tagSize; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_0_OR_GREATER | ||
| using System; | ||
| using System.Security.Cryptography; | ||
|
|
||
| using Renci.SshNet.Common; | ||
|
|
||
| namespace Renci.SshNet.Security.Cryptography.Ciphers | ||
| { | ||
| /// <summary> | ||
| /// AES GCM cipher implementation. | ||
| /// <see href="https://datatracker.ietf.org/doc/html/rfc5647"/>. | ||
| /// </summary> | ||
| public sealed class AesGcmCipher : AeadCipher, IDisposable | ||
| { | ||
| private readonly byte[] _nonce; | ||
| private readonly AesGcm _aesGcm; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="AesGcmCipher"/> class. | ||
| /// </summary> | ||
| /// <param name="key">The key.</param> | ||
| /// <param name="iv">The IV.</param> | ||
| public AesGcmCipher(byte[] key, byte[] iv) | ||
| : base(key, tagSize: 16) | ||
| { | ||
| _nonce = iv.Take(12); | ||
| #if NET8_0_OR_GREATER | ||
| _aesGcm = new AesGcm(key, TagSize); | ||
| #else | ||
| _aesGcm = new AesGcm(key); | ||
| #endif | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public override byte[] Encrypt(byte[] input, int offset, int length) | ||
| { | ||
| // [outbound sequence][packet length field][padding length field sz][payload][random paddings] | ||
| // [--4 bytes(offset)][------4 bytes------][-------------------Plain Text--------------------] | ||
| var packetLengthField = new ReadOnlySpan<byte>(input, offset, 4); | ||
| var plainText = new ReadOnlySpan<byte>(input, offset + 4, length - 4); | ||
|
|
||
| var cipherText = new byte[length - 4]; | ||
| var tag = new byte[TagSize]; | ||
|
|
||
| _aesGcm.Encrypt(_nonce, plainText, cipherText, tag, packetLengthField); | ||
|
|
||
| var result = new byte[length + TagSize]; | ||
|
|
||
| packetLengthField.CopyTo(result); | ||
| Buffer.BlockCopy(cipherText, 0, result, 4, length - 4); | ||
| Buffer.BlockCopy(tag, 0, result, length, TagSize); | ||
|
|
||
| IncrementCounter(); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public override byte[] Decrypt(byte[] input, int offset, int length) | ||
| { | ||
| // [inbound sequence][packet length field][padding length field sz][payload][random paddings][Authenticated TAG] | ||
| // [-----4 bytes----][----4 bytes(offset)][------------------Cipher Text--------------------][-------TAG-------] | ||
| var packetLengthField = new ReadOnlySpan<byte>(input, offset - 4, 4); | ||
|
||
| var cipherText = new ReadOnlySpan<byte>(input, offset, length); | ||
| var tag = new ReadOnlySpan<byte>(input, offset + length, TagSize); | ||
|
||
|
|
||
| var plainText = new byte[length]; | ||
|
|
||
| _aesGcm.Decrypt(_nonce, cipherText, tag, plainText, packetLengthField); | ||
|
|
||
| IncrementCounter(); | ||
|
|
||
| return plainText; | ||
| } | ||
|
|
||
| private void IncrementCounter() | ||
| { | ||
| var invocationCounter = _nonce.Take(4, 8); | ||
| var count = Pack.BigEndianToUInt64(invocationCounter) + 1; | ||
| invocationCounter = Pack.UInt64ToBigEndian(count); | ||
| Buffer.BlockCopy(invocationCounter, 0, _nonce, 4, 8); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Dispose the instance. | ||
| /// </summary> | ||
| /// <param name="disposing">Set to True to dispose of resouces.</param> | ||
| public void Dispose(bool disposing) | ||
| { | ||
| if (disposing) | ||
| { | ||
| _aesGcm.Dispose(); | ||
| } | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public void Dispose() | ||
| { | ||
| // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||
| Dispose(disposing: true); | ||
| GC.SuppressFinalize(this); | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
Uh oh!
There was an error while loading. Please reload this page.