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); }