-
-
Notifications
You must be signed in to change notification settings - Fork 969
Add support for AEAD ChaCha20Poly1305 Cipher #1416
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
Merged
Merged
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
0880a6a
Implements ChaCha20 cipher algorithm.
scott-xu 5471a32
Implements [email protected]
scott-xu da9bc08
Update Cipher.cs
scott-xu aaf49a1
Update ChaCha20Poly1305Cipher.cs
scott-xu 7780ff8
Note that the length of the concatenation of 'packet_length',
scott-xu d29d8de
Use Chaos.Nacl Poly1305Donna
scott-xu 9983a31
Fix build. Fix typo. Update README
scott-xu 920d681
Update README.md
scott-xu bea3b48
Fix build
scott-xu ec84664
Remove trailing whitespace
scott-xu eecffe6
Fix build
scott-xu b67024c
Merge branch 'chacha20-poly1305' of https://github.com/scott-xu/SSH.N…
scott-xu 1bbca33
Merge branch 'chacha20-poly1305' of https://github.com/scott-xu/SSH.N…
scott-xu 898cf08
Change to BouncyCastle
scott-xu 7a057de
Merge branch 'develop' of https://github.com/scott-xu/SSH.NET into ch…
scott-xu b600dd2
Inherit from SymmetricCipher instead of StreamCipher since StreamCiph…
scott-xu 5dbb8d7
Resolve conflicts
scott-xu 15be6bc
Move field to local variable
scott-xu fa69068
Compute poly key stream once
scott-xu 90826d3
Update test/Renci.SshNet.IntegrationTests/CipherTests.cs
scott-xu 4ff14c6
Fix build; Add net48 integration test in CI
scott-xu 379ef1f
Merge branch 'develop' into chacha20-poly1305
scott-xu 9046899
Merge branch 'develop' into chacha20-poly1305
scott-xu 9f3eafd
Merge branch 'develop' into chacha20-poly1305
WojciechNagorski 566fa98
Merge branch 'develop' into chacha20-poly1305
scott-xu 6106068
Merge branch 'develop' into chacha20-poly1305
scott-xu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 |
|---|---|---|
|
|
@@ -392,6 +392,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy | |
| Encryptions.Add("[email protected]", new CipherInfo(256, (key, iv) => new AesGcmCipher(key, iv), isAead: true)); | ||
| } | ||
| #endif | ||
| Encryptions.Add("[email protected]", new CipherInfo(512, (key, iv) => new ChaCha20Poly1305Cipher(key), isAead: true)); | ||
| Encryptions.Add("aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false))); | ||
| Encryptions.Add("aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false))); | ||
| Encryptions.Add("aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false))); | ||
|
|
||
This file contains hidden or 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
157 changes: 157 additions & 0 deletions
157
src/Renci.SshNet/Security/Cryptography/Ciphers/ChaCha20Poly1305Cipher.cs
This file contains hidden or 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,157 @@ | ||
| using System; | ||
| using System.Buffers.Binary; | ||
| using System.Diagnostics; | ||
|
|
||
| using Org.BouncyCastle.Crypto.Engines; | ||
| using Org.BouncyCastle.Crypto.Macs; | ||
| using Org.BouncyCastle.Crypto.Parameters; | ||
| using Org.BouncyCastle.Utilities; | ||
|
|
||
| using Renci.SshNet.Common; | ||
| using Renci.SshNet.Messages.Transport; | ||
|
|
||
| namespace Renci.SshNet.Security.Cryptography.Ciphers | ||
| { | ||
| /// <summary> | ||
| /// ChaCha20Poly1305 cipher implementation. | ||
| /// <see href="https://datatracker.ietf.org/doc/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00"/>. | ||
| /// </summary> | ||
| internal sealed class ChaCha20Poly1305Cipher : SymmetricCipher | ||
| { | ||
| private readonly ChaCha7539Engine _aadCipher = new ChaCha7539Engine(); | ||
| private readonly ChaCha7539Engine _cipher = new ChaCha7539Engine(); | ||
| private readonly Poly1305 _mac = new Poly1305(); | ||
|
|
||
| /// <summary> | ||
| /// Gets the minimun block size. | ||
| /// </summary> | ||
| public override byte MinimumSize | ||
| { | ||
| get | ||
| { | ||
| return 16; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the tag size in bytes. | ||
| /// Poly1305 [Poly1305], also by Daniel Bernstein, is a one-time Carter- | ||
| /// Wegman MAC that computes a 128 bit integrity tag given a message | ||
| /// <see href="https://datatracker.ietf.org/doc/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00#section-1"/>. | ||
| /// </summary> | ||
| public override int TagSize | ||
| { | ||
| get | ||
| { | ||
| return 16; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="ChaCha20Poly1305Cipher"/> class. | ||
| /// </summary> | ||
| /// <param name="key">The key.</param> | ||
| public ChaCha20Poly1305Cipher(byte[] key) | ||
| : base(key) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Encrypts the specified input. | ||
| /// </summary> | ||
| /// <param name="input"> | ||
| /// The input data with below format: | ||
| /// <code> | ||
| /// [outbound sequence field][packet length field][padding length field sz][payload][random paddings] | ||
| /// [----4 bytes----(offset)][------4 bytes------][----------------Plain Text---------------(length)] | ||
| /// </code> | ||
| /// </param> | ||
| /// <param name="offset">The zero-based offset in <paramref name="input"/> at which to begin encrypting.</param> | ||
| /// <param name="length">The number of bytes to encrypt from <paramref name="input"/>.</param> | ||
| /// <returns> | ||
| /// The encrypted data with below format: | ||
| /// <code> | ||
| /// [packet length field][padding length field sz][payload][random paddings][Authenticated TAG] | ||
| /// [------4 bytes------][------------------Cipher Text--------------------][-------TAG-------] | ||
| /// </code> | ||
| /// </returns> | ||
| public override byte[] Encrypt(byte[] input, int offset, int length) | ||
| { | ||
| var output = new byte[length + TagSize]; | ||
|
|
||
| _aadCipher.ProcessBytes(input, offset, 4, output, 0); | ||
| _cipher.ProcessBytes(input, offset + 4, length - 4, output, 4); | ||
|
|
||
| _mac.BlockUpdate(output, 0, length); | ||
| _ = _mac.DoFinal(output, length); | ||
|
|
||
| return output; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Decrypts the first block which is packet length field. | ||
| /// </summary> | ||
| /// <param name="input">The encrypted packet length field.</param> | ||
| /// <returns>The decrypted packet length field.</returns> | ||
| public override byte[] Decrypt(byte[] input) | ||
| { | ||
| var output = new byte[input.Length]; | ||
| _aadCipher.ProcessBytes(input, 0, input.Length, output, 0); | ||
|
|
||
| return output; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Decrypts the specified input. | ||
| /// </summary> | ||
| /// <param name="input"> | ||
| /// The input data with below format: | ||
| /// <code> | ||
| /// [inbound sequence field][packet length field][padding length field sz][payload][random paddings][Authenticated TAG] | ||
| /// [--------4 bytes-------][--4 bytes--(offset)][--------------Cipher Text----------------(length)][-------TAG-------] | ||
| /// </code> | ||
| /// </param> | ||
| /// <param name="offset">The zero-based offset in <paramref name="input"/> at which to begin decrypting and authenticating.</param> | ||
| /// <param name="length">The number of bytes to decrypt and authenticate from <paramref name="input"/>.</param> | ||
| /// <returns> | ||
| /// The decrypted data with below format: | ||
| /// <code> | ||
| /// [padding length field sz][payload][random paddings] | ||
| /// [--------------------Plain Text-------------------] | ||
| /// </code> | ||
| /// </returns> | ||
| public override byte[] Decrypt(byte[] input, int offset, int length) | ||
| { | ||
| Debug.Assert(offset == 8, "The offset must be 8"); | ||
|
|
||
| var tag = new byte[TagSize]; | ||
| _mac.BlockUpdate(input, offset - 4, length + 4); | ||
| _ = _mac.DoFinal(tag, 0); | ||
| if (!Arrays.FixedTimeEquals(TagSize, tag, 0, input, offset + length)) | ||
| { | ||
| throw new SshConnectionException("MAC error", DisconnectReason.MacError); | ||
| } | ||
|
|
||
| var output = new byte[length]; | ||
| _cipher.ProcessBytes(input, offset, length, output, 0); | ||
|
|
||
| return output; | ||
| } | ||
|
|
||
| internal override void SetSequenceNumber(uint sequenceNumber) | ||
| { | ||
| var iv = new byte[12]; | ||
| BinaryPrimitives.WriteUInt64BigEndian(iv.AsSpan(4), sequenceNumber); | ||
|
|
||
| // ChaCha20 encryption and decryption is completely | ||
| // symmetrical, so the 'forEncryption' is | ||
| // irrelevant. (Like 90% of stream ciphers) | ||
| _aadCipher.Init(forEncryption: true, new ParametersWithIV(new KeyParameter(Key, 32, 32), iv)); | ||
| _cipher.Init(forEncryption: true, new ParametersWithIV(new KeyParameter(Key, 0, 32), iv)); | ||
|
|
||
| var keyStream = new byte[64]; | ||
| _cipher.ProcessBytes(keyStream, 0, keyStream.Length, keyStream, 0); | ||
| _mac.Init(new KeyParameter(keyStream, 0, 32)); | ||
| } | ||
| } | ||
| } |
This file contains hidden or 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 hidden or 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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole method makes me shed a tear. Maybe one day we can design something easier to comprehend. I have a bit of an idea
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's becoming "nobody knows why but it just works" 🤣
Looking forward your idea.