1 // Written in the D programming language.
2 
3 module wrapper.sodium.crypto_secretbox;
4 
5 import wrapper.sodium.core; // assure sodium got initialized
6 
7 public
8 import  deimos.sodium.crypto_secretbox : crypto_secretbox_KEYBYTES,
9                                          crypto_secretbox_keybytes,
10                                          crypto_secretbox_NONCEBYTES,
11                                          crypto_secretbox_noncebytes,
12                                          crypto_secretbox_MACBYTES,
13                                          crypto_secretbox_macbytes,
14                                          crypto_secretbox_PRIMITIVE,
15 /*                                       crypto_secretbox_primitive,
16                                          crypto_secretbox_easy,
17                                          crypto_secretbox_open_easy,
18                                          crypto_secretbox_detached,
19                                          crypto_secretbox_open_detached,  */
20                                          crypto_secretbox_keygen;
21 /*                                       crypto_secretbox_ZEROBYTES,
22                                          crypto_secretbox_zerobytes,
23                                          crypto_secretbox_BOXZEROBYTES,
24                                          crypto_secretbox_boxzerobytes,
25                                          crypto_secretbox,
26                                          crypto_secretbox_open;  */
27 
28 import std.exception : assertThrown;
29 import nogc.exception: enforce;
30 
31 
32 string crypto_secretbox_primitive() pure nothrow @nogc @trusted
33 {
34   import std..string : fromStringz;
35   static import deimos.sodium.crypto_secretbox;
36   return  fromStringz(deimos.sodium.crypto_secretbox.crypto_secretbox_primitive()); // strips terminating \0
37 }
38 
39 // overloading some functions between module deimos.sodium.crypto_secretbox and this module
40 
41 alias crypto_secretbox_easy          = deimos.sodium.crypto_secretbox.crypto_secretbox_easy;
42 
43 bool crypto_secretbox_easy(scope ubyte[] c,
44                            scope const ubyte[] m,
45                            const ubyte[crypto_secretbox_NONCEBYTES] nonce,
46                            const ubyte[crypto_secretbox_KEYBYTES] key) @nogc @trusted
47 {
48   const  c_expect_len = m.length + crypto_secretbox_MACBYTES;
49 //  enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_secretbox_MACBYTES: ", c_expect_len);
50   enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_secretbox_MACBYTES");
51   return  crypto_secretbox_easy(c.ptr, m.ptr, m.length, nonce.ptr, key.ptr) == 0; // __attribute__ ((nonnull(1, 4, 5)));
52 }
53 
54 alias crypto_secretbox_open_easy     = deimos.sodium.crypto_secretbox.crypto_secretbox_open_easy;
55 
56 bool crypto_secretbox_open_easy(scope ubyte[] m,
57                                 scope const ubyte[] c,
58                                 const ubyte[crypto_secretbox_NONCEBYTES] nonce,
59                                 const ubyte[crypto_secretbox_KEYBYTES] key) @nogc @trusted
60 {
61 //  enforce(c.length >= crypto_secretbox_MACBYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_secretbox_MACBYTES: ", crypto_secretbox_MACBYTES);
62   enforce(c.length >= crypto_secretbox_MACBYTES, "Expected c.length is not greater_equal to crypto_secretbox_MACBYTES");
63   const  m_expect_len = c.length - crypto_secretbox_MACBYTES;
64 //  enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_secretbox_MACBYTES: ", m_expect_len);
65   enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_secretbox_MACBYTES");
66   return  crypto_secretbox_open_easy(m.ptr, c.ptr, c.length, nonce.ptr, key.ptr) == 0; // __attribute__ ((warn_unused_result)) __attribute__ ((nonnull(2, 4, 5)));
67 }
68 
69 alias crypto_secretbox_detached      = deimos.sodium.crypto_secretbox.crypto_secretbox_detached;
70 
71 bool crypto_secretbox_detached(scope ubyte[] c,
72                                out ubyte[crypto_secretbox_MACBYTES] mac,
73                                scope const ubyte[] m,
74                                const ubyte[crypto_secretbox_NONCEBYTES] nonce, const ubyte[crypto_secretbox_KEYBYTES] key) @nogc @trusted
75 {
76 //  enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length);
77   enforce(c.length == m.length, "Expected c.length is not equal to m.length");
78   return  crypto_secretbox_detached(c.ptr, mac.ptr, m.ptr, m.length, nonce.ptr, key.ptr) == 0; // __attribute__ ((nonnull(1, 2, 5, 6)));
79 }
80 
81 alias crypto_secretbox_open_detached = deimos.sodium.crypto_secretbox.crypto_secretbox_open_detached;
82 
83 bool crypto_secretbox_open_detached(scope ubyte[] m,
84                                     scope const ubyte[] c,
85                                     const ubyte[crypto_secretbox_MACBYTES] mac,
86                                     const ubyte[crypto_secretbox_NONCEBYTES] nonce,
87                                     const ubyte[crypto_secretbox_KEYBYTES] key) @nogc @trusted
88 {
89 //  enforce(m.length == c.length, "Expected m.length: ", m.length, " to be equal to c.length: ", c.length);
90   enforce(m.length == c.length, "Expected m.length is not equal to c.length");
91   return  crypto_secretbox_open_detached(m.ptr, c.ptr, mac.ptr, c.length, nonce.ptr, key.ptr) == 0; // __attribute__ ((warn_unused_result)) __attribute__ ((nonnull(2, 3, 5, 6)));
92 }
93 
94 /* No overloads for -- NaCl compatibility interface ; Requires padding -- */
95 
96 @system
97 unittest {
98   import std.stdio : writeln;
99   import wrapper.sodium.randombytes : randombytes;
100   import deimos.sodium.crypto_secretbox;
101 
102   writeln("unittest block 1 from sodium.crypto_secretbox.d");
103 
104   ubyte[crypto_secretbox_NONCEBYTES]  nonce = void;
105   ubyte[crypto_secretbox_KEYBYTES]    key   = void;
106   randombytes(nonce);
107   randombytes(key);
108 
109   enum message_len = 4;
110   ubyte[crypto_secretbox_ZEROBYTES + message_len]  message, ciphertext, decrypted;
111   message[crypto_secretbox_ZEROBYTES..crypto_secretbox_ZEROBYTES+message_len] = [116, 101, 115, 116];
112   assert(crypto_secretbox     (ciphertext.ptr, message.ptr,    message.length,    nonce.ptr, key.ptr) == 0);
113   assert(crypto_secretbox_open(decrypted.ptr,  ciphertext.ptr, ciphertext.length, nonce.ptr, key.ptr) == 0);
114   assert(decrypted == message);
115 //  writeln("decrypted: ", decrypted[crypto_secretbox_ZEROBYTES..crypto_secretbox_ZEROBYTES+message_len]); // decrypted: [116, 101, 115, 116]
116 }
117 
118 @safe
119 unittest {
120   import std..string : representation;
121   import std.stdio : writeln;
122   import wrapper.sodium.randombytes : randombytes;
123   import wrapper.sodium.utils : sodium_increment;
124 
125   debug writeln("unittest block 2 from sodium.crypto_secretbox.d");
126 
127   assert(crypto_secretbox_keybytes()   == crypto_secretbox_KEYBYTES);
128   assert(crypto_secretbox_noncebytes() == crypto_secretbox_NONCEBYTES);
129   assert(crypto_secretbox_macbytes()   == crypto_secretbox_MACBYTES);
130   assert(crypto_secretbox_primitive()  == crypto_secretbox_PRIMITIVE);
131 
132   auto message = representation("test");
133   // avoid heap allocation, like in the example code
134   enum MESSAGE_LEN = 4;
135   enum CIPHERTEXT_LEN = (crypto_secretbox_MACBYTES + MESSAGE_LEN);
136 
137   ubyte[crypto_secretbox_NONCEBYTES]  nonce = void;
138   ubyte[crypto_secretbox_KEYBYTES]    key   = void;
139   ubyte[CIPHERTEXT_LEN]               ciphertext = void;
140 
141   randombytes(nonce);
142   crypto_secretbox_keygen(key);
143   assertThrown(crypto_secretbox_easy(ciphertext[0..$-1], message, nonce, key));
144   assert(crypto_secretbox_easy(ciphertext, message, nonce, key));
145   version(none) {
146     import std.array : appender;
147     import std.base64 : Base64;
148     auto app = appender!string();
149     app.put("Message (plaintext): test\n");
150     app.put("Ciphertext (base64): ");
151     const(char)[] encoded = Base64.encode(ciphertext);
152     app.put(encoded~"\n");
153     app.put("Nonce      (base64): ");
154     encoded = Base64.encode(nonce);
155     app.put(encoded~"\n");
156     app.put("Key        (base64): ");
157     encoded = Base64.encode(key);
158     app.put(encoded~"\n");
159     writeln(app.data);
160 /* taking this from a previous run:
161 Message (plaintext): test
162 Ciphertext (base64): tNV0M68PZea7+XKsfTeiJuOxVfU=
163 Nonce      (base64): 0gkPP63C0it0WeeO1LIQk4BDLpHFD58Z
164 Key        (base64): AtN67ZJklRbVVJ7R9QwVbKFpZivWXFHq9YlwVbM9n6s=
165 */
166 //ubyte[] decoded = Base64.decode("FPucA9l+");
167     ubyte[crypto_secretbox_KEYBYTES]    key_   = Base64.decode("AtN67ZJklRbVVJ7R9QwVbKFpZivWXFHq9YlwVbM9n6s=");
168     ubyte[crypto_secretbox_NONCEBYTES]  nonce_ = Base64.decode("0gkPP63C0it0WeeO1LIQk4BDLpHFD58Z");
169     ubyte[CIPHERTEXT_LEN]               ciphertext1 = Base64.decode("tNV0M68PZea7+XKsfTeiJuOxVfU=");
170     ubyte[MESSAGE_LEN]                  decrypted_  = void;
171     assert(crypto_secretbox_open_easy(decrypted_, ciphertext1, nonce_, key_), "message forged!");
172     assert(decrypted_ == message);
173   }
174   ubyte[MESSAGE_LEN]  decrypted = void;
175   assertThrown(crypto_secretbox_open_easy(decrypted, ciphertext[0..$-1-crypto_secretbox_MACBYTES], nonce, key));
176   assertThrown(crypto_secretbox_open_easy(decrypted[0..$-1], ciphertext, nonce, key));
177   assert(crypto_secretbox_open_easy(decrypted, ciphertext, nonce, key), "message forged!");
178   assert(decrypted == message);
179 
180   ubyte[crypto_secretbox_MACBYTES]  mac = void;
181   ubyte[MESSAGE_LEN]  ciphertext2;
182   decrypted  = decrypted.init;
183   sodium_increment(nonce);
184   assertThrown(crypto_secretbox_detached(ciphertext2[0..$-1], mac, message, nonce, key));
185   assert(crypto_secretbox_detached(ciphertext2, mac, message, nonce, key));
186 
187   assertThrown(crypto_secretbox_open_detached(decrypted[0..$-1], ciphertext2, mac, nonce, key));
188   assert(crypto_secretbox_open_detached(decrypted, ciphertext2, mac, nonce, key), "message forged!");
189   assert(decrypted == message);
190 }