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     size_t _writeCached;
409 
410     this(Stream stream) pure nothrow @safe @nogc
411     {
412         this._stream = stream;
413     }
414 
415     void encryptAndSend()
416     {
417         NoiseBuffer mbuf;
418         if (_writeCached != 0)
419         {
420             noise_buffer_set_inout(mbuf, &_writeBuf[2], _writeCached, _writeBuf.length - 2);
421             noiseCheck(noise_cipherstate_encrypt(_writeCipher, &mbuf));
422             _writeBuf[0] = cast(ubyte)(mbuf.size >> 8);
423             _writeBuf[1] = cast(ubyte) mbuf.size;
424             _stream.write(_writeBuf[0 .. mbuf.size + 2]);
425             _writeCached = 0;
426         }
427     }
428 
429     size_t readPacket()
430     {
431         ubyte[2] message_size;
432         _stream.read(message_size[]);
433         ushort len = (message_size[0] << 8) | message_size[1];
434         noiseEnforce(len < _readBuf.length, "Ivalid packet length");
435         _stream.read(_readBuf[0 .. len]);
436         return len;
437     }
438 
439     void readAndDecrypt()
440     {
441         NoiseBuffer mbuf;
442         while (_readDecrypted.empty)
443         {
444             auto len = readPacket();
445             noise_buffer_set_input(mbuf, _readBuf.ptr, len);
446             noiseCheck(noise_cipherstate_decrypt(_readCipher, &mbuf));
447             _readDecrypted = _readBuf[0 .. mbuf.size];
448         }
449     }
450 
451     void handshake(scope NoiseSettings settings)
452     {
453         NoiseHandshakeState* handshake;
454 
455         // Check library initialization
456         noiseCheck(initResult, "Failed to initialize libnoise-c");
457         noiseEnforce(sodiumResult != -1, "Failed to initialize libsodium");
458         // Check settings
459         noiseEnforce(!settings.privateKeyPath.empty
460             || !settings.privateKey.empty, "Need either privateKeyPath or privateKey");
461         noiseEnforce(settings.verifyRemoteKey !is null
462             || !settings.remoteKeyPath.empty, "Need either remoteKeyPath or verifyRemoteKey");
463 
464         auto role = settings.kind == NoiseKind.client ? NOISE_ROLE_INITIATOR : NOISE_ROLE_RESPONDER;
465         noiseCheck(noise_handshakestate_new_by_name(&handshake, NoiseProtocolID.ptr,
466             role));
467         scope (exit)
468             noise_handshakestate_free(handshake);
469 
470         // 2*keys
471         enum BufLength = KeySize * 2;
472         auto keyPtr = sodium_malloc(BufLength);
473         noiseEnforce(keyPtr !is null, "Failed to allocate memory");
474         scope (exit)
475             sodium_free(keyPtr);
476 
477         auto privKey = cast(ubyte[]) keyPtr[0 .. KeySize];
478         auto pubKey = cast(ubyte[]) keyPtr[KeySize .. 2 * KeySize];
479 
480         // Set private key
481         auto dh = noise_handshakestate_get_local_keypair_dh(handshake);
482         if (settings.privateKeyPath.empty)
483         {
484             auto key_len = noise_dhstate_get_private_key_length(dh);
485             noiseEnforce(key_len == settings.privateKey.length, "Invalid length for private key");
486             noiseCheck(noise_dhstate_set_keypair_private(dh, settings.privateKey.ptr,
487                 key_len));
488         }
489         else
490         {
491             settings.privateKeyPath.readFile(privKey, privKey.length);
492             noiseCheck(noise_dhstate_set_keypair_private(dh, privKey.ptr, privKey.length));
493         }
494 
495         // Start handshaking
496         NoiseBuffer mbuf;
497         noiseCheck(noise_handshakestate_start(handshake));
498         while (true)
499         {
500             auto action = noise_handshakestate_get_action(handshake);
501 
502             if (action == NOISE_ACTION_WRITE_MESSAGE)
503             {
504                 /* Write the next handshake message with a zero-length payload */
505                 noise_buffer_set_output(mbuf, &_writeBuf[2], _writeBuf.length - 2);
506                 noiseCheck(noise_handshakestate_write_message(handshake, &mbuf, null));
507                 _writeBuf[0] = cast(ubyte)(mbuf.size >> 8);
508                 _writeBuf[1] = cast(ubyte) mbuf.size;
509 
510                 _stream.write(_writeBuf[0 .. mbuf.size + 2]);
511                 _stream.flush();
512             }
513             else if (action == NOISE_ACTION_READ_MESSAGE)
514             {
515                 /* Read the next handshake message and discard the payload */
516                 auto len = readPacket();
517                 noise_buffer_set_input(mbuf, _readBuf.ptr, len);
518                 noiseCheck(noise_handshakestate_read_message(handshake, &mbuf, null));
519             }
520             else
521             {
522                 break;
523             }
524         }
525         noiseEnforce(noise_handshakestate_get_action(handshake) == NOISE_ACTION_SPLIT);
526 
527         // Verify public key
528         auto reDHState = noise_handshakestate_get_remote_public_key_dh(handshake);
529         ubyte[KeySize] recPublicKey;
530         noiseCheck(noise_dhstate_get_public_key(reDHState, recPublicKey.ptr, recPublicKey.length));
531         scope (exit)
532             sodium_memzero(recPublicKey.ptr, recPublicKey.length);
533 
534         if (settings.verifyRemoteKey is null)
535         {
536             readPublicKey(settings.remoteKeyPath, pubKey);
537             enforceEx!AuthException(recPublicKey[] == pubKey[],
538                 "Authentication failed: Public keys not equal");
539         }
540         else
541         {
542             enforceEx!AuthException(settings.verifyRemoteKey(recPublicKey),
543                 "Authentication failed: Verification callback returned false");
544         }
545 
546         noiseCheck(noise_handshakestate_split(handshake, &_writeCipher, &_readCipher));
547     }
548 
549 public:
550     /**
551      * Returns true $(I iff) the end of the input stream has been reached.
552      */
553     @property bool empty()
554     {
555         return _readDecrypted.empty && _stream.empty;
556     }
557 
558     /**
559      * Returns the maximum number of bytes that are known to remain in this stream until the
560      * end is reached. After leastSize() bytes have been read, the stream will either have
561      * reached EOS and empty() returns true, or leastSize() returns again a number > 0.
562      */
563     @property ulong leastSize()
564     {
565         if (!_readDecrypted.empty)
566             return _readDecrypted.length;
567         else
568         {
569             if (empty)
570                 return 0;
571 
572             readAndDecrypt();
573             return _readDecrypted.length;
574         }
575     }
576 
577     /**
578      * Queries if there is data available for immediate, non-blocking read.
579      */
580     @property bool dataAvailableForRead()
581     {
582         // Do not check _stream.dataAvailableForRead: We do not know
583         // if we could read enough data to decrypt the next packet but if we
584         // can't decrypt, we have to block. Could maybe check peek of the
585         // underlying stream instead
586         return !_readDecrypted.empty;
587     }
588 
589     /**
590      * Returns a temporary reference to the data that is currently buffered.
591      * The returned slice typically has the size `leastSize()` or `0` if
592      * `dataAvailableForRead()` returns false. Streams that don't have an
593      * internal buffer will always return an empty slice.
594      * Note that any method invocation on the same stream potentially
595      * invalidates the contents of the returned buffer.
596      */
597     const(ubyte)[] peek()
598     {
599         return _readDecrypted;
600     }
601 
602     /**
603      * Fills the preallocated array 'bytes' with data from the stream.
604      * Throws: An exception if the operation reads past the end of the stream
605      */
606     void read(ubyte[] dst)
607     {
608         while (dst.length != 0)
609         {
610             if (_readDecrypted.empty)
611                 readAndDecrypt();
612 
613             auto incr = min(dst.length, _readDecrypted.length);
614             dst[0 .. incr] = _readDecrypted[0 .. incr];
615             _readDecrypted = _readDecrypted[incr .. $];
616             dst = dst[incr .. $];
617         }
618     }
619 
620     /**
621      * Writes an array of bytes to the stream.
622      */
623     void write(in ubyte[] bytesConst)
624     {
625         const(ubyte)[] bytes = bytesConst;
626         while (bytes.length != 0)
627         {
628             // 2 bytes for length, 16 bytes for MAC
629             enum MaxDataLength = _writeBuf.length - 2 - 16;
630             auto bufFree = MaxDataLength - _writeCached;
631             auto nextWrite = min(bytes.length, bufFree);
632 
633             _writeBuf[2 + _writeCached .. 2 + _writeCached + nextWrite] = bytes[0 .. nextWrite];
634             bytes = bytes[nextWrite .. $];
635             _writeCached += nextWrite;
636 
637             if(_writeCached == MaxDataLength)
638                 encryptAndSend();
639         }
640     }
641 
642     /**
643      * Flushes the stream and makes sure that all data is being written to the output device.
644      */
645     void flush()
646     {
647         encryptAndSend();
648         _stream.flush();
649     }
650 
651     /**
652      * Flushes and finalizes the stream.
653      * Finalize has to be called on certain types of streams. No writes are possible after a
654      * call to finalize().
655      */
656     void finalize()
657     {
658         if (!_finalized)
659         {
660             _finalized = true;
661             noiseCheck(noise_cipherstate_free(_writeCipher));
662             noiseCheck(noise_cipherstate_free(_readCipher));
663         }
664     }
665 
666     /**
667      * Not implemented.
668      */
669     void write(InputStream stream, ulong nbytes = 0)
670     {
671         //TODO
672         noiseEnforce(false, "Not implemented");
673     }
674 }
675 
676 // Test that nonimplemente function write(Stream) throws
677 unittest
678 {
679     auto s = new NoiseStream(null);
680     assertThrown(s.write(null, 1));
681 }
682 
683 private:
684 void noiseEnforce(bool condition, string msg = "", string file = __FILE__,
685     size_t line = __LINE__, Throwable next = null) @safe
686 {
687     if (!condition)
688         throw new NoiseException(msg, file, line, next);
689 }
690 
691 void noiseCheck(int code, string msg = "", string file = __FILE__,
692     size_t line = __LINE__, Throwable next = null) @safe
693 {
694     if (code != NOISE_ERROR_NONE)
695         throw new NoiseException(msg, code, file, line, next);
696 }
697 
698 unittest
699 {
700     assertThrown!NoiseException(noiseCheck(NOISE_ERROR_INVALID_PRIVATE_KEY));
701 }
702 
703 __gshared int initResult;
704 __gshared int sodiumResult = -1;
705 
706 shared static this()
707 {
708     initResult = noise_init();
709     sodiumResult = sodium_init();
710 }
711 
712 version (unittest)
713 {
714     import vibe.d;
715     import std.exception, std.stdio;
716 
717     short testPort = 8000;
718 
719     void testClientAuth()
720     {
721         sleep(dur!"seconds"(1));
722         auto conn = connectTCP("127.0.0.1", testPort);
723         auto settings = NoiseSettings(NoiseKind.client);
724         settings.privateKeyPath = Path("private.key");
725         settings.remoteKeyPath = Path("public.key");
726         assertThrown!AuthException(conn.createNoiseStream(settings));
727         exitEventLoop();
728     }
729 
730     void testClientAuth2()
731     {
732         sleep(dur!"seconds"(1));
733         auto conn = connectTCP("127.0.0.1", testPort);
734         auto settings = NoiseSettings(NoiseKind.client);
735         settings.privateKeyPath = Path("private.key");
736         settings.verifyRemoteKey = (scope const(ubyte[]) remKey) { return false; };
737         assertThrown!AuthException(conn.createNoiseStream(settings));
738         exitEventLoop();
739     }
740 
741     void testClient()
742     {
743         scope (exit)
744             exitEventLoop();
745 
746         sleep(dur!"seconds"(1));
747         auto conn = connectTCP("127.0.0.1", testPort);
748         auto settings = NoiseSettings(NoiseKind.client);
749         settings.privateKeyPath = Path("private.key");
750         settings.remoteKeyPath = Path("public.key");
751         auto stream = conn.createNoiseStream(settings);
752 
753         // Test up to 32kB
754         auto wdata = new ubyte[1024 * 32];
755         auto rdata = wdata.dup;
756         foreach (i, ref entry; wdata)
757         {
758             entry = i % 256;
759         }
760 
761         // Write all different data lengths
762         for (size_t i = 0; i < wdata.length; i++)
763         {
764             stream.write(wdata[0 .. i]);
765         }
766         stream.flush();
767 
768         // Read all different data lengths
769         for (size_t i = 0; i < rdata.length; i++)
770         {
771             stream.read(rdata[0 .. i]);
772             assert(rdata[0 .. i] == wdata[0 .. i]);
773         }
774 
775         // Read/Write all different data lengths
776         for (size_t i = 0; i < wdata.length; i += 512)
777         {
778             stream.write(wdata[0 .. i]);
779             stream.flush();
780             stream.read(rdata[0 .. i]);
781             assert(rdata[0 .. i] == wdata[0 .. i]);
782         }
783 
784         // Read & keep some data in buffer, server sent 128 bytes;
785         stream.read(rdata[0 .. 64]);
786         assert(rdata[0 .. 64] == wdata[0 .. 64]);
787         assert(!stream.empty);
788         assert(stream.dataAvailableForRead);
789         assert(stream.leastSize == 64);
790         assert(stream.peek.length == 64 && stream.peek()[0 .. 64] == wdata[64 .. 128]);
791         // Now drain the internal buffer exactly
792         stream.read(rdata[64 .. 128]);
793         assert(rdata[0 .. 128] == wdata[0 .. 128]);
794         assert(!stream.empty);
795         assert(!stream.dataAvailableForRead);
796         assert(stream.peek.length == 0);
797 
798         // Now this reads in the next packet
799         assert(stream.leastSize == 64);
800         // Now read two 64 byte writes as one 128 byte read
801         stream.read(rdata[0 .. 128]);
802         assert(rdata[0 .. 128] == wdata[0 .. 128]);
803 
804         // Now test stream closed behaviour
805         assert(stream.empty);
806         assert(!stream.dataAvailableForRead);
807         assert(stream.leastSize == 0);
808         assert(stream.peek().length == 0);
809         assertThrown(stream.read(rdata[0 .. 128]));
810 
811         stream.finalize();
812         conn.close();
813     }
814 
815     struct NoiseServer
816     {
817         NoiseSettings settings;
818 
819         void testServer(TCPConnection conn)
820         {
821             auto stream = conn.createNoiseStream(settings);
822 
823             // Test up to 32kB
824             auto wdata = new ubyte[1024 * 32];
825             auto rdata = wdata.dup;
826             foreach (i, ref entry; wdata)
827             {
828                 entry = i % 256;
829             }
830 
831             // Read all different data lengths
832             for (size_t i = 0; i < rdata.length; i++)
833             {
834                 stream.read(rdata[0 .. i]);
835                 assert(rdata[0 .. i] == wdata[0 .. i]);
836             }
837 
838             // Write all different data lengths
839             for (size_t i = 0; i < wdata.length; i++)
840             {
841                 stream.write(wdata[0 .. i]);
842             }
843             stream.flush();
844 
845             // Write/Read different data lengths
846             for (size_t i = 0; i < wdata.length; i += 512)
847             {
848                 stream.read(rdata[0 .. i]);
849                 stream.write(wdata[0 .. i]);
850                 stream.flush();
851                 assert(rdata[0 .. i] == wdata[0 .. i]);
852             }
853 
854             // Send 128 bytes;
855             stream.write(wdata[0 .. 128]);
856             stream.flush();
857             // Send two 64 byte packets
858             stream.write(wdata[0 .. 64]);
859             stream.flush();
860             stream.write(wdata[64 .. 128]);
861             stream.flush();
862 
863             stream.finalize();
864             conn.close();
865         }
866     }
867 }
868 
869 // Test key generation
870 unittest
871 {
872     import std.file;
873 
874     string privFile = "private.key";
875     string pubFile = "public.key";
876 
877     createKeys(privFile, pubFile);
878     scope (exit)
879     {
880         if (privFile.exists)
881             remove(privFile);
882         if (pubFile.exists)
883             remove(pubFile);
884     }
885 
886     assert(privFile.exists && pubFile.exists);
887     auto content = readFileUTF8(pubFile);
888     assert(content.length == 64);
889 
890     auto privContent = read(privFile);
891     assert(privContent.length == 32);
892 }
893 
894 // Full test using key files
895 unittest
896 {
897     import std.file;
898 
899     string privFile = "private.key";
900     string pubFile = "public.key";
901 
902     createKeys(privFile, pubFile);
903     scope (exit)
904     {
905         if (privFile.exists)
906             remove(privFile);
907         if (pubFile.exists)
908             remove(pubFile);
909     }
910 
911     // Run server
912     auto settings = NoiseSettings(NoiseKind.server);
913     settings.privateKeyPath = Path(privFile);
914     settings.remoteKeyPath = Path(pubFile);
915     auto server = NoiseServer(settings);
916     listenTCP(testPort, &server.testServer);
917 
918     // Run client
919     runTask(toDelegate(&testClient));
920 
921     runEventLoop();
922     testPort++;
923 }
924 
925 // Full test using private key file
926 unittest
927 {
928     import std.file;
929 
930     string privFile = "private.key";
931     string pubFile = "public.key";
932 
933     createKeys(privFile, pubFile);
934     scope (exit)
935     {
936         if (privFile.exists)
937             remove(privFile);
938         if (pubFile.exists)
939             remove(pubFile);
940     }
941 
942     ubyte[32] pubKey;
943     readPublicKey(pubFile, pubKey);
944 
945     // Run server
946     auto settings = NoiseSettings(NoiseKind.server);
947     settings.verifyRemoteKey = (scope const(ubyte[]) remKey) {
948         assert(remKey[] == pubKey[]);
949         return remKey[] == pubKey[];
950     };
951     settings.privateKeyPath = Path(privFile);
952     auto server = NoiseServer(settings);
953     listenTCP(testPort, &server.testServer);
954     // Run client
955     runTask(toDelegate(&testClient));
956 
957     runEventLoop();
958     testPort++;
959 }
960 
961 // Full test using public key file
962 unittest
963 {
964     import std.file;
965 
966     string privFile = "private.key";
967     string pubFile = "public.key";
968 
969     createKeys(privFile, pubFile);
970     scope (exit)
971     {
972         if (privFile.exists)
973             remove(privFile);
974         if (pubFile.exists)
975             remove(pubFile);
976     }
977 
978     // Run server
979     auto settings = NoiseSettings(NoiseKind.server);
980     settings.privateKey = readFile(privFile);
981     settings.remoteKeyPath = Path(pubFile);
982     auto server = NoiseServer(settings);
983     listenTCP(testPort, &server.testServer);
984 
985     // Run client
986     runTask(toDelegate(&testClient));
987 
988     runEventLoop();
989     testPort++;
990 }
991 
992 // Full test using no key files
993 unittest
994 {
995     import std.file, std.typecons;
996 
997     string privFile = "private.key";
998     string pubFile = "public.key";
999 
1000     createKeys(privFile, pubFile);
1001     scope (exit)
1002     {
1003         if (privFile.exists)
1004             remove(privFile);
1005         if (pubFile.exists)
1006             remove(pubFile);
1007     }
1008 
1009     ubyte[32] pubKey;
1010     readPublicKey(pubFile, pubKey);
1011 
1012     // Run server
1013     auto settings = NoiseSettings(NoiseKind.server);
1014     settings.verifyRemoteKey = (scope const(ubyte[]) remKey) {
1015         assert(remKey[] == pubKey[]);
1016         return remKey[] == pubKey[];
1017     };
1018     settings.privateKey = readFile(privFile);
1019     auto server = NoiseServer(settings);
1020     listenTCP(testPort, &server.testServer);
1021 
1022     // Run client
1023     runTask(toDelegate(&testClient));
1024 
1025     runEventLoop();
1026     testPort++;
1027 }
1028 
1029 // Fail Authentication
1030 unittest
1031 {
1032     import std.file;
1033 
1034     string privFile = "private.key";
1035     string pubFile = "public.key";
1036 
1037     createKeys(privFile, pubFile);
1038     scope (exit)
1039     {
1040         if (privFile.exists)
1041             remove(privFile);
1042         if (pubFile.exists)
1043             remove(pubFile);
1044     }
1045 
1046     // Run server
1047     auto settings = NoiseSettings(NoiseKind.server);
1048     settings.privateKeyPath = Path(privFile);
1049     settings.verifyRemoteKey = (scope const(ubyte[]) remKey) { return true; };
1050     auto server = NoiseServer(settings);
1051     listenTCP(testPort, &server.testServer);
1052 
1053     // Run client
1054     runTask(toDelegate(&testClientAuth2));
1055 
1056     runEventLoop();
1057     testPort++;
1058 }
1059 
1060 // Fail Authentication
1061 unittest
1062 {
1063     import std.file;
1064 
1065     string privFile = "private.key";
1066     string pubFile = "public.key";
1067     string privFile2 = "private2.key";
1068     string pubFile2 = "public2.key";
1069 
1070     createKeys(privFile, pubFile);
1071     createKeys(privFile2, pubFile2);
1072     scope (exit)
1073     {
1074         if (privFile.exists)
1075             remove(privFile);
1076         if (pubFile.exists)
1077             remove(pubFile);
1078         if (privFile2.exists)
1079             remove(privFile2);
1080         if (pubFile2.exists)
1081             remove(pubFile2);
1082     }
1083 
1084     // Run server
1085     auto settings = NoiseSettings(NoiseKind.server);
1086     settings.privateKeyPath = Path(privFile2);
1087     settings.verifyRemoteKey = (scope const(ubyte[]) remKey) { return true; };
1088     auto server = NoiseServer(settings);
1089     listenTCP(testPort, &server.testServer);
1090 
1091     // Run client
1092     runTask(toDelegate(&testClientAuth));
1093 
1094     runEventLoop();
1095     testPort++;
1096 }
1097 
1098 // Test invalid settings
1099 unittest
1100 {
1101     import std.file, std.typecons;
1102 
1103     string privFile = "private.key";
1104     string pubFile = "public.key";
1105 
1106     createKeys(privFile, pubFile);
1107 
1108     ubyte[32] pubKey;
1109     readPublicKey(pubFile, pubKey);
1110 
1111     // No private key
1112     auto settings = NoiseSettings(NoiseKind.server);
1113     settings.remoteKeyPath = Path(pubFile);
1114 
1115     // No public key verification
1116     settings = NoiseSettings(NoiseKind.server);
1117     settings.privateKey = readFile(privFile);
1118     assertThrown(createNoiseStream(null, settings));
1119 }