From 52fb2a783138a6c0de799d9a69093b275ebbc203 Mon Sep 17 00:00:00 2001 From: Wizou Date: Wed, 29 Sep 2021 04:38:39 +0200 Subject: [PATCH] Fix SHA corruption due to concurrent use of static instance --- src/Client.cs | 30 +++++++--- src/Encryption.cs | 142 +++++++++++++++++++++++----------------------- src/Session.cs | 6 +- 3 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/Client.cs b/src/Client.cs index c7b3242..80a03d1 100644 --- a/src/Client.cs +++ b/src/Client.cs @@ -48,6 +48,13 @@ namespace WTelegram private Task _connecting; private CancellationTokenSource _cts; private int _reactorReconnects = 0; +#if MTPROTO1 + private readonly SHA1 _sha1 = SHA1.Create(); + private readonly SHA1 _sha1Recv = SHA1.Create(); +#else + private readonly SHA256 _sha256 = SHA256.Create(); + private readonly SHA256 _sha256Recv = SHA256.Create(); +#endif /// Welcome to WTelegramClient! 😀 /// Config callback, is queried for: api_id, api_hash, session_pathname @@ -367,7 +374,11 @@ namespace WTelegram } else { - byte[] decrypted_data = EncryptDecryptMessage(data.AsSpan(24, dataLen - 24), false, _dcSession.AuthKey, data, 8); +#if MTPROTO1 + byte[] decrypted_data = EncryptDecryptMessage(data.AsSpan(24, dataLen - 24), false, _dcSession.AuthKey, data, 8, _sha1Recv); +#else + byte[] decrypted_data = EncryptDecryptMessage(data.AsSpan(24, dataLen - 24), false, _dcSession.AuthKey, data, 8, _sha256Recv); +#endif if (decrypted_data.Length < 36) // header below+ctorNb throw new ApplicationException($"Decrypted packet too small: {decrypted_data.Length}"); using var reader = new TL.BinaryReader(new MemoryStream(decrypted_data), this); @@ -393,14 +404,14 @@ namespace WTelegram return null; #if MTPROTO1 if (decrypted_data.Length - 32 - length is < 0 or > 15) throw new ApplicationException($"Unexpected decrypted message_data_length {length} / {decrypted_data.Length - 32}"); - if (!data.AsSpan(8, 16).SequenceEqual(Sha1Recv.ComputeHash(decrypted_data, 0, 32 + length).AsSpan(4))) + if (!data.AsSpan(8, 16).SequenceEqual(_sha1Recv.ComputeHash(decrypted_data, 0, 32 + length).AsSpan(4))) throw new ApplicationException($"Mismatch between MsgKey & decrypted SHA1"); #else if (decrypted_data.Length - 32 - length is < 12 or > 1024) throw new ApplicationException($"Unexpected decrypted message_data_length {length} / {decrypted_data.Length - 32}"); - Sha256Recv.Initialize(); - Sha256Recv.TransformBlock(_dcSession.AuthKey, 96, 32, null, 0); - Sha256Recv.TransformFinalBlock(decrypted_data, 0, decrypted_data.Length); - if (!data.AsSpan(8, 16).SequenceEqual(Sha256Recv.Hash.AsSpan(8, 16))) + _sha256Recv.Initialize(); + _sha256Recv.TransformBlock(_dcSession.AuthKey, 96, 32, null, 0); + _sha256Recv.TransformFinalBlock(decrypted_data, 0, decrypted_data.Length); + if (!data.AsSpan(8, 16).SequenceEqual(_sha256Recv.Hash.AsSpan(8, 16))) throw new ApplicationException($"Mismatch between MsgKey & decrypted SHA1"); #endif var ctorNb = reader.ReadUInt32(); @@ -497,13 +508,14 @@ namespace WTelegram BinaryPrimitives.WriteInt32LittleEndian(clearBuffer.AsSpan(prepend + 28), clearLength - 32); // patch message_data_length RNG.GetBytes(clearBuffer, prepend + clearLength, padding); #if MTPROTO1 - var msgKeyLarge = Sha1.ComputeHash(clearBuffer, 0, clearLength); // padding excluded from computation! + var msgKeyLarge = _sha1.ComputeHash(clearBuffer, 0, clearLength); // padding excluded from computation! const int msgKeyOffset = 4; // msg_key = low 128-bits of SHA1(plaintext) + byte[] encrypted_data = EncryptDecryptMessage(clearBuffer.AsSpan(prepend, clearLength + padding), true, _dcSession.AuthKey, msgKeyLarge, msgKeyOffset, _sha1); #else - var msgKeyLarge = Sha256.ComputeHash(clearBuffer, 0, prepend + clearLength + padding); + var msgKeyLarge = _sha256.ComputeHash(clearBuffer, 0, prepend + clearLength + padding); const int msgKeyOffset = 8; // msg_key = middle 128-bits of SHA256(authkey_part+plaintext+padding) + byte[] encrypted_data = EncryptDecryptMessage(clearBuffer.AsSpan(prepend, clearLength + padding), true, _dcSession.AuthKey, msgKeyLarge, msgKeyOffset, _sha256); #endif - byte[] encrypted_data = EncryptDecryptMessage(clearBuffer.AsSpan(prepend, clearLength + padding), true, _dcSession.AuthKey, msgKeyLarge, msgKeyOffset); writer.Write(_dcSession.AuthKeyID); // int64 auth_key_id writer.Write(msgKeyLarge, msgKeyOffset, 16); // int128 msg_key diff --git a/src/Encryption.cs b/src/Encryption.cs index c567f4d..fc7161c 100644 --- a/src/Encryption.cs +++ b/src/Encryption.cs @@ -15,18 +15,13 @@ namespace WTelegram internal static class Encryption { internal static readonly RNGCryptoServiceProvider RNG = new(); - internal static readonly SHA1 Sha1 = SHA1.Create(); - internal static readonly SHA256 Sha256 = SHA256.Create(); -#if MTPROTO1 - internal static readonly SHA1 Sha1Recv = SHA1.Create(); -#else - internal static readonly SHA256 Sha256Recv = SHA256.Create(); -#endif private static readonly Dictionary PublicKeys = new(); internal static async Task CreateAuthorizationKey(Client client, Session.DCSession session) { if (PublicKeys.Count == 0) LoadDefaultPublicKeys(); + var sha1 = SHA1.Create(); + var sha256 = SHA256.Create(); //1) var nonce = new Int128(RNG); @@ -68,7 +63,7 @@ namespace WTelegram if (clearLength > 255) throw new ApplicationException("PQInnerData too big"); byte[] clearBuffer = clearStream.GetBuffer(); RNG.GetBytes(clearBuffer, clearLength, 255 - clearLength); - Sha1.ComputeHash(clearBuffer, 20, clearLength - 20).CopyTo(clearBuffer, 0); // patch with SHA1 + sha1.ComputeHash(clearBuffer, 20, clearLength - 20).CopyTo(clearBuffer, 0); // patch with SHA1 encrypted_data = BigInteger.ModPow(BigEndianInteger(clearBuffer), // encrypt with RSA key BigEndianInteger(publicKey.e), BigEndianInteger(publicKey.n)).ToByteArray(true, true); #else @@ -87,10 +82,10 @@ namespace WTelegram if (clearLength > 144) throw new ApplicationException("PQInnerData too big"); byte[] clearBuffer = clearStream.GetBuffer(); RNG.GetBytes(clearBuffer, 32 + clearLength, 192 - clearLength); - Sha256.ComputeHash(clearBuffer, 0, 32 + 192).CopyTo(clearBuffer, 224); // append Sha256 + sha256.ComputeHash(clearBuffer, 0, 32 + 192).CopyTo(clearBuffer, 224); // append Sha256 Array.Reverse(clearBuffer, 32, 192); var aes_encrypted = AES_IGE_EncryptDecrypt(clearBuffer.AsSpan(32, 224), aes_key, zero_iv, true); - var hash_aes = Sha256.ComputeHash(aes_encrypted); + var hash_aes = sha256.ComputeHash(aes_encrypted); for (int i = 0; i < 32; i++) // prefix aes_encrypted with temp_key_xor clearBuffer[i] = (byte)(aes_key[i] ^ hash_aes[i]); aes_encrypted.CopyTo(clearBuffer, 32); @@ -115,7 +110,7 @@ namespace WTelegram if (answerObj is not ServerDHInnerData serverDHinnerData) throw new ApplicationException("not server_DH_inner_data"); long padding = encryptedReader.BaseStream.Length - encryptedReader.BaseStream.Position; if (padding >= 16) throw new ApplicationException("Too much pad"); - if (!Enumerable.SequenceEqual(Sha1.ComputeHash(answer, 20, answer.Length - (int)padding - 20), answerHash)) + if (!Enumerable.SequenceEqual(sha1.ComputeHash(answer, 20, answer.Length - (int)padding - 20), answerHash)) throw new ApplicationException("Answer SHA1 mismatch"); if (serverDHinnerData.nonce != nonce) throw new ApplicationException("Nonce mismatch"); if (serverDHinnerData.server_nonce != resPQ.server_nonce) throw new ApplicationException("Server Nonce mismatch"); @@ -147,7 +142,7 @@ namespace WTelegram clearStream.SetLength(clearLength + paddingToAdd); byte[] clearBuffer = clearStream.GetBuffer(); RNG.GetBytes(clearBuffer, clearLength, paddingToAdd); - Sha1.ComputeHash(clearBuffer, 20, clearLength - 20).CopyTo(clearBuffer, 0); + sha1.ComputeHash(clearBuffer, 20, clearLength - 20).CopyTo(clearBuffer, 0); encrypted_data = AES_IGE_EncryptDecrypt(clearBuffer.AsSpan(0, clearLength + paddingToAdd), tmp_aes_key, tmp_aes_iv, true); } @@ -156,7 +151,7 @@ namespace WTelegram var gab = BigInteger.ModPow(g_a, b, dh_prime); var authKey = gab.ToByteArray(true, true); //8) - var authKeyHash = Sha1.ComputeHash(authKey); + var authKeyHash = sha1.ComputeHash(authKey); retry_id = BinaryPrimitives.ReadInt64LittleEndian(authKeyHash); // (auth_key_aux_hash) //9) if (setClientDHparamsAnswer is not DhGenOk dhGenOk) throw new ApplicationException("not dh_gen_ok"); @@ -166,29 +161,29 @@ namespace WTelegram pqInnerData.new_nonce.raw.CopyTo(expected_new_nonceN, 0); expected_new_nonceN[32] = 1; Array.Copy(authKeyHash, 0, expected_new_nonceN, 33, 8); // (auth_key_aux_hash) - if (!Enumerable.SequenceEqual(dhGenOk.new_nonce_hash1.raw, Sha1.ComputeHash(expected_new_nonceN).Skip(4))) + if (!Enumerable.SequenceEqual(dhGenOk.new_nonce_hash1.raw, sha1.ComputeHash(expected_new_nonceN).Skip(4))) throw new ApplicationException("setClientDHparamsAnswer.new_nonce_hashN mismatch"); session.AuthKeyID = BinaryPrimitives.ReadInt64LittleEndian(authKeyHash.AsSpan(12)); session.AuthKey = authKey; session.Salt = BinaryPrimitives.ReadInt64LittleEndian(pqInnerData.new_nonce.raw) ^ BinaryPrimitives.ReadInt64LittleEndian(resPQ.server_nonce.raw); - static (byte[] key, byte[] iv) ConstructTmpAESKeyIV(Int128 server_nonce, Int256 new_nonce) + (byte[] key, byte[] iv) ConstructTmpAESKeyIV(Int128 server_nonce, Int256 new_nonce) { byte[] tmp_aes_key = new byte[32], tmp_aes_iv = new byte[32]; - Sha1.Initialize(); - Sha1.TransformBlock(new_nonce, 0, 32, null, 0); - Sha1.TransformFinalBlock(server_nonce, 0, 16); - Sha1.Hash.CopyTo(tmp_aes_key, 0); // tmp_aes_key := SHA1(new_nonce + server_nonce) - Sha1.Initialize(); - Sha1.TransformBlock(server_nonce, 0, 16, null, 0); - Sha1.TransformFinalBlock(new_nonce, 0, 32); - Array.Copy(Sha1.Hash, 0, tmp_aes_key, 20, 12); // + SHA1(server_nonce, new_nonce)[0:12] - Array.Copy(Sha1.Hash, 12, tmp_aes_iv, 0, 8); // tmp_aes_iv != SHA1(server_nonce, new_nonce)[12:8] - Sha1.Initialize(); - Sha1.TransformBlock(new_nonce, 0, 32, null, 0); - Sha1.TransformFinalBlock(new_nonce, 0, 32); - Sha1.Hash.CopyTo(tmp_aes_iv, 8); // + SHA(new_nonce + new_nonce) + sha1.Initialize(); + sha1.TransformBlock(new_nonce, 0, 32, null, 0); + sha1.TransformFinalBlock(server_nonce, 0, 16); + sha1.Hash.CopyTo(tmp_aes_key, 0); // tmp_aes_key := SHA1(new_nonce + server_nonce) + sha1.Initialize(); + sha1.TransformBlock(server_nonce, 0, 16, null, 0); + sha1.TransformFinalBlock(new_nonce, 0, 32); + Array.Copy(sha1.Hash, 0, tmp_aes_key, 20, 12); // + SHA1(server_nonce, new_nonce)[0:12] + Array.Copy(sha1.Hash, 12, tmp_aes_iv, 0, 8); // tmp_aes_iv != SHA1(server_nonce, new_nonce)[12:8] + sha1.Initialize(); + sha1.TransformBlock(new_nonce, 0, 32, null, 0); + sha1.TransformFinalBlock(new_nonce, 0, 32); + sha1.Hash.CopyTo(tmp_aes_iv, 8); // + SHA(new_nonce + new_nonce) Array.Copy(new_nonce, 0, tmp_aes_iv, 28, 4); // + new_nonce[0:4] return (tmp_aes_key, tmp_aes_iv); } @@ -233,11 +228,12 @@ namespace WTelegram public static void LoadPublicKey(string pem) { using var rsa = RSA.Create(); + using var sha1 = SHA1.Create(); rsa.ImportFromPem(pem); var rsaParam = rsa.ExportParameters(false); var publicKey = new RSAPublicKey { n = rsaParam.Modulus, e = rsaParam.Exponent }; var bareData = publicKey.Serialize(); // bare serialization - var fingerprint = BinaryPrimitives.ReadInt64LittleEndian(Sha1.ComputeHash(bareData, 4, bareData.Length - 4).AsSpan(12)); // 64 lower-order bits of SHA1 + var fingerprint = BinaryPrimitives.ReadInt64LittleEndian(sha1.ComputeHash(bareData, 4, bareData.Length - 4).AsSpan(12)); // 64 lower-order bits of SHA1 PublicKeys[fingerprint] = publicKey; Helpers.Log(1, $"Loaded a public key with fingerprint {fingerprint:X}"); } @@ -276,13 +272,12 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB #endif } - internal static byte[] EncryptDecryptMessage(Span input, bool encrypt, byte[] authKey, byte[] msgKey, int msgKeyOffset) +#if MTPROTO1 + internal static byte[] EncryptDecryptMessage(Span input, bool encrypt, byte[] authKey, byte[] msgKey, int msgKeyOffset, SHA1 sha1) { // first, construct AES key & IV byte[] aes_key = new byte[32], aes_iv = new byte[32]; int x = encrypt ? 0 : 8; -#if MTPROTO1 - var sha1 = encrypt ? Sha1 : Sha1Recv; sha1.Initialize(); sha1.TransformBlock(msgKey, msgKeyOffset, 16, null, 0); // msgKey sha1.TransformFinalBlock(authKey, x, 32); // authKey[x:32] @@ -307,8 +302,14 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB Array.Copy(sha1_b, 0, aes_iv, 12, 8); Array.Copy(sha1_c, 16, aes_iv, 20, 4); Array.Copy(sha1_d, 0, aes_iv, 24, 8); + return AES_IGE_EncryptDecrypt(input, aes_key, aes_iv, encrypt); + } #else - var sha256 = encrypt ? Sha256 : Sha256Recv; + internal static byte[] EncryptDecryptMessage(Span input, bool encrypt, byte[] authKey, byte[] msgKey, int msgKeyOffset, SHA256 sha256) + { + // first, construct AES key & IV + byte[] aes_key = new byte[32], aes_iv = new byte[32]; + int x = encrypt ? 0 : 8; sha256.Initialize(); sha256.TransformBlock(msgKey, msgKeyOffset, 16, null, 0); // msgKey sha256.TransformFinalBlock(authKey, x, 36); // authKey[x:36] @@ -323,9 +324,9 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB Array.Copy(sha256_b, 0, aes_iv, 0, 8); Array.Copy(sha256_a, 8, aes_iv, 8, 16); Array.Copy(sha256_b, 24, aes_iv, 24, 8); -#endif return AES_IGE_EncryptDecrypt(input, aes_key, aes_iv, encrypt); } +#endif private static byte[] AES_IGE_EncryptDecrypt(Span input, byte[] aes_key, byte[] aes_iv, bool encrypt) { @@ -372,32 +373,33 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB var g_256 = g.To256Bytes(); ValidityChecks(p, algo.g); - Sha256.Initialize(); - Sha256.TransformBlock(algo.salt1, 0, algo.salt1.Length, null, 0); - Sha256.TransformBlock(passwordBytes, 0, passwordBytes.Length, null, 0); - Sha256.TransformFinalBlock(algo.salt1, 0, algo.salt1.Length); - var hash = Sha256.Hash; - Sha256.Initialize(); - Sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0); - Sha256.TransformBlock(hash, 0, 32, null, 0); - Sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length); - hash = Sha256.Hash; + using var sha256 = SHA256.Create(); + sha256.Initialize(); + sha256.TransformBlock(algo.salt1, 0, algo.salt1.Length, null, 0); + sha256.TransformBlock(passwordBytes, 0, passwordBytes.Length, null, 0); + sha256.TransformFinalBlock(algo.salt1, 0, algo.salt1.Length); + var hash = sha256.Hash; + sha256.Initialize(); + sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0); + sha256.TransformBlock(hash, 0, 32, null, 0); + sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length); + hash = sha256.Hash; #if NETCOREAPP2_0_OR_GREATER using var derive = new Rfc2898DeriveBytes(hash, algo.salt1, 100000, HashAlgorithmName.SHA512); var pbkdf2 = derive.GetBytes(64); #else var pbkdf2 = PBKDF2_SHA512(hash, algo.salt1, 100000, 64); #endif - Sha256.Initialize(); - Sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0); - Sha256.TransformBlock(pbkdf2, 0, 64, null, 0); - Sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length); - var x = BigEndianInteger(Sha256.Hash); + sha256.Initialize(); + sha256.TransformBlock(algo.salt2, 0, algo.salt2.Length, null, 0); + sha256.TransformBlock(pbkdf2, 0, 64, null, 0); + sha256.TransformFinalBlock(algo.salt2, 0, algo.salt2.Length); + var x = BigEndianInteger(sha256.Hash); - Sha256.Initialize(); - Sha256.TransformBlock(algo.p, 0, 256, null, 0); - Sha256.TransformFinalBlock(g_256, 0, 256); - var k = BigEndianInteger(Sha256.Hash); + sha256.Initialize(); + sha256.TransformBlock(algo.p, 0, 256, null, 0); + sha256.TransformFinalBlock(g_256, 0, 256); + var k = BigEndianInteger(sha256.Hash); var v = BigInteger.ModPow(g, x, p); var k_v = (k * v) % p; @@ -405,29 +407,29 @@ j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB var g_a = BigInteger.ModPow(g, a, p); var g_a_256 = g_a.To256Bytes(); - Sha256.Initialize(); - Sha256.TransformBlock(g_a_256, 0, 256, null, 0); - Sha256.TransformFinalBlock(g_b_256, 0, 256); - var u = BigEndianInteger(Sha256.Hash); + sha256.Initialize(); + sha256.TransformBlock(g_a_256, 0, 256, null, 0); + sha256.TransformFinalBlock(g_b_256, 0, 256); + var u = BigEndianInteger(sha256.Hash); var t = (g_b - k_v) % p; //(positive modulo, if the result is negative increment by p) if (t.Sign < 0) t += p; var s_a = BigInteger.ModPow(t, a + u * x, p); - var k_a = Sha256.ComputeHash(s_a.To256Bytes()); + var k_a = sha256.ComputeHash(s_a.To256Bytes()); - hash = Sha256.ComputeHash(algo.p); - var h2 = Sha256.ComputeHash(g_256); + hash = sha256.ComputeHash(algo.p); + var h2 = sha256.ComputeHash(g_256); for (int i = 0; i < 32; i++) hash[i] ^= h2[i]; - var hs1 = Sha256.ComputeHash(algo.salt1); - var hs2 = Sha256.ComputeHash(algo.salt2); - Sha256.Initialize(); - Sha256.TransformBlock(hash, 0, 32, null, 0); - Sha256.TransformBlock(hs1, 0, 32, null, 0); - Sha256.TransformBlock(hs2, 0, 32, null, 0); - Sha256.TransformBlock(g_a_256, 0, 256, null, 0); - Sha256.TransformBlock(g_b_256, 0, 256, null, 0); - Sha256.TransformFinalBlock(k_a, 0, 32); - var m1 = Sha256.Hash; + var hs1 = sha256.ComputeHash(algo.salt1); + var hs2 = sha256.ComputeHash(algo.salt2); + sha256.Initialize(); + sha256.TransformBlock(hash, 0, 32, null, 0); + sha256.TransformBlock(hs1, 0, 32, null, 0); + sha256.TransformBlock(hs2, 0, 32, null, 0); + sha256.TransformBlock(g_a_256, 0, 256, null, 0); + sha256.TransformBlock(g_b_256, 0, 256, null, 0); + sha256.TransformFinalBlock(k_a, 0, 32); + var m1 = sha256.Hash; return new InputCheckPasswordSRP { A = g_a_256, M1 = m1, srp_id = accountPassword.srp_id }; } diff --git a/src/Session.cs b/src/Session.cs index ff112ee..b177293 100644 --- a/src/Session.cs +++ b/src/Session.cs @@ -35,6 +35,7 @@ namespace WTelegram public DateTime SessionStart => _sessionStart; public readonly SemaphoreSlim _sem = new(1); private readonly DateTime _sessionStart = DateTime.UtcNow; + private readonly SHA256 _sha256 = SHA256.Create(); private string _pathname; private byte[] _apiHash; // used as AES key for encryption of session file @@ -69,10 +70,11 @@ namespace WTelegram internal static Session Load(string pathname, byte[] apiHash) { var input = File.ReadAllBytes(pathname); + using var sha256 = SHA256.Create(); using var aes = Aes.Create(); using var decryptor = aes.CreateDecryptor(apiHash, input[0..16]); var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16); - if (!Encryption.Sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32])) + if (!sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32])) throw new ApplicationException("Integrity check failed in session loading"); return JsonSerializer.Deserialize(utf8Json.AsSpan(32), JsonOptions); } @@ -85,7 +87,7 @@ namespace WTelegram Encryption.RNG.GetBytes(output, 0, 16); using var aes = Aes.Create(); using var encryptor = aes.CreateEncryptor(_apiHash, output[0..16]); - encryptor.TransformBlock(Encryption.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); utf8Json.AsSpan(utf8Json.Length & ~15).CopyTo(finalBlock); encryptor.TransformFinalBlock(finalBlock, 0, utf8Json.Length & 15).CopyTo(output.AsMemory(48 + utf8Json.Length & ~15));