~ruther/NosSmooth

ref: ed76c27b262982f8550d615a3ed83f669c72f2d5 NosSmooth/Core/NosSmooth.Cryptography/CryptographyManager.cs -rw-r--r-- 4.8 KiB
ed76c27b — Rutherther docs: fix MateWalkCommand header 2 years ago
                                                                                
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
142
143
144
145
146
//
//  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;

/// <summary>
/// A storage of server and client cryptography.
/// </summary>
public class CryptographyManager
{
    /// <summary>
    /// Initializes a new instance of the <see cref="CryptographyManager"/> class.
    /// </summary>
    public CryptographyManager()
    {
        ServerWorld = new ServerWorldCryptography(0);
        ServerLogin = new ServerLoginCryptography();
        ClientLogin = new ClientLoginCryptography();
        ClientWorld = new ClientWorldCryptography(0);
    }

    /// <summary>
    /// Gets the cryptography for server world.
    /// </summary>
    public ICryptography ServerWorld { get; }

    /// <summary>
    /// Gets the cryptography for server login.
    /// </summary>
    public ICryptography ServerLogin { get; }

    /// <summary>
    /// Gets the cryptography for client world.
    /// </summary>
    public ICryptography ClientWorld { get; }

    /// <summary>
    /// Gets the cryptography for client login.
    /// </summary>
    public ICryptography ClientLogin { get; }

    /// <summary>
    /// Gets or sets the encryption key of the connection.
    /// </summary>
    public int EncryptionKey
    {
        get => ((ServerWorldCryptography)ServerWorld).EncryptionKey;
        set
        {
            ((ServerWorldCryptography)ServerWorld).EncryptionKey = value;
            ((ClientWorldCryptography)ClientWorld).EncryptionKey = value;
        }
    }

    /// <summary>
    /// Attempts to decrypt a packet that was received by the client. May be login or world packet.
    /// Encryption key is not needed.
    /// </summary>
    /// <remarks>
    /// Expects only failc or NsTeST for login packets.
    /// </remarks>
    /// <param name="data">The received data.</param>
    /// <param name="encoding">The encoding.</param>
    /// <returns>Decrypted packet or empty string in case of fail.</returns>
    public string DecryptUnknownClientPacket(in ReadOnlySpan<byte> 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);
    }

    /// <summary>
    /// 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 <see cref="EncryptionKey"/>.
    /// </summary>
    /// <remarks>
    /// Expects only failc or NsTeST for login packets.
    /// </remarks>
    /// <param name="data">The received data.</param>
    /// <param name="encoding">The encoding.</param>
    /// <returns>Decrypted packet or empty string in case of fail.</returns>
    public string DecryptUnknownServerPacket(in ReadOnlySpan<byte> 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<char>)decryptedString;

          var firstSpaceIndex = decryptedSpan.IndexOf(' ');
          if (firstSpaceIndex != -1)
          {
              if (int.TryParse(decryptedSpan.Slice(firstSpaceIndex + 1), out var obtainedEncryptionKey))
              {
                  EncryptionKey = obtainedEncryptionKey;
              }
          }

        }

        return decryptedString;
    }
}
Do not follow this link