예제 #1
0
        public static async Task Run(CancellationToken cancellationToken)
        {
            // generate a key pair
            byte[] privateKey = KeyGeneration.GeneratePrivateKey();
            byte[] publicKey  = KeyGeneration.GetPublicKeyFromPrivateKey(privateKey);

            // configuring as a client with the default application key.
            // messages will be padded to 64 bytes and cannot exceed
            // 256 bytes.
            var config = new MicroRatchetConfiguration
            {
                ApplicationKey     = new byte[32],
                IsClient           = true,
                MaximumMessageSize = 256,
                MinimumMessageSize = 64
            };

            // create the MicroRatchet context
            var services = new BouncyCastleServices(privateKey);

            using var context = new MicroRatchetContext(services, config, null);

            Console.WriteLine($"Starting Client with public key: {publicKey.ToHexString()}");

            // Create UDP client. No connection since UDP is a
            // connectionless message based protocol. For the client
            // we don't specify a local endpoint.
            using var udp = new UdpClient();
            var serverEndpoint = new IPEndPoint(IPAddress.Loopback, PORT);

            // Step 1: Initialize
            while (!context.IsInitialized && !cancellationToken.IsCancellationRequested)
            {
                try
                {
                    // 1.1 Send initialization request
                    Console.WriteLine("Sending initialization request");
                    byte[] msg = context.InitiateInitialization();
                    await udp.SendAsync(msg, serverEndpoint, cancellationToken);

                    Console.WriteLine($"SENT {msg.Length} bytes INITIALIZATION REQUEST");

                    // 1.2 Receive initialization response
                    var timeout = CancellationTokenSource.CreateLinkedTokenSource(
                        cancellationToken,
                        new CancellationTokenSource(15000).Token).Token;
                    var receivedData = await udp.ReceiveAsync(timeout);

                    Console.WriteLine($"RECEIVED {receivedData.Buffer.Length} bytes FROM SERVER");
                    var res = context.Receive(receivedData.Buffer);

                    if (res.ToSendBack != null)
                    {
                        Console.WriteLine("Received initialization response.");
                        Console.WriteLine($"Server public key: {context.GetRemotePublicKey().ToHexString()}");
                        Console.WriteLine("Sending first message");
                        // 1.3 Send first request
                        await udp.SendAsync(res.ToSendBack, serverEndpoint, cancellationToken);

                        Console.WriteLine($"SENT BACK {res.ToSendBack.Length} bytes");

                        // 1.4 Receive first response
                        timeout = CancellationTokenSource.CreateLinkedTokenSource(
                            cancellationToken,
                            new CancellationTokenSource(15000).Token).Token;
                        receivedData = await udp.ReceiveAsync(timeout);

                        Console.WriteLine($"RECEIVED {receivedData.Buffer.Length} bytes FROM SERVER");
                        res = context.Receive(receivedData.Buffer);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"An exception was encountered: {ex.Message}");
                }
            }

            // the context is now initialized and message can be sent back and forth
            // as needed. Messages being dropped or received out of order does not
            // affect subsequent messages.
            if (!cancellationToken.IsCancellationRequested)
            {
                Console.WriteLine("Client initialized");
                Console.WriteLine("Type stuff in and press enter to send to the server.\n\n");

                // in order to get the console, udp stuff, and MR stuff to work
                // together (even though nothing was written for async), we need
                // some synchronization.
                // A background thread reads from the console and pushes messages
                // into a queue which then in turn get sent to the server by the
                // Send task.
                // A receive task handles messages from the server


                // The receive task handles incoming UDP messages.
                var messages  = new ConcurrentQueue <string>();
                var semaphore = new SemaphoreSlim(0);
                async Task ReceiveTask()
                {
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        var received = await udp.ReceiveAsync(cancellationToken);

                        Console.WriteLine($"RECEIVED {received.Buffer.Length} bytes");

                        try
                        {
                            // pass the received data to the MR context
                            var message = context.Receive(received.Buffer);

                            // The message is always padded to the minimum message size,
                            // so read the incoming message as a null-terminated
                            // string.
                            string msg = message.Payload.DecodeNullTerminatedString();

                            // Print the decrypted and decoded message to the console
                            Console.WriteLine($"RECEIVED MESSAGE: {msg}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"An exception was encountered when receiving a message: {ex}");
                        }
                    }
                }

                // the send task dequeues messages, encrypts, and sends
                // them to the server endpoint as UDP messages.
                async Task SendTask()
                {
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        // wait for a message
                        await semaphore.WaitAsync();

                        messages.TryDequeue(out var payload);

                        // encrypt the message using the context. The message
                        // will be padded to the minimum configured message size.
                        var payloadBytes = Encoding.UTF8.GetBytes(payload);
                        var message      = context.Send(payloadBytes);

                        // send as UDP message to the server endpoint.
                        await udp.SendAsync(message, serverEndpoint, cancellationToken);

                        Console.WriteLine($"SENT {payloadBytes.Length} bytes PAYLOAD, resulting in {message.Length} bytes ENCRYPTED MESSAGE");
                    }
                }

                // The console thread reades lines one at a time and
                // enqueues them for sending.
                var consoleReadThread = new Thread(_ =>
                {
                    for (; ;)
                    {
                        messages.Enqueue(Console.ReadLine());
                        semaphore.Release();
                    }
                });
                // background threads are terminated when the program exits
                consoleReadThread.IsBackground = true;
                consoleReadThread.Start();

                await Task.WhenAll(ReceiveTask(), SendTask());
            }
        }
