Giter Site home page Giter Site logo

Comments (6)

clugh avatar clugh commented on July 26, 2024

Yes, you could use PublicKeyBox.GenerateNonce() for both the "sessionkey" from 20100 and servernonce from 20104. For the sharedkey in 20104, you could use SecretBox.GenerateKey().

from coc-proxy-csharp.

zyxwvuts avatar zyxwvuts commented on July 26, 2024

Thanks for your help - I've spent hours trying to figure this out and I'll bet you will know right off the top of your head. I think I have my ServerState object wrong - probably the nonce.

I made a simplified version of what I am trying to do. It basically adds a couple of hooks in ServerCrypto to call a PrivateServer class that will figure out the message to return. Right now, if you set PrivateServer.privateServer to false, and it will behave like your proxy except it will also write the messages to file (unencrypted). When you set PrivateServer.privateServer to true, it will respond to 10100 and 10101 up until the point we send OwnHomeData. This currently causes the client to crash, so I imagine that the encryption is incorrect.

Below is ServerCrypto, with my change blocks commented. Below that is the PrivateServer class. You have any idea what I am doing wrong?

using System;
using System.Net.Sockets;
using System.Linq;
using Sodium;
using Newtonsoft.Json.Linq;

namespace coc_proxy_csharp
{
    public class ServerCrypto : Protocol
    {
        protected static KeyPair serverKey = PublicKeyBox.GenerateKeyPair(Utilities.HexToBinary("1891d401fadb51d25d3a9174d472a9f691a45b974285d47729c45c6538070d85"));

        public static void DecryptPacket(Socket socket, ServerState state, byte[] packet)
        {
            int messageId = BitConverter.ToInt32(new byte[2].Concat(packet.Take(2)).Reverse().ToArray(), 0);
            int payloadLength = BitConverter.ToInt32(new byte[1].Concat(packet.Skip(2).Take(3)).Reverse().ToArray(), 0);
            int unknown = BitConverter.ToInt32(new byte[2].Concat(packet.Skip(2).Skip(3).Take(2)).Reverse().ToArray(), 0);
            byte[] cipherText = packet.Skip(2).Skip(3).Skip(2).ToArray();
            byte[] plainText;

            if (messageId == 10100)
            {
                plainText = cipherText;
            }
            else if (messageId == 10101)
            {
                state.clientKey = cipherText.Take(32).ToArray();
                byte[] nonce = GenericHash.Hash(state.clientKey.Concat(state.serverKey.PublicKey).ToArray(), null, 24);
                cipherText = cipherText.Skip(32).ToArray();
                plainText = PublicKeyBox.Open(cipherText, nonce, state.serverKey.PrivateKey, state.clientKey);
                state.sessionKey = plainText.Take(24).ToArray();
                state.clientState.nonce = plainText.Skip(24).Take(24).ToArray();
                plainText = plainText.Skip(24).Skip(24).ToArray();
            }
            else
            {
                state.clientState.nonce = Utilities.Increment(Utilities.Increment(state.clientState.nonce));
                plainText = SecretBox.Open(new byte[16].Concat(cipherText).ToArray(), state.clientState.nonce, state.sharedKey);
            }
            try
            {
                JObject decoded = state.decoder.decode(messageId, unknown, plainText);
                Console.WriteLine("{0}: {1}", decoded["name"], decoded["fields"]);
// let private server reply to the message
                if (PrivateServer.privateServer)
                    PrivateServer.HandleMessage(messageId, decoded, state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine("{0} {1}", messageId, Utilities.BinaryToHex(BitConverter.GetBytes(messageId).Reverse().Skip(2).Concat(BitConverter.GetBytes(plainText.Length).Reverse().Skip(1)).Concat(BitConverter.GetBytes(unknown).Reverse().Skip(2)).Concat(plainText).ToArray()));
            }
// don't send the message to the real server
            if (!PrivateServer.privateServer)
                ClientCrypto.EncryptPacket(state.clientState.socket, state.clientState, messageId, unknown, plainText);
        }

        public static void EncryptPacket(Socket socket, ServerState state, int messageId, int unknown, byte[] plainText)
        {
            byte[] cipherText;
            if (messageId == 20100 || (messageId == 20103 && state.sharedKey == null))
            {
                cipherText = plainText;
            }
            else if (messageId == 20103 || messageId == 20104)
            {
// when in private server mode, nonce and shared key will be null
                if (PrivateServer.privateServer && state.nonce == null)
                    state.nonce = PublicKeyBox.GenerateNonce();
                if (PrivateServer.privateServer && state.sharedKey == null)
                    state.sharedKey = SecretBox.GenerateKey();

                byte[] nonce = GenericHash.Hash(state.clientState.nonce.Concat(state.clientKey).Concat(state.serverKey.PublicKey).ToArray(), null, 24);
                plainText = state.nonce.Concat(state.sharedKey).Concat(plainText).ToArray();
                cipherText = PublicKeyBox.Create(plainText, nonce, state.serverKey.PrivateKey, state.clientKey);
            }
            else
            {
                // nonce was already incremented in ClientCrypto.DecryptPacket

// probably need to increment nonce since we aren't using ClientCrypto in private server mode
// tried it with and without incrementing nonce though
                state.nonce = Utilities.Increment(Utilities.Increment(state.nonce));

                cipherText = SecretBox.Create(plainText, state.nonce, state.sharedKey).Skip(16).ToArray();
            }
            byte[] packet = BitConverter.GetBytes(messageId).Reverse().Skip(2).Concat(BitConverter.GetBytes(cipherText.Length).Reverse().Skip(1)).Concat(BitConverter.GetBytes(unknown).Reverse().Skip(2)).Concat(cipherText).ToArray();
// when not in private server mode, write the messages to files so we can use them to test reply
            if (!PrivateServer.privateServer)
            {
                System.IO.Directory.CreateDirectory("replies");
                System.IO.File.WriteAllBytes("replies\\" + messageId + ".dat", packet);
            }
            socket.BeginSend(packet, 0, packet.Length, 0, new AsyncCallback(SendCallback), state);
        }
    }
}

PrivateServer:

using Newtonsoft.Json.Linq;
using System.IO;

namespace coc_proxy_csharp
{
    class PrivateServer
    {
        public static bool privateServer = true;

