| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent cbbbdde commit 8712c99
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,3 +1,7 @@ | |||
| 1 | + using System; | ||
| 2 | + #if !NET | ||
| 3 | + using System.Runtime.CompilerServices; | ||
| 4 | + #endif | ||
| 1 | 5 | using System.Security.Cryptography; | |
| 2 | 6 | ||
| 3 | 7 | using Org.BouncyCastle.Crypto.Prng; | |
@@ -10,5 +14,37 @@ internal static class CryptoAbstraction | |||
| 10 | 14 | internal static readonly RandomNumberGenerator Randomizer = RandomNumberGenerator.Create(); | |
| 11 | 15 | ||
| 12 | 16 | internal static readonly SecureRandom SecureRandom = new SecureRandom(new CryptoApiRandomGenerator(Randomizer)); | |
| 17 | + | ||
| 18 | + #if !NET | ||
| 19 | + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] | ||
| 20 | + #endif | ||
| 21 | + public static bool FixedTimeEquals(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right) | ||
| 22 | + { | ||
| 23 | + #if NET | ||
| 24 | + return CryptographicOperations.FixedTimeEquals(left, right); | ||
| 25 | + #else | ||
| 26 | + // https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CryptographicOperations.cs | ||
| 27 | + | ||
| 28 | + // NoOptimization because we want this method to be exactly as non-short-circuiting | ||
| 29 | + // as written. | ||
| 30 | + // | ||
| 31 | + // NoInlining because the NoOptimization would get lost if the method got inlined. | ||
| 32 | + | ||
| 33 | + if (left.Length != right.Length) | ||
| 34 | + { | ||
| 35 | + return false; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + var length = left.Length; | ||
| 39 | + var accum = 0; | ||
| 40 | + | ||
| 41 | + for (var i = 0; i < length; i++) | ||
| 42 | + { | ||
| 43 | + accum |= left[i] - right[i]; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + return accum == 0; | ||
| 47 | + #endif | ||
| 48 | + } | ||
| 13 | 49 | } | |
| 14 | 50 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,3 +1,6 @@ | |||
| 1 | + #nullable enable | ||
| 2 | + using System; | ||
| 3 | + | ||
| 1 | 4 | namespace Renci.SshNet.Security.Cryptography | |
| 2 | 5 | { | |
| 3 | 6 | /// <summary> | |
@@ -73,5 +76,25 @@ public virtual byte[] Decrypt(byte[] input) | |||
| 73 | 76 | /// The decrypted data. | |
| 74 | 77 | /// </returns> | |
| 75 | 78 | public abstract byte[] Decrypt(byte[] input, int offset, int length); | |
| 79 | + | ||
| 80 | + /// <summary> | ||
| 81 | + /// Decrypts the specified input into a given buffer. | ||
| 82 | + /// </summary> | ||
| 83 | + /// <param name="input">The input.</param> | ||
| 84 | + /// <param name="offset">The zero-based offset in <paramref name="input"/> at which to begin decrypting.</param> | ||
| 85 | + /// <param name="length">The number of bytes to decrypt from <paramref name="input"/>.</param> | ||
| 86 | + /// <param name="output">The output buffer to write to.</param> | ||
| 87 | + /// <param name="outputOffset">The zero-based offset in <paramref name="output"/> at which to write decrypted output.</param> | ||
| 88 | + /// <returns> | ||
| 89 | + /// The number of bytes written to <paramref name="output"/>. | ||
| 90 | + /// </returns> | ||
| 91 | + public virtual int Decrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) | ||
| 92 | + { | ||
| 93 | + var plaintext = Decrypt(input, offset, length); | ||
| 94 | + | ||
| 95 | + plaintext.AsSpan().CopyTo(output.AsSpan(outputOffset)); | ||
| 96 | + | ||
| 97 | + return plaintext.Length; | ||
| 98 | + } | ||
| 76 | 99 | } | |
| 77 | 100 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,5 @@ | |||
| 1 | - using System; | ||
| 1 | + #nullable enable | ||
| 2 | + using System; | ||
| 2 | 3 | using System.Security.Cryptography; | |
| 3 | 4 | ||
| 4 | 5 | using Renci.SshNet.Common; | |
@@ -39,53 +40,45 @@ public BclImpl( | |||
| 39 | 40 | } | |
| 40 | 41 | ||
| 41 | 42 | public override byte[] Encrypt(byte[] input, int offset, int length) | |
| 43 | + { | ||
| 44 | + return Transform(_encryptor, input, offset, length, output: null, 0, out _); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public override byte[] Decrypt(byte[] input, int offset, int length) | ||
| 48 | + { | ||
| 49 | + return Transform(_decryptor, input, offset, length, output: null, 0, out _); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + public override int Decrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) | ||
| 53 | + { | ||
| 54 | + _ = Transform(_decryptor, input, offset, length, output, outputOffset, out var bytesWritten); | ||
| 55 | + | ||
| 56 | + return bytesWritten; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + private byte[] Transform(ICryptoTransform transform, byte[] input, int offset, int length, byte[]? output, int outputOffset, out int bytesWritten) | ||
| 42 | 60 | { | |
| 43 | 61 | if (_aes.Padding != PaddingMode.None) | |
| 44 | 62 | { | |
| 45 | 63 | // If padding has been specified, call TransformFinalBlock to apply | |
| 46 | 64 | // the padding and reset the state. | |
| 47 | - return _encryptor.TransformFinalBlock(input, offset, length); | ||
| 48 | - } | ||
| 49 | 65 | ||
| 50 | - var paddingLength = 0; | ||
| 51 | - if (length % BlockSize > 0) | ||
| 52 | - { | ||
| 53 | - if (_aes.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB) | ||
| 66 | + var finalBlock = transform.TransformFinalBlock(input, offset, length); | ||
| 67 | + | ||
| 68 | + if (output is not null) | ||
| 54 | 69 | { | |
| 55 | - // Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block. | ||
| 56 | - // See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21 | ||
| 57 | - paddingLength = BlockSize - (length % BlockSize); | ||
| 58 | - input = input.Take(offset, length); | ||
| 59 | - length += paddingLength; | ||
| 60 | - Array.Resize(ref input, length); | ||
| 61 | - offset = 0; | ||
| 70 | + finalBlock.AsSpan().CopyTo(output.AsSpan(outputOffset)); | ||
| 62 | 71 | } | |
| 72 | + | ||
| 73 | + bytesWritten = finalBlock.Length; | ||
| 74 | + | ||
| 75 | + return finalBlock; | ||
| 63 | 76 | } | |
| 64 | 77 | ||
| 65 | 78 | // Otherwise, (the most important case) assume this instance is | |
| 66 | 79 | // used for one direction of an SSH connection, whereby the | |
| 67 | 80 | // encrypted data in all packets are considered a single data | |
| 68 | - // stream i.e. we do not want to reset the state between calls to Encrypt. | ||
| 69 | - var output = new byte[length]; | ||
| 70 | - _ = _encryptor.TransformBlock(input, offset, length, output, 0); | ||
| 71 | - | ||
| 72 | - if (paddingLength > 0) | ||
| 73 | - { | ||
| 74 | - // Manually unpad the output. | ||
| 75 | - Array.Resize(ref output, output.Length - paddingLength); | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - return output; | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - public override byte[] Decrypt(byte[] input, int offset, int length) | ||
| 82 | - { | ||
| 83 | - if (_aes.Padding != PaddingMode.None) | ||
| 84 | - { | ||
| 85 | - // If padding has been specified, call TransformFinalBlock to apply | ||
| 86 | - // the padding and reset the state. | ||
| 87 | - return _decryptor.TransformFinalBlock(input, offset, length); | ||
| 88 | - } | ||
| 81 | + // stream i.e. we do not want to reset the state between calls to Decrypt. | ||
| 89 | 82 | ||
| 90 | 83 | var paddingLength = 0; | |
| 91 | 84 | if (length % BlockSize > 0) | |
@@ -95,24 +88,33 @@ public override byte[] Decrypt(byte[] input, int offset, int length) | |||
| 95 | 88 | // Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block. | |
| 96 | 89 | // See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21 | |
| 97 | 90 | paddingLength = BlockSize - (length % BlockSize); | |
| 98 | - input = input.Take(offset, length); | ||
| 99 | - length += paddingLength; | ||
| 100 | - Array.Resize(ref input, length); | ||
| 91 | + | ||
| 92 | + var tmp = new byte[length + paddingLength]; | ||
| 93 | + | ||
| 94 | + input.AsSpan(offset, length).CopyTo(tmp); | ||
| 95 | + | ||
| 96 | + input = tmp; | ||
| 101 | 97 | offset = 0; | |
| 98 | + length = tmp.Length; | ||
| 102 | 99 | } | |
| 103 | 100 | } | |
| 104 | 101 | ||
| 105 | - // Otherwise, (the most important case) assume this instance is | ||
| 106 | - // used for one direction of an SSH connection, whereby the | ||
| 107 | - // encrypted data in all packets are considered a single data | ||
| 108 | - // stream i.e. we do not want to reset the state between calls to Decrypt. | ||
| 109 | - var output = new byte[length]; | ||
| 110 | - _ = _decryptor.TransformBlock(input, offset, length, output, 0); | ||
| 111 | - | ||
| 112 | - if (paddingLength > 0) | ||
| 102 | + if (output is null) | ||
| 113 | 103 | { | |
| 104 | + output = new byte[length]; | ||
| 105 | + | ||
| 106 | + bytesWritten = transform.TransformBlock(input, offset, length, output, outputOffset); | ||
| 107 | + | ||
| 108 | + bytesWritten -= paddingLength; | ||
| 109 | + | ||
| 114 | 110 | // Manually unpad the output. | |
| 115 | - Array.Resize(ref output, output.Length - paddingLength); | ||
| 111 | + Array.Resize(ref output, bytesWritten); | ||
| 112 | + } | ||
| 113 | + else | ||
| 114 | + { | ||
| 115 | + bytesWritten = transform.TransformBlock(input, offset, length, output, outputOffset); | ||
| 116 | + | ||
| 117 | + bytesWritten -= paddingLength; | ||
| 116 | 118 | } | |
| 117 | 119 | ||
| 118 | 120 | return output; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,7 @@ | |||
| 1 | - using System; | ||
| 1 | + #nullable enable | ||
| 2 | + using System; | ||
| 2 | 3 | using System.Buffers.Binary; | |
| 4 | + using System.Diagnostics; | ||
| 3 | 5 | using System.Numerics; | |
| 4 | 6 | using System.Security.Cryptography; | |
| 5 | 7 | ||
@@ -34,12 +36,32 @@ public CtrImpl( | |||
| 34 | 36 | ||
| 35 | 37 | public override byte[] Encrypt(byte[] input, int offset, int length) | |
| 36 | 38 | { | |
| 37 | - return CTREncryptDecrypt(input, offset, length); | ||
| 39 | + return Decrypt(input, offset, length); | ||
| 38 | 40 | } | |
| 39 | 41 | ||
| 40 | 42 | public override byte[] Decrypt(byte[] input, int offset, int length) | |
| 41 | 43 | { | |
| 42 | - return CTREncryptDecrypt(input, offset, length); | ||
| 44 | + ArgumentNullException.ThrowIfNull(input); | ||
| 45 | + | ||
| 46 | + var buffer = CTREncryptDecrypt(input, offset, length, output: null, 0); | ||
| 47 | + | ||
| 48 | + // adjust output for non-blocksized lengths | ||
| 49 | + if (buffer.Length > length) | ||
| 50 | + { | ||
| 51 | + Array.Resize(ref buffer, length); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + return buffer; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + public override int Decrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) | ||
| 58 | + { | ||
| 59 | + ArgumentNullException.ThrowIfNull(input); | ||
| 60 | + ArgumentNullException.ThrowIfNull(output); | ||
| 61 | + | ||
| 62 | + _ = CTREncryptDecrypt(input, offset, length, output, outputOffset); | ||
| 63 | + | ||
| 64 | + return length; | ||
| 43 | 65 | } | |
| 44 | 66 | ||
| 45 | 67 | public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) | |
@@ -52,56 +74,67 @@ public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputC | |||
| 52 | 74 | throw new NotImplementedException($"Invalid usage of {nameof(EncryptBlock)}."); | |
| 53 | 75 | } | |
| 54 | 76 | ||
| 55 | - private byte[] CTREncryptDecrypt(byte[] data, int offset, int length) | ||
| 77 | + private byte[] CTREncryptDecrypt(byte[] data, int offset, int length, byte[]? output, int outputOffset) | ||
| 56 | 78 | { | |
| 57 | - var count = length / BlockSize; | ||
| 58 | - if (length % BlockSize != 0) | ||
| 79 | + var blockSizedLength = length; | ||
| 80 | + if (blockSizedLength % BlockSize != 0) | ||
| 59 | 81 | { | |
| 60 | - count++; | ||
| 82 | + blockSizedLength += BlockSize - (blockSizedLength % BlockSize); | ||
| 61 | 83 | } | |
| 62 | 84 | ||
| 63 | - var buffer = new byte[count * BlockSize]; | ||
| 64 | - CTRCreateCounterArray(buffer); | ||
| 65 | - _ = _encryptor.TransformBlock(buffer, 0, buffer.Length, buffer, 0); | ||
| 66 | - ArrayXOR(buffer, data, offset, length); | ||
| 85 | + Debug.Assert(blockSizedLength % BlockSize == 0); | ||
| 67 | 86 | ||
| 68 | - // adjust output for non-blocksized lengths | ||
| 69 | - if (buffer.Length > length) | ||
| 87 | + if (output is null) | ||
| 70 | 88 | { | |
| 71 | - Array.Resize(ref buffer, length); | ||
| 89 | + output = new byte[blockSizedLength]; | ||
| 90 | + outputOffset = 0; | ||
| 91 | + } | ||
| 92 | + else if (data.AsSpan(offset, length).Overlaps(output.AsSpan(outputOffset, blockSizedLength))) | ||
| 93 | + { | ||
| 94 | + throw new ArgumentException("Input and output buffers must not overlap"); | ||
| 72 | 95 | } | |
| 73 | 96 | ||
| 74 | - return buffer; | ||
| 97 | + CTRCreateCounterArray(output.AsSpan(outputOffset, blockSizedLength)); | ||
| 98 | + | ||
| 99 | + var bytesWritten = _encryptor.TransformBlock(output, outputOffset, blockSizedLength, output, outputOffset); | ||
| 100 | + | ||
| 101 | + Debug.Assert(bytesWritten == blockSizedLength); | ||
| 102 | + | ||
| 103 | + ArrayXOR(output, outputOffset, data, offset, length); | ||
| 104 | + | ||
| 105 | + return output; | ||
| 75 | 106 | } | |
| 76 | 107 | ||
| 77 | 108 | // creates the Counter array filled with incrementing copies of IV | |
| 78 | - private void CTRCreateCounterArray(byte[] buffer) | ||
| 109 | + private void CTRCreateCounterArray(Span<byte> buffer) | ||
| 79 | 110 | { | |
| 111 | + Debug.Assert(buffer.Length % 16 == 0); | ||
| 112 | + | ||
| 80 | 113 | for (var i = 0; i < buffer.Length; i += 16) | |
| 81 | 114 | { | |
| 82 | - BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i + 8), _ivLower); | ||
| 83 | - BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(i), _ivUpper); | ||
| 115 | + BinaryPrimitives.WriteUInt64BigEndian(buffer.Slice(i + 8), _ivLower); | ||
| 116 | + BinaryPrimitives.WriteUInt64BigEndian(buffer.Slice(i), _ivUpper); | ||
| 84 | 117 | ||
| 85 | 118 | _ivLower += 1; | |
| 86 | 119 | _ivUpper += (_ivLower == 0) ? 1UL : 0UL; | |
| 87 | 120 | } | |
| 88 | 121 | } | |
| 89 | 122 | ||
| 90 | 123 | // XOR 2 arrays using Vector<byte> | |
| 91 | - private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length) | ||
| 124 | + private static void ArrayXOR(byte[] buffer, int bufferOffset, byte[] data, int offset, int length) | ||
| 92 | 125 | { | |
| 93 | 126 | var i = 0; | |
| 94 | 127 | ||
| 95 | 128 | var oneVectorFromEnd = length - Vector<byte>.Count; | |
| 96 | 129 | for (; i <= oneVectorFromEnd; i += Vector<byte>.Count) | |
| 97 | 130 | { | |
| 98 | - var v = new Vector<byte>(buffer, i) ^ new Vector<byte>(data, offset + i); | ||
| 99 | - v.CopyTo(buffer, i); | ||
| 131 | + var v = new Vector<byte>(buffer, bufferOffset + i) ^ new Vector<byte>(data, offset + i); | ||
| 132 | + v.CopyTo(buffer, bufferOffset + i); | ||
| 100 | 133 | } | |
| 101 | 134 | ||
| 102 | 135 | for (; i < length; i++) | |
| 103 | 136 | { | |
| 104 | - buffer[i] ^= data[offset + i]; | ||
| 137 | + buffer[bufferOffset + i] ^= data[offset + i]; | ||
| 105 | 138 | } | |
| 106 | 139 | } | |
| 107 | 140 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,6 +77,12 @@ public override byte[] Decrypt(byte[] input, int offset, int length) | |||
| 77 | 77 | return _impl.Decrypt(input, offset, length); | |
| 78 | 78 | } | |
| 79 | 79 | ||
| 80 | + /// <inheritdoc/> | ||
| 81 | + public override int Decrypt(byte[] input, int offset, int length, byte[] output, int outputOffset) | ||
| 82 | + { | ||
| 83 | + return _impl.Decrypt(input, offset, length, output, outputOffset); | ||
| 84 | + } | ||
| 85 | + | ||
| 80 | 86 | /// <inheritdoc/> | |
| 81 | 87 | public void Dispose() | |
| 82 | 88 | { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments