public override Message ProcessIncoming(MessageHeader header, byte[] chiper, ref byte[] plaintext) { var messageDirection = Message.GetMessageDirection(header.ID); // Message instance that we will return to the caller. var message = MessageFactory.Create(header.ID); // Unencrypted byte array. plaintext = null; lock (_lock) { // Handshaking. if (_state <= 1) { // Handshakes are sent unencrypted. // First message by both ends is always sent unencrypted. plaintext = (byte[])chiper.Clone(); // Use the first message direction to configure the processor. if (_state == 0) { _clientCrypto = new Crypto8(GetOppositeDirection(messageDirection), _keyPair); } if (_state == 1) { _serverCrypto = new Crypto8(GetOppositeDirection(messageDirection), Crypto8.GenerateKeyPair()); } } if (_state == 4) { _clientCrypto.UpdateNonce((byte[])_serverNonce.Clone(), UpdateNonceType.Encrypt); _clientCrypto.UpdateNonce((byte[])_clientNonce.Clone(), UpdateNonceType.Decrypt); _clientCrypto.UpdateSharedKey(_serverCrypto.SharedKey); } // _state == 1 means we are the server. // Usually processing 10101 - LoginRequestMessage. if (_state == 2) { _serverCrypto.UpdateSharedKey(Crypto8.SupercellPublicKey); // -> Pre-Encryption. // Copies the public key appended to the beginning of the message. var publicKey = new byte[CoCKeyPair.KeyLength]; Buffer.BlockCopy(chiper, 0, publicKey, 0, CoCKeyPair.KeyLength); Debug.WriteLine($"Public-Key from {header.ID}: {ToHexString(publicKey)}"); // Copies the remaining bytes into the plaintext buffer var plaintextLen = header.Length - CoCKeyPair.KeyLength; plaintext = new byte[plaintextLen]; Buffer.BlockCopy(chiper, CoCKeyPair.KeyLength, plaintext, 0, plaintextLen); // Crypto8 will take publicKey & _keyPair.PublicKey and generate a blake2b nonce _clientCrypto.UpdateSharedKey(publicKey); // Then use _keyPair.PrivateKey, publicKey and nonce to decrypt. _clientCrypto.Decrypt(ref plaintext); // -> Post-Encryption. _sessionKey = new byte[CoCKeyPair.NonceLength]; _clientNonce = new byte[CoCKeyPair.NonceLength]; // Copy the SessionKey and the ClientNonce. Buffer.BlockCopy(plaintext, 0, _sessionKey, 0, CoCKeyPair.NonceLength); Buffer.BlockCopy(plaintext, CoCKeyPair.NonceLength, _clientNonce, 0, CoCKeyPair.NonceLength); Debug.WriteLine($"Session-key from {header.ID}: {ToHexString(_sessionKey)}"); Debug.WriteLine($"Client-nonce from {header.ID}: {ToHexString(_clientNonce)}"); var actualMessage = new byte[plaintext.Length - (CoCKeyPair.NonceLength * 2)]; Buffer.BlockCopy(plaintext, CoCKeyPair.NonceLength * 2, actualMessage, 0, actualMessage.Length); plaintext = actualMessage; } else if (_state == 3) { _clientCrypto.UpdateNonce(_clientNonce, UpdateNonceType.Blake); _serverCrypto.UpdateNonce(_clientNonce, UpdateNonceType.Blake); _serverCrypto.Decrypt(ref chiper); // Post-Encryption // Copies the public key appended to the beginning of the message. _serverNonce = new byte[CoCKeyPair.NonceLength]; Buffer.BlockCopy(chiper, 0, _serverNonce, 0, CoCKeyPair.NonceLength); var publicKey = new byte[CoCKeyPair.KeyLength]; Buffer.BlockCopy(chiper, CoCKeyPair.NonceLength, publicKey, 0, CoCKeyPair.KeyLength); _serverCrypto.UpdateNonce((byte[])_serverNonce.Clone(), UpdateNonceType.Decrypt); _serverCrypto.UpdateNonce((byte[])_clientNonce.Clone(), UpdateNonceType.Encrypt); _serverCrypto.UpdateSharedKey(publicKey); Debug.WriteLine($"Server-Nonce from {header.ID}: {ToHexString(_serverNonce)}"); Debug.WriteLine($"New Public-Key from {header.ID}: {ToHexString(publicKey)}"); // Copies the remaining bytes into the plaintext buffer. var plaintextLen = chiper.Length - CoCKeyPair.KeyLength - CoCKeyPair.NonceLength; plaintext = new byte[plaintextLen]; Buffer.BlockCopy(chiper, CoCKeyPair.KeyLength + CoCKeyPair.NonceLength, plaintext, 0, plaintextLen); } else if (_state > 3) { if (messageDirection == MessageDirection.Client) { _serverCrypto.Decrypt(ref chiper); } if (messageDirection == MessageDirection.Server) { _clientCrypto.Decrypt(ref chiper); } plaintext = chiper; } _state++; } try { using (var reader = new MessageReader(new MemoryStream(plaintext))) message.ReadMessage(reader); } catch (Exception ex) { Console.WriteLine("Ex: " + ex); } return(message); }
private byte[] ProcessIncomingData(MessageDirection direction, MessageHeader header, BufferStream stream) { var plaintext = (byte[])null; var chiper = new byte[header.Length]; stream.Read(chiper, 0, header.Length); // Handshaking. if (_incommingState == 0) { // Handshakes are sent unencrypted. // First message by both ends is always sent unencrypted. plaintext = new byte[header.Length]; Buffer.BlockCopy(chiper, 0, plaintext, 0, header.Length); // Use the first message direction to configure the processor. // If message is coming in, then the going out is opposite direction. _direction = GetOppositeDirection(direction); // If we're the client then we must have a serverkey. if (_direction == MessageDirection.Server && _serverKey == null) { throw new InvalidOperationException("Public key of server was not specified."); } if (_crypto == null) { _crypto = new Crypto8(_direction, _keyPair); } Debug.WriteLine($"Initialized Crypto8 {_direction} with private-key: {ToHexString(_keyPair.PrivateKey)}, public-key {_keyPair.PublicKey}"); } // If somehow the incoming message to process comes from the same direction. // The MessageProcessor can only process message going in One Direction. ;] if (GetOppositeDirection(direction) != _direction) { throw new InvalidOperationException("Tried to process an incoming data from a message coming from the same direction."); // -> Protocol Exception? } // _incomingState == 2 means we are the server. // Usually processing 10101 - LoginRequestMessage. if (_incommingState == 2) { // -> Post-Encryption. // Copies the public key appended to the beginning of the message // sent of message 20100. var publicKey = new byte[KeyPair.KeyLength]; Buffer.BlockCopy(chiper, 0, publicKey, 0, KeyPair.KeyLength); Debug.WriteLine($"Public-Key from {header.Id}: {ToHexString(publicKey)}"); // Copies the remaining bytes into the postChiper buffer which // will be decrypted by _crypto. var tmpPlaintextLen = header.Length - KeyPair.KeyLength; var tmpPlaintext = new byte[tmpPlaintextLen]; Buffer.BlockCopy(chiper, KeyPair.KeyLength, tmpPlaintext, 0, tmpPlaintextLen); // Crypto8 will take the client's publicKey & _keyPair.PublicKey and generate a blake2b nonce _crypto.UpdateSharedKey(publicKey); // Then use _keyPair.PrivateKey, publicKey and nonce to decrypt. _crypto.Decrypt(ref tmpPlaintext); // -> Pre-Encryption. var sessionKey = new byte[KeyPair.NonceLength]; var remoteNonce = new byte[KeyPair.NonceLength]; // Copy the SessionKey and the ClientNonce. Buffer.BlockCopy(tmpPlaintext, 0, sessionKey, 0, KeyPair.NonceLength); Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength, remoteNonce, 0, KeyPair.NonceLength); Debug.WriteLine($"Session-key from {header.Id}: {ToHexString(sessionKey)}"); Debug.WriteLine($"Client-nonce from {header.Id}: {ToHexString(remoteNonce)}"); // Copies the plaintext without the session key and the remote/client nonce. var plaintextLen = tmpPlaintext.Length - (KeyPair.NonceLength * 2); plaintext = new byte[plaintextLen]; Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength * 2, plaintext, 0, plaintextLen); _crypto.UpdateNonce(remoteNonce, UpdateNonceType.Decrypt); _crypto.UpdateNonce(remoteNonce, UpdateNonceType.Blake); _sessionKey = sessionKey; _remoteNonce = remoteNonce; _nstate = States.Authentifying; } // _incomingState == 3 means we are the client. // Usually processing 20104 - LoginSuccessMessage. else if (_incommingState == 3) { // -> Might want to generate random nonce here for the // client nonce. Debug.Assert(_localNonce != null); var tmpPlaintext = (byte[])chiper.Clone(); // _crypto will use this nonce after the second key is passed to it // using _crypto.UpdateSharedKey. var localNonce = _localNonce; _crypto.UpdateNonce(localNonce, UpdateNonceType.Encrypt); _crypto.UpdateNonce(localNonce, UpdateNonceType.Blake); _crypto.Decrypt(ref tmpPlaintext); // -> Post-Encryption var remoteNonce = new byte[KeyPair.NonceLength]; var key = new byte[KeyPair.KeyLength]; // Copies the ServerNonce and the new second secret key. Buffer.BlockCopy(tmpPlaintext, 0, remoteNonce, 0, KeyPair.NonceLength); Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength, key, 0, KeyPair.KeyLength); Debug.WriteLine($"Shared-key from {header.Id}: {ToHexString(key)}"); Debug.WriteLine($"Server-nonce from {header.Id}: {ToHexString(remoteNonce)}"); // Copies the plaintext without the server nonce and the new second secret key. var plaintextLen = tmpPlaintext.Length - KeyPair.NonceLength - KeyPair.KeyLength; plaintext = new byte[plaintextLen]; Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength + KeyPair.KeyLength, plaintext, 0, plaintextLen); _crypto.UpdateNonce(remoteNonce, UpdateNonceType.Decrypt); _crypto.UpdateSharedKey(key); _remoteNonce = remoteNonce; _key = key; _nstate = States.Authentified; } // Messages after the previous states are processed the same way. else if (_incommingState > (int)_direction) { _nstate = States.Completed; plaintext = (byte[])chiper.Clone(); _crypto.Decrypt(ref plaintext); } return(plaintext); }