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;