public static async Task <InitializationResult <AuthenticatedStream> > EstablishEncryptedCommunication( bool asServer, Guid serverGuid, IAuthenticatedConnectionFactory authenticatedConnectionFactory, Stream stream, CancellationToken token) { async Task <CommunicationResult <string> > NotifyRemotePartyAndValidateRemotePartySuccess(string message) { CommunicationResult <string> remoteResult; CommunicationResult writeResult; if (asServer) { remoteResult = await stream.ReceiveStringSafelyAsync(token); writeResult = await stream.WriteSafelyAsync(message, token); } else { writeResult = await stream.WriteSafelyAsync(message, token); remoteResult = await stream.ReceiveStringSafelyAsync(token); } if (!writeResult.Successful && remoteResult.Successful) { return(CommunicationResult <string> .From(writeResult)); } return(remoteResult); } Task <CommunicationResult> NotifyRemoteParty(string message) { return(stream.WriteSafelyAsync(message, token)); } AuthenticationResult authenticationResult; if (asServer) { authenticationResult = await authenticatedConnectionFactory.AuthenticateAsServerAsync(stream, token); } else { authenticationResult = await authenticatedConnectionFactory.AuthenticateAsClientAsync(stream, serverGuid, token); } var result = await authenticationResult.Match( async authenticated => { var successfulInitResult = new InitializationResult { Successful = true, }; var remoteInitResult = await NotifyRemotePartyAndValidateRemotePartySuccess(JsonConvert.SerializeObject(successfulInitResult)); if (remoteInitResult.Successful) { var remoteInitializationResult = JsonConvert.DeserializeObject <InitializationResult>(remoteInitResult.Result); if (remoteInitializationResult.Successful) { return(new InitializationResult <AuthenticatedStream> { Successful = true, Result = authenticated.AuthenticatedStream, }); } else { var self = asServer ? "server" : "client"; var remote = asServer ? "client" : "server"; return(new InitializationResult <AuthenticatedStream> { Successful = false, Error = new InitializationError { ErrorType = InitializationErrorType.Authentication, Message = $"The remote {remote} encountered a problem authentication this {self}. Their message: '{remoteInitializationResult.Error.Message}'", }, }); } } else { return(From <AuthenticatedStream>(remoteInitResult)); } }, async authenticationFailed => { var failedInitResult = new InitializationResult { Successful = false, Error = new InitializationError { ErrorType = InitializationErrorType.Authentication, Message = authenticationFailed.Exception.Message, }, }; await NotifyRemoteParty(JsonConvert.SerializeObject(failedInitResult)); return(InitializationResult <AuthenticatedStream> .From(failedInitResult)); }, async notSupported => { var failedInitResult = new InitializationResult { Successful = false, Error = new InitializationError { ErrorType = InitializationErrorType.Authentication, Message = notSupported.Exception.Message, } }; await NotifyRemoteParty(JsonConvert.SerializeObject(failedInitResult)); return(InitializationResult <AuthenticatedStream> .From(failedInitResult)); }, taskCanceled => { var failedInitResult = new InitializationResult <AuthenticatedStream> { Successful = false, Error = new InitializationError { ErrorType = InitializationErrorType.CancellationRequested, }, }; return(Task.FromResult(failedInitResult)); }); return(result); }
public static async Task <InitializationResult> HandleInitializationOfClient( Stream clientStream, byte[] presharedKey, Guid serverGuid, IAuthenticatedConnectionFactory authenticatedConnectionFactory, ICryptographicService otp, Action <Guid, AuthenticatedStream> onClientConnected, Action <Guid, X509Certificate> onClientRegistered, Func <Guid, X509Certificate> getClientPublicKey, CancellationToken token) { token = token.AddTimeout(DefaultTimeout); if (token.IsCancellationRequested) { return(new InitializationResult { Successful = false, Error = new InitializationError { ErrorType = InitializationErrorType.CancellationRequested, } }); } if (!await GetAndVerifyPresharedKey(clientStream, presharedKey, token)) { return(new InitializationResult { Successful = false, Error = new InitializationError { ErrorType = InitializationErrorType.Protocol, Message = "The received pre-shared key did not match the pre-shared key for this application", }, }); } await clientStream.WriteSafelyAsync(serverGuid, token); var clientGuidResult = await clientStream.ReceiveGuidSafelyAsync(token); if (!clientGuidResult.Successful) { return(InitializationResult.From(clientGuidResult)); } var clientGuid = clientGuidResult.Result; var initiationModeResult = await clientStream.ReceiveInt32SafelyAsync(token); if (!initiationModeResult.Successful) { return(InitializationResult.From(initiationModeResult)); } switch ((InitiationMode)initiationModeResult.Result) { case InitiationMode.Otp: var clientRegistrationResult = await HandleClientRegistrationSafelyAsync(clientStream, serverGuid, authenticatedConnectionFactory, otp, token); if (clientRegistrationResult.Successful) { var(certificate, stream) = clientRegistrationResult.Result; onClientRegistered(clientGuid, certificate); onClientConnected(clientGuid, stream); } else { return(clientRegistrationResult); } return(new InitializationResult { Successful = true, }); case InitiationMode.Standard: var clientCertificate = getClientPublicKey(clientGuid); if (clientCertificate == null) { return(InitializationResult.Failed); } //var sessionKey = SymmetricKey.GenerateNewKey(symmetric.KeyLength); var encryptedStreamResult = await EstablishEncryptedCommunication(true, serverGuid, authenticatedConnectionFactory, clientStream, token); if (!encryptedStreamResult.Successful) { return(encryptedStreamResult); } onClientConnected(clientGuid, encryptedStreamResult.Result); return(new InitializationResult { Successful = true, }); case InitiationMode.None: return(new InitializationResult() { Successful = false, Error = new InitializationError() { ErrorType = InitializationErrorType.Protocol, Message = "Client did not send a valid initiation mode", }, }); default: throw new ProtocolException($"invalid initiation mode {initiationModeResult.Result}"); } }
//private static TimeSpan DefaultTimeout = TimeSpan.FromMinutes(5); private static InitializationResult <TResult> From <TResult>(CommunicationResult result) => InitializationResult <TResult> .From(InitializationResult.From(result));