コード例 #1
0
ファイル: PolyTests.cs プロジェクト: cilliemalan/MicroRatchet
        public void PolyReferenceTests(byte[] key, byte[] iv, byte[] data, byte[] expected)
        {
            Poly pmac = new Poly(Common.AesFactory);

            pmac.Init(key, iv, 16);
            pmac.Process(new ArraySegment <byte>(data));
            byte[] mac = pmac.Compute();

            var bcpoly = new Poly1305(new AesEngine());

            bcpoly.Init(new ParametersWithIV(new KeyParameter(key), iv));
            bcpoly.BlockUpdate(data, 0, data.Length);
            byte[] bcmac = new byte[bcpoly.GetMacSize()];
            bcpoly.DoFinal(bcmac, 0);

            var _key      = string.Join(", ", key.Select(b => $"0x{b:x2}"));
            var _iv       = string.Join(", ", iv.Select(b => $"0x{b:x2}"));
            var _data     = string.Join(", ", data.Select(b => $"0x{b:x2}"));
            var _expected = string.Join(", ", bcmac.Select(b => $"0x{b:x2}"));

            Log.Verbose($"[InlineData(new byte[] {{{_key}}}, new byte[] {{{_iv}}}, new byte[] {{{_data}}}, new byte[] {{{_expected}}})]");

            Assert.Equal(expected, mac);
            Assert.Equal(expected, bcmac);
        }
コード例 #2
0
ファイル: PolyTests.cs プロジェクト: cilliemalan/MicroRatchet
        public void PolyMultiTest()
        {
            RandomNumberGenerator rng = new RandomNumberGenerator();

            byte[] iv   = rng.Generate(16);
            byte[] key  = rng.Generate(32);
            byte[] data = rng.Generate(100);

            Poly pmac = new Poly(Common.AesFactory);

            pmac.Init(key, iv, 16);
            pmac.Process(new ArraySegment <byte>(data));
            byte[] mac1 = pmac.Compute();

            pmac = new Poly(Common.AesFactory);
            pmac.Init(key, iv, 16);
            pmac.Process(new ArraySegment <byte>(data, 0, 50));
            pmac.Process(new ArraySegment <byte>(data, 50, 50));
            byte[] mac2 = pmac.Compute();

            pmac = new Poly(Common.AesFactory);
            pmac.Init(key, iv, 16);
            pmac.Process(new ArraySegment <byte>(data, 0, 33));
            pmac.Process(new ArraySegment <byte>(data, 33, 33));
            pmac.Process(new ArraySegment <byte>(data, 66, 34));
            byte[] mac3 = pmac.Compute();

            Assert.Equal(mac1, mac2);
            Assert.Equal(mac1, mac3);
        }
コード例 #3
0
ファイル: PolyTests.cs プロジェクト: cilliemalan/MicroRatchet
        public void PolyReuseKeyTest(int ivBytes, int dataBytes, int macSize)
        {
            RandomNumberGenerator rng = new RandomNumberGenerator();

            byte[] iv    = rng.Generate(ivBytes);
            byte[] key   = rng.Generate(32);
            byte[] data1 = rng.Generate(dataBytes);
            byte[] data2 = rng.Generate(dataBytes);

            var pmac = new Poly(Common.AesFactory);

            pmac.Init(key, iv, macSize);
            pmac.Process(new ArraySegment <byte>(data1));
            byte[] mac1 = pmac.Compute();
            pmac.Init(key, iv, macSize);
            pmac.Process(new ArraySegment <byte>(data2));
            byte[] mac2 = pmac.Compute();

            var bcpoly1 = new Poly1305(new AesEngine());

            bcpoly1.Init(new ParametersWithIV(new KeyParameter(key), iv));
            bcpoly1.BlockUpdate(data1, 0, data1.Length);
            byte[] bcmac1 = new byte[bcpoly1.GetMacSize()];
            bcpoly1.DoFinal(bcmac1, 0);
            bcmac1 = bcmac1.Take(macSize).ToArray();

            var bcpoly2 = new Poly1305(new AesEngine());

            bcpoly2.Init(new ParametersWithIV(new KeyParameter(key), iv));
            bcpoly2.BlockUpdate(data2, 0, data2.Length);
            byte[] bcmac2 = new byte[bcpoly2.GetMacSize()];
            bcpoly2.DoFinal(bcmac2, 0);
            bcmac2 = bcmac2.Take(macSize).ToArray();

            Assert.Equal(bcmac1, mac1);
            Assert.Equal(bcmac2, mac2);
        }
