1 module box;
2 
3 import random;
4 
5 extern (C) {
6   size_t crypto_box_seedbytes();
7   size_t crypto_box_publickeybytes();
8   size_t crypto_box_secretkeybytes();
9   size_t crypto_box_beforenmbytes();
10   size_t crypto_box_noncebytes();
11   size_t crypto_box_zerobytes();
12   size_t crypto_box_boxzerobytes();
13   size_t crypto_box_macbytes();
14 
15   char *crypto_box_primitive();
16 
17   int crypto_box_seed_keypair(ref ubyte[32] pk, ref ubyte[32] sk);
18   int crypto_box_keypair(ref ubyte[32] pk, ref ubyte[32] sk);
19   int crypto_box_easy(ubyte *c, ubyte *m, ulong mlen, ref ubyte[24] n, ref ubyte[32] pk, ref ubyte[32] sk);
20   int crypto_box_open_easy(ubyte *m, ubyte *c, ulong clen, ref ubyte[24] n, ref ubyte[32] pk, ref ubyte[32] sk);
21 }
22 
23 struct EncryptedBoxMessage {
24   ubyte[] message;
25   ubyte[24] nonce;
26 }
27 
28 class BoxKeyPair {
29   ubyte[32] public_key;
30   ubyte[32] secret_key;
31 
32   this() {
33     assert(crypto_box_keypair(this.public_key, this.secret_key) == 0);
34   }
35 
36   this(ubyte[32] pkey, ubyte[32] skey) {
37     this.public_key = pkey;
38     this.secret_key = skey;
39   }
40 
41   EncryptedBoxMessage encrypt(string data, BoxKeyPair other) {
42     return this.encrypt(cast(ubyte[])data, other);
43   }
44 
45   EncryptedBoxMessage encrypt(ubyte[] data, BoxKeyPair other) {
46     ubyte[24] nonce;
47     randombytes_buf(&nonce[0], 24);
48     return this.encrypt(data, nonce, other);
49   }
50 
51   EncryptedBoxMessage encrypt(ubyte[] data, ubyte[24] nonce, BoxKeyPair other) {
52     ubyte[] output = new ubyte[data.length + crypto_box_macbytes()];
53 
54     assert(crypto_box_easy(&output[0], &data[0], data.length, nonce, other.public_key, this.secret_key) == 0);
55     return EncryptedBoxMessage(output, nonce);
56   }
57 
58   ubyte[] decrypt(EncryptedBoxMessage msg, BoxKeyPair other) {
59     ubyte[] output = new ubyte[msg.message.length - crypto_box_macbytes()];
60     assert(crypto_box_open_easy(&output[0], &msg.message[0], msg.message.length, msg.nonce, other.public_key, this.secret_key) == 0);
61     return output;
62   }
63 
64 }
65 
66 unittest {
67   auto jim = new BoxKeyPair;
68   auto alex = new BoxKeyPair;
69 
70   // Test that encryption works
71   EncryptedBoxMessage emsg = jim.encrypt("hey alex, your password is 1", alex);
72   assert(alex.decrypt(emsg, jim) == "hey alex, your password is 1");
73 
74   // Test that encryption with custom nonce works
75   ubyte[24] nonce = cast(ubyte[24])"nonce";
76   ubyte[] msg = cast(ubyte[])"test";
77   EncryptedBoxMessage noncemsg = jim.encrypt(msg, nonce, alex);
78   assert(alex.decrypt(noncemsg, jim) == msg);
79   assert(noncemsg.nonce == nonce);
80 }