This commit is contained in:
irungentoo 2015-03-31 19:44:51 -04:00
commit 99e0fde297
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
3 changed files with 253 additions and 339 deletions

View File

@ -55,7 +55,6 @@ END_TEST
START_TEST(test_save_friend)
{
TOX_ERR_ENCRYPTED_NEW err = TOX_ERR_ENCRYPTED_NEW_OK;
Tox *tox1 = tox_new(0, 0, 0, 0);
Tox *tox2 = tox_new(0, 0, 0, 0);
ck_assert_msg(tox1 || tox2, "Failed to create 2 tox instances");
@ -63,99 +62,109 @@ START_TEST(test_save_friend)
tox_callback_friend_request(tox2, accept_friend_request, &to_compare);
uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address);
int test = tox_friend_add(tox1, address, (uint8_t *)"Gentoo", 7, 0);
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test);
uint32_t test = tox_friend_add(tox1, address, (uint8_t *)"Gentoo", 7, 0);
ck_assert_msg(test != UINT32_MAX, "Failed to add friend");
uint32_t size = tox_encrypted_size(tox1);
size_t size = tox_get_savedata_size(tox1);
uint8_t data[size];
test = tox_encrypted_save(tox1, data, "correcthorsebatterystaple", 25);
ck_assert_msg(test == 0, "failed to encrypted save");
//ck_assert_msg(tox_is_save_encrypted(data) == 1, "magic number missing");
tox_get_savedata(tox1, data);
size_t size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t enc_data[size2];
TOX_ERR_ENCRYPTION error1;
bool ret = tox_pass_encrypt(data, size, "correcthorsebatterystaple", 25, enc_data, &error1);
ck_assert_msg(ret, "failed to encrypted save: %u", error1);
ck_assert_msg(tox_is_data_encrypted(enc_data), "magic number missing");
Tox *tox3 = tox_encrypted_new(0, data, size, "correcthorsebatterystaple", 25, &err);
ck_assert_msg(err == TOX_ERR_ENCRYPTED_NEW_OK, "failed to encrypted new");
TOX_ERR_NEW err2;
Tox *tox3 = tox_new(0, enc_data, size2, &err2);
ck_assert_msg(err2 == TOX_ERR_NEW_LOAD_ENCRYPTED, "wrong error! %u. should fail with %u", err2,
TOX_ERR_NEW_LOAD_ENCRYPTED);
uint8_t dec_data[size];
TOX_ERR_DECRYPTION err3;
ret = tox_pass_decrypt(enc_data, size2, "correcthorsebatterystaple", 25, dec_data, &err3);
ck_assert_msg(ret, "failed to decrypt save: %u", err3);
tox3 = tox_new(0, dec_data, size, &err2);
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to load from decrypted data: %u", err2);
uint8_t address2[TOX_PUBLIC_KEY_SIZE];
test = tox_friend_get_public_key(tox3, 0, address2, 0);
ck_assert_msg(test == 1, "no friends!");
ret = tox_friend_get_public_key(tox3, 0, address2, 0);
ck_assert_msg(ret, "no friends!");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match!");
size = tox_encrypted_size(tox3);
size = tox_get_savedata_size(tox3);
uint8_t data2[size];
tox_get_savedata(tox3, data2);
uint8_t key[32 + crypto_box_BEFORENMBYTES];
memcpy(key, salt, 32);
memcpy(key + 32, known_key2, crypto_box_BEFORENMBYTES);
test = tox_encrypted_key_save(tox3, data2, key);
ck_assert_msg(test == 0, "failed to encrypted save the second");
//ck_assert_msg(tox_is_save_encrypted(data2) == 1, "magic number the second missing");
size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t encdata2[size2];
ret = tox_pass_key_encrypt(data2, size, key, encdata2, &error1);
ck_assert_msg(ret, "failed to key encrypt %u", error1);
ck_assert_msg(tox_is_data_encrypted(encdata2), "magic number the second missing");
// first test tox_encrypted_key_new
Tox *tox4 = tox_encrypted_key_new(0, data2, size, key, &err);
ck_assert_msg(err == TOX_ERR_ENCRYPTED_NEW_OK, "failed to encrypted new the second");
uint8_t address4[TOX_PUBLIC_KEY_SIZE];
test = tox_friend_get_public_key(tox4, 0, address4, 0);
ck_assert_msg(test == 1, "no friends! the second");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match! the second");
// now test compaitibilty with tox_encrypted_new, first manually...
uint8_t out1[size], out2[size];
//printf("Trying to decrypt from pw:\n");
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");
ret = tox_pass_decrypt(encdata2, size2, pw, pwlen, out1, &err3);
ck_assert_msg(ret, "failed to pw decrypt %u", err3);
ret = tox_pass_key_decrypt(encdata2, size2, key, out2, &err3);
ck_assert_msg(ret, "failed to key decrypt %u", err3);
ck_assert_msg(memcmp(out1, out2, size) == 0, "differing output data");
// and now with the code in use (I only bothered with manually to debug this, and it seems a waste
// to remove the manual check now that it's there)
Tox *tox5 = tox_encrypted_new(0, data2, size, pw, pwlen, &err);
ck_assert_msg(err == TOX_ERR_ENCRYPTED_NEW_OK, "failed to encrypted new the third");
Tox *tox4 = tox_new(0, out1, size, &err2);
ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to new the third");
uint8_t address5[TOX_PUBLIC_KEY_SIZE];
test = tox_friend_get_public_key(tox4, 0, address5, 0);
ck_assert_msg(test == 1, "no friends! the third");
ret = tox_friend_get_public_key(tox4, 0, address5, 0);
ck_assert_msg(ret, "no friends! the third");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match! the third");
tox_kill(tox1);
tox_kill(tox2);
tox_kill(tox3);
tox_kill(tox4);
tox_kill(tox5);
}
END_TEST
START_TEST(test_keys)
{
uint8_t key[tox_pass_key_length()];
tox_derive_key_from_pass("123qweasdzxc", 12, key);
TOX_ERR_ENCRYPTION encerr;
TOX_ERR_DECRYPTION decerr;
TOX_ERR_KEY_DERIVATION keyerr;
uint8_t key[TOX_PASS_KEY_LENGTH];
bool ret = tox_derive_key_from_pass("123qweasdzxc", 12, key, &keyerr);
ck_assert_msg(ret, "generic failure 1: %u", keyerr);
uint8_t *string = "No Patrick, mayonnaise is not an instrument."; // 44
uint8_t encrypted[44 + tox_pass_encryption_extra_length()];
int sz = tox_pass_key_encrypt(string, 44, key, encrypted);
uint8_t encrypted[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
ret = tox_pass_key_encrypt(string, 44, key, encrypted, &encerr);
ck_assert_msg(ret, "generic failure 2: %u", encerr);
uint8_t encrypted2[44 + tox_pass_encryption_extra_length()];
int sz2 = tox_pass_encrypt(string, 44, "123qweasdzxc", 12, encrypted2);
uint8_t encrypted2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
ret = tox_pass_encrypt(string, 44, "123qweasdzxc", 12, encrypted2, &encerr);
ck_assert_msg(ret, "generic failure 3: %u", encerr);
ck_assert_msg(sz == sz2, "an encryption failed");
uint8_t out1[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
uint8_t out2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
uint8_t out1[44 + tox_pass_encryption_extra_length()];
uint8_t out2[44 + tox_pass_encryption_extra_length()];
sz = tox_pass_key_decrypt(encrypted, 44 + tox_pass_encryption_extra_length(), key, out1);
ck_assert_msg(sz == 44, "sz isn't right");
ret = tox_pass_key_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, key, out1, &decerr);
ck_assert_msg(ret, "generic failure 4: %u", decerr);
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 1 failed");
sz2 = tox_pass_decrypt(encrypted2, 44 + tox_pass_encryption_extra_length(), "123qweasdzxc", 12, out2);
ck_assert_msg(sz2 == 44, "sz2 isn't right");
ret = tox_pass_decrypt(encrypted2, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out2, &decerr);
ck_assert_msg(ret, "generic failure 5: %u", &decerr);
ck_assert_msg(memcmp(out2, string, 44) == 0, "decryption 2 failed");
// test that pass_decrypt can decrypt things from pass_key_encrypt
sz = tox_pass_decrypt(encrypted, 44 + tox_pass_encryption_extra_length(), "123qweasdzxc", 12, out1);
ck_assert_msg(sz == 44, "sz isn't right");
ret = tox_pass_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out1, &decerr);
ck_assert_msg(ret, "generic failure 6: %u", decerr);
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");
uint8_t salt[TOX_PASS_SALT_LENGTH];
ck_assert_msg(tox_get_salt(encrypted, salt), "couldn't get salt");
uint8_t key2[TOX_PASS_KEY_LENGTH];
ret = tox_derive_key_with_salt("123qweasdzxc", 12, salt, key2, &keyerr);
ck_assert_msg(ret, "generic failure 7: %u", keyerr);
ck_assert_msg(0 == memcmp(key, key2, TOX_PASS_KEY_LENGTH), "salt comparison failed");
}
END_TEST
@ -163,7 +172,7 @@ Suite *encryptsave_suite(void)
{
Suite *s = suite_create("encryptsave");
DEFTESTCASE_SLOW(known_kdf, 60); /* is 5-10 seconds on my computer, but is directly dependent on CPU */
DEFTESTCASE_SLOW(known_kdf, 60);
DEFTESTCASE_SLOW(save_friend, 20);
DEFTESTCASE_SLOW(keys, 30);

View File

@ -37,81 +37,69 @@
#include <crypto_hash_sha256.h>
#endif
#define TOX_PASS_ENCRYPTION_EXTRA_LENGTH (crypto_box_MACBYTES + crypto_box_NONCEBYTES \
+ crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
#if TOX_PASS_SALT_LENGTH != crypto_pwhash_scryptsalsa208sha256_SALTBYTES
#error TOX_PASS_SALT_LENGTH is assumed to be equal to crypto_pwhash_scryptsalsa208sha256_SALTBYTES
#endif
#define TOX_PASS_KEY_LENGTH (crypto_pwhash_scryptsalsa208sha256_SALTBYTES + crypto_box_KEYBYTES)
#if TOX_PASS_KEY_LENGTH != (crypto_pwhash_scryptsalsa208sha256_SALTBYTES + crypto_box_KEYBYTES)
#error TOX_PASS_KEY_LENGTH is assumed to be equal to (crypto_pwhash_scryptsalsa208sha256_SALTBYTES + crypto_box_KEYBYTES)
#endif
int tox_pass_encryption_extra_length()
{
return TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
}
#if TOX_PASS_ENCRYPTION_EXTRA_LENGTH != (crypto_box_MACBYTES + crypto_box_NONCEBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
#error TOX_PASS_ENCRYPTION_EXTRA_LENGTH is assumed to be equal to (crypto_box_MACBYTES + crypto_box_NONCEBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH)
#endif
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
/* 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)
{
return tox_get_savedata_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)
* returns true if magic number matches
* success does not say anything about the validity of the data, only that data of
* the appropriate size was copied
*/
int tox_get_salt(uint8_t *data, uint8_t *salt)
bool tox_get_salt(const uint8_t *data, uint8_t *salt)
{
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0)
return -1;
return 0;
data += TOX_ENC_SAVE_MAGIC_LENGTH;
memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
return 0;
return 1;
}
/* 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.
* 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
* returns true on success
*/
int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key)
bool tox_derive_key_from_pass(uint8_t *passphrase, size_t pplength, uint8_t *out_key, TOX_ERR_KEY_DERIVATION *error)
{
uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
randombytes(salt, sizeof salt);
return tox_derive_key_with_salt(passphrase, pplength, salt, out_key);
return tox_derive_key_with_salt(passphrase, pplength, salt, out_key, error);
}
/* 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)
bool tox_derive_key_with_salt(uint8_t *passphrase, size_t pplength, uint8_t *salt, uint8_t *out_key,
TOX_ERR_KEY_DERIVATION *error)
{
if (pplength == 0)
return -1;
if (pplength == 0 || !passphrase || !salt || !out_key) {
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_NULL);
return 0;
}
uint8_t passkey[crypto_hash_sha256_BYTES];
crypto_hash_sha256(passkey, passphrase, pplength);
@ -127,27 +115,33 @@ int tox_derive_key_with_salt(uint8_t *passphrase, uint32_t pplength, uint8_t *sa
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
/* out of memory most likely */
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_FAILED);
return 0;
}
sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
memcpy(out_key, salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
memcpy(out_key + crypto_pwhash_scryptsalsa208sha256_SALTBYTES, key, crypto_box_KEYBYTES);
return 0;
SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_OK);
return 1;
}
/* Encrypt arbitrary with a key produced by tox_derive_key_from_pass. The output
/* 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
* returns true on success
*/
int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *key, uint8_t *out)
bool tox_pass_key_encrypt(const uint8_t *data, size_t data_len, const uint8_t *key, uint8_t *out,
TOX_ERR_ENCRYPTION *error)
{
if (data_len == 0 || !data || !key || !out) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
return 0;
}
/* the output data consists of, in order:
* salt, nonce, mac, enc_data
* where the mac is automatically prepended by the encrypt()
@ -173,78 +167,62 @@ int tox_pass_key_encrypt(const uint8_t *data, uint32_t data_len, const uint8_t *
/* now encrypt */
if (encrypt_data_symmetric(key, nonce, data, data_len, out)
!= data_len + crypto_box_MACBYTES) {
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_FAILED);
return 0;
}
return 0;
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_OK);
return 1;
}
/* 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
* to tox_derive_key_from_pass and tox_pass_key_encrypt.
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *passphrase, uint32_t pplength, uint8_t *out)
bool tox_pass_encrypt(const uint8_t *data, size_t data_len, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_ENCRYPTION *error)
{
uint8_t key[TOX_PASS_KEY_LENGTH];
TOX_ERR_KEY_DERIVATION _error;
if (tox_derive_key_from_pass(passphrase, pplength, key) == -1)
return -1;
if (!tox_derive_key_from_pass(passphrase, pplength, key, &_error)) {
if (_error == TOX_ERR_KEY_DERIVATION_NULL) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_NULL);
} else if (_error == TOX_ERR_KEY_DERIVATION_FAILED) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED);
}
return tox_pass_key_encrypt(data, data_len, key, out);
}
return 0;
}
/* Save the messenger data encrypted with the given password.
* data must be at least tox_encrypted_size().
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint32_t pplength)
{
/* first get plain save data */
uint32_t temp_size = tox_get_savedata_size(tox);
uint8_t temp_data[temp_size];
tox_get_savedata(tox, temp_data);
/* now encrypt */
return tox_pass_encrypt(temp_data, temp_size, passphrase, pplength, data);
}
/* 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)
{
/* first get plain save data */
uint32_t temp_size = tox_get_savedata_size(tox);
uint8_t temp_data[temp_size];
tox_get_savedata(tox, temp_data);
/* encrypt */
return tox_pass_key_encrypt(temp_data, temp_size, key, data);
return tox_pass_key_encrypt(data, data_len, key, out, error);
}
/* 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
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns true on success
*/
int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *key, uint8_t *out)
bool tox_pass_key_decrypt(const uint8_t *data, size_t length, const uint8_t *key, uint8_t *out,
TOX_ERR_DECRYPTION *error)
{
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH
|| 0 != memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH))
return -1;
if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
return 0;
}
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
return 0;
}
data += TOX_ENC_SAVE_MAGIC_LENGTH;
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
size_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
//uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
uint8_t nonce[crypto_box_NONCEBYTES];
@ -257,20 +235,30 @@ int tox_pass_key_decrypt(const uint8_t *data, uint32_t length, const uint8_t *ke
/* decrypt the data */
if (decrypt_data_symmetric(key, nonce, data, decrypt_length + crypto_box_MACBYTES, out)
!= decrypt_length) {
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_FAILED);
return 0;
}
return decrypt_length;
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_OK);
return 1;
}
/* Decrypts the given data with the given passphrase. The output array must be
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
* to tox_pass_key_decrypt.
*
* returns the length of the output data (== data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) on success
* returns -1 on failure
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns true on success
*/
int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength, uint8_t *out)
bool tox_pass_decrypt(const uint8_t *data, size_t length, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_DECRYPTION *error)
{
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_BAD_FORMAT);
return 0;
}
uint8_t passkey[crypto_hash_sha256_BYTES];
crypto_hash_sha256(passkey, passphrase, pplength);
@ -286,60 +274,18 @@ int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
/* out of memory most likely */
return -1;
SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED);
return 0;
}
sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
return tox_pass_key_decrypt(data, length, key, out);
}
/* Load the new messenger from encrypted data of size length.
* All other arguments are like toxcore/tox_new().
*
* returns NULL on failure; see the documentation in toxcore/tox.h.
*/
Tox *tox_encrypted_new(const struct Tox_Options *options, const uint8_t *data, size_t length, uint8_t *passphrase,
size_t pplength, TOX_ERR_ENCRYPTED_NEW *error)
{
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t temp_data[decrypt_length];
if (tox_pass_decrypt(data, length, passphrase, pplength, temp_data)
!= decrypt_length) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTED_NEW_LOAD_DECRYPTION_FAILED);
return NULL;
}
return tox_new(options, temp_data, decrypt_length, error);
}
/* Load the messenger from encrypted data of size length, with key from tox_derive_key.
* All other arguments are like toxcore/tox_new().
*
* returns NULL on failure; see the documentation in toxcore/tox.h.
*/
Tox *tox_encrypted_key_new(const struct Tox_Options *options, const uint8_t *data, size_t length, uint8_t *key,
TOX_ERR_ENCRYPTED_NEW *error)
{
uint32_t decrypt_length = length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t temp_data[decrypt_length];
if (tox_pass_key_decrypt(data, length, key, temp_data)
!= decrypt_length) {
SET_ERROR_PARAMETER(error, TOX_ERR_ENCRYPTED_NEW_LOAD_DECRYPTION_FAILED);
return NULL;
}
return tox_new(options, temp_data, decrypt_length, error);
return tox_pass_key_decrypt(data, length, key, out, error);
}
/* Determines whether or not the given data is encrypted (by checking the magic number)
*
* returns 1 if it is encrypted
* returns 0 otherwise
*/
int tox_is_data_encrypted(const uint8_t *data)
bool tox_is_data_encrypted(const uint8_t *data)
{
if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0)
return 1;

