/// <inheritdoc /> public async Task <ITransport?> DoHandshakeAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); _logger.LogDebug("Doing protocol handshake..."); ITransport currentTransport = _context.Transport ?? throw new InvalidOperationException("Cannot do handshake before a transport has been created."); // Negotiate the protocol version that both sides will use RfbProtocolVersion protocolVersion = await NegotiateProtocolVersionAsync(currentTransport, cancellationToken).ConfigureAwait(false); _state.ProtocolVersion = protocolVersion; // Negotiate which security type will be used ISecurityType usedSecurityType = await NegotiateSecurityTypeAsync(currentTransport, cancellationToken).ConfigureAwait(false); _state.UsedSecurityType = usedSecurityType; // Execute authentication _logger.LogDebug("Negotiated security type: {name}({id}). Authenticating...", usedSecurityType.Name, usedSecurityType.Id); AuthenticationResult authenticationResult = await usedSecurityType .AuthenticateAsync(_context.Connection.Parameters.AuthenticationHandler, cancellationToken).ConfigureAwait(false); // When a tunnel was built, use that transport for further communication if (authenticationResult.TunnelTransport != null) { currentTransport = authenticationResult.TunnelTransport; } if (authenticationResult.ExpectSecurityResult) { // Read the security result ReadOnlyMemory <byte> securityResultBytes = await currentTransport.Stream.ReadAllAsync(4, cancellationToken).ConfigureAwait(false); uint securityResult = BinaryPrimitives.ReadUInt32BigEndian(securityResultBytes.Span); // Authentication failed? if (securityResult > 0) { // From version 3.8 onwards the server sends a reason if (protocolVersion >= RfbProtocolVersion.RFB_3_8) { string reason = await ReadFailureReasonAsync(currentTransport, cancellationToken).ConfigureAwait(false); throw new HandshakeFailedException($"Authentication failed: {reason}"); } // There is no reason throw new HandshakeFailedException("Authentication failed" + (securityResult == 2 ? " because of too many attempts." : ".")); } } return(authenticationResult.TunnelTransport); }
/// <inhertitdoc /> public async Task <TInput> ProvideAuthenticationInputAsync <TInput>(RfbConnection connection, ISecurityType securityType, IAuthenticationInputRequest <TInput> request) where TInput : class, IAuthenticationInput { if (typeof(TInput) == typeof(PasswordAuthenticationInput)) { string?password = await Dispatcher.UIThread.InvokeAsync(async() => await EnterPasswordInteraction.Handle(Unit.Default)).ConfigureAwait(false); // TODO: Implement canceling of authentication input requests instead of passing an empty password! if (password == null) { password = string.Empty; } return((TInput)Convert.ChangeType(new PasswordAuthenticationInput(password), typeof(TInput))); } throw new InvalidOperationException("The authentication input request is not supported by the interactive authentication handler."); }