1 /**
2 * Cryptobox Message Routines
3 * 
4 * Copyright:
5 * (C) 2009,2013 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.constructs.cryptobox_psk;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_CRYPTOBOX):
15 
16 import botan.rng.rng;
17 import botan.algo_base.symkey;
18 import botan.filters.pipe;
19 import botan.libstate.lookup;
20 import botan.mac.mac;
21 import botan.utils.mem_ops;
22 import botan.utils.loadstor;
23 import botan.utils.types;
24 /**
25 * This namespace holds various high-level crypto functions
26 */
27 struct CryptoBox {
28 
29     /**
30     * Encrypt a message using a shared secret key
31     * Params:
32     *  input = the input data
33     *  input_len = the length of input in bytes
34     *  master_key = the key used to encrypt the message
35     *  rng = a ref to a random number generator, such as AutoSeededRNG
36     */
37     static Vector!ubyte encrypt(const(ubyte)* input, size_t input_len,
38                                 in SymmetricKey master_key,
39                                 RandomNumberGenerator rng)
40     {
41         Unique!KDF kdf = getKdf(CRYPTOBOX_KDF);
42         
43         const SecureVector!ubyte cipher_key_salt = rng.randomVec(KEY_KDF_SALT_LENGTH);
44         
45         const SecureVector!ubyte mac_key_salt = rng.randomVec(KEY_KDF_SALT_LENGTH);
46         
47         SymmetricKey cipher_key = kdf.deriveKey(CIPHER_KEY_LENGTH, master_key.bitsOf(), cipher_key_salt);
48         
49         SymmetricKey mac_key = kdf.deriveKey(MAC_KEY_LENGTH, master_key.bitsOf(), mac_key_salt);
50         
51         InitializationVector cipher_iv = InitializationVector(rng, 16);
52         
53         Unique!MessageAuthenticationCode mac = retrieveMac(CRYPTOBOX_MAC).clone();
54         mac.setKey(mac_key);
55         
56         Pipe pipe = Pipe(getCipher(CRYPTOBOX_CIPHER, cipher_key, cipher_iv, ENCRYPTION));
57         pipe.processMsg(input, input_len);
58         SecureVector!ubyte ctext = pipe.readAll(0);
59         
60         SecureVector!ubyte output = SecureVector!ubyte(MAGIC_LENGTH);
61         storeBigEndian(CRYPTOBOX_MAGIC, output.ptr);
62         output ~= cipher_key_salt[];
63         output ~= mac_key_salt[];
64 		auto vec = cipher_iv.bitsOf();
65         output ~= vec[];
66         output ~= ctext[];
67 
68         mac.update(output);
69         
70         output ~= mac.finished();
71         return output.unlock();
72     }
73 
74     /**
75     * Encrypt a message using a shared secret key
76     * Params:
77     *  input = the input data
78     *  input_len = the length of input in bytes
79     *  master_key = the key used to encrypt the message
80     */
81     static SecureVector!ubyte decrypt(const(ubyte)* input, size_t input_len, in SymmetricKey master_key)
82     {
83         __gshared immutable size_t MIN_CTEXT_SIZE = 16; // due to using CBC with padding
84         
85         __gshared immutable size_t MIN_POSSIBLE_LENGTH = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH + 
86                                                          MIN_CTEXT_SIZE + MAC_OUTPUT_LENGTH;
87         
88         if (input_len < MIN_POSSIBLE_LENGTH)
89             throw new DecodingError("Encrypted input too short to be valid");
90         
91         if (loadBigEndian!uint(input, 0) != CRYPTOBOX_MAGIC)
92             throw new DecodingError("Unknown header value in cryptobox");
93         
94         Unique!KDF kdf = getKdf(CRYPTOBOX_KDF);
95         
96         const(ubyte)* cipher_key_salt = &input[MAGIC_LENGTH];
97         
98         const(ubyte)* mac_key_salt = &input[MAGIC_LENGTH + KEY_KDF_SALT_LENGTH];
99         
100         SymmetricKey mac_key = kdf.deriveKey(MAC_KEY_LENGTH,
101                                               master_key.bitsOf(),
102                                               mac_key_salt,
103                                               KEY_KDF_SALT_LENGTH);
104         
105         Unique!MessageAuthenticationCode mac = retrieveMac(CRYPTOBOX_MAC).clone();
106         mac.setKey(mac_key);
107         
108         mac.update(input, input_len - MAC_OUTPUT_LENGTH);
109         SecureVector!ubyte computed_mac = mac.finished();
110         
111         if (!sameMem(&input[input_len - MAC_OUTPUT_LENGTH], computed_mac.ptr, computed_mac.length))
112             throw new DecodingError("MAC verification failed");
113         
114         SymmetricKey cipher_key = kdf.deriveKey(CIPHER_KEY_LENGTH, master_key.bitsOf(), cipher_key_salt, KEY_KDF_SALT_LENGTH);
115         
116         InitializationVector cipher_iv = InitializationVector(&input[MAGIC_LENGTH+2*KEY_KDF_SALT_LENGTH], CIPHER_IV_LENGTH);
117         
118         const size_t CTEXT_OFFSET = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH;
119         
120         Pipe pipe = Pipe(getCipher(CRYPTOBOX_CIPHER, cipher_key, cipher_iv, DECRYPTION));
121         pipe.processMsg(&input[CTEXT_OFFSET],
122         input_len - (MAC_OUTPUT_LENGTH + CTEXT_OFFSET));
123         return pipe.readAll();
124     }
125 
126 }
127 
128 private:
129 
130 __gshared immutable uint CRYPTOBOX_MAGIC = 0x571B0E4F;
131 __gshared immutable string CRYPTOBOX_CIPHER = "AES-256/CBC";
132 __gshared immutable string CRYPTOBOX_MAC = "HMAC(SHA-256)";
133 __gshared immutable string CRYPTOBOX_KDF = "KDF2(SHA-256)";
134 
135 __gshared immutable size_t MAGIC_LENGTH = 4;
136 __gshared immutable size_t KEY_KDF_SALT_LENGTH = 10;
137 __gshared immutable size_t MAC_KEY_LENGTH = 32;
138 __gshared immutable size_t CIPHER_KEY_LENGTH = 32;
139 __gshared immutable size_t CIPHER_IV_LENGTH = 16;
140 __gshared immutable size_t MAC_OUTPUT_LENGTH = 32;