        internal static void HandleMessage(int messageId, JObject decoded, ServerState state)
        {
            if (messageId == 10100)
                reply(state, File.ReadAllBytes("replies\\20100.dat"));
            else if (messageId == 10101)
            {
                reply(state, File.ReadAllBytes("replies\\20104.dat"));
                reply(state, File.ReadAllBytes("replies\\24101.dat"));
            }
        }

        private static void reply(ServerState state, byte[] unencryptedPacket)
        {
            int messageId = BitConverter.ToInt32(new byte[2].Concat(unencryptedPacket.Take(2)).Reverse().ToArray(), 0);
            int unknown = BitConverter.ToInt32(new byte[2].Concat(unencryptedPacket.Skip(2).Skip(3).Take(2)).Reverse().ToArray(), 0);
            byte[] payload = unencryptedPacket.Skip(2).Skip(3).Skip(2).ToArray();
            ServerCrypto.EncryptPacket(state.socket, state, messageId, unknown, payload);
        }
    }
}

from coc-proxy-csharp.

clugh avatar clugh commented on July 26, 2024

Two things. First, you're storing encrypted packets (edited for emphasis):

byte[] packet = [...].Concat(cipherText).[...];
System.IO.File.WriteAllBytes([...], packet);

Second, you're sending the saved and generated nonce and sharedkey with packet 20104, as the line that concatenates them is called before the data is saved and again when it is encrypted.

My suggestion to solve both issues would be to store plaintext at the top of encryptPacket(), rather than packet at the bottom.

Also, you should make a fork and push your code there. That'll make it easier for me to run it and provide a patch where necessary.

from coc-proxy-csharp.

zyxwvuts avatar zyxwvuts commented on July 26, 2024

Whoops, you're right. I was only saving and loading messages so I could post a nice simple example here. The real standalone server constructs its messages from scratch. My actual problem wasn't related to encryption at all - it was 7 extra bytes (past what's defined in OwnHomeData.json) that I wasn't properly writing. I'm experienced in dealing with those types of problems, though, and got it fixed - it's just the encryption that was way over my head. Thanks again for all your help!

from coc-proxy-csharp.

tc-maxx avatar tc-maxx commented on July 26, 2024

@zyxwvuts : Could you post your bug fixed privateServer code? I would like to test it? Thank you!

from coc-proxy-csharp.

zyxwvuts avatar zyxwvuts commented on July 26, 2024

@tc-maxx the code above should work, except for the way it saves the messages it encrypts to disk. Move the saving to the top of the Encrypt method and it works fine. Obviously for a real private server, though, you won't be just saving messages to disk and replaying them back. I was just doing that until I had the encryption correct. Now, I read information from a database and generate the messages on the fly.

from coc-proxy-csharp.

Related Issues (4)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.