1 module chloride.secretbox; 2 3 import chloride.core; 4 import chloride.random : randomArray; 5 6 import std.array; 7 8 import deimos.sodium.crypto_secretbox; 9 10 /// 11 unittest { 12 import std..string : representation; 13 immutable ubyte[] message = representation("hello"); 14 immutable key = makeSecretKey(); 15 auto box = secretBox(message, key); 16 assert(openSecretBox(box, key) == message); 17 } 18 19 alias SecretKey = ubyte[crypto_secretbox_KEYBYTES]; 20 alias Nonce = ubyte[crypto_secretbox_NONCEBYTES]; 21 alias SecretMacLength = crypto_secretbox_MACBYTES; 22 23 /** 24 * A struct containing both the encrypted cipher (using secret key encryption) 25 * and the nonce used to encrypt it. 26 */ 27 struct SecretBox { 28 /// The encrypted message 29 ubyte[] ciphertext; 30 /// The nonce used to encrypt it (and needed for decryption) 31 Nonce nonce; 32 } 33 34 /** 35 * Wrapper around `crypto_secretbox_easy`. Encrypts a message using a nonce and key 36 * and returns the cipher with MAC as a prefix. 37 */ 38 ubyte[] encryptSecretBox(in ubyte[] message, 39 in Nonce nonce, 40 in SecretKey key) { 41 auto cipher = uninitializedArray!(ubyte[])(message.length + SecretMacLength); 42 auto result = crypto_secretbox_easy(cipher.ptr, message.ptr, message.length, 43 nonce.ptr, key.ptr); 44 enforceSodium(result == 0); 45 return cipher; 46 } 47 48 49 /** 50 * Wrapper around `crypto_secretbox_open_easy`. Decrypts a cipher using a nonce and key 51 * and returns the decrypted message. 52 * If decryption fails, null is returned. 53 */ 54 ubyte[] decryptSecretBox(in ubyte[] cipher, 55 in Nonce nonce, 56 in SecretKey key) in { 57 assert(cipher.length > SecretMacLength); 58 } body { 59 auto message = uninitializedArray!(ubyte[])(cipher.length - SecretMacLength); 60 auto result = crypto_secretbox_open_easy(message.ptr, cipher.ptr, cipher.length, 61 nonce.ptr, key.ptr); 62 if (result == 0) { 63 return message; 64 } else { 65 return null; 66 } 67 } 68 /** 69 * Wrapper around `crypto_secretbox_easy` that encrypts in place. 70 * `buffer` is changed in place from the message to the cipher. Note 71 * that it will be reallocated to be large enough to include the MAC, which 72 * may cause the array to be copied. 73 */ 74 void encryptSecretBoxInPlace(ref ubyte[] buffer, 75 in Nonce nonce, 76 in SecretKey key) { 77 auto messageLen = buffer.length; 78 buffer.length += SecretMacLength; // make sure we have enough space for the MAC 79 auto result = crypto_secretbox_easy(buffer.ptr, buffer.ptr, messageLen, nonce.ptr, key.ptr); 80 enforceSodium(result == 0); 81 } 82 83 /** 84 * Wrapper around `crypto_secretbox_open_easy` that decrypts in place. 85 * `buffer` is changed in place from the cipher to the decrypted message. 86 * If decryption is successful, true is returned and `buffer` is updated with 87 * the length of the decrypted message, which will be shorter than the cipher. 88 * Otherwise true will be returned. 89 */ 90 bool decryptSecretBoxInPlace(ref ubyte[] buffer, 91 in Nonce nonce, 92 in SecretKey key) in { 93 assert(buffer.length > SecretMacLength); 94 } body { 95 auto result = crypto_secretbox_open_easy(buffer.ptr, buffer.ptr, buffer.length, 96 nonce.ptr, key.ptr); 97 if (result == 0) { 98 buffer = buffer[0 .. $ - SecretMacLength]; 99 return true; 100 } else { 101 return false; 102 } 103 } 104 105 /** 106 * Encrypt `message` using `key` and a newly generated nonce. Return a `SecreBox` with 107 * the encrypted ciphertext and the generated nonce. 108 */ 109 SecretBox secretBox(in ubyte[] message, in SecretKey key) { 110 SecretBox result; 111 result.nonce = makeNonce(); 112 result.ciphertext = encryptSecretBox(message, result.nonce, key); 113 return result; 114 } 115 116 /** 117 * Decrypt `box` which contains the ciphertext and the nonce used to generate it. 118 * If decryption fails it returns null. 119 */ 120 ubyte[] openSecretBox(in SecretBox box, in SecretKey key) { 121 return decryptSecretBox(box.ciphertext, box.nonce, key); 122 } 123 124 /** 125 * Generate a nonce suitable for secret key encryption. 126 */ 127 alias makeNonce = randomArray!Nonce; 128 129 /** 130 * Generate a key suitable for secret key encryption. 131 */ 132 alias makeSecretKey = randomArray!SecretKey;