1 // Written in the D programming language.
2 
3 /* Authenticated Encryption with Additional Data : AES256-GCM */
4 
5 module wrapper.sodium.crypto_aead_aes256gcm;
6 
7 import wrapper.sodium.core; // assure sodium got initialized
8 
9 public
10 import  deimos.sodium.crypto_aead_aes256gcm : crypto_aead_aes256gcm_is_available,
11                                               crypto_aead_aes256gcm_KEYBYTES,
12                                               crypto_aead_aes256gcm_keybytes,
13                                               crypto_aead_aes256gcm_NSECBYTES,
14                                               crypto_aead_aes256gcm_nsecbytes,
15                                               crypto_aead_aes256gcm_NPUBBYTES,
16                                               crypto_aead_aes256gcm_npubbytes,
17                                               crypto_aead_aes256gcm_ABYTES,
18                                               crypto_aead_aes256gcm_abytes,
19                                               crypto_aead_aes256gcm_MESSAGEBYTES_MAX,
20                                               crypto_aead_aes256gcm_messagebytes_max,
21                                               crypto_aead_aes256gcm_state,
22                                               crypto_aead_aes256gcm_statebytes,
23 /*                                            crypto_aead_aes256gcm_encrypt,
24                                               crypto_aead_aes256gcm_decrypt,
25                                               crypto_aead_aes256gcm_encrypt_detached,
26                                               crypto_aead_aes256gcm_decrypt_detached,
27                                               crypto_aead_aes256gcm_beforenm,
28                                               crypto_aead_aes256gcm_encrypt_afternm,
29                                               crypto_aead_aes256gcm_decrypt_afternm,
30                                               crypto_aead_aes256gcm_encrypt_detached_afternm,
31                                               crypto_aead_aes256gcm_decrypt_detached_afternm; */
32                                               crypto_aead_aes256gcm_keygen;
33 
34 import std.exception : assertThrown, assertNotThrown;
35 import nogc.exception: enforce;
36 
37 // overloading some functions between module deimos.sodium.crypto_aead_aes256gcm and this module
38 
39 
40 /* Wrapper(s)/substitute(s) for 'deimos' functions */
41 
42 
43 
44 alias crypto_aead_aes256gcm_encrypt = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_encrypt;
45 
46 /**
47  * The function crypto_aead_aes256gcm_encrypt() encrypts a message `m` using a secret key `k` (crypto_aead_aes256gcm_KEYBYTES bytes)
48  * and a public nonce `npub` (crypto_aead_aes256gcm_NPUBBYTES bytes).
49  * The encrypted message, as well as a tag authenticating both the confidential message m and ad.length bytes of non-confidential data `ad`,
50  * are put into `c`.
51  * ad can also be an empty array if no additional data are required.
52  * At most m.length + crypto_aead_aes256gcm_ABYTES bytes are put into c, reflected by the length of `c`.
53  * The function always returns true.
54  * The public nonce npub should never ever be reused with the same key. The recommended way to generate it is to use
55  * randombytes_buf() for the first message, and then to increment it for each subsequent message using the same key.
56  * Params:
57  * Returns:
58  * Throws:
59  * See_Also:
60  */
61 bool  crypto_aead_aes256gcm_encrypt(scope ubyte[] c,
62                                     scope const ubyte[] m,
63                                     scope const ubyte[] ad,
64                                     const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
65                                     const ubyte[crypto_aead_aes256gcm_KEYBYTES] k) @nogc @trusted
66 {
67 //  enforce(m.length, "Error invoking crypto_aead_aes256gcm_encrypt: m is null"); // not required
68   enforce(m.length <= 16UL * ((1UL << 32) - 2));
69   immutable  c_expect_len = m.length + crypto_aead_aes256gcm_ABYTES;
70 //  enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_aead_aes256gcm_ABYTES: ", c_expect_len);
71   enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_aead_aes256gcm_ABYTES");
72   ulong clen_p;
73   bool result = crypto_aead_aes256gcm_encrypt(c.ptr, &clen_p, m.ptr, m.length, ad.ptr, ad.length, null, npub.ptr, k.ptr) == 0;
74   assert(clen_p    == c_expect_len); // okay to be removed in release code
75   return  result;
76 }
77 
78 alias crypto_aead_aes256gcm_decrypt = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_decrypt;
79 
80 /**
81  * The function crypto_aead_aes256gcm_decrypt() verifies that the ciphertext `c` (as produced by crypto_aead_aes256gcm_encrypt()),
82  * includes a valid tag using a secret key `k`, a public nonce `npub`, and additional data `ad`. c.length is the ciphertext length
83  * in bytes with the authenticator, so it has to be at least aead_aes256gcm_ABYTES.
84  *
85  * ad can be an empty array if no additional data are required.
86  * The function returns false if the verification fails.
87  * If the verification succeeds, the function returns true, puts the decrypted message into `m` and stores its actual number of bytes into `mlen_p`.
88  *  (and `c`'s .length might shrink to the length actually required).
89  * At most c.length - crypto_aead_aes256gcm_ABYTES bytes will be put into m.
90  */
91 bool  crypto_aead_aes256gcm_decrypt(scope ubyte[] m,
92                                     scope const ubyte[] c,
93                                     scope const ubyte[] ad,
94                                     const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
95                                     const ubyte[crypto_aead_aes256gcm_KEYBYTES] k) @nogc @trusted
96 {
97   enforce(c.length <= 16UL * (1UL << 32));
98 //  enforce(c.length >= crypto_aead_aes256gcm_ABYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_aead_aes256gcm_ABYTES: ", crypto_aead_aes256gcm_ABYTES);
99   enforce(c.length >= crypto_aead_aes256gcm_ABYTES, "Expected c.length is not greater_equal to crypto_aead_aes256gcm_ABYTES");
100   immutable  m_expect_len = c.length - crypto_aead_aes256gcm_ABYTES;
101 //  enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_aead_aes256gcm_ABYTES: ", m_expect_len);
102   enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_aead_aes256gcm_ABYTES");
103   ulong mlen_p;
104   bool result = crypto_aead_aes256gcm_decrypt(m.ptr, &mlen_p, null, c.ptr, c.length, ad.ptr, ad.length, npub.ptr, k.ptr) == 0;
105   if (result)
106     assert(mlen_p ==  m_expect_len); // okay to be removed in release code
107   return  result;
108 }
109 
110 alias crypto_aead_aes256gcm_encrypt_detached = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_encrypt_detached;
111 
112 bool  crypto_aead_aes256gcm_encrypt_detached(scope ubyte[] c,
113                                              out ubyte[crypto_aead_aes256gcm_ABYTES] mac,
114                                              scope const ubyte[] m,
115                                              scope const ubyte[] ad,
116                                              const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
117                                              const ubyte[crypto_aead_aes256gcm_KEYBYTES] k) @nogc @trusted
118 {
119 //  enforce(m.length, "Error invoking crypto_aead_aes256gcm_encrypt_detached: m is null"); // not required
120   enforce(m.length <= 16UL * ((1UL << 32) - 2));
121 //  enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length);
122   enforce(c.length == m.length, "Expected c.length is not equal to m.length");
123   ulong maclen_p;
124   bool result =  crypto_aead_aes256gcm_encrypt_detached(c.ptr, mac.ptr, &maclen_p, m.ptr, m.length, ad.ptr, ad.length, null, npub.ptr, k.ptr) == 0;
125   assert(maclen_p == crypto_aead_aes256gcm_ABYTES); // okay to be removed in release code
126   return  result;
127 }
128 
129 alias crypto_aead_aes256gcm_decrypt_detached = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_decrypt_detached;
130 
131 bool crypto_aead_aes256gcm_decrypt_detached(scope ubyte[] m,
132                                             scope const ubyte[] c,
133                                             const ubyte[crypto_aead_aes256gcm_ABYTES] mac,
134                                             scope const ubyte[] ad,
135                                             const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
136                                             const ubyte[crypto_aead_aes256gcm_KEYBYTES] k) @nogc @trusted
137 {
138 //  enforce(c.length, "Error invoking crypto_aead_aes256gcm_decrypt_detached: c is null"); // not required
139   enforce(c.length <= 16UL * (1UL << 32));
140 //  enforce(m.length == c.length, "Expected m.length: ", m.length, " to be equal to c.length: ", c.length);
141   enforce(m.length == c.length, "Expected m.lengthis not equal to c.length");
142   return  crypto_aead_aes256gcm_decrypt_detached(m.ptr, null, c.ptr, c.length, mac.ptr, ad.ptr, ad.length, npub.ptr, k.ptr) == 0;
143 }
144 
145 /* -- Precomputation interface -- */
146 
147 alias crypto_aead_aes256gcm_beforenm = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_beforenm;
148 
149 pragma(inline, true)
150 bool crypto_aead_aes256gcm_beforenm(ref crypto_aead_aes256gcm_state ctx,
151                                     const ubyte[crypto_aead_aes256gcm_KEYBYTES] k) @nogc pure @trusted
152 {
153   return  crypto_aead_aes256gcm_beforenm(&ctx, k.ptr) == 0;
154 }
155 
156 alias crypto_aead_aes256gcm_encrypt_afternm = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_encrypt_afternm;
157 
158 bool crypto_aead_aes256gcm_encrypt_afternm(scope ubyte[] c,
159                                            scope const ubyte[] m,
160                                            scope const ubyte[] ad,
161                                            const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
162                                            const ref crypto_aead_aes256gcm_state ctx) @nogc @trusted
163 {
164 //  enforce(m.length, "Error invoking crypto_aead_aes256gcm_encrypt_afternm: m is null"); // not required
165   enforce(m.length <= 16UL * ((1UL << 32) - 2));
166   immutable  c_expect_len = m.length + crypto_aead_aes256gcm_ABYTES;
167 //  enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_aead_aes256gcm_ABYTES: ", c_expect_len);
168   enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_aead_aes256gcm_ABYTES");
169   ulong clen_p;
170   bool result =  crypto_aead_aes256gcm_encrypt_afternm(c.ptr, &clen_p, m.ptr, m.length, ad.ptr, ad.length, null, npub.ptr, &ctx) == 0;
171   assert(clen_p    == c_expect_len); // okay to be removed in release code
172   return  result;
173 }
174 
175 alias crypto_aead_aes256gcm_decrypt_afternm = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_decrypt_afternm;
176 
177 bool  crypto_aead_aes256gcm_decrypt_afternm(scope ubyte[] m,
178                                             scope const ubyte[] c,
179                                             scope const ubyte[] ad,
180                                             const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
181                                             const ref crypto_aead_aes256gcm_state ctx) @nogc @trusted
182 {
183   enforce(c.length <= 16UL * (1UL << 32));
184 //  enforce(c.length >= crypto_aead_aes256gcm_ABYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_aead_aes256gcm_ABYTES: ", crypto_aead_aes256gcm_ABYTES);
185   enforce(c.length >= crypto_aead_aes256gcm_ABYTES, "Expected c.length is not greater_equal to crypto_aead_aes256gcm_ABYTES");
186   immutable  m_expect_len = c.length - crypto_aead_aes256gcm_ABYTES;
187 //  enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_aead_aes256gcm_ABYTES: ", m_expect_len);
188   enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_aead_aes256gcm_ABYTES");
189   ulong mlen_p;
190   bool result =  crypto_aead_aes256gcm_decrypt_afternm(m.ptr, &mlen_p, null, c.ptr, c.length, ad.ptr, ad.length, npub.ptr, &ctx) == 0;
191   if (result)
192     assert(mlen_p  == m_expect_len); // okay to be removed in release code
193   return  result;
194 }
195 
196 alias crypto_aead_aes256gcm_encrypt_detached_afternm = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_encrypt_detached_afternm;
197 
198 bool  crypto_aead_aes256gcm_encrypt_detached_afternm(scope ubyte[] c,
199                                                      out ubyte[crypto_aead_aes256gcm_ABYTES] mac,
200                                                      scope const ubyte[] m,
201                                                      scope const ubyte[] ad,
202                                                      const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
203                                                      const ref crypto_aead_aes256gcm_state ctx) @nogc @trusted
204 {
205 //  enforce(m.length, "Error invoking crypto_aead_aes256gcm_encrypt_detached_afternm: m is null"); // not required
206   enforce(m.length <= 16UL * ((1UL << 32) - 2));
207 //  enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length);
208   enforce(c.length == m.length, "Expected c.length is not equal to m.length");
209   ulong maclen_p;
210   bool result =  crypto_aead_aes256gcm_encrypt_detached_afternm(c.ptr, mac.ptr, &maclen_p, m.ptr, m.length, ad.ptr, ad.length, null, npub.ptr, &ctx) == 0;
211   assert(maclen_p == crypto_aead_aes256gcm_ABYTES); // okay to be removed in release code
212   return  result;
213 }
214 
215 alias crypto_aead_aes256gcm_decrypt_detached_afternm = deimos.sodium.crypto_aead_aes256gcm.crypto_aead_aes256gcm_decrypt_detached_afternm;
216 
217 bool  crypto_aead_aes256gcm_decrypt_detached_afternm(scope ubyte[] m,
218                                                      scope const ubyte[] c,
219                                                      const ubyte[crypto_aead_aes256gcm_ABYTES] mac,
220                                                      scope const ubyte[] ad,
221                                                      const ubyte[crypto_aead_aes256gcm_NPUBBYTES] npub,
222                                                      const ref crypto_aead_aes256gcm_state ctx) @nogc @trusted
223 {
224 //  enforce(c.length, "Error invoking crypto_aead_aes256gcm_decrypt_detached_afternm: c is null"); // not required
225   enforce(c.length <= 16UL * (1UL << 32));
226 //  enforce(m.length == c.length, "Expected m.length: ", m.length, " to be equal to c.length: ", c.length);
227   enforce(m.length == c.length, "Expected m.length is not equal to c.length");
228   return  crypto_aead_aes256gcm_decrypt_detached_afternm(m.ptr, null, c.ptr, c.length, mac.ptr, ad.ptr, ad.length, npub.ptr, &ctx) == 0;
229 }
230 
231 
232 version(unittest)
233 {
234   import wrapper.sodium.randombytes : randombytes;
235   // share a key and nonce in the following unittests
236   ubyte[crypto_aead_aes256gcm_NPUBBYTES]  nonce = void;
237   ubyte[crypto_aead_aes256gcm_KEYBYTES]   key   = void;
238 
239   static this() {
240     randombytes(nonce);
241     randombytes(key);
242   }
243 }
244 
245 
246 @system
247 unittest
248 {
249   import std.string : representation;
250   import std.stdio : writeln;
251   debug writeln("unittest block 1 from sodium.crypto_aead_aes256gcm.d");
252 
253   if (crypto_aead_aes256gcm_is_available() == 1) {
254     auto message         = representation("test");
255     enum message_len = 4UL;
256     auto additional_data = representation("A typical use case for additional data is to store protocol-specific metadata " ~
257       "about the message, such as its length and encoding. (non-confidential, non-encrypted data");
258     ubyte[message_len + crypto_aead_aes256gcm_ABYTES]  ciphertext;
259     ulong   ciphertext_len;
260 
261     crypto_aead_aes256gcm_encrypt(ciphertext.ptr, &ciphertext_len, message.ptr, message.length,
262       additional_data.ptr, additional_data.length, null, nonce.ptr, key.ptr);
263 
264     ubyte[message_len]  decrypted;
265     ulong decrypted_len;
266     assert(ciphertext_len == ciphertext.length);
267     assert(crypto_aead_aes256gcm_decrypt(decrypted.ptr, &decrypted_len, null, ciphertext.ptr, ciphertext_len,
268         additional_data.ptr, additional_data.length, nonce.ptr, key.ptr) == 0);
269     assert(decrypted == message); //writeln("Decrypted message (aead_aes256gcm): ", cast(string)decrypted);
270     assert(decrypted_len == decrypted.length);
271 
272     // test null for &ciphertext_len
273     crypto_aead_aes256gcm_encrypt(ciphertext.ptr, null, message.ptr, message.length,
274       additional_data.ptr, additional_data.length, null, nonce.ptr, key.ptr);
275     crypto_aead_aes256gcm_decrypt(decrypted.ptr, null, null, ciphertext.ptr, ciphertext_len,
276       additional_data.ptr, additional_data.length, nonce.ptr, key.ptr);
277 
278     align(16) crypto_aead_aes256gcm_state  ctx;
279     debug writeln("address of ctx: ", &ctx);
280 //    debug writeln("(cast(size_t)&ctx & 0x0FUL): ", (cast(size_t)&ctx & 0x0FUL));
281     assert((cast(size_t)&ctx & 0x0FUL) == 0);
282     crypto_aead_aes256gcm_beforenm(&ctx, key.ptr);
283   }
284 }
285 
286 @safe
287 unittest
288 {
289   import std.string : representation;
290   import std.stdio : writeln, writefln;
291   import wrapper.sodium.utils : sodium_increment;
292 
293 version(none/*viWindowsX86*/) {
294   writeln("early return for Windows X86: unittest block 2 from sodium.crypto_aead_aes256gcm.d");
295   writeln("There is some not yet checked issue with 'Access Violation' running -m32_mscoff (alignment?), yet -m32 is okay");
296   return;
297 }
298 else {
299   debug writeln("unittest block 2 from sodium.crypto_aead_aes256gcm.d");
300 
301   assert(crypto_aead_aes256gcm_keybytes()         == crypto_aead_aes256gcm_KEYBYTES);
302   assert(crypto_aead_aes256gcm_nsecbytes()        == crypto_aead_aes256gcm_NSECBYTES);
303   assert(crypto_aead_aes256gcm_npubbytes()        == crypto_aead_aes256gcm_NPUBBYTES);
304   assert(crypto_aead_aes256gcm_abytes()           == crypto_aead_aes256gcm_ABYTES);
305   assert(crypto_aead_aes256gcm_messagebytes_max() == crypto_aead_aes256gcm_MESSAGEBYTES_MAX); // see travis Build #74
306 //  debug writeln("crypto_aead_aes256gcm_MESSAGEBYTES_MAX:   ", crypto_aead_aes256gcm_MESSAGEBYTES_MAX);
307 //  debug writeln("crypto_aead_aes256gcm_messagebytes_max(): ", crypto_aead_aes256gcm_messagebytes_max());
308   assert(crypto_aead_aes256gcm_statebytes()       == crypto_aead_aes256gcm_state.sizeof);
309 
310 
311   if (crypto_aead_aes256gcm_is_available() == 1) {
312     auto message         = representation("test");
313     enum message_len = 4UL;
314     auto additional_data = representation("A typical use case for additional data is to store protocol-specific metadata " ~
315       "about the message, such as its length and encoding. (non-confidential, non-encrypted data");
316     ubyte[message_len + crypto_aead_aes256gcm_ABYTES]  ciphertext1;
317     sodium_increment(nonce);
318 
319     assertThrown   (crypto_aead_aes256gcm_encrypt(ciphertext1[0..$-1],              message, additional_data, nonce, key));
320     assertNotThrown(crypto_aead_aes256gcm_encrypt(ciphertext1[0..$-message.length], null,    additional_data, nonce, key));
321     assertNotThrown(crypto_aead_aes256gcm_encrypt(ciphertext1,                      message, null,            nonce, key));
322 
323     assert(crypto_aead_aes256gcm_encrypt(ciphertext1, message, additional_data, nonce, key));
324 
325     ubyte[message_len]  decrypted;
326     assertThrown   (crypto_aead_aes256gcm_decrypt(decrypted,         ciphertext1[0..crypto_aead_aes256gcm_ABYTES-1], additional_data, nonce, key));
327     assertThrown   (crypto_aead_aes256gcm_decrypt(decrypted[0..$-1], ciphertext1,                                    additional_data, nonce, key));
328     assertNotThrown(crypto_aead_aes256gcm_decrypt(decrypted,         ciphertext1,                                    null,            nonce, key));
329 
330     assert(crypto_aead_aes256gcm_decrypt(decrypted, ciphertext1, additional_data, nonce, key));
331     assert(decrypted == message);
332 
333     ubyte[message_len]  ciphertext2;
334     ubyte[crypto_aead_aes256gcm_ABYTES] mac;
335     sodium_increment(nonce);
336 
337     assertThrown   (crypto_aead_aes256gcm_encrypt_detached(ciphertext2[0..$-1],              mac, message, additional_data, nonce, key));
338     assertNotThrown(crypto_aead_aes256gcm_encrypt_detached(ciphertext2[0..$-message.length], mac, null,    additional_data, nonce, key));
339     assertNotThrown(crypto_aead_aes256gcm_encrypt_detached(ciphertext2,                      mac, message, null,            nonce, key));
340 
341     assert(crypto_aead_aes256gcm_encrypt_detached(ciphertext2, mac, message, additional_data, nonce, key));
342 
343     assertNotThrown(crypto_aead_aes256gcm_decrypt_detached(decrypted[0..$-message.length], null,       mac, additional_data, nonce, key));
344     assertThrown   (crypto_aead_aes256gcm_decrypt_detached(decrypted[0..$-1],              ciphertext2, mac, additional_data, nonce, key));
345     assertNotThrown(crypto_aead_aes256gcm_decrypt_detached(decrypted,                      ciphertext2, mac, null,            nonce, key));
346 
347     assert(crypto_aead_aes256gcm_decrypt_detached(decrypted, ciphertext2, mac, additional_data, nonce, key));
348     assert(decrypted == message);
349 
350     /* -- Precomputation interface -- */
351 
352     align(16) crypto_aead_aes256gcm_state  ctx;
353     assert(crypto_aead_aes256gcm_beforenm(ctx, key));
354 
355     sodium_increment(nonce);
356 version(GNU) {}
357 else {
358     assertThrown   (crypto_aead_aes256gcm_encrypt_afternm(ciphertext1[0..$-1],              message, additional_data, nonce, ctx));
359     assertNotThrown(crypto_aead_aes256gcm_encrypt_afternm(ciphertext1[0..$-message.length], null,    additional_data, nonce, ctx));
360     assertNotThrown(crypto_aead_aes256gcm_encrypt_afternm(ciphertext1,                      message, null,            nonce, ctx));
361 }
362     assert(crypto_aead_aes256gcm_encrypt_afternm(ciphertext1, message, additional_data, nonce, ctx));
363 
364 version(GNU) {}
365 else {
366     assertThrown   (crypto_aead_aes256gcm_decrypt_afternm(decrypted,         ciphertext1[0..crypto_aead_aes256gcm_ABYTES-1], additional_data, nonce, ctx));
367     assertThrown   (crypto_aead_aes256gcm_decrypt_afternm(decrypted[0..$-1], ciphertext1,                                    additional_data, nonce, ctx));
368     assertNotThrown(crypto_aead_aes256gcm_decrypt_afternm(decrypted,         ciphertext1,                                    null,            nonce, ctx));
369 }
370     assert(crypto_aead_aes256gcm_decrypt_afternm(decrypted, ciphertext1, additional_data, nonce, ctx));
371     assert(decrypted == message);
372 
373     sodium_increment(nonce);
374 
375 version(GNU) {}
376 else {
377     assertThrown   (crypto_aead_aes256gcm_encrypt_detached_afternm(ciphertext2[0..$-1],              mac, message, additional_data, nonce, ctx));
378     assertNotThrown(crypto_aead_aes256gcm_encrypt_detached_afternm(ciphertext2[0..$-message.length], mac, null,    additional_data, nonce, ctx));
379     assertNotThrown(crypto_aead_aes256gcm_encrypt_detached_afternm(ciphertext2,                      mac, message, null,            nonce, ctx));
380 }
381     assert(crypto_aead_aes256gcm_encrypt_detached_afternm(ciphertext2, mac, message, additional_data, nonce, ctx));
382 
383 version(GNU) {}
384 else {
385     assertThrown   (crypto_aead_aes256gcm_decrypt_detached_afternm(decrypted[0..$-1],              ciphertext2, mac, additional_data, nonce, ctx));
386     assertNotThrown(crypto_aead_aes256gcm_decrypt_detached_afternm(decrypted[0..$-message.length], null,       mac, additional_data, nonce, ctx));
387     assertNotThrown(crypto_aead_aes256gcm_decrypt_detached_afternm(decrypted,                      ciphertext2, mac, null,            nonce, ctx));
388 }
389     assert(crypto_aead_aes256gcm_decrypt_detached_afternm(decrypted, ciphertext2, mac, additional_data, nonce, ctx));
390     assert(decrypted == message);
391 
392     ubyte[crypto_aead_aes256gcm_KEYBYTES] k;
393     crypto_aead_aes256gcm_keygen(k);
394   } // if (crypto_aead_aes256gcm_is_available() == 1)
395 }
396 }
397 
398 @nogc @safe
399 unittest
400 {
401   if (crypto_aead_aes256gcm_is_available() == 1) {
402     // usage is not cryptographically safe here; it's purpose is to test @nogc @safe
403     import std.string : representation;
404     ubyte[crypto_aead_aes256gcm_NPUBBYTES]  n = nonce;
405     ubyte[crypto_aead_aes256gcm_KEYBYTES]   k;
406     crypto_aead_aes256gcm_keygen(k);
407     ubyte[4] message  = [116, 101, 115, 116]; //representation("test");
408     ubyte[4] decrypted;
409     enum  m_len  = 4UL;
410     auto additional_data = representation("A typical use case for additional data is to store protocol-specific metadata " ~
411       "about the message, such as its length and encoding. (non-confidential, non-encrypted data");
412     ubyte[m_len+crypto_aead_aes256gcm_ABYTES] ciphertext1;
413     ubyte[m_len]                              ciphertext2;
414 
415     ubyte[      crypto_aead_aes256gcm_ABYTES] mac;
416     assert(crypto_aead_aes256gcm_encrypt(ciphertext1, message, additional_data, n, k));
417     assert(crypto_aead_aes256gcm_decrypt(decrypted, ciphertext1, additional_data, n, k));
418     assert(decrypted == message);
419     decrypted = decrypted.init;
420     assert(crypto_aead_aes256gcm_encrypt_detached(ciphertext2, mac, message, additional_data, n, k));
421     assert(crypto_aead_aes256gcm_decrypt_detached(decrypted, ciphertext2, mac, additional_data, n, k));
422     assert(decrypted == message);
423 version(Win32) {}
424 else {
425     align(16) crypto_aead_aes256gcm_state  ctx;
426     decrypted = decrypted.init;
427     ciphertext1 = ciphertext1.init;
428     ciphertext2 = ciphertext2.init;
429     assert(crypto_aead_aes256gcm_beforenm(ctx, key));
430     assert(crypto_aead_aes256gcm_encrypt_afternm(ciphertext1, message, additional_data, n, ctx));
431     assert(crypto_aead_aes256gcm_decrypt_afternm(decrypted, ciphertext1, additional_data, n, ctx));
432     assert(decrypted == message);
433     decrypted = decrypted.init;
434     assert(crypto_aead_aes256gcm_encrypt_detached_afternm(ciphertext2, mac, message, additional_data, n, ctx));
435     assert(crypto_aead_aes256gcm_decrypt_detached_afternm(decrypted, ciphertext2, mac, additional_data, n, ctx));
436     assert(decrypted == message);
437 }
438   }
439 }