1 module dcrypt.nacl.box; 2 3 /// High level API for asymmetric authenticated encryption. 4 /// Compatible to http://nacl.cr.yp.to/box.html. 5 6 public import dcrypt.nacl.secretbox; 7 import dcrypt.ecc.curve25519; 8 import dcrypt.streamcipher.salsa; 9 import dcrypt.util; 10 import dcrypt.exceptions: InvalidCipherTextException; 11 12 /// Encrypt a message using asymmetric cryptography. 13 /// 14 /// Params: 15 /// msg = The secret message. 16 /// nonce = A number unique per (secret_key, public_key) pair. 17 /// secret_key = Your secret key. 18 /// public_key = Recipients public key. 19 /// 20 /// Returns: Encrypted and authenticated packet. 21 public ubyte[] box(in ubyte[] msg, in ubyte[] nonce, in ubyte[] secret_key, in ubyte[] public_key) @safe nothrow { 22 23 ubyte[32] shared_key = derive_shared_key(secret_key, public_key); 24 scope(exit) { 25 wipe(shared_key); 26 } 27 28 return secretbox(msg, nonce, shared_key); 29 } 30 31 /// Generate a public key from a secret key. 32 /// 33 /// Params: 34 /// secret_key = A 32 byte secret key, choosen randomly. 35 /// 36 /// Returns: Returns the public key matching to the given secret key. 37 public ubyte[32] box_pubkey(in ubyte[] secret_key) nothrow @safe @nogc 38 in { 39 assert(secret_key.length == 32, "Secret key must be 32 bytes."); 40 } body { 41 return curve25519_scalarmult(secret_key); 42 } 43 44 /// Decrypts a packet generated by `box()`. 45 /// 46 /// Params: 47 /// box = The received packet. 48 /// nonce = A number unique per (secret_key, public_key) pair. 49 /// secret_key = Your secret key. 50 /// public_key = Senders public key. 51 /// 52 /// Returns: Plaintext if authentication tag is valid. 53 /// 54 /// Throws: IllegalCipherTextException if authentication tag is not valid. 55 ubyte[] box_open(in ubyte[] box, in ubyte[] nonce, in ubyte[] secret_key, in ubyte[] public_key) @safe { 56 57 ubyte[32] shared_key = derive_shared_key(secret_key, public_key); 58 scope(exit) { 59 wipe(shared_key); 60 } 61 62 return secretbox_open(box, nonce, shared_key); 63 } 64 65 /// Derive a shared key from a secret and a public key. Used for box() and box_open(). 66 /// 67 /// Returns: The shared key. 68 private ubyte[32] derive_shared_key(in ubyte[] secret_key, in ubyte[] public_key) @safe nothrow @nogc { 69 ubyte[32] shared_key; 70 71 immutable ubyte[16] zero_nonce = 0; 72 73 shared_key = curve25519_scalarmult(secret_key, public_key); 74 shared_key = HSalsa(shared_key, zero_nonce); 75 76 return shared_key; 77 } 78 79 /// Test vectors from naclcrypto-20090310.pdf 80 unittest { 81 alias immutable ubyte[] octets; 82 83 octets alice_sk = cast(octets) x" 84 77076d0a7318a57d3c16c17251b26645 85 df4c2f87ebc0992ab177fba51db92c2a"; 86 87 octets bob_pk = cast(octets) x" 88 de9edb7d7b7dc1b4d35b61c2ece43537 89 3f8343c85b78674dadfc7e146f882b4f"; 90 91 octets nonce = cast(octets) x" 92 69696ee955b62b73cd62bda875fc73d6 93 8219e0036b7a0b37"; 94 95 octets msg = cast(octets) x" 96 be075fc53c81f2d5cf141316ebeb0c7b 97 5228c52a4c62cbd44b66849b64244ffc 98 e5ecbaaf33bd751a1ac728d45e6c6129 99 6cdc3c01233561f41db66cce314adb31 100 0e3be8250c46f06dceea3a7fa1348057 101 e2f6556ad6b1318a024a838f21af1fde 102 048977eb48f59ffd4924ca1c60902e52 103 f0a089bc76897040e082f93776384864 104 5e0705"; 105 106 octets boxed_ref = cast(octets) x" 107 f3ffc7703f9400e52a7dfb4b3d3305d9 108 8e993b9f48681273c29650ba32fc76ce 109 48332ea7164d96a4476fb8c531a1186a 110 c0dfc17c98dce87b4da7f011ec48c972 111 71d2c20f9b928fe2270d6fb863d51738 112 b48eeee314a7cc8ab932164548e526ae 113 90224368517acfeabd6bb3732bc0e9da 114 99832b61ca01b6de56244a9e88d5f9b3 115 7973f622a43d14a6599b1f654cb45a74 116 e355a5"; 117 118 test_box(msg, boxed_ref, nonce, alice_sk, bob_pk); 119 } 120 121 version(unittest) { 122 /// Helper function for testing. 123 /// Params: 124 /// msg = Plaintext. 125 /// boxed_ref = Expected ciphertext with authentication tag. 126 /// nonce = A number unique per (sk, pk) pair. 127 /// sk = Own secret key. 128 /// pk = Public key. 129 void test_box(in ubyte[] msg, in ubyte[] boxed_ref, in ubyte[] nonce, in ubyte[] sk, in ubyte[] pk) { 130 // test encryption 131 ubyte[] boxed = box(msg, nonce, sk, pk); 132 if(boxed_ref !is null) { 133 assert(boxed == boxed_ref, "box() failed"); 134 } 135 136 // test decryption 137 if(boxed_ref !is null) { 138 ubyte[] unboxed = box_open(boxed_ref, nonce, sk, pk); 139 assert(unboxed == msg, "box_open failed"); 140 } else { 141 ubyte[] unboxed = box_open(boxed, nonce, sk, pk); 142 assert(unboxed == msg, "box_open failed"); 143 } 144 145 // test invalid authentication 146 ubyte[] tampered_box = boxed.dup; 147 tampered_box[$-1] ^= 1; 148 149 bool exception = false; 150 try { 151 ubyte[] unboxed = box_open(tampered_box, nonce, sk, pk); 152 assert(false, "Invalid ciphertext passed as valid."); 153 } catch(InvalidCipherTextException e) { 154 exception = true; 155 } finally { 156 assert(exception, "Expected exception has not been thrown."); 157 } 158 } 159 }