예제 #2
0
        public static async Task Run(CancellationToken cancellationToken)
        {
            // generate a key pair
            byte[] privateKey = KeyGeneration.GeneratePrivateKey();
            byte[] publicKey  = KeyGeneration.GetPublicKeyFromPrivateKey(privateKey);

            // configuring as a server with the default application key.
            // messages will be padded to 64 bytes and cannot exceed
            // 256 bytes.
            var config = new MicroRatchetConfiguration
            {
                ApplicationKey     = new byte[32],
                IsClient           = false,
                MaximumMessageSize = 256,
                MinimumMessageSize = 64
            };

            // create the MicroRatchet context
            var services = new BouncyCastleServices(privateKey);

            using var context = new MicroRatchetContext(services, config, null);

            Console.WriteLine($"Starting Server with public key: {publicKey.ToHexString()}");

            // Create UDP client. For the server we set
            // the local endpoint to be localhost,
            // listening on the configured port.
            var        serverEndpoint = new IPEndPoint(IPAddress.Loopback, PORT);
            IPEndPoint clientEndpoint = null;

            using var udp = new UdpClient(serverEndpoint);

            if (!cancellationToken.IsCancellationRequested)
            {
                // in order to get the console, udp stuff, and MR stuff to work
                // together (even though nothing was written for async), we need
                // some synchronization.
                // A background thread reads from the console and pushes messages
                // into a queue which then in turn get sent to the server by the
                // Send task.
                // A receive task handles messages from the server


                // The receive task handles incoming UDP messages.
                var messages  = new ConcurrentQueue <string>();
                var semaphore = new SemaphoreSlim(0);
                async Task ReceiveTask()
                {
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        var received = await udp.ReceiveAsync(cancellationToken);

                        Console.WriteLine($"RECEIVED {received.Buffer.Length} bytes");

                        try
                        {
                            // pass the received data to the MR context
                            var message = context.Receive(received.Buffer);

                            // if ToSendBack is not null, the context has accepted
                            // an incoming initialization message and now is bound
                            // to the session from that client key.
                            if (message.ToSendBack != null)
                            {
                                // send the response back
                                await udp.SendAsync(message.ToSendBack, message.ToSendBack.Length, received.RemoteEndPoint);

                                Console.WriteLine($"SENT {message.ToSendBack.Length} bytes RESPONSE");

                                if (context.IsInitialized)
                                {
                                    // message.ToSendBack != null && context.IsInitialized
                                    // typically happens once per session.
                                    clientEndpoint = received.RemoteEndPoint;
                                    Console.WriteLine($"Server initialized with remote public key {context.GetRemotePublicKey().ToHexString()}.");
                                    Console.WriteLine("Type stuff in and press enter to send to the client.\n\n");
                                }
                            }
                            else if (message.Payload != null)
                            {
                                // if a payload is given, the message contains
                                // data sent by the client, which we print to the console.

                                // The message is always padded to the minimum message size,
                                // so read the incoming message as a null-terminated
                                // string.
                                string msg = message.Payload.DecodeNullTerminatedString();

                                // Print the decrypted and decoded message to the console
                                Console.WriteLine($"RECEIVED MESSAGE: {msg}");
                            }
                        }
                        catch (Exception ex)
                        {
                            // one exception you would see in this situation is if second client
                            // tries to initialize a session. Because the context is bound to a remote
                            // public key as soon as a message comes in, and this demo application
                            // contains no logic to handle that situation, it will simply stay bound
                            // to the first session and throw an exception if another client tries to
                            // connect.
                            Console.WriteLine($"An exception was encountered when receiving a message: {ex}");
                        }
                    }
                }

                // the send task dequeues messages, encrypts, and sends
                // them to the server endpoint as UDP messages.
                async Task SendTask()
                {
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        // wait for a message
                        await semaphore.WaitAsync();

                        messages.TryDequeue(out var payload);

                        // encrypt the message using the context. The message
                        // will be padded to the minimum configured message size.
                        var payloadBytes = Encoding.UTF8.GetBytes(payload);
                        var message      = context.Send(payloadBytes);

                        // send as UDP message to the server endpoint.
                        await udp.SendAsync(message, clientEndpoint, cancellationToken);

                        Console.WriteLine($"SENT {payloadBytes.Length} bytes PAYLOAD, resulting in {message.Length} bytes ENCRYPTED MESSAGE");
                    }
                }

                // The console thread reades lines one at a time and
                // enqueues them for sending.
                var consoleReadThread = new Thread(_ =>
                {
                    for (; ;)
                    {
                        string line = Console.ReadLine();
                        if (clientEndpoint != null)
                        {
                            messages.Enqueue(line);
                            semaphore.Release();
                        }
                        else
                        {
                            Console.WriteLine("Cannot send message as no client has initialized a session");
                        }
                    }
                });
                // background threads are terminated when the program exits
                consoleReadThread.IsBackground = true;
                consoleReadThread.Start();

                await Task.WhenAll(ReceiveTask(), SendTask());
            }
        }