reduce allocations for encryption

This commit is contained in:
Wizou 2022-01-07 01:14:16 +01:00
parent 0c1785596d
commit 6bdb0b9cc7
2 changed files with 21 additions and 24 deletions

View file

@ -16,6 +16,14 @@ namespace WTelegram
{ {
internal static readonly RNGCryptoServiceProvider RNG = new(); internal static readonly RNGCryptoServiceProvider RNG = new();
private static readonly Dictionary<long, RSAPublicKey> PublicKeys = new(); private static readonly Dictionary<long, RSAPublicKey> PublicKeys = new();
private static readonly Aes AesECB = Aes.Create();
static Encryption()
{
AesECB.Mode = CipherMode.ECB;
AesECB.Padding = PaddingMode.Zeros;
if (AesECB.BlockSize != 128) throw new ApplicationException("AES Blocksize is not 16 bytes");
}
internal static async Task CreateAuthorizationKey(Client client, Session.DCSession session) internal static async Task CreateAuthorizationKey(Client client, Session.DCSession session)
{ {
@ -332,31 +340,25 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
private static byte[] AES_IGE_EncryptDecrypt(Span<byte> input, byte[] aes_key, byte[] aes_iv, bool encrypt) private static byte[] AES_IGE_EncryptDecrypt(Span<byte> input, byte[] aes_key, byte[] aes_iv, bool encrypt)
{ {
using var aes = Aes.Create();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.Zeros;
if (aes.BlockSize != 128) throw new ApplicationException("AES Blocksize is not 16 bytes");
if (input.Length % 16 != 0) throw new ApplicationException("intput size not divisible by 16"); if (input.Length % 16 != 0) throw new ApplicationException("intput size not divisible by 16");
// code adapted from PHP implementation found at https://mgp25.com/AESIGE/ // code adapted from PHP implementation found at https://mgp25.com/AESIGE/
var output = new byte[input.Length]; var output = new byte[input.Length];
var xPrev = aes_iv.AsSpan(encrypt ? 16 : 0, 16); var xPrev = aes_iv.AsSpan(encrypt ? 16 : 0, 16);
var yPrev = aes_iv.AsSpan(encrypt ? 0 : 16, 16); var yPrev = aes_iv.AsSpan(encrypt ? 0 : 16, 16);
var aesCrypto = encrypt ? aes.CreateEncryptor(aes_key, null) : aes.CreateDecryptor(aes_key, null); var aesCrypto = encrypt ? AesECB.CreateEncryptor(aes_key, null) : AesECB.CreateDecryptor(aes_key, null);
using (aesCrypto) using (aesCrypto)
{ {
byte[] yXOR = new byte[16]; byte[] yXOR = new byte[16];
for (int i = 0; i < input.Length; i += 16) for (int i = 0; i < input.Length; i += 16)
{ {
var x = input.Slice(i, 16);
var y = output.AsSpan(i, 16);
for (int j = 0; j < 16; j++) for (int j = 0; j < 16; j++)
yXOR[j] = (byte)(x[j] ^ yPrev[j]); yXOR[j] = (byte)(input[i + j] ^ yPrev[j]);
var yFinal = aesCrypto.TransformFinalBlock(yXOR, 0, 16); aesCrypto.TransformBlock(yXOR, 0, 16, output, i);
for (int j = 0; j < 16; j++) for (int j = 0; j < 16; j++)
y[j] = (byte)(yFinal[j] ^ xPrev[j]); output[i + j] ^= xPrev[j];
xPrev = x; xPrev = input.Slice(i, 16);
yPrev = y; yPrev = output.AsSpan(i, 16);
} }
} }
return output; return output;
@ -367,12 +369,12 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
{ {
readonly ICryptoTransform encryptor; readonly ICryptoTransform encryptor;
readonly byte[] ivec; readonly byte[] ivec;
byte[] ecount; readonly byte[] ecount = new byte[16];
int num; int num;
public AesCtr(Aes aes, byte[] key, byte[] iv) public AesCtr(byte[] key, byte[] iv)
{ {
encryptor = aes.CreateEncryptor(key, null); encryptor = AesECB.CreateEncryptor(key, null);
ivec = iv; ivec = iv;
} }
@ -384,7 +386,7 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
{ {
if (num == 0) if (num == 0)
{ {
ecount = encryptor.TransformFinalBlock(ivec, 0, 16); encryptor.TransformBlock(ivec, 0, 16, ecount, 0);
for (int n = 15; n >= 0; n--) // increment big-endian counter for (int n = 15; n >= 0; n--) // increment big-endian counter
if (++ivec[n] != 0) break; if (++ivec[n] != 0) break;
} }
@ -421,12 +423,8 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
sha256.TransformFinalBlock(secret, 0, 16); sha256.TransformFinalBlock(secret, 0, 16);
recvKey = sha256.Hash; recvKey = sha256.Hash;
} }
using var aes = Aes.Create(); var sendCtr = new AesCtr(sendKey, sendIV);
aes.Mode = CipherMode.ECB; var recvCtr = new AesCtr(recvKey, recvIV);
aes.Padding = PaddingMode.None;
if (aes.BlockSize != 128) throw new ApplicationException("AES Blocksize is not 16 bytes");
var sendCtr = new AesCtr(aes, sendKey, sendIV);
var recvCtr = new AesCtr(aes, recvKey, recvIV);
var encrypted = (byte[])preamble.Clone(); var encrypted = (byte[])preamble.Clone();
sendCtr.EncryptDecrypt(encrypted, 64); sendCtr.EncryptDecrypt(encrypted, 64);
for (int i = 56; i < 64; i++) for (int i = 56; i < 64; i++)

View file

@ -41,6 +41,7 @@ namespace WTelegram
private readonly SHA256 _sha256 = SHA256.Create(); private readonly SHA256 _sha256 = SHA256.Create();
private string _pathname; private string _pathname;
private byte[] _apiHash; // used as AES key for encryption of session file private byte[] _apiHash; // used as AES key for encryption of session file
private static readonly Aes aes = Aes.Create();
internal static Session LoadOrCreate(string pathname, byte[] apiHash) internal static Session LoadOrCreate(string pathname, byte[] apiHash)
{ {
@ -67,7 +68,6 @@ namespace WTelegram
{ {
var input = File.ReadAllBytes(pathname); var input = File.ReadAllBytes(pathname);
using var sha256 = SHA256.Create(); using var sha256 = SHA256.Create();
using var aes = Aes.Create();
using var decryptor = aes.CreateDecryptor(apiHash, input[0..16]); using var decryptor = aes.CreateDecryptor(apiHash, input[0..16]);
var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16); var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16);
if (!sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32])) if (!sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32]))
@ -81,7 +81,6 @@ namespace WTelegram
var finalBlock = new byte[16]; var finalBlock = new byte[16];
var output = new byte[(16 + 32 + utf8Json.Length + 16) & ~15]; var output = new byte[(16 + 32 + utf8Json.Length + 16) & ~15];
Encryption.RNG.GetBytes(output, 0, 16); Encryption.RNG.GetBytes(output, 0, 16);
using var aes = Aes.Create();
using var encryptor = aes.CreateEncryptor(_apiHash, output[0..16]); using var encryptor = aes.CreateEncryptor(_apiHash, output[0..16]);
encryptor.TransformBlock(_sha256.ComputeHash(utf8Json), 0, 32, output, 16); encryptor.TransformBlock(_sha256.ComputeHash(utf8Json), 0, 32, output, 16);
encryptor.TransformBlock(utf8Json, 0, utf8Json.Length & ~15, output, 48); encryptor.TransformBlock(utf8Json, 0, utf8Json.Length & ~15, output, 48);