1 /**
2  * 
3  */
4 module vibe.noise;
5 
6 import vibe.core.stream, vibe.core.file;
7 import std.array : empty;
8 import std.exception : enforceEx;
9 import std.algorithm : min;
10 import deimos.sodium;
11 import noise;
12 
13 /**
14  * Exception thrown on internal errors.
15  */
16 class NoiseException : Exception
17 {
18 public:
19     @trusted pure nothrow this(string message, int error, string file = __FILE__,
20         size_t line = __LINE__, Throwable next = null)
21     {
22         import core.stdc..string : strlen;
23 
24         char[128] msgBuf;
25         noise_strerror(error, msgBuf.ptr, msgBuf.length);
26         char[] errMsg = msgBuf[0 .. strlen(msgBuf.ptr)];
27         super(message ~ errMsg.idup, file, line, next);
28     }
29 
30     @safe pure nothrow this(string message, string file = __FILE__,
31         size_t line = __LINE__, Throwable next = null)
32     {
33         super(message, file, line, next);
34     }
35 }
36 
37 /**
38  * Exception thrown when authentication in createNoiseStream failed.
39  */
40 class AuthException : Exception
41 {
42 public:
43     @safe pure nothrow this(string message, string file = __FILE__,
44         size_t line = __LINE__, Throwable next = null)
45     {
46         super(message, file, line, next);
47     }
48 }
49 
50 /// Key size of public and private keys.
51 enum KeySize = 32;
52 
53 /**
54  * Create a public/private keypair to be used with createNoiseStream.
55  *
56  * Warning: When using the overload taking two `ubyte[]` buffers,
57  * the privKey buffer needs to be a secure buffer. Avoid copying this
58  * data around and make sure to (properly!) zero the data if you no longer need it.
59  * Also guarding the key memory page as done by libsodium is recommended.
60  *
61  * The overloads not taking a privateKey `ubyte[]` buffer handle all these details.
62  */
63 void createKeys(string privKeyFile, string pubKeyFile)
64 {
65     createKeys(Path(privKeyFile), Path(pubKeyFile));
66 }
67 
68 /// ditto
69 void createKeys(Path privKeyFile, Path pubKeyFile)
70 {
71     // 1*keys, then 2*keySize+1 for hex encoding
72     enum BufLength = KeySize + KeySize * 2 + 1;
73     auto keyPtr = sodium_malloc(BufLength);
74     noiseEnforce(keyPtr !is null, "Failed to allocate memory");
75     scope (exit)
76         sodium_free(keyPtr);
77 
78     auto pubKey = cast(ubyte[]) keyPtr[0 .. KeySize];
79     auto pubKeyHex = cast(char[]) keyPtr[KeySize .. BufLength];
80 
81     createKeys(privKeyFile, pubKey);
82     pubKeyHex = keyToHex(pubKey, pubKeyHex);
83 
84     writeFileUTF8(pubKeyFile, cast(string) pubKeyHex);
85 }
86 
87 /// ditto
88 void createKeys(string privKeyFile, ubyte[] pubKey)
89 {
90     createKeys(Path(privKeyFile), pubKey);
91 }
92 
93 /// ditto
94 void createKeys(Path privKeyFile, ubyte[] pubKey)
95 {
96     auto keyPtr = sodium_malloc(KeySize);
97     noiseEnforce(keyPtr !is null, "Failed to allocate memory");
98     scope (exit)
99         sodium_free(keyPtr);
100 
101     auto privKey = cast(ubyte[]) keyPtr[0 .. KeySize];
102     createKeys(privKey, pubKey);
103     writeFile(privKeyFile, privKey);
104 }
105 
106 /// ditto
107 void createKeys(ubyte[] privKey, ubyte[] pubKey)
108 {
109     noiseEnforce(sodiumResult != -1, "Failed to initialize libsodium");
110     noiseEnforce(privKey.length == KeySize && pubKey.length == KeySize,
111         "Buffers for keys must be 32 byte long");
112 
113     NoiseDHState* dh;
114     // Generate a keypair
115     noiseCheck(noise_dhstate_new_by_id(&dh, NOISE_DH_CURVE25519));
116     scope (exit)
117         noise_dhstate_free(dh);
118 
119     noiseCheck(noise_dhstate_generate_keypair(dh));
120     noiseCheck(noise_dhstate_get_keypair(dh, privKey.ptr, privKey.length,
121         pubKey.ptr, pubKey.length));
122 }
123 
124 // Test invalid buffer lengths
125 unittest
126 {
127     ubyte[31] sBuf;
128     ubyte[32] buf;
129     ubyte[33] lBuf;
130 
131     assertThrown(createKeys(buf[], sBuf[]));
132     assertThrown(createKeys(buf[], lBuf[]));
133     assertThrown(createKeys(sBuf[], buf[]));
134     assertThrown(createKeys(lBuf[], buf[]));
135 }
136 
137 /**
138  * Convert key from binary to hex format.
139  *
140  * Note: The KeyHex buffer needs to be 2*KeySize+1 bytes long as
141  * the function will internally write a `\0` at the end of the buffer.
142  * The return value returns a slice with adjusted length to exclude this trailing
143  * `\0`.
144  */
145 char[] keyToHex(ubyte[] keyBin, char[] keyHex)
146 {
147     noiseEnforce(keyBin.length == KeySize && keyHex.length == 2 * KeySize + 1,
148         "Invalid input buffer size");
149     noiseEnforce(sodium_bin2hex(keyHex.ptr, keyHex.length, keyBin.ptr, keyBin.length) !is null);
150     return keyHex[0 .. $ - 1];
151 }
152 
153 // Test invalid buffer lengths
154 unittest
155 {
156     ubyte[31] sBuf;
157     ubyte[32] buf;
158     char[64 + 1] hBuf;
159 
160     assertThrown(keyToHex(buf[], cast(char[]) sBuf[]));
161     assertThrown(keyToHex(sBuf[], hBuf[]));
162 }
163 
164 /**
165  * Convert key from hex to binary format.
166  *
167  * Note: keyHex.length needs to be 2*KeySize.
168  */
169 void keyFromHex(char[] keyHex, ubyte[] keyBin)
170 {
171     noiseEnforce(keyBin.length == KeySize && keyHex.length == 2 * KeySize,
172         "Invalid input buffer size");
173 
174     size_t readLength = 0;
175     auto result = sodium_hex2bin(keyBin.ptr, keyBin.length, keyHex.ptr,
176         keyHex.length, null, &readLength, null);
177 
178     noiseEnforce(result == 0 && readLength == 32, "Invalid public key input");
179 }
180 
181 /**
182  * Reads a public key file writen by createKeys.
183  */
184 void readPublicKey(string file, ubyte[] data)
185 {
186     readPublicKey(Path(file), data);
187 }
188 
189 /// ditto
190 void readPublicKey(Path file, ubyte[] data)
191 {
192     noiseEnforce(data.length == 32, "Key buffer needs to be 32 byte long");
193 
194     // 3 bytes BOM, 2 * keySize for hex encoding, allow up to 4 trailing bytes
195     // (CRLF CRLF)
196     ubyte[3 + 2 * 32 + 4] hexBuf;
197     scope (exit)
198         sodium_memzero(hexBuf.ptr, hexBuf.length);
199 
200     ubyte[] hexRead;
201     // If file is too large, report error
202     try
203         hexRead = file.readFile(hexBuf[], hexBuf.length);
204     catch (Exception e)
205         noiseEnforce(false, "Invalid public key file");
206 
207     // Remove BOM
208     if (hexRead.length >= 3 && hexRead[0 .. 3] == [0xEF, 0xBB, 0xBF])
209     {
210         hexRead = hexRead[3 .. $];
211     }
212 
213     // Need at least 64 bytes
214     noiseEnforce(hexRead.length >= 64, "Invalid public key file");
215 
216     // Convert to binary
217     keyFromHex(cast(char[]) hexRead[0 .. 2 * KeySize], data);
218 }
219 
220 // Test whether readPublicKey reads data correctly
221 unittest
222 {
223     import std.file;
224 
225     string privFile = "private.key";
226     string pubFile = "public.key";
227     scope (exit)
228     {
229         if (privFile.exists)
230             remove(privFile);
231         if (pubFile.exists)
232             remove(pubFile);
233     }
234 
235     // Create & write key
236     ubyte[KeySize] pubKey;
237     char[2 * KeySize + 1] pubKeyHex;
238     createKeys(privFile, pubKey[]);
239     writeFileUTF8(Path(pubFile), cast(string) keyToHex(pubKey, pubKeyHex[]));
240 
241     // Read & verify key
242     ubyte[32] readKey;
243     readPublicKey(pubFile, readKey[]);
244     assert(readKey[] == pubKey);
245 
246     // Trailing new line
247     writeFileUTF8(Path(pubFile), (cast(string) keyToHex(pubKey, pubKeyHex[])) ~ "\r\n");
248     readPublicKey(pubFile, readKey[]);
249     assert(readKey[] == pubKey);
250 
251     // No BOM
252     writeFile(Path(pubFile), cast(ubyte[]) keyToHex(pubKey, pubKeyHex[]));
253     readPublicKey(pubFile, readKey[]);
254     assert(readKey[] == pubKey);
255 
256     // No BOM + trailing newline
257     writeFile(Path(pubFile), cast(ubyte[])(keyToHex(pubKey, pubKeyHex[]) ~ ['\r', '\n']));
258     readPublicKey(pubFile, readKey[]);
259     assert(readKey[] == pubKey);
260 }
261 
262 // Test whether readPublicKey reads file generated by createKeys
263 unittest
264 {
265     import std.file;
266 
267     string privFile = "private.key";
268     string pubFile = "public.key";
269     scope (exit)
270     {
271         if (privFile.exists)
272             remove(privFile);
273         if (pubFile.exists)
274             remove(pubFile);
275     }
276 
277     // Create & write key
278     ubyte[32] readKey;
279     createKeys(privFile, pubFile);
280     readPublicKey(pubFile, readKey[]);
281 
282     // Test short buffer
283     assertThrown(readPublicKey(pubFile, readKey[0 .. 31]));
284 }
285 
286 // Read invalid files
287 unittest
288 {
289     import std.file;
290 
291     string pubFile = "public.key";
292     scope (exit)
293     {
294         if (pubFile.exists)
295             remove(pubFile);
296     }
297 
298     ubyte[KeySize] key;
299     // too short
300     writeFileUTF8(Path(pubFile), "aabb00");
301     assertThrown(readPublicKey(pubFile, key[]));
302 
303     // too long
304     writeFileUTF8(Path(pubFile),
305         "aabb00aabb00aabb00aaaabb00aabb00aabb00aaaabb00" ~ "aabb00aabb00aaaabb00aabb00aabb00aaaabb00aabb00aabb00aaaabb00aabb0" ~ "0aabb00aaaabb00aabb00aabb00aaaabb00aabb00aabb00aa");
306     assertThrown(readPublicKey(pubFile, key[]));
307 
308     // non-existent file
309     remove(pubFile);
310     assertThrown(readPublicKey(pubFile, key[]));
311 
312     // invalid data
313     writeFileUTF8(Path(pubFile),
314         "xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ~ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
315     assertThrown(readPublicKey(pubFile, key[]));
316 }
317 
318 /**
319  * Kind of NoiseStream.
320  */
321 enum NoiseKind
322 {
323     ///
324     server,
325     ///
326     client
327 }
328 
329 /**
330  * Provide a delegate of this types to verify the public key of the node
331  * connected to.
332  */
333 alias VerifyKeyDelegate = scope nothrow bool delegate(scope const(ubyte[]));
334 
335 /**
336  * Settings for the connectNoiseStream function.
337  */
338 struct NoiseSettings
339 {
340     /// Client or server role.
341     NoiseKind kind;
342     /**
343      * Path to private key file. Either this or the privateKey field
344      * must be set. If both are set the privateKeyPath field is used.
345      */
346     Path privateKeyPath;
347 
348     /**
349      * Private key in memory.
350      *
351      * Warning: When using this overload, make sure to use secure memory.
352      * The memory should be cleared after the createNoiseStream call returns.
353      *
354      * The privateKeyPath allows for a simpler, secure API.
355      */
356     const(ubyte)[] privateKey;
357 
358     /**
359      * If provided will be used to verify the remote public key.
360      * If this is not set remoteKeyPath is used instead.
361      */
362     VerifyKeyDelegate verifyRemoteKey;
363 
364     /**
365      * Path to public key file of remote server
366      */
367     Path remoteKeyPath;
368 
369     /**
370      * Convenience constructor.
371      */
372     public this(NoiseKind kind)
373     {
374         this.kind = kind;
375     }
376 }
377 
378 /**
379  * Create a noise encrypted stream on top of a normal stream.
380  * 
381  * Throws: NoiseException on internal error, AuthException if authentication failed.
382  * Exceptions thrown from the low-level stream get passed through.
383  */
384 NoiseStream createNoiseStream(Stream stream, scope NoiseSettings settings)
385 {
386     auto cstream = new NoiseStream(stream);
387     cstream.handshake(settings);
388     return cstream;
389 }
390 
391 /**
392  * Wraps a normal Stream to add encryption based on the
393  * Noise_XX_25519_ChaChaPoly_BLAKE2b protocol.
394  */
395 class NoiseStream : Stream
396 {
397 private:
398     enum NoiseProtocolID = "Noise_XX_25519_ChaChaPoly_BLAKE2b";
399     NoiseCipherState* _writeCipher, _readCipher;
400     Stream _stream;
401     bool _finalized;
402     // Read buffer contains encrypted or decrypted data
403     ubyte[2048] _readBuf;
404     // Slice into _readBuf with remaining decrypted data
405     ubyte[] _readDecrypted;
406     // Buffer used for writing only
407     ubyte[2048] _writeBuf;
408 
409     this(Stream stream) pure nothrow @safe @nogc
410     {
411         this._stream = stream;
412     }
413 
414     size_t readPacket()
415     {
416         ubyte[2] message_size;
417         _stream.read(message_size[]);
418         ushort len = (message_size[0] << 8) | message_size[1];
419         noiseEnforce(len < _readBuf.length, "Ivalid packet length");
420         _stream.read(_readBuf[0 .. len]);
421         return len;
422     }
423 
424     void readAndDecrypt()
425     {
426         NoiseBuffer mbuf;
427         while (_readDecrypted.empty)
428         {
429             auto len = readPacket();
430             noise_buffer_set_input(mbuf, _readBuf.ptr, len);
431             noiseCheck(noise_cipherstate_decrypt(_readCipher, &mbuf));
432             _readDecrypted = _readBuf[0 .. mbuf.size];
433         }
434     }
435 
436     void handshake(scope NoiseSettings settings)
437     {
438         NoiseHandshakeState* handshake;
439 
440         // Check library initialization
441         noiseCheck(initResult, "Failed to initialize libnoise-c");
442         noiseEnforce(sodiumResult != -1, "Failed to initialize libsodium");
443         // Check settings
444         noiseEnforce(!settings.privateKeyPath.empty
445             || !settings.privateKey.empty, "Need either privateKeyPath or privateKey");
446         noiseEnforce(settings.verifyRemoteKey !is null
447             || !settings.remoteKeyPath.empty, "Need either remoteKeyPath or verifyRemoteKey");
448 
449         auto role = settings.kind == NoiseKind.client ? NOISE_ROLE_INITIATOR : NOISE_ROLE_RESPONDER;
450         noiseCheck(noise_handshakestate_new_by_name(&handshake, NoiseProtocolID.ptr,
451             role));
452         scope (exit)
453             noise_handshakestate_free(handshake);
454 
455         // 2*keys
456         enum BufLength = KeySize * 2;
457         auto keyPtr = sodium_malloc(BufLength);
458         noiseEnforce(keyPtr !is null, "Failed to allocate memory");
459         scope (exit)
460             sodium_free(keyPtr);
461 
462         auto privKey = cast(ubyte[]) keyPtr[0 .. KeySize];
463         auto pubKey = cast(ubyte[]) keyPtr[KeySize .. 2 * KeySize];
464 
465         // Set private key
466         auto dh = noise_handshakestate_get_local_keypair_dh(handshake);
467         if (settings.privateKeyPath.empty)
468         {
469             auto key_len = noise_dhstate_get_private_key_length(dh);
470             noiseEnforce(key_len == settings.privateKey.length, "Invalid length for private key");
471             noiseCheck(noise_dhstate_set_keypair_private(dh, settings.privateKey.ptr,
472                 key_len));
473         }
474         else
475         {
476             settings.privateKeyPath.readFile(privKey, privKey.length);
477             noiseCheck(noise_dhstate_set_keypair_private(dh, privKey.ptr, privKey.length));
478         }
479 
480         // Start handshaking
481         NoiseBuffer mbuf;
482         noiseCheck(noise_handshakestate_start(handshake));
483         while (true)
484         {
485             auto action = noise_handshakestate_get_action(handshake);
486 
487             if (action == NOISE_ACTION_WRITE_MESSAGE)
488             {
489                 /* Write the next handshake message with a zero-length payload */
490                 noise_buffer_set_output(mbuf, &_writeBuf[2], _writeBuf.length - 2);
491                 noiseCheck(noise_handshakestate_write_message(handshake, &mbuf, null));
492                 _writeBuf[0] = cast(ubyte)(mbuf.size >> 8);
493                 _writeBuf[1] = cast(ubyte) mbuf.size;
494 
495                 _stream.write(_writeBuf[0 .. mbuf.size + 2]);
496                 _stream.flush();
497             }
498             else if (action == NOISE_ACTION_READ_MESSAGE)
499             {
500                 /* Read the next handshake message and discard the payload */
501                 auto len = readPacket();
502                 noise_buffer_set_input(mbuf, _readBuf.ptr, len);
503                 noiseCheck(noise_handshakestate_read_message(handshake, &mbuf, null));
504             }
505             else
506             {
507                 break;
508             }
509         }
510         noiseEnforce(noise_handshakestate_get_action(handshake) == NOISE_ACTION_SPLIT);
511 
512         // Verify public key
513         auto reDHState = noise_handshakestate_get_remote_public_key_dh(handshake);
514         ubyte[KeySize] recPublicKey;
515         noiseCheck(noise_dhstate_get_public_key(reDHState, recPublicKey.ptr, recPublicKey.length));
516         scope (exit)
517             sodium_memzero(recPublicKey.ptr, recPublicKey.length);
518 
519         if (settings.verifyRemoteKey is null)
520         {
521             readPublicKey(settings.remoteKeyPath, pubKey);
522             enforceEx!AuthException(recPublicKey[] == pubKey[],
523                 "Authentication failed: Public keys not equal");
524         }
525         else
526         {
527             enforceEx!AuthException(settings.verifyRemoteKey(recPublicKey),
528                 "Authentication failed: Verification callback returned false");
529         }
530 
531         noiseCheck(noise_handshakestate_split(handshake, &_writeCipher, &_readCipher));
532     }
533 
534 public:
535     /**
536      * Returns true $(I iff) the end of the input stream has been reached.
537      */
538     @property bool empty()
539     {
540         return _readDecrypted.empty && _stream.empty;
541     }
542 
543     /**
544      * Returns the maximum number of bytes that are known to remain in this stream until the
545      * end is reached. After leastSize() bytes have been read, the stream will either have
546      * reached EOS and empty() returns true, or leastSize() returns again a number > 0.
547      */
548     @property ulong leastSize()
549     {
550         if (!_readDecrypted.empty)
551             return _readDecrypted.length;
552         else
553         {
554             if (empty)
555                 return 0;
556 
557             readAndDecrypt();
558             return _readDecrypted.length;
559         }
560     }
561 
562     /**
563      * Queries if there is data available for immediate, non-blocking read.
564      */
565     @property bool dataAvailableForRead()
566     {
567         // Do not check _stream.dataAvailableForRead: We do not know
568         // if we could read enough data to decrypt the next packet but if we
569         // can't decrypt, we have to block. Could maybe check peek of the
570         // underlying stream instead
571         return !_readDecrypted.empty;
572     }
573 
574     /**
575      * Returns a temporary reference to the data that is currently buffered.
576      * The returned slice typically has the size `leastSize()` or `0` if
577      * `dataAvailableForRead()` returns false. Streams that don't have an
578      * internal buffer will always return an empty slice.
579      * Note that any method invocation on the same stream potentially
580      * invalidates the contents of the returned buffer.
581      */
582     const(ubyte)[] peek()
583     {
584         return _readDecrypted;
585     }
586 
587     /**
588      * Fills the preallocated array 'bytes' with data from the stream.
589      * Throws: An exception if the operation reads past the end of the stream
590      */
591     void read(ubyte[] dst)
592     {
593         while (dst.length != 0)
594         {
595             if (_readDecrypted.empty)
596                 readAndDecrypt();
597 
598             auto incr = min(dst.length, _readDecrypted.length);
599             dst[0 .. incr] = _readDecrypted[0 .. incr];
600             _readDecrypted = _readDecrypted[incr .. $];
601             dst = dst[incr .. $];
602         }
603     }
604 
605     /**
606      * Writes an array of bytes to the stream.
607      */
608     void write(in ubyte[] bytesConst)
609     {
610         const(ubyte)[] bytes = bytesConst;
611         NoiseBuffer mbuf;
612         while (bytes.length != 0)
613         {
614             // 2 bytes for length, 16 bytes for MAC
615             enum MaxDataLength = _writeBuf.length - 2 - 16;
616             auto nextWrite = min(bytes.length, MaxDataLength);
617             _writeBuf[2 .. nextWrite + 2] = bytes[0 .. nextWrite];
618             bytes = bytes[nextWrite .. $];
619 
620             noise_buffer_set_inout(mbuf, &_writeBuf[2], nextWrite, _writeBuf.length - 2);
621             noiseCheck(noise_cipherstate_encrypt(_writeCipher, &mbuf));
622             _writeBuf[0] = cast(ubyte)(mbuf.size >> 8);
623             _writeBuf[1] = cast(ubyte) mbuf.size;
624             _stream.write(_writeBuf[0 .. mbuf.size + 2]);
625         }
626     }
627 
628     /**
629      * Flushes the stream and makes sure that all data is being written to the output device.
630      */
631     void flush()
632     {
633         _stream.flush();
634         // We always create one crypted packet in write
635         // TODO: is this a problem for small writes? Is flush for network like
636         // streams well supported? Then we could fill the buffer before writing...
637     }
638 
639     /**
640      * Flushes and finalizes the stream.
641      * Finalize has to be called on certain types of streams. No writes are possible after a
642      * call to finalize().
643      */
644     void finalize()
645     {
646         if (!_finalized)
647         {
648             _finalized = true;
649             noiseCheck(noise_cipherstate_free(_writeCipher));
650             noiseCheck(noise_cipherstate_free(_readCipher));
651         }
652     }
653 
654     /**
655      * Not implemented.
656      */
657     void write(InputStream stream, ulong nbytes = 0)
658     {
659         //TODO
660         noiseEnforce(false, "Not implemented");
661     }
662 }
663 
664 // Test that nonimplemente function write(Stream) throws
665 unittest
666 {
667     auto s = new NoiseStream(null);
668     assertThrown(s.write(null, 1));
669 }
670 
671 private:
672 void noiseEnforce(bool condition, string msg = "", string file = __FILE__,
673     size_t line = __LINE__, Throwable next = null) @safe
674 {
675     if (!condition)
676         throw new NoiseException(msg, file, line, next);
677 }
678 
679 void noiseCheck(int code, string msg = "", string file = __FILE__,
680     size_t line = __LINE__, Throwable next = null) @safe
681 {
682     if (code != NOISE_ERROR_NONE)
683         throw new NoiseException(msg, code, file, line, next);
684 }
685 
686 unittest
687 {
688     assertThrown!NoiseException(noiseCheck(NOISE_ERROR_INVALID_PRIVATE_KEY));
689 }
690 
691 __gshared int initResult;
692 __gshared int sodiumResult = -1;
693 
694 shared static this()
695 {
696     initResult = noise_init();
697     sodiumResult = sodium_init();
698 }
699 
700 version (unittest)
701 {
702     import vibe.d;
703     import std.exception, std.stdio;
704 
705     short testPort = 8000;
706 
707     void testClientAuth()
708     {
709         sleep(dur!"seconds"(1));
710         auto conn = connectTCP("127.0.0.1", testPort);
711         auto settings = NoiseSettings(NoiseKind.client);
712         settings.privateKeyPath = Path("private.key");
713         settings.remoteKeyPath = Path("public.key");
714         assertThrown!AuthException(conn.createNoiseStream(settings));
715         exitEventLoop();
716     }
717 
718     void testClientAuth2()
719     {
720         sleep(dur!"seconds"(1));
721         auto conn = connectTCP("127.0.0.1", testPort);
722         auto settings = NoiseSettings(NoiseKind.client);
723         settings.privateKeyPath = Path("private.key");
724         settings.verifyRemoteKey = (scope const(ubyte[]) remKey) { return false; };
725         assertThrown!AuthException(conn.createNoiseStream(settings));
726         exitEventLoop();
727     }
728  
729     void testClient()
730     {
731         scope (exit)
732             exitEventLoop();
733 
734         sleep(dur!"seconds"(1));
735         auto conn = connectTCP("127.0.0.1", testPort);
736         auto settings = NoiseSettings(NoiseKind.client);
737         settings.privateKeyPath = Path("private.key");
738         settings.remoteKeyPath = Path("public.key");
739         auto stream = conn.createNoiseStream(settings);
740 
741         // Test up to 32kB
742         auto wdata = new ubyte[1024 * 32];
743         auto rdata = wdata.dup;
744         foreach (i, ref entry; wdata)
745         {
746             entry = i % 256;
747         }
748 
749         // Write all different data lengths
750         for (size_t i = 0; i < wdata.length; i++)
751         {
752             stream.write(wdata[0 .. i]);
753         }
754 
755         // Read all different data lengths
756         for (size_t i = 0; i < rdata.length; i++)
757         {
758             stream.read(rdata[0 .. i]);
759             assert(rdata[0 .. i] == wdata[0 .. i]);
760         }
761 
762         // Read/Write all different data lengths
763         for (size_t i = 0; i < wdata.length; i += 512)
764         {
765             stream.write(wdata[0 .. i]);
766             stream.read(rdata[0 .. i]);
767             assert(rdata[0 .. i] == wdata[0 .. i]);
768         }
769 
770         // Read & keep some data in buffer, server sent 128 bytes;
771         stream.read(rdata[0 .. 64]);
772         assert(rdata[0 .. 64] == wdata[0 .. 64]);
773         assert(!stream.empty);
774         assert(stream.dataAvailableForRead);
775         assert(stream.leastSize == 64);
776         assert(stream.peek.length == 64 && stream.peek()[0 .. 64] == wdata[64 .. 128]);
777         // Now drain the internal buffer exactly
778         stream.read(rdata[64 .. 128]);
779         assert(rdata[0 .. 128] == wdata[0 .. 128]);
780         assert(!stream.empty);
781         assert(!stream.dataAvailableForRead);
782         assert(stream.peek.length == 0);
783 
784         // Now this reads in the next packet
785         assert(stream.leastSize == 64);
786         // Now read two 64 byte writes as one 128 byte read
787         stream.read(rdata[0 .. 128]);
788         assert(rdata[0 .. 128] == wdata[0 .. 128]);
789 
790         // Now test stream closed behaviour
791         assert(stream.empty);
792         assert(!stream.dataAvailableForRead);
793         assert(stream.leastSize == 0);
794         assert(stream.peek().length == 0);
795         assertThrown(stream.read(rdata[0 .. 128]));
796 
797         stream.finalize();
798         conn.close();
799     }
800 
801     struct NoiseServer
802     {
803         NoiseSettings settings;
804 
805         void testServer(TCPConnection conn)
806         {
807             scope (failure)
808                 exitEventLoop();
809 
810             auto stream = conn.createNoiseStream(settings);
811 
812             // Test up to 32kB
813             auto wdata = new ubyte[1024 * 32];
814             auto rdata = wdata.dup;
815             foreach (i, ref entry; wdata)
816             {
817                 entry = i % 256;
818             }
819 
820             // Read all different data lengths
821             for (size_t i = 0; i < rdata.length; i++)
822             {
823                 stream.read(rdata[0 .. i]);
824                 assert(rdata[0 .. i] == wdata[0 .. i]);
825             }
826 
827             // Write all different data lengths
828             for (size_t i = 0; i < wdata.length; i++)
829             {
830                 stream.write(wdata[0 .. i]);
831             }
832 
833             // Write/Read different data lengths
834             for (size_t i = 0; i < wdata.length; i += 512)
835             {
836                 stream.read(rdata[0 .. i]);
837                 stream.write(wdata[0 .. i]);
838                 assert(rdata[0 .. i] == wdata[0 .. i]);
839             }
840 
841             // Send 128 bytes;
842             stream.write(wdata[0 .. 128]);
843             // Send two 64 byte packets
844             stream.write(wdata[0 .. 64]);
845             stream.write(wdata[64 .. 128]);
846 
847             stream.flush();
848             stream.finalize();
849             conn.close();
850         }
851     }
852 }
853 
854 // Test key generation
855 unittest
856 {
857     import std.file;
858 
859     string privFile = "private.key";
860     string pubFile = "public.key";
861 
862     createKeys(privFile, pubFile);
863     scope (exit)
864     {
865         if (privFile.exists)
866             remove(privFile);
867         if (pubFile.exists)
868             remove(pubFile);
869     }
870 
871     assert(privFile.exists && pubFile.exists);
872     auto content = readFileUTF8(pubFile);
873     assert(content.length == 64);
874 
875     auto privContent = read(privFile);
876     assert(privContent.length == 32);
877 }
878 
879 // Full test using key files
880 unittest
881 {
882     import std.file;
883 
884     string privFile = "private.key";
885     string pubFile = "public.key";
886 
887     createKeys(privFile, pubFile);
888     scope (exit)
889     {
890         if (privFile.exists)
891             remove(privFile);
892         if (pubFile.exists)
893             remove(pubFile);
894     }
895 
896     // Run server
897     auto settings = NoiseSettings(NoiseKind.server);
898     settings.privateKeyPath = Path(privFile);
899     settings.remoteKeyPath = Path(pubFile);
900     auto server = NoiseServer(settings);
901     listenTCP(testPort, &server.testServer);
902 
903     // Run client
904     runTask(toDelegate(&testClient));
905 
906     runEventLoop();
907     testPort++;
908 }
909 
910 // Full test using private key file
911 unittest
912 {
913     import std.file;
914 
915     string privFile = "private.key";
916     string pubFile = "public.key";
917 
918     createKeys(privFile, pubFile);
919     scope (exit)
920     {
921         if (privFile.exists)
922             remove(privFile);
923         if (pubFile.exists)
924             remove(pubFile);
925     }
926 
927     ubyte[32] pubKey;
928     readPublicKey(pubFile, pubKey);
929 
930     // Run server
931     auto settings = NoiseSettings(NoiseKind.server);
932     settings.verifyRemoteKey = (scope const(ubyte[]) remKey) {
933         assert(remKey[] == pubKey[]);
934         return remKey[] == pubKey[];
935     };
936     settings.privateKeyPath = Path(privFile);
937     auto server = NoiseServer(settings);
938     listenTCP(testPort, &server.testServer);
939     // Run client
940     runTask(toDelegate(&testClient));
941 
942     runEventLoop();
943     testPort++;
944 }
945 
946 // Full test using public key file
947 unittest
948 {
949     import std.file;
950 
951     string privFile = "private.key";
952     string pubFile = "public.key";
953 
954     createKeys(privFile, pubFile);
955     scope (exit)
956     {
957         if (privFile.exists)
958             remove(privFile);
959         if (pubFile.exists)
960             remove(pubFile);
961     }
962 
963     // Run server
964     auto settings = NoiseSettings(NoiseKind.server);
965     settings.privateKey = readFile(privFile);
966     settings.remoteKeyPath = Path(pubFile);
967     auto server = NoiseServer(settings);
968     listenTCP(testPort, &server.testServer);
969 
970     // Run client
971     runTask(toDelegate(&testClient));
972 
973     runEventLoop();
974     testPort++;
975 }
976 
977 // Full test using no key files
978 unittest
979 {
980     import std.file, std.typecons;
981 
982     string privFile = "private.key";
983     string pubFile = "public.key";
984 
985     createKeys(privFile, pubFile);
986     scope (exit)
987     {
988         if (privFile.exists)
989             remove(privFile);
990         if (pubFile.exists)
991             remove(pubFile);
992     }
993 
994     ubyte[32] pubKey;
995     readPublicKey(pubFile, pubKey);
996 
997     // Run server
998     auto settings = NoiseSettings(NoiseKind.server);
999     settings.verifyRemoteKey = (scope const(ubyte[]) remKey) {
1000         assert(remKey[] == pubKey[]);
1001         return remKey[] == pubKey[];
1002     };
1003     settings.privateKey = readFile(privFile);
1004     auto server = NoiseServer(settings);
1005     listenTCP(testPort, &server.testServer);
1006 
1007     // Run client
1008     runTask(toDelegate(&testClient));
1009 
1010     runEventLoop();
1011     testPort++;
1012 }
1013 
1014 // Fail Authentication
1015 unittest
1016 {
1017     import std.file;
1018 
1019     string privFile = "private.key";
1020     string pubFile = "public.key";
1021 
1022     createKeys(privFile, pubFile);
1023     scope (exit)
1024     {
1025         if (privFile.exists)
1026             remove(privFile);
1027         if (pubFile.exists)
1028             remove(pubFile);
1029     }
1030 
1031     // Run server
1032     auto settings = NoiseSettings(NoiseKind.server);
1033     settings.privateKeyPath = Path(privFile);
1034     settings.remoteKeyPath = Path(pubFile);
1035     auto server = NoiseServer(settings);
1036     listenTCP(testPort, &server.testServer);
1037 
1038     // Run client
1039     runTask(toDelegate(&testClientAuth2));
1040 
1041     runEventLoop();
1042     testPort++;
1043 }
1044 
1045 // Fail Authentication
1046 unittest
1047 {
1048     import std.file;
1049 
1050     string privFile = "private.key";
1051     string pubFile = "public.key";
1052     string privFile2 = "private2.key";
1053     string pubFile2 = "public2.key";
1054 
1055     createKeys(privFile, pubFile);
1056     createKeys(privFile2, pubFile2);
1057     scope (exit)
1058     {
1059         if (privFile.exists)
1060             remove(privFile);
1061         if (pubFile.exists)
1062             remove(pubFile);
1063         if (privFile2.exists)
1064             remove(privFile2);
1065         if (pubFile2.exists)
1066             remove(pubFile2);
1067     }
1068 
1069     // Run server
1070     auto settings = NoiseSettings(NoiseKind.server);
1071     settings.privateKeyPath = Path(privFile2);
1072     settings.remoteKeyPath = Path(pubFile);
1073     auto server = NoiseServer(settings);
1074     listenTCP(testPort, &server.testServer);
1075 
1076     // Run client
1077     runTask(toDelegate(&testClientAuth));
1078 
1079     runEventLoop();
1080     testPort++;
1081 }
1082 
1083 // Test invalid settings
1084 unittest
1085 {
1086     import std.file, std.typecons;
1087 
1088     string privFile = "private.key";
1089     string pubFile = "public.key";
1090 
1091     createKeys(privFile, pubFile);
1092 
1093     ubyte[32] pubKey;
1094     readPublicKey(pubFile, pubKey);
1095 
1096     // No private key
1097     auto settings = NoiseSettings(NoiseKind.server);
1098     settings.remoteKeyPath = Path(pubFile);
1099 
1100     // No public key verification
1101     settings = NoiseSettings(NoiseKind.server);
1102     settings.privateKey = readFile(privFile);
1103     assertThrown(createNoiseStream(null, settings));
1104 }