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) START_TEST(test_save_friend)
{ {
TOX_ERR_ENCRYPTED_NEW err = TOX_ERR_ENCRYPTED_NEW_OK;
Tox *tox1 = tox_new(0, 0, 0, 0); Tox *tox1 = tox_new(0, 0, 0, 0);
Tox *tox2 = 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"); 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); tox_callback_friend_request(tox2, accept_friend_request, &to_compare);
uint8_t address[TOX_ADDRESS_SIZE]; uint8_t address[TOX_ADDRESS_SIZE];
tox_self_get_address(tox2, address); tox_self_get_address(tox2, address);
int test = tox_friend_add(tox1, address, (uint8_t *)"Gentoo", 7, 0); uint32_t test = tox_friend_add(tox1, address, (uint8_t *)"Gentoo", 7, 0);
ck_assert_msg(test == 0, "Failed to add friend error code: %i", test); 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]; uint8_t data[size];
test = tox_encrypted_save(tox1, data, "correcthorsebatterystaple", 25); tox_get_savedata(tox1, data);
ck_assert_msg(test == 0, "failed to encrypted save"); size_t size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
//ck_assert_msg(tox_is_save_encrypted(data) == 1, "magic number missing"); 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); TOX_ERR_NEW err2;
ck_assert_msg(err == TOX_ERR_ENCRYPTED_NEW_OK, "failed to encrypted new"); 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]; uint8_t address2[TOX_PUBLIC_KEY_SIZE];
test = tox_friend_get_public_key(tox3, 0, address2, 0); ret = tox_friend_get_public_key(tox3, 0, address2, 0);
ck_assert_msg(test == 1, "no friends!"); ck_assert_msg(ret, "no friends!");
ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match!"); 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]; uint8_t data2[size];
tox_get_savedata(tox3, data2);
uint8_t key[32 + crypto_box_BEFORENMBYTES]; uint8_t key[32 + crypto_box_BEFORENMBYTES];
memcpy(key, salt, 32); memcpy(key, salt, 32);
memcpy(key + 32, known_key2, crypto_box_BEFORENMBYTES); memcpy(key + 32, known_key2, crypto_box_BEFORENMBYTES);
test = tox_encrypted_key_save(tox3, data2, key); size2 = size + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
ck_assert_msg(test == 0, "failed to encrypted save the second"); uint8_t encdata2[size2];
//ck_assert_msg(tox_is_save_encrypted(data2) == 1, "magic number the second missing"); 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]; uint8_t out1[size], out2[size];
//printf("Trying to decrypt from pw:\n"); ret = tox_pass_decrypt(encdata2, size2, pw, pwlen, out1, &err3);
uint32_t sz1 = tox_pass_decrypt(data2, size, pw, pwlen, out1); ck_assert_msg(ret, "failed to pw decrypt %u", err3);
uint32_t sz2 = tox_pass_key_decrypt(data2, size, key, out2); ret = tox_pass_key_decrypt(encdata2, size2, key, out2, &err3);
ck_assert_msg(sz1 == sz2, "differing output sizes"); ck_assert_msg(ret, "failed to key decrypt %u", err3);
ck_assert_msg(memcmp(out1, out2, sz1) == 0, "differing output data"); 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 // 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) // to remove the manual check now that it's there)
Tox *tox5 = tox_encrypted_new(0, data2, size, pw, pwlen, &err); Tox *tox4 = tox_new(0, out1, size, &err2);
ck_assert_msg(err == TOX_ERR_ENCRYPTED_NEW_OK, "failed to encrypted new the third"); ck_assert_msg(err2 == TOX_ERR_NEW_OK, "failed to new the third");
uint8_t address5[TOX_PUBLIC_KEY_SIZE]; uint8_t address5[TOX_PUBLIC_KEY_SIZE];
test = tox_friend_get_public_key(tox4, 0, address5, 0); ret = tox_friend_get_public_key(tox4, 0, address5, 0);
ck_assert_msg(test == 1, "no friends! the third"); 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"); ck_assert_msg(memcmp(address, address2, TOX_PUBLIC_KEY_SIZE) == 0, "addresses don't match! the third");
tox_kill(tox1); tox_kill(tox1);
tox_kill(tox2); tox_kill(tox2);
tox_kill(tox3); tox_kill(tox3);
tox_kill(tox4); tox_kill(tox4);
tox_kill(tox5);
} }
END_TEST END_TEST
START_TEST(test_keys) START_TEST(test_keys)
{ {
uint8_t key[tox_pass_key_length()]; TOX_ERR_ENCRYPTION encerr;
tox_derive_key_from_pass("123qweasdzxc", 12, key); 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 *string = "No Patrick, mayonnaise is not an instrument."; // 44
uint8_t encrypted[44 + tox_pass_encryption_extra_length()]; uint8_t encrypted[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
int sz = tox_pass_key_encrypt(string, 44, key, encrypted); 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()]; uint8_t encrypted2[44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
int sz2 = tox_pass_encrypt(string, 44, "123qweasdzxc", 12, encrypted2); 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()]; ret = tox_pass_key_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, key, out1, &decerr);
uint8_t out2[44 + tox_pass_encryption_extra_length()]; ck_assert_msg(ret, "generic failure 4: %u", decerr);
sz = tox_pass_key_decrypt(encrypted, 44 + tox_pass_encryption_extra_length(), key, out1);
ck_assert_msg(sz == 44, "sz isn't right");
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 1 failed"); 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); ret = tox_pass_decrypt(encrypted2, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out2, &decerr);
ck_assert_msg(sz2 == 44, "sz2 isn't right"); ck_assert_msg(ret, "generic failure 5: %u", &decerr);
ck_assert_msg(memcmp(out2, string, 44) == 0, "decryption 2 failed"); ck_assert_msg(memcmp(out2, string, 44) == 0, "decryption 2 failed");
// test that pass_decrypt can decrypt things from pass_key_encrypt // 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); ret = tox_pass_decrypt(encrypted, 44 + TOX_PASS_ENCRYPTION_EXTRA_LENGTH, "123qweasdzxc", 12, out1, &decerr);
ck_assert_msg(sz == 44, "sz isn't right"); ck_assert_msg(ret, "generic failure 6: %u", decerr);
ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 3 failed"); ck_assert_msg(memcmp(out1, string, 44) == 0, "decryption 3 failed");
uint8_t salt[tox_pass_salt_length()]; uint8_t salt[TOX_PASS_SALT_LENGTH];
ck_assert_msg(0 == tox_get_salt(encrypted, salt), "couldn't get salt"); ck_assert_msg(tox_get_salt(encrypted, salt), "couldn't get salt");
uint8_t key2[tox_pass_key_length()]; uint8_t key2[TOX_PASS_KEY_LENGTH];
tox_derive_key_with_salt("123qweasdzxc", 12, salt, key2); ret = tox_derive_key_with_salt("123qweasdzxc", 12, salt, key2, &keyerr);
ck_assert_msg(0 == memcmp(key, key2, tox_pass_key_length()), "salt comparison failed"); 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 END_TEST
@ -163,7 +172,7 @@ Suite *encryptsave_suite(void)
{ {
Suite *s = suite_create("encryptsave"); 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(save_friend, 20);
DEFTESTCASE_SLOW(keys, 30); DEFTESTCASE_SLOW(keys, 30);

View File

@ -37,81 +37,69 @@
#include <crypto_hash_sha256.h> #include <crypto_hash_sha256.h>
#endif #endif
#define TOX_PASS_ENCRYPTION_EXTRA_LENGTH (crypto_box_MACBYTES + crypto_box_NONCEBYTES \ #if TOX_PASS_SALT_LENGTH != crypto_pwhash_scryptsalsa208sha256_SALTBYTES
+ crypto_pwhash_scryptsalsa208sha256_SALTBYTES + TOX_ENC_SAVE_MAGIC_LENGTH) #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() #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)
return TOX_PASS_ENCRYPTION_EXTRA_LENGTH; #endif
}
int tox_pass_key_length() /* Clients should consider alerting their users that, unlike plain data, if even one bit
{
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. * becomes corrupted, the data will be entirely unrecoverable.
* Ditto if they forget their password, there is no way to recover the data. * 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 /* 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 * derive_key_with_salt to produce the same key as was previously used. Any encrpyted
* data with this module can be used as input. * data with this module can be used as input.
* *
* returns -1 if the magic number is wrong * returns true if magic number matches
* returns 0 otherwise (no guarantee about validity of data) * 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) if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) != 0)
return -1; return 0;
data += TOX_ENC_SAVE_MAGIC_LENGTH; data += TOX_ENC_SAVE_MAGIC_LENGTH;
memcpy(salt, data, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); 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 /* 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. * 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 password is zeroed after key derivation.
* The key should only be used with the other functions in this module, as it * The key should only be used with the other functions in this module, as it
* includes a salt. * 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 true on success
* returns -1 on failure
*/ */
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]; uint8_t salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
randombytes(salt, sizeof salt); 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. /* Same as above, except with use the given salt for deterministic key derivation.
* The salt must be tox_salt_length() bytes in length. * 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) if (pplength == 0 || !passphrase || !salt || !out_key) {
return -1; SET_ERROR_PARAMETER(error, TOX_ERR_KEY_DERIVATION_NULL);
return 0;
}
uint8_t passkey[crypto_hash_sha256_BYTES]; uint8_t passkey[crypto_hash_sha256_BYTES];
crypto_hash_sha256(passkey, passphrase, pplength); 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_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) { crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
/* out of memory most likely */ /* 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 */ sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
memcpy(out_key, salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES); memcpy(out_key, salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
memcpy(out_key + crypto_pwhash_scryptsalsa208sha256_SALTBYTES, key, crypto_box_KEYBYTES); 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. * array must be at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
* key must be TOX_PASS_KEY_LENGTH bytes. * key must be TOX_PASS_KEY_LENGTH bytes.
* If you already have a symmetric key from somewhere besides this module, simply * If you already have a symmetric key from somewhere besides this module, simply
* call encrypt_data_symmetric in toxcore/crypto_core directly. * call encrypt_data_symmetric in toxcore/crypto_core directly.
* *
* * returns true on success
* 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) 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: /* the output data consists of, in order:
* salt, nonce, mac, enc_data * salt, nonce, mac, enc_data
* where the mac is automatically prepended by the encrypt() * 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 */ /* now encrypt */
if (encrypt_data_symmetric(key, nonce, data, data_len, out) if (encrypt_data_symmetric(key, nonce, data, data_len, out)
!= data_len + crypto_box_MACBYTES) { != 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 /* 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. * to tox_derive_key_from_pass and tox_pass_key_encrypt.
* *
* returns 0 on success * returns true on success
* returns -1 on failure
*/ */
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]; uint8_t key[TOX_PASS_KEY_LENGTH];
TOX_ERR_KEY_DERIVATION _error;
if (tox_derive_key_from_pass(passphrase, pplength, key) == -1) if (!tox_derive_key_from_pass(passphrase, pplength, key, &_error)) {
return -1; 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. return tox_pass_key_encrypt(data, data_len, key, out, error);
* 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);
} }
/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by /* This is the inverse of tox_pass_key_encrypt, also using only keys produced by
* tox_derive_key_from_pass. * tox_derive_key_from_pass.
* *
* returns the length of the output data (== data_len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) on success * the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
* returns -1 on failure *
* 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 if (length <= TOX_PASS_ENCRYPTION_EXTRA_LENGTH) {
|| 0 != memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH)) SET_ERROR_PARAMETER(error, TOX_ERR_DECRYPTION_INVALID_LENGTH);
return -1; 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; 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 salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
uint8_t nonce[crypto_box_NONCEBYTES]; 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 */ /* decrypt the data */
if (decrypt_data_symmetric(key, nonce, data, decrypt_length + crypto_box_MACBYTES, out) if (decrypt_data_symmetric(key, nonce, data, decrypt_length + crypto_box_MACBYTES, out)
!= decrypt_length) { != 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 /* 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 * the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
* 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) 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]; uint8_t passkey[crypto_hash_sha256_BYTES];
crypto_hash_sha256(passkey, passphrase, pplength); 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_OPSLIMIT_INTERACTIVE * 2, /* slightly stronger */
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) { crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) != 0) {
/* out of memory most likely */ /* 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 */ sodium_memzero(passkey, crypto_hash_sha256_BYTES); /* wipe plaintext pw */
return tox_pass_key_decrypt(data, length, key, out); return tox_pass_key_decrypt(data, length, key, out, error);
}
/* 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);
} }
/* Determines whether or not the given data is encrypted (by checking the magic number) /* 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) if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0)
return 1; return 1;

View File

@ -30,6 +30,7 @@ extern "C" {
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#ifndef TOX_DEFINED #ifndef TOX_DEFINED
#define TOX_DEFINED #define TOX_DEFINED
@ -37,21 +38,11 @@ typedef struct Tox Tox;
struct Tox_Options; struct Tox_Options;
#endif #endif
// these functions provide access to these defines in toxencryptsave.c, which #define TOX_PASS_SALT_LENGTH 32
// otherwise aren't actually available in clients... #define TOX_PASS_KEY_LENGTH 64
int tox_pass_encryption_extra_length(); #define TOX_PASS_ENCRYPTION_EXTRA_LENGTH 80
int tox_pass_key_length(); /* This module is conceptually organized into two parts. The first part are the functions
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 * 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 * 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, * 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. * favor using the first part intead of the second part.
* *
* The encrypted data is prepended with a magic number, to aid validity checking * 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 * Clients should consider alerting their users that, unlike plain data, if even one bit
* becomes corrupted, the data will be entirely unrecoverable. * becomes corrupted, the data will be entirely unrecoverable.
* Ditto if they forget their password, there is no way to recover the data. * 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 ******************************* /******************************* BEGIN PART 2 *******************************
* For simplicty, the second part of the module is presented first. The API for * 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 /* 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. * 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 true on success
*
* returns 0 on success
* returns -1 on failure
*/ */
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 /* 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. * 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 true on success
* returns -1 on failure
*/ */
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);
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);
/******************************* BEGIN PART 1 ******************************* /******************************* 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 /* 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. * 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 password is zeroed after key derivation.
* The key should only be used with the other functions in this module, as it * 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 * 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. * password, you also must know the random salt that was used. See below.
* *
* returns 0 on success * returns true on success
* returns -1 on failure
*/ */
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. /* Same as above, except with use the given salt for deterministic key derivation.
* The salt must be tox_salt_length() bytes in length. * 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 /* 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 * derive_key_with_salt to produce the same key as was previously used. Any encrpyted
* data with this module can be used as input. * data with this module can be used as input.
* *
* returns -1 if the magic number is wrong * returns true if magic number matches
* returns 0 otherwise (no guarantee about validity of data) * 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. */ /* Now come the functions that are analogous to the part 2 functions. */
/* Encrypt arbitrary with a key produced by tox_derive_key_. 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. * array must be at least data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes long.
* key must be tox_pass_key_length() bytes. * key must be TOX_PASS_KEY_LENGTH bytes.
* If you already have a symmetric key from somewhere besides this module, simply * If you already have a symmetric key from somewhere besides this module, simply
* call encrypt_data_symmetric in toxcore/crypto_core directly. * call encrypt_data_symmetric in toxcore/crypto_core directly.
* *
* returns 0 on success * returns true 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); bool tox_pass_key_encrypt(const uint8_t *data, size_t data_len, const uint8_t *key, uint8_t *out,
TOX_ERR_ENCRYPTION *error);
/* 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);
/* This is the inverse of tox_pass_key_encrypt, also using only keys produced by /* This is the inverse of tox_pass_key_encrypt, also using only keys produced by
* tox_derive_key_from_pass. * tox_derive_key_from_pass.
* *
* returns the length of the output data (== data_len - tox_pass_encryption_extra_length()) on success * the output data has size data_length - TOX_PASS_ENCRYPTION_EXTRA_LENGTH
* 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().
* *
* 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, bool tox_pass_key_decrypt(const uint8_t *data, size_t length, const uint8_t *key, uint8_t *out,
TOX_ERR_ENCRYPTED_NEW *error); TOX_ERR_DECRYPTION *error);
/* Determines whether or not the given data is encrypted (by checking the magic number) /* 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 #ifdef __cplusplus
} }