View File

@ -30,6 +30,7 @@ extern "C" {
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifndef TOX_DEFINED
#define TOX_DEFINED
@ -37,21 +38,11 @@ typedef struct Tox Tox;
struct Tox_Options;
#endif
// these functions provide access to these defines in toxencryptsave.c, which
// otherwise aren't actually available in clients...
int tox_pass_encryption_extra_length();
#define TOX_PASS_SALT_LENGTH 32
#define TOX_PASS_KEY_LENGTH 64
#define TOX_PASS_ENCRYPTION_EXTRA_LENGTH 80
int tox_pass_key_length();
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
/* This module 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,
@ -61,13 +52,74 @@ uint32_t tox_encrypted_size(const Tox *tox);
* 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).
* (no guarantees are made of course). Any data to be decrypted must start with
* the magic number.
*
* 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.
*/
typedef enum TOX_ERR_KEY_DERIVATION {
TOX_ERR_KEY_DERIVATION_OK,
/**
* Some input data, or maybe the output pointer, was null.
*/
TOX_ERR_KEY_DERIVATION_NULL,
/**
* The crypto lib was unable to derive a key from the given passphrase,
* which is usually a lack of memory issue. The functions accepting keys
* do not produce this error.
*/
TOX_ERR_KEY_DERIVATION_FAILED
} TOX_ERR_KEY_DERIVATION;
typedef enum TOX_ERR_ENCRYPTION {
TOX_ERR_ENCRYPTION_OK,
/**
* Some input data, or maybe the output pointer, was null.
*/
TOX_ERR_ENCRYPTION_NULL,
/**
* The crypto lib was unable to derive a key from the given passphrase,
* which is usually a lack of memory issue. The functions accepting keys
* do not produce this error.
*/
TOX_ERR_ENCRYPTION_KEY_DERIVATION_FAILED,
/**
* The encryption itself failed.
*/
TOX_ERR_ENCRYPTION_FAILED
} TOX_ERR_ENCRYPTION;
typedef enum TOX_ERR_DECRYPTION {
TOX_ERR_DECRYPTION_OK,
/**
* Some input data, or maybe the output pointer, was null.
*/
TOX_ERR_DECRYPTION_NULL,
/**
* The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes
*/
TOX_ERR_DECRYPTION_INVALID_LENGTH,
/**
* The input data is missing the magic number (i.e. wasn't created by this
* module, or is corrupted)
*/
TOX_ERR_DECRYPTION_BAD_FORMAT,
/**
* The crypto lib was unable to derive a key from the given passphrase,
* which is usually a lack of memory issue. The functions accepting keys
* do not produce this error.
*/
TOX_ERR_DECRYPTION_KEY_DERIVATION_FAILED,
/**
* The encrypted byte array could not be decrypted. Either the data was
* corrupt or the password/key was incorrect.
*/
TOX_ERR_DECRYPTION_FAILED
} TOX_ERR_DECRYPTION;
/******************************* BEGIN PART 2 *******************************
* For simplicty, the second part of the module is presented first. The API for
@ -77,98 +129,25 @@ uint32_t tox_encrypted_size(const Tox *tox);
*/
/* 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
* at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
* to tox_derive_key_from_pass and tox_pass_key_encrypt.
*
* tox_encrypted_save() is a good example of how to use this function.
*
* returns 0 on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *passphrase, uint32_t pplength, uint8_t *out);
bool tox_pass_encrypt(const uint8_t *data, size_t data_len, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_ENCRYPTION *error);
/* Save the messenger data encrypted with the given password.
* data must be at least tox_encrypted_size().
*
* NOTE: Unlike tox_save(), this function may fail. Be sure to check its return
* value.
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_save(const Tox *tox, uint8_t *data, uint8_t *passphrase, uint32_t pplength);
/* 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
* at least data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long. This delegates
* to tox_pass_key_decrypt.
*
* tox_encrypted_load() is a good example of how to use this function.
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success
* returns -1 on failure
* returns true on success
*/
int tox_pass_decrypt(const uint8_t *data, uint32_t length, uint8_t *passphrase, uint32_t pplength, uint8_t *out);
typedef enum TOX_ERR_ENCRYPTED_NEW {
TOX_ERR_ENCRYPTED_NEW_OK,
TOX_ERR_ENCRYPTED_NEW_NULL,
/**
* The function was unable to allocate enough memory to store the internal
* structures for the Tox object.
*/
TOX_ERR_ENCRYPTED_NEW_MALLOC,
/**
* The function was unable to bind to a port. This may mean that all ports
* have already been bound, e.g. by other Tox instances, or it may mean
* a permission error. You may be able to gather more information from errno.
*/
TOX_ERR_ENCRYPTED_NEW_PORT_ALLOC,
/**
* proxy_type was invalid.
*/
TOX_ERR_ENCRYPTED_NEW_PROXY_BAD_TYPE,
/**
* proxy_type was valid but the proxy_host passed had an invalid format
* or was NULL.
*/
TOX_ERR_ENCRYPTED_NEW_PROXY_BAD_HOST,
/**
* proxy_type was valid, but the proxy_port was invalid.
*/
TOX_ERR_ENCRYPTED_NEW_PROXY_BAD_PORT,
/**
* The proxy host passed could not be resolved.
*/
TOX_ERR_ENCRYPTED_NEW_PROXY_NOT_FOUND,
/**
* The byte array to be loaded contained an encrypted save.
*/
TOX_ERR_ENCRYPTED_NEW_LOAD_ENCRYPTED,
/**
* The data format was invalid. This can happen when loading data that was
* saved by an older version of Tox, or when the data has been corrupted.
* When loading from badly formatted data, some data may have been loaded,
* and the rest is discarded. Passing an invalid length parameter also
* causes this error.
*/
TOX_ERR_ENCRYPTED_NEW_LOAD_BAD_FORMAT,
/**
* The encrypted byte array could not be decrypted. Either the data was
* corrupt or the password/key was incorrect.
*
* NOTE: This error code is only set by tox_encrypted_new() and
* tox_encrypted_key_new(), in the toxencryptsave module.
*/
TOX_ERR_ENCRYPTED_NEW_LOAD_DECRYPTION_FAILED
} TOX_ERR_ENCRYPTED_NEW;
/* Load the new messenger from encrypted data of size length.
* All other arguments are like toxcore/tox_new().
*
* returns NULL on failure; see the documentation in toxcore/tox.h.
*/
Tox *tox_encrypted_new(const struct Tox_Options *options, const uint8_t *data, size_t length, uint8_t *passphrase,
size_t pplength, TOX_ERR_ENCRYPTED_NEW *error);
bool tox_pass_decrypt(const uint8_t *data, size_t length, uint8_t *passphrase, size_t pplength, uint8_t *out,
TOX_ERR_DECRYPTION *error);
/******************************* BEGIN PART 1 *******************************
@ -177,7 +156,7 @@ Tox *tox_encrypted_new(const struct Tox_Options *options, const uint8_t *data, s
*/
/* Generates a secret symmetric key from the given passphrase. out_key must be at least
* tox_pass_key_length() bytes long.
* 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
@ -185,72 +164,52 @@ Tox *tox_encrypted_new(const struct Tox_Options *options, const uint8_t *data, s
* 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
* returns true on success
*/
int tox_derive_key_from_pass(uint8_t *passphrase, uint32_t pplength, uint8_t *out_key);
bool tox_derive_key_from_pass(uint8_t *passphrase, size_t pplength, uint8_t *out_key, TOX_ERR_KEY_DERIVATION *error);
/* 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);
bool tox_derive_key_with_salt(uint8_t *passphrase, size_t pplength, uint8_t *salt, uint8_t *out_key,
TOX_ERR_KEY_DERIVATION *error);
/* 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)
* returns true if magic number matches
* success does not say anything about the validity of the data, only that data of
* the appropriate size was copied
*/
int tox_get_salt(uint8_t *data, uint8_t *salt);
bool tox_get_salt(const 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.
/* 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
* returns true on success
*/
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().
*
* NOTE: Unlike tox_save(), this function may fail. Be sure to check its return
* value.
*
* returns 0 on success
* returns -1 on failure
*/
int tox_encrypted_key_save(const Tox *tox, uint8_t *data, uint8_t *key);
bool tox_pass_key_encrypt(const uint8_t *data, size_t data_len, const uint8_t *key, uint8_t *out,
TOX_ERR_ENCRYPTION *error);
/* 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.
* All other arguments are like toxcore/tox_new().
* the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
*
* returns NULL on failure; see the documentation in toxcore/tox.h.
* returns true on success
*/
Tox *tox_encrypted_key_new(const struct Tox_Options *options, const uint8_t *data, size_t length, uint8_t *key,
TOX_ERR_ENCRYPTED_NEW *error);
bool tox_pass_key_decrypt(const uint8_t *data, size_t length, const uint8_t *key, uint8_t *out,
TOX_ERR_DECRYPTION *error);
/* Determines whether or not the given data is encrypted (by checking the magic number)
*
* returns 1 if it is encrypted
* returns 0 otherwise
*/
int tox_is_data_encrypted(const uint8_t *data);
bool tox_is_data_encrypted(const uint8_t *data);
#ifdef __cplusplus
}