1 // Written in the D programming language. 2 3 module wrapper.sodium.crypto_box; 4 5 import wrapper.sodium.core; // assure sodium got initialized 6 7 public 8 import deimos.sodium.crypto_box : crypto_box_SEEDBYTES, 9 crypto_box_seedbytes, 10 crypto_box_PUBLICKEYBYTES, 11 crypto_box_publickeybytes, 12 crypto_box_SECRETKEYBYTES, 13 crypto_box_secretkeybytes, 14 crypto_box_NONCEBYTES, 15 crypto_box_noncebytes, 16 crypto_box_MACBYTES, 17 crypto_box_macbytes, 18 crypto_box_MESSAGEBYTES_MAX, 19 crypto_box_messagebytes_max, 20 crypto_box_PRIMITIVE, 21 /* crypto_box_primitive, 22 crypto_box_seed_keypair, 23 crypto_box_keypair, 24 crypto_box_easy, 25 crypto_box_open_easy, 26 crypto_box_detached, 27 crypto_box_open_detached, */ 28 crypto_box_BEFORENMBYTES, 29 crypto_box_beforenmbytes, 30 /* crypto_box_beforenm, 31 crypto_box_easy_afternm, 32 crypto_box_open_easy_afternm, 33 crypto_box_detached_afternm, 34 crypto_box_open_detached_afternm, */ 35 crypto_box_SEALBYTES, 36 crypto_box_sealbytes; 37 /* crypto_box_seal, 38 crypto_box_seal_open, 39 crypto_box_ZEROBYTES, 40 crypto_box_zerobytes, 41 crypto_box_BOXZEROBYTES, 42 crypto_box_boxzerobytes, 43 crypto_box, 44 crypto_box_open, 45 crypto_box_afternm, 46 crypto_box_open_afternm; */ 47 48 49 import std.exception : assertThrown; 50 import nogc.exception: enforce; 51 52 53 54 string crypto_box_primitive() @nogc nothrow pure @trusted 55 { 56 import std..string : fromStringz; 57 static import deimos.sodium.crypto_box; 58 const(char)[] c_arr; 59 try 60 c_arr = fromStringz(deimos.sodium.crypto_box.crypto_box_primitive()); // strips terminating \0 61 catch (Exception t) { /* known not to throw */ } 62 return c_arr; 63 } 64 65 // overloading some functions between module deimos.sodium.crypto_box and this module 66 67 68 alias crypto_box_seed_keypair = deimos.sodium.crypto_box.crypto_box_seed_keypair; 69 70 /** 71 * Using crypto_box_seed_keypair(), the key pair can be deterministically derived from a single key `seed`. 72 */ 73 pragma(inline, true) 74 bool crypto_box_seed_keypair(out ubyte[crypto_box_PUBLICKEYBYTES] pkey, out ubyte[crypto_box_SECRETKEYBYTES] skey, 75 const ubyte[crypto_box_SEEDBYTES] seed) pure @nogc @trusted 76 { 77 return crypto_box_seed_keypair(pkey.ptr, skey.ptr, seed.ptr) == 0; 78 } 79 80 alias crypto_box_keypair = deimos.sodium.crypto_box.crypto_box_keypair; 81 82 /** 83 * The crypto_box_keypair() function randomly generates a secret key and a corresponding public key. 84 * The public key is put into `pkey` and the secret key into `skey`. 85 */ 86 pragma(inline, true) 87 bool crypto_box_keypair(out ubyte[crypto_box_PUBLICKEYBYTES] pkey, out ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted 88 { 89 return crypto_box_keypair(pkey.ptr, skey.ptr) == 0; 90 } 91 92 alias crypto_box_easy = deimos.sodium.crypto_box.crypto_box_easy; 93 94 /** 95 */ 96 bool crypto_box_easy(scope ubyte[] c, scope const ubyte[] m, const ubyte[crypto_box_NONCEBYTES] n, 97 const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted 98 { 99 const c_expect_len = m.length + crypto_box_MACBYTES; 100 // enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_box_MACBYTES: ", c_expect_len); 101 enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_box_MACBYTES"); 102 return crypto_box_easy(c.ptr, m.ptr, m.length, n.ptr, pkey.ptr, skey.ptr) == 0; 103 } 104 105 alias crypto_box_open_easy = deimos.sodium.crypto_box.crypto_box_open_easy; 106 107 /** 108 */ 109 bool crypto_box_open_easy(scope ubyte[] m, scope const ubyte[] c, const ubyte[crypto_box_NONCEBYTES] n, 110 const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted 111 { 112 // enforce(c.length >= crypto_box_MACBYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_box_MACBYTES: ", crypto_box_MACBYTES); 113 enforce(c.length >= crypto_box_MACBYTES, "Expected c.length is not greater_equal to crypto_box_MACBYTES"); 114 const m_expect_len = c.length - crypto_box_MACBYTES; 115 // enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_box_MACBYTES: ", m_expect_len); 116 enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_box_MACBYTES"); 117 return crypto_box_open_easy(m.ptr, c.ptr, c.length, n.ptr, pkey.ptr, skey.ptr) == 0; 118 } 119 120 alias crypto_box_detached = deimos.sodium.crypto_box.crypto_box_detached; 121 122 /** 123 */ 124 bool crypto_box_detached(scope ubyte[] c, out ubyte[crypto_box_MACBYTES] mac, scope const ubyte[] m, const ubyte[crypto_box_NONCEBYTES] n, 125 const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted 126 { 127 // enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length); 128 enforce(c.length == m.length, "Expected c.length is not equal to m.length"); 129 return crypto_box_detached(c.ptr, mac.ptr, m.ptr, m.length, n.ptr, pkey.ptr, skey.ptr) == 0; 130 } 131 132 alias crypto_box_open_detached = deimos.sodium.crypto_box.crypto_box_open_detached; 133 134 /** 135 */ 136 pragma(inline, true) 137 bool crypto_box_open_detached(scope ubyte[] m, scope const ubyte[] c, const ubyte[crypto_box_MACBYTES] mac, const ubyte[crypto_box_NONCEBYTES] n, 138 const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted 139 { 140 enforce(m.length >= c.length, "Error in crypto_box_open_detached: m.length < c.length"); 141 return crypto_box_open_detached(m.ptr, c.ptr, mac.ptr, c.length, n.ptr, pkey.ptr, skey.ptr) == 0; 142 } 143 144 145 /* -- Precomputation interface -- */ 146 147 alias crypto_box_beforenm = deimos.sodium.crypto_box.crypto_box_beforenm; 148 149 pragma(inline, true) 150 bool crypto_box_beforenm(out ubyte[crypto_box_BEFORENMBYTES] k, 151 const ubyte[crypto_box_PUBLICKEYBYTES] pk, 152 const ubyte[crypto_box_SECRETKEYBYTES] sk) pure nothrow @nogc @trusted // __attribute__ ((warn_unused_result)); 153 { 154 return crypto_box_beforenm(k.ptr, pk.ptr, sk.ptr) == 0; 155 } 156 157 alias crypto_box_easy_afternm = deimos.sodium.crypto_box.crypto_box_easy_afternm; 158 159 pragma(inline, true) 160 bool crypto_box_easy_afternm(scope ubyte[] c, 161 scope const ubyte[] m, 162 const ubyte[crypto_box_NONCEBYTES] n, 163 const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted 164 { 165 const c_expect_len = m.length + crypto_box_MACBYTES; 166 // enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_box_MACBYTES: ", c_expect_len); 167 enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_box_MACBYTES"); 168 return crypto_box_easy_afternm(c.ptr, m.ptr, m.length, n.ptr, k.ptr) == 0; 169 } 170 171 alias crypto_box_open_easy_afternm = deimos.sodium.crypto_box.crypto_box_open_easy_afternm; 172 173 pragma(inline, true) 174 bool crypto_box_open_easy_afternm(scope ubyte[] m, 175 scope const ubyte[] c, 176 const ubyte[crypto_box_NONCEBYTES] n, 177 const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted // __attribute__ ((warn_unused_result)); 178 { 179 // enforce(c.length >= crypto_box_MACBYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_box_MACBYTES: ", crypto_box_MACBYTES); 180 enforce(c.length >= crypto_box_MACBYTES, "Expected c.length is not greater_equal to crypto_box_MACBYTES"); 181 const m_expect_len = c.length - crypto_box_MACBYTES; 182 // enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_box_MACBYTES: ", m_expect_len); 183 enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_box_MACBYTES"); 184 return crypto_box_open_easy_afternm(m.ptr, c.ptr, c.length, n.ptr, k.ptr) == 0; 185 } 186 187 alias crypto_box_detached_afternm = deimos.sodium.crypto_box.crypto_box_detached_afternm; 188 189 pragma(inline, true) 190 bool crypto_box_detached_afternm(scope ubyte[] c, 191 out ubyte[crypto_box_MACBYTES] mac, 192 scope const ubyte[] m, 193 const ubyte[crypto_box_NONCEBYTES] n, 194 const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted 195 { 196 // enforce(c.length == m.length, "Expected c.length: ", c.length, " to be equal to m.length: ", m.length); 197 enforce(c.length == m.length, "Expected c.length is not equal to m.length"); 198 return crypto_box_detached_afternm(c.ptr, mac.ptr, m.ptr, m.length, n.ptr, k.ptr) == 0; 199 } 200 201 alias crypto_box_open_detached_afternm = deimos.sodium.crypto_box.crypto_box_open_detached_afternm; 202 203 pragma(inline, true) 204 bool crypto_box_open_detached_afternm(scope ubyte[] m, 205 scope const ubyte[] c, 206 const ubyte[crypto_box_MACBYTES] mac, 207 const ubyte[crypto_box_NONCEBYTES] n, 208 const ubyte[crypto_box_BEFORENMBYTES] k) @nogc @trusted // __attribute__ ((warn_unused_result)); 209 { 210 // enforce(m.length == c.length, "Expected m.length: ", m.length, " to be equal to c.length: ", c.length); 211 enforce(m.length == c.length, "Expected m.length is not equal to c.length"); 212 return crypto_box_open_detached_afternm(m.ptr, c.ptr, mac.ptr, c.length, n.ptr, k.ptr) == 0; 213 } 214 215 216 /* -- Ephemeral SK interface -- */ 217 218 alias crypto_box_seal = deimos.sodium.crypto_box.crypto_box_seal; 219 220 /** 221 * The crypto_box_seal() function encrypts a message `m` for a recipient whose public key is `pkey`. 222 * It puts the ciphertext whose length is crypto_box_SEALBYTES + m.length into `c`. 223 * The function creates a new key pair for each message, and attaches the public key to the ciphertext. 224 * The secret key is overwritten and is not accessible after this function returns. 225 */ 226 bool crypto_box_seal(scope ubyte[] c, scope const ubyte[] m, const ubyte[crypto_box_PUBLICKEYBYTES] pkey) @nogc @trusted 227 { 228 const c_expect_len = m.length + crypto_box_SEALBYTES; 229 // enforce(c.length == c_expect_len, "Expected c.length: ", c.length, " to be equal to m.length + crypto_box_SEALBYTES: ", c_expect_len); 230 enforce(c.length == c_expect_len, "Expected c.length is not equal to m.length + crypto_box_SEALBYTES"); 231 return crypto_box_seal(c.ptr, m.ptr, m.length, pkey.ptr) == 0; 232 } 233 234 alias crypto_box_seal_open = deimos.sodium.crypto_box.crypto_box_seal_open; 235 236 /** 237 */ 238 bool crypto_box_seal_open(scope ubyte[] m, scope const ubyte[] c, 239 const ubyte[crypto_box_PUBLICKEYBYTES] pkey, const ubyte[crypto_box_SECRETKEYBYTES] skey) @nogc @trusted 240 { 241 // enforce(c.length >= crypto_box_SEALBYTES, "Expected c.length: ", c.length, " to be greater_equal to crypto_box_SEALBYTES: ", crypto_box_SEALBYTES); 242 enforce(c.length >= crypto_box_SEALBYTES, "Expected c.length is not greater_equal to crypto_box_SEALBYTES"); 243 const m_expect_len = c.length - crypto_box_SEALBYTES; 244 // enforce(m.length == m_expect_len, "Expected m.length: ", m.length, " to be equal to c.length - crypto_box_SEALBYTES: ", m_expect_len); 245 enforce(m.length == m_expect_len, "Expected m.length is not equal to c.length - crypto_box_SEALBYTES"); 246 return crypto_box_seal_open(m.ptr, c.ptr, c.length, pkey.ptr, skey.ptr) == 0; 247 } 248 249 /* No overloads for -- NaCl compatibility interface ; Requires padding -- */ 250 251 252 @safe 253 unittest { 254 import std.stdio : writeln; 255 debug writeln("unittest block 1 from sodium.crypto_box.d"); 256 } 257 258 @safe 259 unittest { 260 import std..string : representation; 261 import wrapper.sodium.randombytes : randombytes; 262 import wrapper.sodium.utils : sodium_increment; 263 264 assert(crypto_box_seedbytes() == crypto_box_SEEDBYTES); 265 assert(crypto_box_publickeybytes() == crypto_box_PUBLICKEYBYTES); 266 assert(crypto_box_secretkeybytes() == crypto_box_SECRETKEYBYTES); 267 assert(crypto_box_noncebytes() == crypto_box_NONCEBYTES); 268 assert(crypto_box_macbytes() == crypto_box_MACBYTES); 269 assert(crypto_box_messagebytes_max() == crypto_box_MESSAGEBYTES_MAX); 270 assert(crypto_box_primitive() == crypto_box_PRIMITIVE); 271 assert(crypto_box_beforenmbytes() == crypto_box_BEFORENMBYTES); 272 assert(crypto_box_sealbytes() == crypto_box_SEALBYTES); 273 274 enum MESSAGE_LEN = 4; 275 ubyte[MESSAGE_LEN] message = [116, 101, 115, 116]; //representation("test"); 276 // avoid heap allocation, like in the example code 277 enum CIPHERTEXT_LEN = (crypto_box_MACBYTES + MESSAGE_LEN); 278 ubyte[crypto_box_SEEDBYTES] seed = void; 279 randombytes(seed); 280 281 ubyte[crypto_box_PUBLICKEYBYTES] alice_publickey = void; 282 ubyte[crypto_box_SECRETKEYBYTES] alice_secretkey = void; 283 assert(crypto_box_seed_keypair(alice_publickey, alice_secretkey, seed)); 284 // check crypto_scalarmult_base (compute alice_publickey from alice_secretkey) 285 { 286 import wrapper.sodium.crypto_scalarmult; 287 assert(crypto_scalarmult_BYTES == crypto_box_PUBLICKEYBYTES); 288 assert(crypto_scalarmult_SCALARBYTES == crypto_box_SECRETKEYBYTES); 289 ubyte[crypto_box_PUBLICKEYBYTES] alice_publickey_computed; 290 assert(crypto_scalarmult_base(alice_publickey_computed, alice_secretkey)); 291 assert(alice_publickey_computed == alice_publickey); 292 } 293 294 ubyte[crypto_box_PUBLICKEYBYTES] bob_publickey = void; 295 ubyte[crypto_box_SECRETKEYBYTES] bob_secretkey = void; 296 crypto_box_keypair(bob_publickey, bob_secretkey); 297 298 ubyte[crypto_box_NONCEBYTES] nonce = void; 299 ubyte[CIPHERTEXT_LEN] ciphertext1 = void; 300 randombytes(nonce); 301 // Alice encrypts message for Bob 302 assertThrown(crypto_box_easy(ciphertext1[0..$-1], message, nonce, bob_publickey, alice_secretkey)); 303 assert(crypto_box_easy(ciphertext1, message, nonce, bob_publickey, alice_secretkey)); 304 // { /* error */ } 305 306 ubyte[MESSAGE_LEN] decrypted = void; 307 // Bob decrypts ciphertext from Alice 308 assertThrown(crypto_box_open_easy(decrypted[0..$-1], ciphertext1, nonce, alice_publickey, bob_secretkey)); 309 assert(crypto_box_open_easy(decrypted, ciphertext1, nonce, alice_publickey, bob_secretkey)); 310 assert(decrypted == message); 311 // 312 ubyte[MESSAGE_LEN] ciphertext2; 313 ubyte[crypto_box_MACBYTES] mac = void; 314 decrypted = ubyte.init; 315 sodium_increment(nonce); 316 assertThrown(crypto_box_detached(ciphertext2[0..$-1], mac, message, nonce, bob_publickey, alice_secretkey)); 317 assert(crypto_box_detached(ciphertext2, mac, message, nonce, bob_publickey, alice_secretkey)); 318 319 assertThrown(crypto_box_open_detached(decrypted[0..$-1], ciphertext2, mac, nonce, alice_publickey, bob_secretkey)); 320 assert(crypto_box_open_detached(decrypted, ciphertext2, mac, nonce, alice_publickey, bob_secretkey)); 321 assert(decrypted == message); 322 // 323 ubyte[crypto_box_BEFORENMBYTES] k; 324 assert(crypto_box_beforenm(k, bob_publickey, alice_secretkey)); 325 ciphertext1 = ubyte.init; 326 decrypted = ubyte.init; 327 sodium_increment(nonce); 328 assert(crypto_box_easy_afternm(ciphertext1, message, nonce, k)); 329 assert(crypto_box_open_easy_afternm(decrypted, ciphertext1, nonce, k)); 330 assert(decrypted == message); 331 332 ciphertext2 = ubyte.init; 333 decrypted = ubyte.init; 334 mac = ubyte.init; 335 sodium_increment(nonce); 336 assert(crypto_box_detached_afternm(ciphertext2, mac, message, nonce, k)); 337 assert(crypto_box_open_detached_afternm(decrypted, ciphertext2, mac, nonce, k)); 338 assert(decrypted == message); 339 // 340 enum CIPHERTEXTSEALED_LEN = (crypto_box_SEALBYTES + MESSAGE_LEN); 341 ubyte[CIPHERTEXTSEALED_LEN] ciphertext_sealed = void; 342 decrypted = ubyte.init; 343 344 assertThrown(crypto_box_seal(ciphertext_sealed[0..$-1], message, bob_publickey)); 345 assert(crypto_box_seal(ciphertext_sealed, message, bob_publickey)); 346 347 assertThrown(crypto_box_seal_open(decrypted[0..$-1], ciphertext_sealed, bob_publickey, bob_secretkey)); 348 assert(crypto_box_seal_open(decrypted, ciphertext_sealed, bob_publickey, bob_secretkey)); 349 assert(decrypted == message); 350 351 }