Beispiel #1
0
        protected override void OnReceived(EndPoint endPoint, ReadOnlySpan <byte> buffer)
        {
            _logger.Verbose($"Handling OnReceived (EndPoint='{endPoint}', Size={buffer.Length}).");
            if (buffer.Length <= 0)
            {
                ReceiveAsync();
                return;
            }

            // Retrieve the session
            if (!_sessionService.TryGetSession(endPoint, out var session))
            {
                session = _sessionService.OpenSession(this, endPoint);
            }

            // Read the message
            var      bufferReader = new SpanBufferReader(buffer);
            IMessage message;

            try
            {
                var isEncrypted = bufferReader.ReadBool();
                if (isEncrypted)
                {
                    message = _encryptedMessageReader.ReadFrom(
                        ref bufferReader,
                        session.ReceiveKey, session.ReceiveMac
                        );
                }
                else
                {
                    message = _messageReader.ReadFrom(ref bufferReader);
                }
            }
            catch (Exception e)
            {
                _logger.Warning(e, $"Failed to read message (EndPoint='{session.EndPoint}').");
                ReceiveAsync();
                return;
            }

            // Pass it off to a message receiver
            Task.Run(async() =>
            {
                // TODO: This logic should probably be expanded in case of other
                // message receivers being added (i.e. dedicated servers)
                if (message is not IEncryptedMessage)
                {
                    await _handshakeMessageReceiver.OnReceived(session, message).ConfigureAwait(false);
                }
                else
                {
                    await _userMessageReceiver.OnReceived(session, message).ConfigureAwait(false);
                }
            });

            ReceiveAsync();
        }
        /// <inheritdoc cref="IEncryptedMessageReader.ReadFrom"/>
        public IEncryptedMessage ReadFrom(ref SpanBufferReader bufferReader, byte[] key, HMAC hmac, byte?packetProperty)
        {
            var sequenceId      = bufferReader.ReadUInt32();
            var iv              = bufferReader.ReadBytes(16).ToArray();
            var decryptedBuffer = bufferReader.RemainingData.ToArray();

            using (var cryptoTransform = _aesCryptoServiceProvider.CreateDecryptor(key, iv))
            {
                var bytesWritten = 0;
                for (var i = decryptedBuffer.Length; i >= cryptoTransform.InputBlockSize; i -= bytesWritten)
                {
                    var inputCount = cryptoTransform.CanTransformMultipleBlocks
                        ? (i / cryptoTransform.InputBlockSize * cryptoTransform.InputBlockSize)
                        : cryptoTransform.InputBlockSize;
                    bytesWritten = cryptoTransform.TransformBlock(
                        decryptedBuffer, bytesWritten, inputCount,
                        decryptedBuffer, bytesWritten
                        );
                }
            }

            var paddingByteCount    = decryptedBuffer[decryptedBuffer.Length - 1] + 1;
            var hmacStart           = decryptedBuffer.Length - paddingByteCount - 10;
            var decryptedBufferSpan = decryptedBuffer.AsSpan();
            var hash             = decryptedBufferSpan.Slice(hmacStart, 10);
            var hashBufferWriter = new SpanBufferWriter(stackalloc byte[decryptedBuffer.Length + 4]);

            hashBufferWriter.WriteBytes(decryptedBufferSpan.Slice(0, hmacStart));
            hashBufferWriter.WriteUInt32(sequenceId);
            Span <byte> computedHash = stackalloc byte[32];

            if (!hmac.TryComputeHash(hashBufferWriter.Data, computedHash, out _))
            {
                throw new Exception("Failed to compute message hash.");
            }
            if (!hash.SequenceEqual(computedHash.Slice(0, 10)))
            {
                throw new Exception("Message hash does not match the computed hash.");
            }

            bufferReader = new SpanBufferReader(decryptedBuffer);
            if (_messageReader.ReadFrom(ref bufferReader, packetProperty) is not IEncryptedMessage message)
            {
                throw new Exception(
                          "Successfully decrypted message but failed to cast to type " +
                          $"'{nameof(IEncryptedMessage)}'."
                          );
            }

            message.SequenceId = sequenceId;
            return(message);
        }
        public void Signal(ISession session, ReadOnlySpan <byte> buffer)
        {
            var      bufferReader = new SpanBufferReader(buffer);
            IMessage message;

            try
            {
                var isEncrypted = bufferReader.ReadBool();
                if (isEncrypted)
                {
                    if (session.EncryptionParameters is null)
                    {
                        _logger.Warning(
                            "Received an encrypted messsage before any " +
                            "encryption parameters were established " +
                            $"(EndPoint='{session.EndPoint}')."
                            );
                        return;
                    }
                    message = _encryptedMessageReader.ReadFrom(
                        ref bufferReader,
                        session.EncryptionParameters.ReceiveKey,
                        session.EncryptionParameters.ReceiveMac,
                        PacketProperty
                        );
                }
                else
                {
                    message = _messageReader.ReadFrom(ref bufferReader, PacketProperty);
                }
            }
            catch (Exception e)
            {
                _logger.Warning(e, $"Failed to read message (EndPoint='{session.EndPoint}').");
                return;
            }
            Signal(session, message);
        }