コード例 #4
0
ファイル: PolyTests.cs プロジェクト: cilliemalan/MicroRatchet
        public void BasicPolyTests(int ivBytes, int dataBytes, int macSize)
        {
            RandomNumberGenerator rng = new RandomNumberGenerator();

            byte[] iv   = rng.Generate(ivBytes);
            byte[] key  = rng.Generate(32);
            byte[] data = rng.Generate(dataBytes);

            Poly pmac   = new Poly(Common.AesFactory);
            var  bcpoly = new Poly1305(new AesEngine());

            pmac.Init(key, iv, macSize);
            bcpoly.Init(new ParametersWithIV(new KeyParameter(key), iv));
            pmac.Process(new ArraySegment <byte>(data));
            byte[] mac = pmac.Compute();

            bcpoly.BlockUpdate(data, 0, data.Length);
            byte[] bcmac = new byte[bcpoly.GetMacSize()];
            bcpoly.DoFinal(bcmac, 0);
            bcmac = bcmac.Take(macSize).ToArray();

            Assert.Equal(bcmac, mac);
        }
コード例 #5
0
        static void Main(string[] args)
        {
            int    messageCount     = 1000000;
            double clientDropChance = 0.0;
            double serverDropChance = 0.0;
            var    defaultServices  = new BouncyCastleServices(KeyGeneration.GeneratePrivateKey());

            Random r = new Random();
            RandomNumberGenerator rng = new RandomNumberGenerator();
            Stopwatch             sw  = new Stopwatch();

            Console.WriteLine("Generating data...");
            byte[] keys   = new byte[16 * 1000000];
            byte[] blocks = new byte[16 * 1000000];
            byte[] output = new byte[32 * 1000000];
            rng.Generate(keys);
            rng.Generate(blocks);

            Thread.Sleep(1000);
            {
                var poly = new Poly(defaultServices.AesFactory);
                poly.Init(new ArraySegment <byte>(keys, 0, 32), new ArraySegment <byte>(blocks, 0, 16), 16);
                poly.Init(new ArraySegment <byte>(keys, 1000, 32), new ArraySegment <byte>(blocks, 1000, 16), 16);
                Console.WriteLine("Doing Pol1305 MACs");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 32)
                {
                    poly.Process(blocks, i, 16);
                    poly.Process(blocks, i, 16);
                    poly.Process(blocks, i, 16);
                    poly.Process(blocks, i, 16);
                    poly.Compute(new ArraySegment <byte>(output, i, 16));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 32) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var sha = System.Security.Cryptography.SHA256.Create();
                sha.ComputeHash(blocks, 10000, 16);
                Console.WriteLine("Doing SHA256 hashes (dotnet)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    sha.ComputeHash(blocks, i, 16);
                }

                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var sha = new Org.BouncyCastle.Crypto.Digests.Sha256Digest();
                sha.BlockUpdate(blocks, 10000, 16);
                sha.DoFinal(output, 10000);
                Console.WriteLine("Doing SHA256 hashes (bc)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    sha.BlockUpdate(blocks, i, 16);
                    sha.DoFinal(output, i * 2);
                }

                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }

            Thread.Sleep(1000);
            {
                var aes = System.Security.Cryptography.Aes.Create();
                aes.Mode = System.Security.Cryptography.CipherMode.ECB;
                var key = new byte[16];
                Array.Copy(keys, 10000, key, 0, 16);
                aes.CreateEncryptor(key, null);
                Console.WriteLine("Calculating AES keys (dotnet)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 16)
                {
                    Array.Copy(keys, i, key, 0, 16);
                    aes.CreateEncryptor(key, null);
                }

                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var aes = System.Security.Cryptography.Aes.Create();
                aes.Mode = System.Security.Cryptography.CipherMode.ECB;
                var key = new byte[16];
                Array.Copy(keys, key, 16);
                var enc = aes.CreateEncryptor(key, null);
                enc.TransformBlock(blocks, 10000, 16, output, 10000);
                Console.WriteLine("Processing AES blocks (dotnet");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    enc.TransformBlock(blocks, i, 16, output, i);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                Org.BouncyCastle.Crypto.Engines.AesEngine aes = new Org.BouncyCastle.Crypto.Engines.AesEngine();
                aes.Init(true, new KeyParameter(keys, 10000, 16));
                aes.Init(true, new KeyParameter(keys, 20000, 16));
                Console.WriteLine("Calculating AES keys (bc)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 16)
                {
                    aes.Init(true, new KeyParameter(keys, i, 16));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                Org.BouncyCastle.Crypto.Engines.AesEngine aes = new Org.BouncyCastle.Crypto.Engines.AesEngine();
                aes.Init(true, new KeyParameter(keys, 12300, 16));
                aes.ProcessBlock(blocks, 10000, output, 10000);
                Console.WriteLine("Processing AES blocks (bc)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    aes.ProcessBlock(blocks, i, output, i);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var aes = new Org.BouncyCastle.Crypto.Engines.AesLightEngine();
                aes.Init(true, new KeyParameter(keys, 10000, 16));
                aes.Init(true, new KeyParameter(keys, 20000, 16));
                Console.WriteLine("Calculating AES keys (bc light)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 16)
                {
                    aes.Init(true, new KeyParameter(keys, i, 16));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var aes = new Org.BouncyCastle.Crypto.Engines.AesLightEngine();
                aes.Init(true, new KeyParameter(keys, 12340, 16));
                aes.ProcessBlock(blocks, 10000, output, 10000);
                Console.WriteLine("Processing AES blocks (bc light)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    aes.ProcessBlock(blocks, i, output, i);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }

            Thread.Sleep(1000);
            {
                SymmetricRacthet sr = new SymmetricRacthet();
                var(client, server) = CreateAndInitialize();
                var kdf = new AesKdf(client.Services.AesFactory);
                sr.Initialize(rng.Generate(32));
                Console.WriteLine("Testing Symmetric Ratchet Speed");
                sr.RatchetForSending(kdf);
                sr.RatchetForSending(kdf);
                sr.RatchetForSending(kdf);
                sr.RatchetForSending(kdf);
                sw.Reset();
                sw.Start();
                int cnt = 1000000;
                for (int i = 0; i < cnt; i++)
                {
                    sr.RatchetForSending(kdf);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({cnt / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"It would take { (double)int.MaxValue / 2 / (cnt / sw.Elapsed.TotalSeconds) / 60:F0} minutes to do 2^32 ratchets");
            }

            Thread.Sleep(1000);
            {
                Console.WriteLine("Testing one way message send speed (small: 16 bytes)...");
                var(client, server) = CreateAndInitialize();
                var messagesToSend = Enumerable.Range(0, messageCount).Select(_ => rng.Generate(16)).ToArray();
                var messagesSent   = new List <byte[]>(messageCount);
                var m1             = client.Send(new byte[16]);
                var m2             = client.Send(new byte[16]);
                var m3             = client.Send(new byte[16]);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    messagesSent.Add(client.Send(messagesToSend[i]));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");


                Thread.Sleep(1000);
                Console.WriteLine("Testing one way message receive speed (small: 16 bytes)...");
                server.Receive(m1);
                server.Receive(m2);
                server.Receive(m3);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    server.Receive(messagesSent[i]);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");
            }

            Thread.Sleep(2000);
            {
                Console.WriteLine("Testing one way message send speed (large: 64 bytes)...");
                var(client, server) = CreateAndInitialize();
                var messagesToSend = Enumerable.Range(0, messageCount).Select(_ => rng.Generate(64)).ToArray();
                var messagesSent   = new List <byte[]>(messageCount);
                var m1             = client.Send(new byte[16]);
                var m2             = client.Send(new byte[16]);
                var m3             = client.Send(new byte[16]);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    messagesSent.Add(client.Send(messagesToSend[i]));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");


                Thread.Sleep(1000);
                Console.WriteLine("Testing one way message receive speed (large: 64 bytes)...");
                server.Receive(m1);
                server.Receive(m2);
                server.Receive(m3);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    server.Receive(messagesSent[i]);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");
            }

            messageCount /= 10;
            Thread.Sleep(2000);
            {
                Console.WriteLine("Testing one way message send speed (IP: 1350 bytes)...");
                var(client, server) = CreateAndInitialize(1350);
                var messagesToSend = Enumerable.Range(0, messageCount).Select(_ => rng.Generate(1300)).ToArray();
                var messagesSent   = new List <byte[]>(messageCount);
                var m1             = client.Send(new byte[16]);
                var m2             = client.Send(new byte[16]);
                var m3             = client.Send(new byte[16]);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    messagesSent.Add(client.Send(messagesToSend[i]));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");


                Thread.Sleep(1000);
                Console.WriteLine("Testing one way message receive speed (IP: 1350 bytes)...");
                server.Receive(m1);
                server.Receive(m2);
                server.Receive(m3);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    server.Receive(messagesSent[i]);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");
            }

            Thread.Sleep(1000);
            {
                Console.WriteLine("Testing ECDHratchet speed...");
                var(client, server) = CreateAndInitialize(1350);
                var messagesToSend = Enumerable.Range(0, messageCount / 4000).Select(_ => rng.Generate(32)).ToArray();
                server.Receive(client.Send(new byte[32]));
                client.Receive(server.Send(new byte[32]));
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount / 4000; i++)
                {
                    var m1 = client.Send(messagesToSend[i]);
                    server.Receive(m1);
                    var m2 = server.Send(messagesToSend[i]);
                    client.Receive(m2);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(messageCount / 2000) / sw.Elapsed.TotalSeconds:F0}/s)");
            }

            messageCount *= 10;
            Thread.Sleep(1000);
            {
                var(client, server) = CreateAndInitialize();
                var              clientMessages           = new HashSet <byte[]>(Enumerable.Range(0, messageCount).Select(_ => rng.Generate(32)));
                var              serverMessages           = new HashSet <byte[]>(Enumerable.Range(0, messageCount).Select(_ => rng.Generate(32)));
                Queue <byte[]>   clientMessagesToSend     = new Queue <byte[]>(clientMessages);
                Queue <byte[]>   serverMessagesToSend     = new Queue <byte[]>(serverMessages);
                var              messagesSentFromClient   = new Queue <byte[]>();
                var              messagesSentFromServer   = new Queue <byte[]>();
                HashSet <byte[]> messagesReceivedByClient = new HashSet <byte[]>();
                HashSet <byte[]> messagesReceivedByServer = new HashSet <byte[]>();

                byte[] DoubleInSize(byte[] payload) => payload.Concat(payload).ToArray();

                int clientSent = 0, serverSent = 0, clientReceived = 0, serverReceived = 0, clientDropped = 0, serverDropped = 0;
                Console.WriteLine($"Sending {messageCount}/{clientDropChance:P0} and {messageCount}/{serverDropChance:P0}");

                sw.Reset();
                sw.Start();
                double oldTime = 0;
                int    oldCnt  = 0;
                for (int i = 0; ; i++)
                {
                    bool anyMessagesToReceive = messagesSentFromClient.TryPeek(out var _) || messagesSentFromServer.TryPeek(out var _);
                    bool anyMessagesToSend    = clientMessagesToSend.TryPeek(out var _) || serverMessagesToSend.TryPeek(out var _);
                    if (!anyMessagesToReceive && !anyMessagesToSend)
                    {
                        break;
                    }

                    if (i % 1000 == 0)
                    {
                        var totalReceived = clientReceived + serverReceived + clientDropped + serverDropped;
                        var totalAll      = messageCount + messageCount;
                        var percentage    = (double)totalReceived / totalAll;

                        var newTime   = sw.Elapsed.TotalSeconds;
                        var deltaTime = newTime - oldTime;
                        var deltaCnt  = totalReceived - oldCnt;

                        double perSecond = 0;
                        if (oldTime != 0)
                        {
                            perSecond = deltaCnt / deltaTime;
                        }
                        Console.Write($"\r{percentage:P0} - c: {clientSent}/{clientDropped} -> {serverReceived}  s: {serverSent}/{serverDropped} -> {clientReceived}   ({perSecond:F0}/s)  ");

                        oldCnt  = totalReceived;
                        oldTime = newTime;
                    }

                    var    clientOrServer = r.Next(2);
                    var    sendOrReceive  = r.Next(2);
                    double ratio          = (double)messageCount / messageCount;
                    int    maxClient      = 100;
                    int    maxServer      = (int)(100 / ratio);
                    var    maxMessages    = r.Next(clientOrServer == 0 ? maxClient : maxServer) + 1;

                    if (anyMessagesToSend && (sendOrReceive == 0 || !anyMessagesToReceive))
                    {
                        if (clientOrServer == 0) // send from client
                        {
                            while (maxMessages-- > 0)
                            {
                                clientMessagesToSend.TryDequeue(out var payload);
                                if (payload != null)
                                {
                                    payload = r.Next(10) > 7 ? DoubleInSize(payload) : payload;
                                    var message = client.Send(payload);
                                    if (r.NextDouble() > clientDropChance)
                                    {
                                        clientSent++;
                                        messagesSentFromClient.Enqueue(message);
                                    }
                                    else
                                    {
                                        clientDropped++;
                                    }
                                }
                            }
                        }
                        else
                        {
                            while (maxMessages-- > 0)
                            {
                                serverMessagesToSend.TryDequeue(out var payload);
                                if (payload != null)
                                {
                                    payload = r.Next(10) > 7 ? DoubleInSize(payload) : payload;
                                    var message = server.Send(payload);
                                    if (r.NextDouble() > serverDropChance)
                                    {
                                        serverSent++;
                                        messagesSentFromServer.Enqueue(message);
                                    }
                                    else
                                    {
                                        serverDropped++;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        if (clientOrServer != 0)  // receive by client
                        {
                            while (maxMessages-- > 0)
                            {
                                messagesSentFromServer.TryDequeue(out var message);
                                if (message != null)
                                {
                                    var payload = client.Receive(message).Payload;
                                    messagesReceivedByClient.Add(payload);
                                    clientReceived++;
                                }
                            }
                        }
                        else // receive by server
                        {
                            while (maxMessages-- > 0)
                            {
                                messagesSentFromClient.TryDequeue(out var message);
                                if (message != null)
                                {
                                    var payload = server.Receive(message).Payload;
                                    messagesReceivedByServer.Add(payload);
                                    serverReceived++;
                                }
                            }
                        }
                    }
                }

                Console.WriteLine("Done");
            }
        }