1 module drypt.nacl.api;
2 
3 public import dcrypt.nacl.secretbox;
4 public import dcrypt.nacl.box;
5 public import dcrypt.exceptions: InvalidCipherTextException;
6 
7 public import dcrypt.random.random: randombytes = nextBytes;
8 public import dcrypt.util: wipe;
9 
10 public alias secretbox crypto_secretbox;
11 public alias secretbox_open crypto_secretbox_open;
12 public alias box crypto_box;
13 public alias box_open crypto_box_open;
14 public alias box_pubkey crypto_box_pubkey;
15 public alias box_keypair crypto_box_keypair;
16 
17 /// Encrypt and decrypt a message using symmetric authenticated encryption.
18 unittest {
19 	ubyte[32] secret_key;
20 	ubyte[24] nonce;
21 
22 	/// Generate a random key.
23 	randombytes(secret_key);
24 
25 	/// Generate a unique nonce.
26 	/// Don't use the same nonce together with the same key more than once.
27 	randombytes(nonce);
28 
29 	const ubyte[] msg = cast (const ubyte[]) "Hi Bob!";
30 
31 	/// Let's encrypt!
32 	ubyte[] boxed = crypto_secretbox(msg, nonce, secret_key); /// This can now be transmitted over a insecure channel.
33 
34 	ubyte[] recv_msg;
35 	// Decrypt the message.
36 	try {
37 		recv_msg = crypto_secretbox_open(boxed, nonce, secret_key);
38 		assert(recv_msg == msg, "Decryption produced unexpected result.");
39 	} catch(InvalidCipherTextException e) {
40 		/// An exception is thrown if the cipher text is not valid.
41 		/// This is the case if either the ciphertext is not the result
42 		/// of crypto_secretbox with the same key and nonce.
43 		assert(false);
44 	}
45 
46 	/// Possibly someone tries to maliciously modify the encrypted message ...
47 	ubyte[] tampered = boxed.dup;
48 	tampered[$-1] ^= 1;
49 
50 	/// ... then decryption fails.
51 	bool exceptionThrown = false;
52 	try {
53 		recv_msg = crypto_secretbox_open(tampered, nonce, secret_key);
54 	} catch(InvalidCipherTextException e) {
55 		exceptionThrown = true;
56 	}
57 	assert(exceptionThrown, "Tampered message has not been rejected!");
58 }
59 
60 
61 /// Encrypt and decrypt a message using asymmetric authenticated encryption.
62 unittest {
63 
64 	ubyte[32] alice_sk, bob_sk, alice_pk, bob_pk;
65 
66 	/// Make sure sensitive data gets erased on scope exit.
67 	scope(exit) {
68 		wipe(alice_sk);
69 		wipe(bob_sk);
70 	}
71 
72 	/// Generate two random keypairs.
73 	crypto_box_keypair(alice_sk, alice_pk);
74 	crypto_box_keypair(bob_sk, bob_pk);
75 
76 	/// If you already have your secret key, that's the way to get the public key:
77 	///		alice_pk = crypto_box_pubkey(alice_sk);	/// Alice's public key
78 	///		bob_pk = crypto_box_pubkey(bob_sk);		/// Bob's public key
79 
80 	ubyte[24] shared_nonce;	/// A shared nonce. Can be transmitted in plaintext.
81 	randombytes(shared_nonce);
82 
83 	/// Alice sends a message to Bob.
84 	const ubyte[] msg = cast (const ubyte[]) "Hi Bob!";
85 	ubyte[] boxed = crypto_box(msg, shared_nonce, alice_sk, bob_pk);
86 
87 	/// Bob receives two messages, one has been modified by Eve.
88 	ubyte[] tampered = boxed.dup;
89 	tampered[$-1] ^= 1;
90 
91 	/// Bob decrypts the messages
92 
93 	ubyte[] recv_msg;
94 
95 	// The message as sent by alice
96 	try {
97 		recv_msg = crypto_box_open(boxed, shared_nonce, bob_sk, alice_pk);
98 		assert(recv_msg == msg, "Decryption produced unexpected result.");
99 	} catch(InvalidCipherTextException e) {
100 		assert(false);
101 	}
102 
103 	// Try to decrypt the forged message.
104 	bool exceptionThrown = false;
105 	try {
106 		recv_msg = crypto_box_open(tampered, shared_nonce, bob_sk, alice_pk);
107 		assert(false, "Tampered message has not been rejected!");
108 	} catch(InvalidCipherTextException e) {
109 		exceptionThrown = true;
110 	}
111 	assert(exceptionThrown, "Tampered message has not been rejected!");
112 }
113 
114 /// Generate a keypair.
115 public void box_keypair(out ubyte[32] sk, out ubyte[32] pk) nothrow @safe @nogc {
116 	randombytes(sk);
117 	pk = box_pubkey(sk[]);
118 }
119