// // CryptographyManager.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Buffers; using System.Text; namespace NosSmooth.Cryptography; /// /// A storage of server and client cryptography. /// public class CryptographyManager { /// /// Initializes a new instance of the class. /// public CryptographyManager() { ServerWorld = new ServerWorldCryptography(0); ServerLogin = new ServerLoginCryptography(); ClientLogin = new ClientLoginCryptography(); ClientWorld = new ClientWorldCryptography(0); } /// /// Gets the cryptography for server world. /// public ICryptography ServerWorld { get; } /// /// Gets the cryptography for server login. /// public ICryptography ServerLogin { get; } /// /// Gets the cryptography for client world. /// public ICryptography ClientWorld { get; } /// /// Gets the cryptography for client login. /// public ICryptography ClientLogin { get; } /// /// Gets or sets the encryption key of the connection. /// public int EncryptionKey { get => ((ServerWorldCryptography)ServerWorld).EncryptionKey; set { ((ServerWorldCryptography)ServerWorld).EncryptionKey = value; ((ClientWorldCryptography)ClientWorld).EncryptionKey = value; } } /// /// Attempts to decrypt a packet that was received by the client. May be login or world packet. /// Encryption key is not needed. /// /// /// Expects only failc or NsTeST for login packets. /// /// The received data. /// The encoding. /// Decrypted packet or empty string in case of fail. public string DecryptUnknownClientPacket(in ReadOnlySpan data, Encoding encoding) { if (data.Length < 6) { return ClientWorld.Decrypt(data, encoding); } var firstCharDecrypted = ClientLogin.Decrypt(data.Slice(0, 1), encoding); if (firstCharDecrypted.StartsWith("f") || firstCharDecrypted.StartsWith("N")) { var beginning = data.Slice(0, 6); var beginningLoginDecrypted = ClientLogin.Decrypt(beginning, encoding); if (beginningLoginDecrypted.StartsWith("failc") || beginningLoginDecrypted.StartsWith("NsTeST")) { return ClientLogin.Decrypt(data, encoding); } } return ClientWorld.Decrypt(data, encoding); } /// /// Attempts to decrypt a packet that was received by the server. May be login or world packet. /// Encryption key is needed for world packets. If the packet contains session id, /// it will be saved to . /// /// /// Expects only failc or NsTeST for login packets. /// /// The received data. /// The encoding. /// Decrypted packet or empty string in case of fail. public string DecryptUnknownServerPacket(in ReadOnlySpan data, Encoding encoding) { var firstCharDecrypted = ClientLogin.Decrypt(data.Slice(0, 1), encoding); if (firstCharDecrypted.StartsWith("N") || firstCharDecrypted.StartsWith("f") || firstCharDecrypted.StartsWith("c")) { var beginning = data.Slice(0, 4); var beginningLoginDecrypted = ClientLogin.Decrypt(beginning, encoding); if (beginningLoginDecrypted.StartsWith("NoS0") || beginningLoginDecrypted.StartsWith("f_st") || beginningLoginDecrypted.StartsWith("c_cl")) { return ServerLogin.Decrypt(data, encoding); } } var decryptedString = ServerWorld.Decrypt(data, encoding); if (EncryptionKey == 0) { // we are not in a session, so the packet may be the session id. // or we are in an initialized session and won't know the encryption key... var decryptedSpan = (ReadOnlySpan)decryptedString; var firstSpaceIndex = decryptedSpan.IndexOf(' '); if (firstSpaceIndex != -1) { if (int.TryParse(decryptedSpan.Slice(firstSpaceIndex + 1), out var obtainedEncryptionKey)) { EncryptionKey = obtainedEncryptionKey; } } } return decryptedString; } }