Exemple #1
0
        public static async Task <InitializationResult <AuthenticatedStream> > RegisterWithServerAsync(
            Stream serverStream,
            byte[] presharedKey,
            Guid ownGuid,
            Guid serverGuid,
            IAuthenticatedConnectionFactory authenticatedConnectionFactory,
            X509Certificate2 clientCertificate,
            X509Certificate serverCertificate,
            ICryptographicService otp,
            CancellationToken token)
        {
            InitializationResult <AuthenticatedStream> From(CommunicationResult res) => From <AuthenticatedStream>(res);

            if (!otp.CanEncrypt)
            {
                throw new ArgumentException("otp needs to be able to encrypt");
            }
            token = token.AddTimeout(DefaultTimeout);

            await serverStream.WriteSafelyAsync(presharedKey, token);

            var serverGuidResult = await serverStream.ReceiveGuidSafelyAsync(token);

            if (!serverGuidResult.Successful)
            {
                return(From(serverGuidResult));
            }
            if (!serverGuidResult.Result.Equals(serverGuid))
            {
                return(new InitializationResult <AuthenticatedStream>
                {
                    Successful = false,
                    Error = new InitializationError
                    {
                        ErrorType = InitializationErrorType.Identification,
                        Message = $"Expected server to be '{serverGuid}', but instead found '{serverGuidResult.Result}'",
                    },
                });
            }
            await serverStream.WriteSafelyAsync(
                ownGuid,
                (int)InitiationMode.Otp,
                token);

            var exportCertificate    = clientCertificate.Export(X509ContentType.Cert);
            var encryptedCertificate = otp.Encrypt(exportCertificate);
            await serverStream.WriteSafelyAsync(
                (int)CommunicationData.PublicKey,
                encryptedCertificate.Length,
                encryptedCertificate,
                token);

            return(await EstablishEncryptedCommunication(false, serverGuid, authenticatedConnectionFactory,
                                                         serverStream, token));
        }
Exemple #2
0
 public LapiClient(
     Guid ownGuid,
     ImmutableArray <byte> presharedKey,
     IAuthenticatedConnectionFactory authenticatedConnectionFactory,
     IOtpServiceFactory otpServiceFactory)
 {
     this.ownGuid      = ownGuid;
     this.presharedKey = presharedKey;
     this.authenticatedConnectionFactory = authenticatedConnectionFactory;
     this.otpServiceFactory = otpServiceFactory;
 }
Exemple #3
0
 public LapiServer(
     Guid ownGuid,
     ImmutableArray <byte> presharedKey,
     IAuthenticatedConnectionFactory authenticatedConnectionFactory,
     IOtpServiceFactory otpServiceFactory,
     IServer serverImplementation)
 {
     this.presharedKey = presharedKey;
     this.authenticatedConnectionFactory = authenticatedConnectionFactory;
     this.otpServiceFactory    = otpServiceFactory;
     this.serverImplementation = serverImplementation;
     this.ownGuid = ownGuid;
 }
Exemple #4
0
        public static async Task <InitializationResult <AuthenticatedStream> > ConnectToServerAsync(
            Stream serverStream,
            byte[] presharedKey,
            Guid ownGuid,
            Guid serverGuid,
            IAuthenticatedConnectionFactory authenticatedConnectionFactory,
            X509Certificate serverCertificate)
        {
            var timeoutCancellationTokenSource = new CancellationTokenSource();

            timeoutCancellationTokenSource.CancelAfter(DefaultTimeout);
            var token = timeoutCancellationTokenSource.Token;

            await serverStream.WriteSafelyAsync(presharedKey, token);

            var serverGuidResult = await serverStream.ReceiveGuidSafelyAsync(token);

            if (!serverGuidResult.Successful)
            {
                return(From <AuthenticatedStream>(serverGuidResult));
            }
            if (!serverGuidResult.Result.Equals(serverGuid))
            {
                return(new InitializationResult <AuthenticatedStream>
                {
                    Successful = false,
                    Error = new InitializationError
                    {
                        ErrorType = InitializationErrorType.Identification,
                        Message = $"Expected server to be '{serverGuid}', but instead found '{serverGuidResult.Result}'",
                    },
                });
            }
            await serverStream.WriteSafelyAsync(
                ownGuid,
                (int)InitiationMode.Standard,
                token);

            var encryptedStreamResult = await EstablishEncryptedCommunication(false, serverGuid, authenticatedConnectionFactory,
                                                                              serverStream, token);

            return(encryptedStreamResult);
        }
Exemple #5
0
        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}");
            }
        }
Exemple #6
0
        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);
        }
Exemple #7
0
        public static async Task <InitializationResult <(X509Certificate, AuthenticatedStream)> > HandleClientRegistrationSafelyAsync(
            Stream clientStream,
            Guid serverGuid,
            IAuthenticatedConnectionFactory authenticatedConnectionFactory,
            ICryptographicService otp,
            CancellationToken token)
        {
            InitializationResult <(X509Certificate, AuthenticatedStream)> From(CommunicationResult res) => From <(X509Certificate, AuthenticatedStream)>(res);

            if (!otp.CanDecrypt)
            {
                throw new ArgumentException("otp needs to be able to decrypt");
            }

            var dataTypeResult = await clientStream.ReceiveInt32SafelyAsync(token);

            if (!dataTypeResult.Successful)
            {
                return(From(dataTypeResult));
            }
            if ((CommunicationData)dataTypeResult.Result != CommunicationData.PublicKey)
            {
                return(new InitializationResult <(X509Certificate, AuthenticatedStream)>
                {
                    Successful = false,
                    Error = new InitializationError
                    {
                        ErrorType = InitializationErrorType.Protocol,
                        Message = $"Client sent unexpected data",
                    },
                });
            }

            var encryptedLengthResult = await clientStream.ReceiveInt32SafelyAsync(token);

            if (!encryptedLengthResult.Successful)
            {
                return(From(encryptedLengthResult));
            }
            var encryptedCertificateResult = await clientStream.ReadSafelyAsync(encryptedLengthResult.Result, token);

            if (!encryptedCertificateResult.Successful)
            {
                return(From(encryptedCertificateResult));
            }
            var clientCertificate = new X509Certificate(otp.Decrypt(encryptedCertificateResult.Result));

            var authenticationResult = await EstablishEncryptedCommunication(true, serverGuid, authenticatedConnectionFactory, clientStream, token);

            if (authenticationResult.Successful)
            {
                return(new InitializationResult <(X509Certificate, AuthenticatedStream)>
                {
                    Successful = true,
                    Result = (clientCertificate, authenticationResult.Result),
                });
            }
            else
            {
                return(InitializationResult <(X509Certificate, AuthenticatedStream)> .From(authenticationResult));
            }
        }