From d90ee9d4e447f7520fee899b7b6211125095242b Mon Sep 17 00:00:00 2001 From: dubslow Date: Thu, 23 Oct 2014 04:19:18 -0500 Subject: [PATCH] fix #1124 by adding salt manip functions Also, all data now has the magic number prepended. This is incompatible for all but the save/load functions, but I think nothing besides the one experimental qTox branch used any of those, which is why I feel confident about the change. --- auto_tests/encryptsave_test.c | 10 ++- toxencryptsave/toxencryptsave.c | 92 ++++++++++++--------- toxencryptsave/toxencryptsave.h | 138 +++++++++++++++++++++----------- 3 files changed, 155 insertions(+), 85 deletions(-) diff --git a/auto_tests/encryptsave_test.c b/auto_tests/encryptsave_test.c index 85952392..3045fbfa 100644 --- a/auto_tests/encryptsave_test.c +++ b/auto_tests/encryptsave_test.c @@ -99,8 +99,8 @@ START_TEST(test_save_friend) // now test compaitibilty with tox_encrypted_load, first manually... uint8_t out1[size], out2[size]; printf("Trying to decrypt from pw:\n"); - uint32_t sz1 = tox_pass_decrypt(data2+TOX_ENC_SAVE_MAGIC_LENGTH, size-TOX_ENC_SAVE_MAGIC_LENGTH, pw, pwlen, out1); - uint32_t sz2 = tox_pass_key_decrypt(data2+TOX_ENC_SAVE_MAGIC_LENGTH, size-TOX_ENC_SAVE_MAGIC_LENGTH, key, out2); + uint32_t sz1 = tox_pass_decrypt(data2, size, pw, pwlen, out1); + uint32_t sz2 = tox_pass_key_decrypt(data2, size, key, out2); ck_assert_msg(sz1 == sz2, "differing output sizes"); ck_assert_msg(memcmp(out1, out2, sz1) == 0, "differing output data"); @@ -145,6 +145,12 @@ START_TEST(test_keys) sz = tox_pass_decrypt(encrypted, 44+tox_pass_encryption_extra_length(), "123qweasdzxc", 12, out1); ck_assert_msg(sz == 44, "sz isn't right"); ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 3 failed"); + + uint8_t salt[tox_pass_salt_length()]; + ck_assert_msg(0 == tox_get_salt(encrypted, salt), "couldn't get salt"); + uint8_t key2[tox_pass_key_length()]; + tox_derive_key_with_salt("123qweasdzxc", 12, salt, key2); + ck_assert_msg(0 == memcmp(key, key2, tox_pass_key_length()), "salt comparison failed"); } END_TEST diff --git a/toxencryptsave/toxencryptsave.c b/toxencryptsave/toxencryptsave.c index 6567eb96..c037ae58 100644 --- a/toxencryptsave/toxencryptsave.c +++ b/toxencryptsave/toxencryptsave.c @@ -36,9 +36,9 @@ #endif #define TOX_PASS_ENCRYPTION_EXTRA_LENGTH (crypto_box_MACBYTES + crypto_box_NONCEBYTES \ - + crypto_pwhash_scryptsalsa208sha256_SALTBYTES) + + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH) -#define TOX_PASS_KEY_LENGTH (crypto_box_KEYBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES) +#define TOX_PASS_KEY_LENGTH (crypto_pwhash_scryptsalsa208sha256_SALTBYTES + crypto_box_KEYBYTES) int tox_pass_encryption_extra_length() { @@ -50,6 +50,11 @@ int tox_pass_key_length() return TOX_PASS_KEY_LENGTH; } +int tox_pass_salt_length() +{ + return crypto_pwhash_scryptsalsa208sha256_SALTBYTES; +} + /* This "module" provides functions analogous to tox_load and tox_save in toxcore * Clients should consider alerting their users that, unlike plain data, if even one bit * becomes corrupted, the data will be entirely unrecoverable. @@ -59,7 +64,23 @@ int tox_pass_key_length() /* return size of the messenger data (for encrypted saving). */ uint32_t tox_encrypted_size(const Tox *tox) { - return tox_size(tox) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH + TOX_ENC_SAVE_MAGIC_LENGTH; + return tox_size(tox) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH; +} + +/* This retrieves the salt used to encrypt the given data, which can then be passed to + * derive_key_with_salt to produce the same key as was previously used. Any encrpyted + * data with this module can be used as input. + * + * returns -1 if the magic number is wrong + * returns 0 otherwise (no guarantee about validity of data) + */ +int tox_get_salt(uint8_t *data, uint8_t *salt) +{ + if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) + return -1; + data += TOX_ENC_SAVE_MAGIC_LENGTH; + memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); + return 0; } /* Generates a secret symmetric key from the given passphrase. out_key must be at least @@ -75,20 +96,29 @@ uint32_t tox_encrypted_size(const Tox *tox) * returns -1 on failure */ int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key) +{ + uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; + randombytes(salt, sizeof salt); + return tox_derive_key_with_salt(passphrase, pplength, salt, out_key); +} + +/* Same as above, except with use the given salt for deterministic key derivation. + * The salt must be tox_salt_length() bytes in length. + */ +int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *salt, uint8_t *out_key) { if (pplength == 0) return -1; uint8_t passkey[crypto_hash_sha256_BYTES]; crypto_hash_sha256(passkey, passphrase, pplength); - /* First derive a key from the password */ + + uint8_t key[crypto_box_KEYBYTES]; + + /* Derive a key from the password */ /* http://doc.libsodium.org/key_derivation/README.html */ /* note that, according to the documentation, a generic pwhash interface will be created * once the pwhash competition (https://password-hashing.net/) is over */ - uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; - uint8_t key[crypto_box_KEYBYTES]; - randombytes(salt, sizeof salt); - if (crypto_pwhash_scryptsalsa208sha256( key, sizeof(key), passkey, sizeof(passkey), salt, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */ @@ -123,13 +153,17 @@ int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t * * need them to decrypt the data */ - /* first add the prefix */ - uint8_t nonce[crypto_box_NONCEBYTES]; - random_nonce(nonce); + /* first add the magic number */ + memcpy(out, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH); + out += TOX_ENC_SAVE_MAGIC_LENGTH; + /* then add the rest prefix */ memcpy(out, key, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); key += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; out += crypto_pwhash_scryptsalsa208sha256_SALTBYTES; + + uint8_t nonce[crypto_box_NONCEBYTES]; + random_nonce(nonce); memcpy(out, nonce, crypto_box_NONCEBYTES); out += crypto_box_NONCEBYTES; @@ -172,11 +206,6 @@ int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint3 uint8_t temp_data[temp_size]; tox_save(tox, temp_data); - /* the output data consists of, in order: magic number, enc_data */ - /* first add the magic number */ - memcpy(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH); - data += TOX_ENC_SAVE_MAGIC_LENGTH; - /* now encrypt */ return tox_pass_encrypt(temp_data, temp_size, passphrase, pplength, data); } @@ -194,11 +223,6 @@ int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key) uint8_t temp_data[temp_size]; tox_save(tox, temp_data); - /* the output data consists of, in order: magic number, enc_data */ - /* first add the magic number */ - memcpy(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH); - data += TOX_ENC_SAVE_MAGIC_LENGTH; - /* encrypt */ return tox_pass_key_encrypt(temp_data, temp_size, key, data); } @@ -211,8 +235,10 @@ int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key) */ int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out) { - if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) + if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH + || 0 != memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH)) return -1; + data += TOX_ENC_SAVE_MAGIC_LENGTH; uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; //uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; @@ -241,12 +267,11 @@ int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *ke */ int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength, uint8_t *out) { - uint8_t passkey[crypto_hash_sha256_BYTES]; crypto_hash_sha256(passkey, passphrase, pplength); uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; - memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); + memcpy(salt, data+TOX_ENC_SAVE_MAGIC_LENGTH, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); /* derive the key */ uint8_t key[crypto_box_KEYBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; @@ -272,12 +297,6 @@ int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, */ int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength) { - if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) - return -1; - - data += TOX_ENC_SAVE_MAGIC_LENGTH; - length -= TOX_ENC_SAVE_MAGIC_LENGTH; - uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; uint8_t temp_data[decrypt_length]; @@ -295,12 +314,6 @@ int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t * */ int tox_encrypted_key_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *key) { - if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) - return -1; - - data += TOX_ENC_SAVE_MAGIC_LENGTH; - length -= TOX_ENC_SAVE_MAGIC_LENGTH; - uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; uint8_t temp_data[decrypt_length]; @@ -316,10 +329,15 @@ int tox_encrypted_key_load(Tox *tox, const uint8_t *data, uint32_t length, uint8 * returns 1 if it is encrypted * returns 0 otherwise */ -int tox_is_save_encrypted(const uint8_t *data) +int tox_is_data_encrypted(const uint8_t *data) { if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0) return 1; else return 0; } + +int tox_is_save_encrypted(const uint8_t *data) +{ + return tox_is_data_encrypted(data); +} diff --git a/toxencryptsave/toxencryptsave.h b/toxencryptsave/toxencryptsave.h index 14334ea7..5a5ef0cf 100644 --- a/toxencryptsave/toxencryptsave.h +++ b/toxencryptsave/toxencryptsave.h @@ -35,46 +35,44 @@ extern "C" { typedef struct Tox Tox; #endif -// these two functions provide access to these defines in toxencryptsave.c, which -//otherwise aren't actually available in clients... +// these functions provide access to these defines in toxencryptsave.c, which +// otherwise aren't actually available in clients... int tox_pass_encryption_extra_length(); int tox_pass_key_length(); -/* This "module" provides functions analogous to tox_load and tox_save in toxcore +int tox_pass_salt_length(); + +/* return size of the messenger data (for encrypted Messenger saving). */ +uint32_t tox_encrypted_size(const Tox *tox); + +/* This "module" provides functions analogous to tox_load and tox_save in toxcore, + * as well as functions for encryption of arbitrary client data (e.g. chat logs). + * + * It is conceptually organized into two parts. The first part are the functions + * with "key" in the name. To use these functions, first derive an encryption key + * from a password with tox_derive_key_from_pass, and use the returned key to + * encrypt the data. The second part takes the password itself instead of the key, + * and then delegates to the first part to derive the key before de/encryption, + * which can simplify client code; however, key derivation is very expensive + * compared to the actual encryption, so clients that do a lot of encryption should + * favor using the first part intead of the second part. + * + * The encrypted data is prepended with a magic number, to aid validity checking + * (no guarantees are made of course). + * * Clients should consider alerting their users that, unlike plain data, if even one bit * becomes corrupted, the data will be entirely unrecoverable. * Ditto if they forget their password, there is no way to recover the data. */ -/* return size of the messenger data (for encrypted saving). */ -uint32_t tox_encrypted_size(const Tox *tox); -/* Generates a secret symmetric key from the given passphrase. out_key must be at least - * tox_pass_key_length() bytes long. - * Be sure to not compromise the key! Only keep it in memory, do not write to disk. - * This function is fairly cheap, but irungentoo insists that you be allowed to - * cache the result if you want, to minimize computation for repeated encryptions. - * The password is zeroed after key derivation. - * The key should only be used with the other functions in this module, as it - * includes a salt. - * - * returns 0 on success - * returns -1 on failure +/******************************* BEGIN PART 2 ******************************* + * For simplicty, the second part of the module is presented first. The API for + * the first part is analgous, with some extra functions for key handling. If + * your code spends too much time using these functions, consider using the part + * 1 functions instead. */ -int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key); - -/* Encrypt arbitrary with a key produced by tox_derive_key_from_pass. The output - * array must be at least data_len + tox_pass_encryption_extra_length() bytes long. - * key must be tox_pass_key_length() bytes. - * If you already have a symmetric key from somewhere besides this module, simply - * call encrypt_data_symmetric in toxcore/crypto_core directly. - * - * - * returns 0 on success - * returns -1 on failure - */ -int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *key, uint8_t *out); /* Encrypts the given data with the given passphrase. The output array must be * at least data_len + tox_pass_encryption_extra_length() bytes long. This delegates @@ -95,22 +93,6 @@ int tox_pass_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *passphrase */ int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint32_t pplength); -/* Save the messenger data encrypted with the given key from tox_derive_key. - * data must be at least tox_encrypted_size(). - * - * returns 0 on success - * returns -1 on failure - */ -int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key); - -/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by - * tox_derive_key_from_pass. - * - * returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success - * returns -1 on failure - */ -int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out); - /* Decrypts the given data with the given passphrase. The output array must be * at least data_len - tox_pass_encryption_extra_length() bytes long. This delegates * to tox_pass_key_decrypt. @@ -129,6 +111,69 @@ int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, */ int tox_encrypted_load(Tox *tox, const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength); + +/******************************* BEGIN PART 1 ******************************* + * And now part "1", which does the actual encryption, and is rather less cpu + * intensive than part one. The first 3 functions are for key handling. + */ + +/* Generates a secret symmetric key from the given passphrase. out_key must be at least + * tox_pass_key_length() bytes long. + * Be sure to not compromise the key! Only keep it in memory, do not write to disk. + * The password is zeroed after key derivation. + * The key should only be used with the other functions in this module, as it + * includes a salt. + * Note that this function is not deterministic; to derive the same key from a + * password, you also must know the random salt that was used. See below. + * + * returns 0 on success + * returns -1 on failure + */ +int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key); + +/* Same as above, except with use the given salt for deterministic key derivation. + * The salt must be tox_salt_length() bytes in length. + */ +int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *salt, uint8_t *out_key); + +/* This retrieves the salt used to encrypt the given data, which can then be passed to + * derive_key_with_salt to produce the same key as was previously used. Any encrpyted + * data with this module can be used as input. + * + * returns -1 if the magic number is wrong + * returns 0 otherwise (no guarantee about validity of data) + */ +int tox_get_salt(uint8_t *data, uint8_t *salt); + +/* Now come the functions that are analogous to the part 2 functions. */ + +/* Encrypt arbitrary with a key produced by tox_derive_key_. The output + * array must be at least data_len + tox_pass_encryption_extra_length() bytes long. + * key must be tox_pass_key_length() bytes. + * If you already have a symmetric key from somewhere besides this module, simply + * call encrypt_data_symmetric in toxcore/crypto_core directly. + * + * returns 0 on success + * returns -1 on failure + */ +int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *key, uint8_t *out); + +/* Save the messenger data encrypted with the given key from tox_derive_key. + * data must be at least tox_encrypted_size(). + * + * returns 0 on success + * returns -1 on failure + */ +int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key); + +/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by + * tox_derive_key_from_pass. + * + * returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success + * returns -1 on failure + */ +int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out); + /* Load the messenger from encrypted data of size length, with key from tox_derive_key. * * returns 0 on success @@ -141,7 +186,8 @@ int tox_encrypted_key_load(Tox *tox, const uint8_t *data, uint32_t length, uint8 * returns 1 if it is encrypted * returns 0 otherwise */ -int tox_is_save_encrypted(const uint8_t *data); +int tox_is_data_encrypted(const uint8_t *data); +int tox_is_save_encrypted(const uint8_t *data); // poorly-named alias for backwards compat (oh irony...) #ifdef __cplusplus }