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         output ~= cipher_iv.bitsOf()[];
65         output ~= ctext[];
66 
67         mac.update(output);
68         
69         output ~= mac.finished();
70         return output.unlock();
71     }
72 
73     /**
74     * Encrypt a message using a shared secret key
75     * Params:
76     *  input = the input data
77     *  input_len = the length of input in bytes
78     *  master_key = the key used to encrypt the message
79     */
80     static SecureVector!ubyte decrypt(const(ubyte)* input, size_t input_len, in SymmetricKey master_key)
81     {
82         __gshared immutable size_t MIN_CTEXT_SIZE = 16; // due to using CBC with padding
83         
84         __gshared immutable size_t MIN_POSSIBLE_LENGTH = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH + 
85                                                          MIN_CTEXT_SIZE + MAC_OUTPUT_LENGTH;
86         
87         if (input_len < MIN_POSSIBLE_LENGTH)
88             throw new DecodingError("Encrypted input too short to be valid");
89         
90         if (loadBigEndian!uint(input, 0) != CRYPTOBOX_MAGIC)
91             throw new DecodingError("Unknown header value in cryptobox");
92         
93         Unique!KDF kdf = getKdf(CRYPTOBOX_KDF);
94         
95         const(ubyte)* cipher_key_salt = &input[MAGIC_LENGTH];
96         
97         const(ubyte)* mac_key_salt = &input[MAGIC_LENGTH + KEY_KDF_SALT_LENGTH];
98         
99         SymmetricKey mac_key = kdf.deriveKey(MAC_KEY_LENGTH,
100                                               master_key.bitsOf(),
101                                               mac_key_salt,
102                                               KEY_KDF_SALT_LENGTH);
103         
104         Unique!MessageAuthenticationCode mac = retrieveMac(CRYPTOBOX_MAC).clone();
105         mac.setKey(mac_key);
106         
107         mac.update(input, input_len - MAC_OUTPUT_LENGTH);
108         SecureVector!ubyte computed_mac = mac.finished();
109         
110         if (!sameMem(&input[input_len - MAC_OUTPUT_LENGTH], computed_mac.ptr, computed_mac.length))
111             throw new DecodingError("MAC verification failed");
112         
113         SymmetricKey cipher_key = kdf.deriveKey(CIPHER_KEY_LENGTH, master_key.bitsOf(), cipher_key_salt, KEY_KDF_SALT_LENGTH);
114         
115         InitializationVector cipher_iv = InitializationVector(&input[MAGIC_LENGTH+2*KEY_KDF_SALT_LENGTH], CIPHER_IV_LENGTH);
116         
117         const size_t CTEXT_OFFSET = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH;
118         
119         Pipe pipe = Pipe(getCipher(CRYPTOBOX_CIPHER, cipher_key, cipher_iv, DECRYPTION));
120         pipe.processMsg(&input[CTEXT_OFFSET],
121         input_len - (MAC_OUTPUT_LENGTH + CTEXT_OFFSET));
122         return pipe.readAll();
123     }
124 
125 }
126 
127 private:
128 
129 __gshared immutable uint CRYPTOBOX_MAGIC = 0x571B0E4F;
130 __gshared immutable string CRYPTOBOX_CIPHER = "AES-256/CBC";
131 __gshared immutable string CRYPTOBOX_MAC = "HMAC(SHA-256)";
132 __gshared immutable string CRYPTOBOX_KDF = "KDF2(SHA-256)";
133 
134 __gshared immutable size_t MAGIC_LENGTH = 4;
135 __gshared immutable size_t KEY_KDF_SALT_LENGTH = 10;
136 __gshared immutable size_t MAC_KEY_LENGTH = 32;
137 __gshared immutable size_t CIPHER_KEY_LENGTH = 32;
138 __gshared immutable size_t CIPHER_IV_LENGTH = 16;
139 __gshared immutable size_t MAC_OUTPUT_LENGTH = 32;