Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief cryptographic interface using the Botan library
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include <botan/auto_rng.h>
11 : #include <botan/filters.h>
12 : #include <botan/hmac.h>
13 : #include <botan/init.h>
14 : #include <botan/lookup.h>
15 : #include <botan/pbkdf2.h>
16 : #include <botan/pipe.h>
17 : #include <botan/sha2_32.h>
18 : #include <botan/symkey.h>
19 : #include <kdbplugin.h>
20 : #include <memory>
21 :
22 : using namespace ckdb;
23 : using namespace Botan;
24 : using std::unique_ptr;
25 :
26 : extern "C" {
27 :
28 : #include "botan_operations.h"
29 : #include "crypto.h"
30 : #include "gpg.h"
31 : #include "helper.h"
32 : #include <kdbassert.h>
33 : #include <kdberrors.h>
34 : #include <string.h>
35 :
36 : /**
37 : * @brief derive the cryptographic key and IV for a given (Elektra) Key k
38 : * @param config KeySet holding the plugin/backend configuration
39 : * @param errorKey holds an error description in case of failure
40 : * @param masterKey holds the decrypted master password from the plugin configuration
41 : * @param k the (Elektra)-Key to be encrypted
42 : * @param cKey holds a unique pointer to an allocated SymmetricKey.
43 : * @param cIv holds a unique pointer to an allocated InitializationVector.
44 : * @retval -1 on failure. errorKey holds the error description.
45 : * @retval 1 on success
46 : */
47 0 : static int getKeyIvForEncryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, unique_ptr<SymmetricKey> & cKey,
48 : unique_ptr<InitializationVector> & cIv)
49 : {
50 : byte salt[ELEKTRA_CRYPTO_DEFAULT_SALT_LEN];
51 0 : char * saltHexString = NULL;
52 0 : const size_t requiredKeyBytes = ELEKTRA_CRYPTO_BOTAN_KEYSIZE + ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE;
53 :
54 0 : ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
55 :
56 : try
57 : {
58 : // generate the salt
59 0 : AutoSeeded_RNG rng;
60 0 : rng.randomize (salt, sizeof (salt));
61 0 : const int encodingResult = ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, salt, sizeof (salt), &saltHexString);
62 0 : if (encodingResult < 0)
63 : {
64 : // error in libinvoke - errorKey has been set by base64Encode
65 : return -1;
66 : }
67 0 : if (!saltHexString)
68 : {
69 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (errorKey, "Memory allocation failed");
70 : return -1;
71 : }
72 0 : keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, saltHexString);
73 0 : elektraFree (saltHexString);
74 :
75 : // read iteration count
76 0 : const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
77 :
78 : // generate/derive the cryptographic key and the IV
79 0 : PKCS5_PBKDF2 pbkdf (new HMAC (new SHA_256));
80 : OctetString derived = pbkdf.derive_key (
81 0 : requiredKeyBytes, std::string (reinterpret_cast<const char *> (keyValue (masterKey)), keyGetValueSize (masterKey)),
82 0 : salt, sizeof (salt), iterations);
83 :
84 0 : cKey = unique_ptr<SymmetricKey> (new SymmetricKey (derived.begin (), ELEKTRA_CRYPTO_BOTAN_KEYSIZE));
85 0 : cIv = unique_ptr<InitializationVector> (
86 0 : new InitializationVector (derived.begin () + ELEKTRA_CRYPTO_BOTAN_KEYSIZE, ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE));
87 :
88 0 : return 1;
89 : }
90 0 : catch (std::exception const & e)
91 : {
92 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to create a cryptographic key for encryption. Reason: %s", e.what ());
93 : return -1;
94 : }
95 : }
96 :
97 : /**
98 : * @brief derive the cryptographic key and IV for a given (Elektra) Key k
99 : * @param config KeySet holding the plugin/backend configuration
100 : * @param errorKey holds an error description in case of failure
101 : * @param masterKey holds the decrypted master password from the plugin configuration
102 : * @param k the (Elektra)-Key to be encrypted
103 : * @param cKey holds a unique pointer to an allocated SymmetricKey.
104 : * @param cIv holds a unique pointer to an allocated InitializationVector.
105 : * @retval -1 on failure. errorKey holds the error description.
106 : * @retval 1 on success
107 : */
108 0 : static int getKeyIvForDecryption (KeySet * config, Key * errorKey, Key * masterKey, Key * k, unique_ptr<SymmetricKey> & cKey,
109 : unique_ptr<InitializationVector> & cIv)
110 : {
111 0 : const size_t requiredKeyBytes = ELEKTRA_CRYPTO_BOTAN_KEYSIZE + ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE;
112 : kdb_octet_t * saltBuffer;
113 0 : kdb_unsigned_long_t saltBufferLen = 0;
114 :
115 0 : ELEKTRA_ASSERT (masterKey != NULL, "Parameter `masterKey` must not be NULL");
116 :
117 : // get the salt
118 0 : if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, &saltBuffer, &saltBufferLen) != 1)
119 : {
120 : return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromPayload)()
121 : }
122 :
123 : // get the iteration count
124 0 : const kdb_unsigned_long_t iterations = ELEKTRA_PLUGIN_FUNCTION (getIterationCount) (errorKey, config);
125 :
126 : try
127 : {
128 : // derive the cryptographic key and the IV
129 0 : PKCS5_PBKDF2 pbkdf (new HMAC (new SHA_256));
130 : OctetString derived = pbkdf.derive_key (
131 0 : requiredKeyBytes, std::string (reinterpret_cast<const char *> (keyValue (masterKey)), keyGetValueSize (masterKey)),
132 0 : saltBuffer, saltBufferLen, iterations);
133 :
134 0 : cKey = unique_ptr<SymmetricKey> (new SymmetricKey (derived.begin (), ELEKTRA_CRYPTO_BOTAN_KEYSIZE));
135 0 : cIv = unique_ptr<InitializationVector> (
136 0 : new InitializationVector (derived.begin () + ELEKTRA_CRYPTO_BOTAN_KEYSIZE, ELEKTRA_CRYPTO_BOTAN_BLOCKSIZE));
137 :
138 0 : return 1;
139 : }
140 0 : catch (std::exception const & e)
141 : {
142 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to restore the cryptographic key for decryption. Reason: %s", e.what ());
143 : return -1;
144 : }
145 : }
146 :
147 16 : int elektraCryptoBotanInit (Key * errorKey)
148 : {
149 : try
150 : {
151 48 : LibraryInitializer::initialize ();
152 : }
153 0 : catch (std::exception const & e)
154 : {
155 0 : ELEKTRA_SET_PLUGIN_MISBEHAVIOR_ERRORF (errorKey, "Botan initialization failed. Reason: %s", e.what ());
156 : return -1; // failure
157 : }
158 16 : return 1; // success
159 : }
160 :
161 0 : int elektraCryptoBotanEncrypt (KeySet * pluginConfig, Key * k, Key * errorKey, Key * masterKey)
162 : {
163 : // get cryptographic material
164 0 : unique_ptr<SymmetricKey> cryptoKey;
165 0 : unique_ptr<InitializationVector> cryptoIv;
166 0 : if (getKeyIvForEncryption (pluginConfig, errorKey, masterKey, k, cryptoKey, cryptoIv) != 1)
167 : {
168 : return -1;
169 : }
170 :
171 : // prepare the salt for payload output
172 0 : kdb_unsigned_long_t saltLen = 0;
173 0 : kdb_octet_t * salt = NULL;
174 :
175 0 : if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromMetakey) (errorKey, k, &salt, &saltLen) != 1)
176 : {
177 : return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromMetakey)()
178 : }
179 :
180 : // remove salt as metakey because it will be encoded into the crypto payload
181 0 : keySetMeta (k, ELEKTRA_CRYPTO_META_SALT, NULL);
182 :
183 : try
184 : {
185 : // setup pipe and crypto filter
186 0 : Pipe encryptor (get_cipher (ELEKTRA_CRYPTO_BOTAN_ALGORITHM, *cryptoKey, *cryptoIv, ENCRYPTION));
187 : kdb_octet_t flags;
188 :
189 0 : switch (keyIsString (k))
190 : {
191 : case 1: // string
192 0 : flags = ELEKTRA_CRYPTO_FLAG_STRING;
193 0 : break;
194 : case -1: // NULL pointer
195 0 : flags = ELEKTRA_CRYPTO_FLAG_NULL;
196 0 : break;
197 : default: // binary
198 0 : flags = ELEKTRA_CRYPTO_FLAG_NONE;
199 0 : break;
200 : }
201 :
202 : // encryption process
203 0 : encryptor.start_msg ();
204 0 : encryptor.write (static_cast<const byte *> (&flags), sizeof (kdb_octet_t));
205 :
206 0 : if (flags == ELEKTRA_CRYPTO_FLAG_STRING)
207 : {
208 0 : const std::string stringVal (keyString (k));
209 0 : encryptor.write (stringVal);
210 : }
211 0 : else if (flags == ELEKTRA_CRYPTO_FLAG_NONE && keyGetValueSize (k) > 0)
212 : {
213 0 : encryptor.write (static_cast<const byte *> (keyValue (k)), keyGetValueSize (k));
214 : }
215 0 : encryptor.end_msg ();
216 :
217 : // write the salt and the encrypted data back to the Key
218 0 : const size_t msgLength = encryptor.remaining ();
219 0 : if (msgLength > 0)
220 : {
221 : auto buffer = unique_ptr<byte[]>{
222 0 : new byte[msgLength + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN + sizeof (kdb_unsigned_long_t) + saltLen]
223 0 : };
224 0 : size_t bufferIndex = 0;
225 :
226 0 : memcpy (&buffer[bufferIndex], ELEKTRA_CRYPTO_MAGIC_NUMBER, ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN);
227 0 : bufferIndex += ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
228 0 : memcpy (&buffer[bufferIndex], &saltLen, sizeof (kdb_unsigned_long_t));
229 0 : bufferIndex += sizeof (kdb_unsigned_long_t);
230 0 : memcpy (&buffer[bufferIndex], salt, saltLen);
231 0 : bufferIndex += saltLen;
232 :
233 0 : const size_t buffered = encryptor.read (&buffer[bufferIndex], msgLength);
234 0 : keySetBinary (k, &buffer[0], buffered + bufferIndex);
235 : }
236 : }
237 0 : catch (std::exception const & e)
238 : {
239 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Encryption failed. Reason: %s", e.what ());
240 0 : elektraFree (salt);
241 : return -1; // failure
242 : }
243 :
244 0 : elektraFree (salt);
245 : return 1; // success
246 : }
247 :
248 0 : int elektraCryptoBotanDecrypt (KeySet * pluginConfig, Key * k, Key * errorKey, Key * masterKey)
249 : {
250 : // get cryptographic material
251 0 : unique_ptr<SymmetricKey> cryptoKey;
252 0 : unique_ptr<InitializationVector> cryptoIv;
253 0 : if (getKeyIvForDecryption (pluginConfig, errorKey, masterKey, k, cryptoKey, cryptoIv) != 1)
254 : {
255 : return -1;
256 : }
257 :
258 : // parse salt length from crypto payload
259 0 : kdb_unsigned_long_t saltLen = 0;
260 0 : if (ELEKTRA_PLUGIN_FUNCTION (getSaltFromPayload) (errorKey, k, NULL, &saltLen) != 1)
261 : {
262 : return -1; // error set by ELEKTRA_PLUGIN_FUNCTION(getSaltFromPayload)()
263 : }
264 0 : saltLen += sizeof (kdb_unsigned_long_t);
265 :
266 : // set payload pointer
267 0 : const byte * payload = reinterpret_cast<const byte *> (keyValue (k)) + saltLen + ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
268 0 : const size_t payloadLen = keyGetValueSize (k) - saltLen - ELEKTRA_CRYPTO_MAGIC_NUMBER_LEN;
269 :
270 : try
271 : {
272 : // setup pipe and crypto filter
273 0 : Pipe decryptor (get_cipher (ELEKTRA_CRYPTO_BOTAN_ALGORITHM, *cryptoKey, *cryptoIv, DECRYPTION));
274 0 : kdb_octet_t flags = ELEKTRA_CRYPTO_FLAG_NONE;
275 :
276 : // decrypt the content
277 0 : decryptor.process_msg (payload, payloadLen);
278 :
279 0 : if (decryptor.remaining () > 0)
280 : {
281 : // decode the "header" flags
282 0 : const size_t flagLength = decryptor.read (static_cast<byte *> (&flags), sizeof (kdb_octet_t));
283 0 : if (flagLength != sizeof (kdb_octet_t))
284 : {
285 0 : throw std::length_error ("Failed to restore the original data type of the value.");
286 : }
287 : }
288 :
289 0 : const size_t msgLength = decryptor.remaining ();
290 0 : if (msgLength > 0)
291 : {
292 0 : if (flags == ELEKTRA_CRYPTO_FLAG_STRING)
293 : {
294 0 : const std::string stringVal = decryptor.read_all_as_string ();
295 0 : keySetString (k, stringVal.c_str ());
296 : }
297 : else
298 : {
299 0 : auto buffer = unique_ptr<byte[]>{ new byte[msgLength] };
300 0 : const size_t buffered = decryptor.read (&buffer[0], msgLength);
301 0 : keySetBinary (k, &buffer[0], buffered);
302 : }
303 : }
304 : else
305 : {
306 0 : keySetBinary (k, NULL, 0);
307 : }
308 : }
309 0 : catch (std::exception const & e)
310 : {
311 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Decryption failed. Reason: %s", e.what ());
312 : return -1; // failure
313 : }
314 :
315 0 : return 1; // success
316 : }
317 :
318 : /**
319 : * @brief create a random sequence of characters with given length.
320 : * @param errorKey holds an error description in case of failure.
321 : * @param length the number of random bytes to be generated.
322 : * @returns allocated buffer holding a hex-encoded random string or NULL in case of error. Must be freed by the caller.
323 : */
324 0 : char * elektraCryptoBotanCreateRandomString (Key * errorKey, const kdb_unsigned_short_t length)
325 : {
326 : try
327 : {
328 0 : char * hexString = NULL;
329 0 : auto buffer = unique_ptr<kdb_octet_t[]>{ new kdb_octet_t[length] };
330 0 : AutoSeeded_RNG rng;
331 0 : rng.randomize (&buffer[0], length);
332 0 : if (ELEKTRA_PLUGIN_FUNCTION (base64Encode) (errorKey, &buffer[0], length, &hexString) < 0)
333 : {
334 : // error in libinvoke - errorKey has been set by base64Encode
335 : return 0;
336 : }
337 0 : return hexString;
338 : }
339 0 : catch (std::exception const & e)
340 : {
341 0 : ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "Failed to generate random string. Reason: %s", e.what ());
342 : return 0;
343 : }
344 : }
345 :
346 : } // extern "C"
|