1 // Written in the D programming language. 2 3 module wrapper.sodium.crypto_aead_xchacha20poly1305; 4 5 import wrapper.sodium.core; // assure sodium got initialized 6 7 public 8 import deimos.sodium.crypto_aead_xchacha20poly1305 : crypto_aead_xchacha20poly1305_ietf_KEYBYTES, 9 crypto_aead_xchacha20poly1305_ietf_keybytes, 10 crypto_aead_xchacha20poly1305_ietf_NSECBYTES, 11 crypto_aead_xchacha20poly1305_ietf_nsecbytes, 12 crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, 13 crypto_aead_xchacha20poly1305_ietf_npubbytes, 14 crypto_aead_xchacha20poly1305_ietf_ABYTES, 15 crypto_aead_xchacha20poly1305_ietf_abytes, 16 crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX, 17 crypto_aead_xchacha20poly1305_ietf_messagebytes_max, 18 /* crypto_aead_xchacha20poly1305_ietf_encrypt, 19 crypto_aead_xchacha20poly1305_ietf_decrypt, 20 crypto_aead_xchacha20poly1305_ietf_encrypt_detached, 21 crypto_aead_xchacha20poly1305_ietf_decrypt_detached, */ 22 crypto_aead_xchacha20poly1305_ietf_keygen; 23 ; 24 25 26 import std.exception : assertThrown, assertNotThrown; 27 import nogc.exception: enforce; 28 29 // overloading some functions between module deimos.sodium.crypto_aead_xchacha20poly1305 and this module 30 31 alias crypto_aead_xchacha20poly1305_ietf_encrypt = deimos.sodium.crypto_aead_xchacha20poly1305.crypto_aead_xchacha20poly1305_ietf_encrypt; 32 33 /** 34 * The function crypto_aead_xchacha20poly1305_ietf_encrypt() 35 * encrypts a message `m` using a secret key `k` (crypto_aead_xchacha20poly1305_ietf_KEYBYTES bytes) 36 * and a public nonce `npub` (crypto_aead_xchacha20poly1305_ietf_NPUBBYTES bytes). 37 * The encrypted message, as well as a tag authenticating both the confidential message m and ad.length bytes of non-confidential data `ad`, 38 * are put into `c`. 39 * ad can also be an empty array if no additional data are required. 40 * At most m.length + crypto_aead_xchacha20poly1305_ietf_ABYTES bytes are put into `c`, and the actual number of bytes is stored into `clen_p`. 41 * The function always returns true. 42 * The public nonce npub should never ever be reused with the same key. The recommended way to generate it is to use 43 * randombytes_buf() for the first message, and then to increment it for each subsequent message using the same key. 44 */ 45 bool crypto_aead_xchacha20poly1305_ietf_encrypt(scope ubyte[] c, 46 scope const ubyte[] m, 47 scope const ubyte[] ad, 48 const ubyte[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] npub, 49 const ubyte[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] k) @nogc @trusted 50 { 51 // enforce(m.length, "Error invoking crypto_aead_xchacha20poly1305_ietf_encrypt: m is null"); // TODO check if m.ptr==null would be okay 52 enforce(c.length == m.length + crypto_aead_xchacha20poly1305_ietf_ABYTES, "Error invoking crypto_aead_xchacha20poly1305_ietf_encrypt: out buffer too small"); 53 ulong clen_p; 54 bool result = crypto_aead_xchacha20poly1305_ietf_encrypt(c.ptr, &clen_p, m.ptr, m.length, ad.ptr, ad.length, null, npub.ptr, k.ptr) == 0; 55 if (result) 56 assert(clen_p == m.length + crypto_aead_xchacha20poly1305_ietf_ABYTES); // okay to be removed in release code 57 return result; 58 } 59 60 alias crypto_aead_xchacha20poly1305_ietf_decrypt = deimos.sodium.crypto_aead_xchacha20poly1305.crypto_aead_xchacha20poly1305_ietf_decrypt; 61 62 /** 63 * The function crypto_aead_xchacha20poly1305_ietf_decrypt() 64 verifies that the ciphertext `c` (as produced by crypto_aead_xchacha20poly1305_ietf_encrypt()), 65 * includes a valid tag using a secret key `k`, a public nonce `npub`, and additional data `ad`. c.length is the ciphertext length 66 * in bytes with the authenticator, so it has to be at least crypto_aead_xchacha20poly1305_ietf_ABYTES. 67 * 68 * ad can be an empty array if no additional data are required. 69 * The function returns false if the verification fails. 70 * If the verification succeeds, the function returns true, puts the decrypted message into `m` and stores its actual number of bytes into `mlen_p`. 71 * At most c.length - crypto_aead_xchacha20poly1305_ietf_ABYTES bytes will be put into m. 72 */ 73 bool crypto_aead_xchacha20poly1305_ietf_decrypt(scope ubyte[] m, 74 scope const ubyte[] c, 75 scope const ubyte[] ad, 76 const ubyte[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] npub, 77 const ubyte[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] k) @nogc @trusted 78 { 79 // enforce(c.length >= crypto_aead_xchacha20poly1305_ietf_ABYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_aead_xchacha20poly1305_ietf_ABYTES: ", crypto_aead_xchacha20poly1305_ietf_ABYTES); 80 enforce(c.length >= crypto_aead_xchacha20poly1305_ietf_ABYTES, "Expected c.length is not greater_equal to crypto_aead_xchacha20poly1305_ietf_ABYTES"); 81 const m_expect_len = c.length - crypto_aead_xchacha20poly1305_ietf_ABYTES; 82 // enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_aead_xchacha20poly1305_ietf_ABYTES: ", m_expect_len); 83 enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_aead_xchacha20poly1305_ietf_ABYTES"); 84 ulong mlen_p; 85 bool result = crypto_aead_xchacha20poly1305_ietf_decrypt(m.ptr, &mlen_p, null, c.ptr, c.length, ad.ptr, ad.length, npub.ptr, k.ptr) == 0; 86 if (result) 87 assert(mlen_p == m_expect_len); // okay to be removed in release code 88 return result; 89 } 90 91 alias crypto_aead_xchacha20poly1305_ietf_encrypt_detached = deimos.sodium.crypto_aead_xchacha20poly1305.crypto_aead_xchacha20poly1305_ietf_encrypt_detached; 92 93 bool crypto_aead_xchacha20poly1305_ietf_encrypt_detached(scope ubyte[] c, 94 out ubyte[crypto_aead_xchacha20poly1305_ietf_ABYTES] mac, 95 scope const ubyte[] m, 96 scope const ubyte[] ad, 97 const ubyte[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] npub, 98 const ubyte[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] k) @nogc @trusted 99 { 100 // enforce(m.length, "Error invoking crypto_aead_xchacha20poly1305_ietf_encrypt_detached: m is null"); // TODO check if m.ptr==null would be okay 101 // enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length); 102 enforce(c.length == m.length, "Expected c.length is not equal to m.length"); 103 ulong maclen_p; 104 bool result = crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c.ptr, mac.ptr, &maclen_p, m.ptr, m.length, ad.ptr, ad.length, null, npub.ptr, k.ptr) == 0; 105 assert(maclen_p == crypto_aead_xchacha20poly1305_ietf_ABYTES); 106 return result; 107 } 108 109 alias crypto_aead_xchacha20poly1305_ietf_decrypt_detached = deimos.sodium.crypto_aead_xchacha20poly1305.crypto_aead_xchacha20poly1305_ietf_decrypt_detached; 110 111 bool crypto_aead_xchacha20poly1305_ietf_decrypt_detached(scope ubyte[] m, 112 scope const ubyte[] c, 113 const ubyte[crypto_aead_xchacha20poly1305_ietf_ABYTES] mac, 114 scope const ubyte[] ad, 115 const ubyte[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] npub, 116 const ubyte[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] k) @nogc @trusted 117 { 118 // enforce(c.length, "Error invoking crypto_aead_xchacha20poly1305_ietf_decrypt_detached: c is null"); // TODO check if c.ptr==null would be okay 119 // enforce(m.length == c.length, "Expected m.length: ", m.length, " to be equal to c.length: ", c.length); 120 enforce(m.length == c.length, "Expected m.length is not equal to c.length"); 121 return crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m.ptr, null, c.ptr, c.length, mac.ptr, ad.ptr, ad.length, npub.ptr, k.ptr) == 0; 122 } 123 124 125 126 version(unittest) 127 { 128 import wrapper.sodium.randombytes : randombytes; 129 // share a key and nonce in the following unittests 130 ubyte[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] nonce = void; 131 ubyte[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] key = void; 132 133 static this() { 134 randombytes(nonce); 135 randombytes(key); 136 } 137 } 138 139 140 @system 141 unittest 142 { 143 import std..string : representation; 144 import std.stdio : writeln; 145 debug writeln("unittest block 1 from sodium.crypto_aead_xchacha20poly1305.d"); 146 147 auto message = representation("test"); 148 enum message_len = 4UL; 149 auto additional_data = representation("A typical use case for additional data is to store protocol-specific metadata " ~ 150 "about the message, such as its length and encoding. (non-confidential, non-encrypted data"); 151 ubyte[message_len + crypto_aead_xchacha20poly1305_ietf_ABYTES] ciphertext; 152 ulong ciphertext_len; 153 154 crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext.ptr, &ciphertext_len, message.ptr, message.length, 155 additional_data.ptr, additional_data.length, null, nonce.ptr, key.ptr); 156 157 ubyte[message_len] decrypted; 158 ulong decrypted_len; 159 assert(ciphertext_len == ciphertext.length); 160 assert(crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted.ptr, &decrypted_len, null, ciphertext.ptr, ciphertext_len, 161 additional_data.ptr, additional_data.length, nonce.ptr, key.ptr) == 0); 162 assert(decrypted == message); //writeln("Decrypted message (aead_chacha20poly1305): ", cast(string)decrypted); 163 assert(decrypted_len == decrypted.length); 164 165 // test null for &ciphertext_len / decrypted_len 166 crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext.ptr, null, message.ptr, message.length, 167 additional_data.ptr, additional_data.length, null, nonce.ptr, key.ptr); 168 crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted.ptr, null, null, ciphertext.ptr, ciphertext_len, 169 additional_data.ptr, additional_data.length, nonce.ptr, key.ptr); 170 171 ubyte[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] k; 172 crypto_aead_xchacha20poly1305_ietf_keygen(k); 173 } 174 175 @safe 176 unittest 177 { 178 import std..string : representation; 179 import std.stdio : writeln, writefln; 180 import wrapper.sodium.utils : sodium_increment; 181 debug writeln("unittest block 2 from sodium.crypto_aead_xchacha20poly1305.d"); 182 183 assert(crypto_aead_xchacha20poly1305_ietf_keybytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); 184 assert(crypto_aead_xchacha20poly1305_ietf_nsecbytes() == crypto_aead_xchacha20poly1305_ietf_NSECBYTES); 185 assert(crypto_aead_xchacha20poly1305_ietf_npubbytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); 186 assert(crypto_aead_xchacha20poly1305_ietf_abytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); 187 assert(crypto_aead_xchacha20poly1305_ietf_messagebytes_max() == crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); 188 189 190 auto message = representation("test"); 191 enum message_len = 4UL; 192 auto additional_data = representation("A typical use case for additional data is to store protocol-specific metadata " ~ 193 "about the message, such as its length and encoding. (non-confidential, non-encrypted data"); 194 ubyte[message_len + crypto_aead_xchacha20poly1305_ietf_ABYTES] ciphertext1; 195 sodium_increment(nonce); 196 197 assertThrown (crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext1[0..$-1], message, additional_data, nonce, key)); 198 assertNotThrown(crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext1[0..$-message.length], null, additional_data, nonce, key)); 199 assertNotThrown(crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext1, message, null, nonce, key)); 200 201 assert(crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext1, message, additional_data, nonce, key)); 202 203 ubyte[message_len] decrypted; 204 assertThrown (crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, ciphertext1[0..crypto_aead_xchacha20poly1305_ietf_ABYTES-1], additional_data, nonce, key)); 205 assertThrown (crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted[0..$-1], ciphertext1, additional_data, nonce, key)); 206 assertNotThrown(crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, ciphertext1, null, nonce, key)); 207 208 assert(crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, ciphertext1, additional_data, nonce, key)); 209 assert(decrypted == message); 210 // 211 ubyte[message_len] ciphertext2; 212 ubyte[crypto_aead_xchacha20poly1305_ietf_ABYTES] mac; 213 sodium_increment(nonce); 214 215 assertThrown (crypto_aead_xchacha20poly1305_ietf_encrypt_detached(ciphertext2[0..$-1], mac, message, additional_data, nonce, key)); 216 assertNotThrown(crypto_aead_xchacha20poly1305_ietf_encrypt_detached(ciphertext2[0..$-message.length], mac, null, additional_data, nonce, key)); 217 assertNotThrown(crypto_aead_xchacha20poly1305_ietf_encrypt_detached(ciphertext2, mac, message, null, nonce, key)); 218 219 assert(crypto_aead_xchacha20poly1305_ietf_encrypt_detached(ciphertext2, mac, message, additional_data, nonce, key)); 220 221 assertThrown (crypto_aead_xchacha20poly1305_ietf_decrypt_detached(decrypted[0..$-1], ciphertext2, mac, additional_data, nonce, key)); 222 assertNotThrown(crypto_aead_xchacha20poly1305_ietf_decrypt_detached(decrypted[0..$-message.length], null, mac, additional_data, nonce, key)); 223 assertNotThrown(crypto_aead_xchacha20poly1305_ietf_decrypt_detached(decrypted, ciphertext2, mac, null, nonce, key)); 224 225 assert(crypto_aead_xchacha20poly1305_ietf_decrypt_detached(decrypted, ciphertext2, mac, additional_data, nonce, key)); 226 assert(decrypted == message); 227 } 228 229 230 @nogc @safe 231 unittest 232 { 233 // usage is not cryptographically safe here; it's purpose is to test @nogc @safe 234 import std..string : representation; 235 ubyte[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES] n = nonce; 236 ubyte[crypto_aead_xchacha20poly1305_ietf_KEYBYTES] k; 237 crypto_aead_xchacha20poly1305_ietf_keygen(k); 238 ubyte[4] message = [116, 101, 115, 116]; //representation("test"); 239 ubyte[4] decrypted; 240 enum m_len = 4UL; 241 auto additional_data = representation("A typical use case for additional data is to store protocol-specific metadata " ~ 242 "about the message, such as its length and encoding. (non-confidential, non-encrypted data"); 243 ubyte[m_len+crypto_aead_xchacha20poly1305_ietf_ABYTES] ciphertext1; 244 ubyte[m_len] ciphertext2; 245 ubyte[ crypto_aead_xchacha20poly1305_ietf_ABYTES] mac; 246 247 assert(crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext1, message, additional_data, n, k)); 248 assert(crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, ciphertext1, additional_data, n, k)); 249 assert(decrypted == message); 250 decrypted = decrypted.init; 251 assert(crypto_aead_xchacha20poly1305_ietf_encrypt_detached(ciphertext2, mac, message, additional_data, n, k)); 252 assert(crypto_aead_xchacha20poly1305_ietf_decrypt_detached(decrypted, ciphertext2, mac, additional_data, n, k)); 253 assert(decrypted == message); 254 }