using System; using System.Security.Cryptography; using System.Text; using NSec.Cryptography; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Crypto.Parameters; namespace Novaria.Common.Crypto { public static class AeadTool { public static ClientType clientType = ClientType.PC; // PC uses AESGCM while Android uses chacha20 (Aes256Gcm not supported on android ig) // for now set this field when switching, TODO: implement auto client detection from sdk auth request public static AeadTool.AeadEncryptHandler Encrypt; public static AeadTool.AeadDecryptHandler Decrypt; private static readonly byte[] associatedData = new byte[13]; public static readonly int NonceSize = 12; public static readonly int MacSize = 128; public static readonly int KeySize = 32; public static readonly int IVSize = 16; public static byte[] PS_REQUEST_NONCE = new byte[12]; // hardcoded nonce, probably makes it easier for server idk? could also just randomly generate but me lazy public static byte[] PS_PUBLIC_KEY { get => DiffieHellman.Instance.ServerPublicKey.ToByteArray(); } public static string TOKEN = "seggs"; // hardcoded for now public static readonly byte[] DEFAULT_SERVERLIST_KEY = new byte[] { 74, 72, 42, 67, 80, 51, 50, 57, 89, 120, 111, 103, 81, 74, 69, 120 }; public static readonly byte[] DEFAULT_SERVERLIST_IV = new byte[] { 225, 92, 61, 72, 193, 89, 3, 64, 50, 61, 50, 145, 59, 128, 99, 72 }; public static readonly byte[] DEFAULT_AND_IV = new byte[] { 105, 7, 110, 72, 167, 117, 102, 212, 150, 44, 52, 229, 65, 61, 204, 205 }; public static readonly byte[] DEFAULT_WIN_IV = new byte[] { 144, 129, 81, 233, 8, 4, 33, 39, 106, 181, 229, 64, 68, 134, 31, 107 }; public static readonly byte[] IKE_KEY = Encoding.ASCII.GetBytes("3LS9&oYdsp^5wi8&ZxC#c7MZg73hbEDw"); private static GcmBlockCipher cipher; private static AesEngine engine; public delegate void AeadEncryptHandler(Span result, ReadOnlySpan key, ReadOnlySpan nonce, ReadOnlySpan data, bool needAssociatedData); public delegate bool AeadDecryptHandler(Span result, ReadOnlySpan key, ReadOnlySpan nonce, ReadOnlySpan data, Span associated); public static ReadOnlySpan AEADMARK { get { return AeadTool.associatedData.AsSpan(12, 1); } } public static byte[] key3 { get; set; } static AeadTool() { PS_REQUEST_NONCE[0] = 42; PS_REQUEST_NONCE[11] = 42; InitAeadTool(); } public static void InitAeadTool() { if (clientType == ClientType.PC) { AeadTool.InitBouncyCastle(); AeadTool.Encrypt = new AeadTool.AeadEncryptHandler(AeadTool.Encrypt_AESGCM_BouncyCastle); AeadTool.Decrypt = new AeadTool.AeadDecryptHandler(AeadTool.Dencrypt_AESGCM_BouncyCastle); AeadTool.associatedData[AeadTool.associatedData.Length - 1] = 2; Console.WriteLine("Select PC client, now using AESGCM"); return; } AeadTool.Encrypt = new AeadTool.AeadEncryptHandler(AeadTool.Encrypt_ChaCha20); AeadTool.Decrypt = new AeadTool.AeadDecryptHandler(AeadTool.Dencrypt_ChaCha20); AeadTool.associatedData[AeadTool.associatedData.Length - 1] = 1; Console.WriteLine("Select Mobile client, now using ChaCha20"); } public static void InitBouncyCastle() { AeadTool.engine = new AesEngine(); AeadTool.cipher = new GcmBlockCipher(AeadTool.engine); } private static void Encrypt_AESGCM_BouncyCastle(Span result, ReadOnlySpan key, ReadOnlySpan nonce, ReadOnlySpan data, bool needAssociatedData) { if (!needAssociatedData) { byte[] array = AeadTool.Encrypt_BouncyCastle(key, nonce, data, null); result.Clear(); MemoryExtensions.CopyTo(array, result); return; } nonce.CopyTo(MemoryExtensions.AsSpan(AeadTool.associatedData, 0, 12)); Span associated = MemoryExtensions.AsSpan(AeadTool.associatedData); byte[] array2 = AeadTool.Encrypt_BouncyCastle(key, nonce, data, associated); result.Clear(); MemoryExtensions.CopyTo(array2, result); } private static bool Dencrypt_AESGCM_BouncyCastle(Span result, ReadOnlySpan key, ReadOnlySpan nonce, ReadOnlySpan data, Span associated) { bool result2; try { byte[] array = AeadTool.Decrypt_BouncyCastle(key.ToArray(), nonce, data.ToArray(), associated); result.Clear(); MemoryExtensions.CopyTo(array, result); result2 = true; } catch (Exception ex) { Console.WriteLine(ex.Message); result2 = false; } return result2; } private static void Encrypt_ChaCha20(Span result, ReadOnlySpan key, ReadOnlySpan nonce, ReadOnlySpan data, bool needAssociatedData) { Algorithm chaCha20Poly = AeadAlgorithm.ChaCha20Poly1305; KeyBlobFormat keyBlobFormat = KeyBlobFormat.RawSymmetricKey; KeyCreationParameters keyCreationParameters = default(KeyCreationParameters); using (Key key2 = Key.Import(chaCha20Poly, key, keyBlobFormat, ref keyCreationParameters)) { if (!needAssociatedData) { AeadAlgorithm.ChaCha20Poly1305.Encrypt(key2, nonce, null, data, result); } else { nonce.CopyTo(AeadTool.associatedData.AsSpan(0, 12)); AeadAlgorithm.ChaCha20Poly1305.Encrypt(key2, nonce, AeadTool.associatedData.AsSpan(), data, result); } } } private static bool Dencrypt_ChaCha20(Span result, ReadOnlySpan key, ReadOnlySpan nonce, ReadOnlySpan data, Span associatedData) { Algorithm chaCha20Poly = AeadAlgorithm.ChaCha20Poly1305; KeyBlobFormat keyBlobFormat = KeyBlobFormat.RawSymmetricKey; KeyCreationParameters keyCreationParameters = default(KeyCreationParameters); bool result2; using (Key key2 = Key.Import(chaCha20Poly, key, keyBlobFormat, ref keyCreationParameters)) { result2 = AeadAlgorithm.ChaCha20Poly1305.Decrypt(key2, nonce, associatedData, data, result); } return result2; } // probably wrong public static byte[] DecryptAesCBCInfo(byte[] key, byte[] IV, byte[] cipherBytes) { PaddedBufferedBlockCipher paddedBufferedBlockCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()), new Pkcs7Padding()); ParametersWithIV parametersWithIV = new ParametersWithIV(new KeyParameter(key), IV); paddedBufferedBlockCipher.Init(false, parametersWithIV); byte[] array = new byte[paddedBufferedBlockCipher.GetOutputSize(cipherBytes.Length)]; int num = paddedBufferedBlockCipher.ProcessBytes(cipherBytes, array, 0); paddedBufferedBlockCipher.DoFinal(array, num); return array; } public static byte[] EncryptAesCBCInfo(byte[] key, byte[] IV, byte[] plainBytes) { using (Aes aes = Aes.Create()) { aes.Key = key; aes.IV = IV; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using (ICryptoTransform encryptor = aes.CreateEncryptor()) { return encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length); } } } private static byte[] Encrypt_BouncyCastle(ReadOnlySpan key, ReadOnlySpan nonce, ReadOnlySpan secretMessage, Span associated) { if (key == null || key.Length != AeadTool.KeySize) { throw new ArgumentException(string.Format("Key needs to be {0} bit!", AeadTool.KeySize), "key"); } if (secretMessage == null || secretMessage.Length == 0) { throw new ArgumentException("Secret Message Required!", "secretMessage"); } if (AeadTool.cipher == null) { Console.WriteLine("Must Init:BouncyCastleAesGcm"); return null; } AeadParameters aeadParameters = new AeadParameters(new KeyParameter(key.ToArray()), AeadTool.MacSize, nonce.ToArray(), associated.ToArray()); AeadTool.cipher.Init(true, aeadParameters); byte[] array = new byte[AeadTool.cipher.GetOutputSize(secretMessage.Length)]; int num = AeadTool.cipher.ProcessBytes(secretMessage.ToArray(), 0, secretMessage.Length, array, 0); AeadTool.cipher.DoFinal(array, num); return array; } private static byte[] Decrypt_BouncyCastle(byte[] key, ReadOnlySpan nonce, byte[] cipherText, Span associated) { if (cipherText == null || cipherText.Length == 0) { throw new ArgumentException("Encrypted Message Required!", "encryptedMessage"); } if (AeadTool.cipher == null) { throw new ArgumentException("BouncyCastleAesGcm must be initialized first"); } AeadParameters aeadParameters = new AeadParameters(new KeyParameter(key), AeadTool.MacSize, nonce.ToArray(), associated.ToArray()); AeadTool.cipher.Init(false, aeadParameters); byte[] array = new byte[AeadTool.cipher.GetOutputSize(cipherText.Length)]; int num = AeadTool.cipher.ProcessBytes(cipherText, 0, cipherText.Length, array, 0); AeadTool.cipher.DoFinal(array, num); return array; } } public enum ClientType { PC, Mobile, } }