323 lines
7.7 KiB
C
323 lines
7.7 KiB
C
/*
|
|
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
|
*
|
|
* It may be used under the GNU GPL versions 2 or 3
|
|
* or any future license endorsed by Mnemosyne LLC.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "transmission.h"
|
|
#include "crypto.h"
|
|
#include "crypto-utils.h"
|
|
|
|
#include "libtransmission-test.h"
|
|
|
|
#include "crypto-test-ref.h"
|
|
|
|
static int test_torrent_hash(void)
|
|
{
|
|
tr_crypto a;
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
|
{
|
|
hash[i] = i;
|
|
}
|
|
|
|
tr_cryptoConstruct(&a, NULL, true);
|
|
|
|
check(!tr_cryptoHasTorrentHash(&a));
|
|
check(tr_cryptoGetTorrentHash(&a) == NULL);
|
|
|
|
tr_cryptoSetTorrentHash(&a, hash);
|
|
check(tr_cryptoHasTorrentHash(&a));
|
|
check(tr_cryptoGetTorrentHash(&a) != NULL);
|
|
check(memcmp(tr_cryptoGetTorrentHash(&a), hash, SHA_DIGEST_LENGTH) == 0);
|
|
|
|
tr_cryptoDestruct(&a);
|
|
|
|
for (i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
|
{
|
|
hash[i] = i + 1;
|
|
}
|
|
|
|
tr_cryptoConstruct(&a, hash, false);
|
|
|
|
check(tr_cryptoHasTorrentHash(&a));
|
|
check(tr_cryptoGetTorrentHash(&a) != NULL);
|
|
check(memcmp(tr_cryptoGetTorrentHash(&a), hash, SHA_DIGEST_LENGTH) == 0);
|
|
|
|
tr_cryptoSetTorrentHash(&a, NULL);
|
|
check(!tr_cryptoHasTorrentHash(&a));
|
|
check(tr_cryptoGetTorrentHash(&a) == NULL);
|
|
|
|
tr_cryptoDestruct(&a);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int test_encrypt_decrypt(void)
|
|
{
|
|
tr_crypto a;
|
|
tr_crypto_ b;
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
|
const char test1[] = { "test1" };
|
|
char buf11[sizeof(test1)], buf12[sizeof(test1)];
|
|
const char test2[] = { "@#)C$@)#(*%bvkdjfhwbc039bc4603756VB3)" };
|
|
char buf21[sizeof(test2)], buf22[sizeof(test2)];
|
|
int i;
|
|
|
|
for (i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
|
{
|
|
hash[i] = (uint8_t)i;
|
|
}
|
|
|
|
tr_cryptoConstruct(&a, hash, false);
|
|
tr_cryptoConstruct_(&b, hash, true);
|
|
check(tr_cryptoComputeSecret(&a, tr_cryptoGetMyPublicKey_(&b, &i)));
|
|
check(tr_cryptoComputeSecret_(&b, tr_cryptoGetMyPublicKey(&a, &i)));
|
|
|
|
tr_cryptoEncryptInit(&a);
|
|
tr_cryptoEncrypt(&a, sizeof(test1), test1, buf11);
|
|
tr_cryptoDecryptInit_(&b);
|
|
tr_cryptoDecrypt_(&b, sizeof(test1), buf11, buf12);
|
|
check_streq(test1, buf12);
|
|
|
|
tr_cryptoEncryptInit_(&b);
|
|
tr_cryptoEncrypt_(&b, sizeof(test2), test2, buf21);
|
|
tr_cryptoDecryptInit(&a);
|
|
tr_cryptoDecrypt(&a, sizeof(test2), buf21, buf22);
|
|
check_streq(test2, buf22);
|
|
|
|
tr_cryptoDestruct_(&b);
|
|
tr_cryptoDestruct(&a);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int test_sha1(void)
|
|
{
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
|
uint8_t hash_[SHA_DIGEST_LENGTH];
|
|
|
|
check(tr_sha1(hash, "test", 4, NULL));
|
|
check(tr_sha1_(hash_, "test", 4, NULL));
|
|
check(memcmp(hash, "\xa9\x4a\x8f\xe5\xcc\xb1\x9b\xa6\x1c\x4c\x08\x73\xd3\x91\xe9\x87\x98\x2f\xbb\xd3",
|
|
SHA_DIGEST_LENGTH) == 0);
|
|
check(memcmp(hash, hash_, SHA_DIGEST_LENGTH) == 0);
|
|
|
|
check(tr_sha1(hash, "1", 1, "22", 2, "333", 3, NULL));
|
|
check(tr_sha1_(hash_, "1", 1, "22", 2, "333", 3, NULL));
|
|
check(memcmp(hash, "\x1f\x74\x64\x8e\x50\xa6\xa6\x70\x8e\xc5\x4a\xb3\x27\xa1\x63\xd5\x53\x6b\x7c\xed",
|
|
SHA_DIGEST_LENGTH) == 0);
|
|
check(memcmp(hash, hash_, SHA_DIGEST_LENGTH) == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int test_ssha1(void)
|
|
{
|
|
struct
|
|
{
|
|
const char* const plain_text;
|
|
const char* const ssha1;
|
|
}
|
|
test_data[] =
|
|
{
|
|
{ "test", "{15ad0621b259a84d24dcd4e75b09004e98a3627bAMbyRHJy" },
|
|
{ "QNY)(*#$B)!_X$B !_B#($^!)*&$%CV!#)&$C!@$(P*)", "{10e2d7acbb104d970514a147cd16d51dfa40fb3c0OSwJtOL" }
|
|
};
|
|
|
|
size_t i;
|
|
|
|
#define HASH_COUNT (4 * 1024)
|
|
|
|
for (i = 0; i < sizeof(test_data) / sizeof(*test_data); ++i)
|
|
{
|
|
char* const phrase = tr_strdup(test_data[i].plain_text);
|
|
char** hashes = tr_new(char*, HASH_COUNT);
|
|
size_t j;
|
|
|
|
check(tr_ssha1_matches(test_data[i].ssha1, phrase));
|
|
check(tr_ssha1_matches_(test_data[i].ssha1, phrase));
|
|
|
|
for (j = 0; j < HASH_COUNT; ++j)
|
|
{
|
|
hashes[j] = j % 2 == 0 ? tr_ssha1(phrase) : tr_ssha1_(phrase);
|
|
|
|
check(hashes[j] != NULL);
|
|
|
|
/* phrase matches each of generated hashes */
|
|
check(tr_ssha1_matches(hashes[j], phrase));
|
|
check(tr_ssha1_matches_(hashes[j], phrase));
|
|
}
|
|
|
|
for (j = 0; j < HASH_COUNT; ++j)
|
|
{
|
|
size_t k;
|
|
|
|
/* all hashes are different */
|
|
for (k = 0; k < HASH_COUNT; ++k)
|
|
{
|
|
check(k == j || strcmp(hashes[j], hashes[k]) != 0);
|
|
}
|
|
}
|
|
|
|
/* exchange two first chars */
|
|
phrase[0] ^= phrase[1];
|
|
phrase[1] ^= phrase[0];
|
|
phrase[0] ^= phrase[1];
|
|
|
|
for (j = 0; j < HASH_COUNT; ++j)
|
|
{
|
|
/* changed phrase doesn't match the hashes */
|
|
check(!tr_ssha1_matches(hashes[j], phrase));
|
|
check(!tr_ssha1_matches_(hashes[j], phrase));
|
|
}
|
|
|
|
for (j = 0; j < HASH_COUNT; ++j)
|
|
{
|
|
tr_free(hashes[j]);
|
|
}
|
|
|
|
tr_free(hashes);
|
|
tr_free(phrase);
|
|
}
|
|
|
|
#undef HASH_COUNT
|
|
|
|
/* should work with different salt lengths as well */
|
|
check(tr_ssha1_matches("{a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "test"));
|
|
check(tr_ssha1_matches("{d209a21d3bc4f8fc4f8faf347e69f3def597eb170pySy4ai1ZPMjeU1", "test"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int test_random(void)
|
|
{
|
|
int i;
|
|
|
|
/* test that tr_rand_int () stays in-bounds */
|
|
for (i = 0; i < 100000; ++i)
|
|
{
|
|
const int val = tr_rand_int(100);
|
|
check(val >= 0);
|
|
check(val < 100);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool base64_eq(const char* a, const char* b)
|
|
{
|
|
for (;; ++a, ++b)
|
|
{
|
|
while (*a == '\r' || *a == '\n')
|
|
{
|
|
++a;
|
|
}
|
|
|
|
while (*b == '\r' || *b == '\n')
|
|
{
|
|
++b;
|
|
}
|
|
|
|
if (*a == '\0' || *b == '\0' || *a != *b)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return *a == *b;
|
|
}
|
|
|
|
static int test_base64(void)
|
|
{
|
|
size_t len;
|
|
char* in, * out;
|
|
size_t i;
|
|
|
|
out = tr_base64_encode_str("YOYO!", &len);
|
|
check_uint_eq(strlen(out), len);
|
|
check(base64_eq("WU9ZTyE=", out));
|
|
in = tr_base64_decode_str(out, &len);
|
|
check_uint_eq(5, len);
|
|
check_streq("YOYO!", in);
|
|
tr_free(in);
|
|
tr_free(out);
|
|
|
|
out = tr_base64_encode("", 0, &len);
|
|
check_uint_eq(0, len);
|
|
check_streq("", out);
|
|
tr_free(out);
|
|
out = tr_base64_decode("", 0, &len);
|
|
check_uint_eq(0, len);
|
|
check_streq("", out);
|
|
tr_free(out);
|
|
|
|
out = tr_base64_encode(NULL, 0, &len);
|
|
check_uint_eq(0, len);
|
|
check(out == NULL);
|
|
out = tr_base64_decode(NULL, 0, &len);
|
|
check_uint_eq(0, len);
|
|
check(out == NULL);
|
|
|
|
#define MAX_BUF_SIZE 1024
|
|
|
|
for (i = 1; i <= MAX_BUF_SIZE; ++i)
|
|
{
|
|
size_t j;
|
|
char buf[MAX_BUF_SIZE + 1];
|
|
|
|
for (j = 0; j < i; ++j)
|
|
{
|
|
buf[j] = (char)tr_rand_int_weak(256);
|
|
}
|
|
|
|
out = tr_base64_encode(buf, j, &len);
|
|
check_uint_eq(strlen(out), len);
|
|
in = tr_base64_decode(out, len, &len);
|
|
check_uint_eq(j, len);
|
|
check(memcmp(in, buf, len) == 0);
|
|
tr_free(in);
|
|
tr_free(out);
|
|
|
|
for (j = 0; j < i; ++j)
|
|
{
|
|
buf[j] = (char)(1 + tr_rand_int_weak(255));
|
|
}
|
|
|
|
buf[j] = '\0';
|
|
|
|
out = tr_base64_encode_str(buf, &len);
|
|
check_uint_eq(strlen(out), len);
|
|
in = tr_base64_decode_str(out, &len);
|
|
check_uint_eq(j, len);
|
|
check_streq(in, buf);
|
|
tr_free(in);
|
|
tr_free(out);
|
|
}
|
|
|
|
#undef MAX_BUF_SIZE
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
const testFunc tests[] =
|
|
{
|
|
test_torrent_hash,
|
|
test_encrypt_decrypt,
|
|
test_sha1,
|
|
test_ssha1,
|
|
test_random,
|
|
test_base64
|
|
};
|
|
|
|
return runTests(tests, NUM_TESTS(tests));
|
|
}
|