forked from sshnet/SSH.NET
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAesCipher.BclImpl.cs
More file actions
141 lines (112 loc) · 5.24 KB
/
AesCipher.BclImpl.cs
File metadata and controls
141 lines (112 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#nullable enable
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)
{
ArgumentNullException.ThrowIfNull(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)
{
return Transform(_encryptor, input, offset, length, output: null, 0, out _);
}
public override byte[] Decrypt(byte[] input, int offset, int length)
{
return Transform(_decryptor, input, offset, length, output: null, 0, out _);
}
public override int Decrypt(byte[] input, int offset, int length, byte[] output, int outputOffset)
{
_ = Transform(_decryptor, input, offset, length, output, outputOffset, out var bytesWritten);
return bytesWritten;
}
private byte[] Transform(ICryptoTransform transform, byte[] input, int offset, int length, byte[]? output, int outputOffset, out int bytesWritten)
{
if (_aes.Padding != PaddingMode.None)
{
// If padding has been specified, call TransformFinalBlock to apply
// the padding and reset the state.
var finalBlock = transform.TransformFinalBlock(input, offset, length);
if (output is not null)
{
finalBlock.AsSpan().CopyTo(output.AsSpan(outputOffset));
}
bytesWritten = finalBlock.Length;
return finalBlock;
}
// 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 paddingLength = 0;
if (length % BlockSize > 0)
{
if (_aes.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB)
{
// Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block.
// See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21
paddingLength = BlockSize - (length % BlockSize);
var tmp = new byte[length + paddingLength];
input.AsSpan(offset, length).CopyTo(tmp);
input = tmp;
offset = 0;
length = tmp.Length;
}
}
if (output is null)
{
output = new byte[length];
bytesWritten = transform.TransformBlock(input, offset, length, output, outputOffset);
bytesWritten -= paddingLength;
// Manually unpad the output.
Array.Resize(ref output, bytesWritten);
}
else
{
bytesWritten = transform.TransformBlock(input, offset, length, output, outputOffset);
bytesWritten -= paddingLength;
}
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)}.");
}
public void Dispose()
{
_aes.Dispose();
_encryptor.Dispose();
_decryptor.Dispose();
}
}
}
}