transmission/tests/libtransmission/crypto-test.cc

238 lines
7.4 KiB
C++

// This file Copyright (C) 2013-2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <array>
#include <cstring>
#include <numeric>
#include <string>
#include <string_view>
#include <unordered_set>
#include "transmission.h"
#include "crypto.h"
#include "crypto-utils.h"
#include "utils.h"
#include "crypto-test-ref.h"
#include "gtest/gtest.h"
using namespace std::literals;
namespace
{
auto constexpr SomeHash = tr_sha1_digest_t{
std::byte{ 0 }, std::byte{ 1 }, std::byte{ 2 }, std::byte{ 3 }, std::byte{ 4 }, std::byte{ 5 }, std::byte{ 6 },
std::byte{ 7 }, std::byte{ 8 }, std::byte{ 9 }, std::byte{ 10 }, std::byte{ 11 }, std::byte{ 12 }, std::byte{ 13 },
std::byte{ 14 }, std::byte{ 15 }, std::byte{ 16 }, std::byte{ 17 }, std::byte{ 18 }, std::byte{ 19 },
};
} // namespace
TEST(Crypto, torrentHash)
{
auto a = tr_crypto{};
EXPECT_FALSE(tr_cryptoGetTorrentHash(&a));
tr_cryptoSetTorrentHash(&a, SomeHash);
EXPECT_TRUE(tr_cryptoGetTorrentHash(&a));
EXPECT_EQ(SomeHash, *tr_cryptoGetTorrentHash(&a));
a = tr_crypto{ &SomeHash, false };
EXPECT_TRUE(tr_cryptoGetTorrentHash(&a));
EXPECT_EQ(SomeHash, *tr_cryptoGetTorrentHash(&a));
}
TEST(Crypto, encryptDecrypt)
{
auto a = tr_crypto{ &SomeHash, false };
auto b = tr_crypto_{ &SomeHash, true };
auto public_key_length = int{};
EXPECT_TRUE(tr_cryptoComputeSecret(&a, tr_cryptoGetMyPublicKey_(&b, &public_key_length)));
EXPECT_TRUE(tr_cryptoComputeSecret_(&b, tr_cryptoGetMyPublicKey(&a, &public_key_length)));
auto const input1 = std::string{ "test1" };
auto encrypted1 = std::array<char, 128>{};
auto decrypted1 = std::array<char, 128>{};
tr_cryptoEncryptInit(&a);
tr_cryptoEncrypt(&a, input1.size(), input1.data(), encrypted1.data());
tr_cryptoDecryptInit_(&b);
tr_cryptoDecrypt_(&b, input1.size(), encrypted1.data(), decrypted1.data());
EXPECT_EQ(input1, std::string(decrypted1.data(), input1.size()));
auto const input2 = std::string{ "@#)C$@)#(*%bvkdjfhwbc039bc4603756VB3)" };
auto encrypted2 = std::array<char, 128>{};
auto decrypted2 = std::array<char, 128>{};
tr_cryptoEncryptInit_(&b);
tr_cryptoEncrypt_(&b, input2.size(), input2.data(), encrypted2.data());
tr_cryptoDecryptInit(&a);
tr_cryptoDecrypt(&a, input2.size(), encrypted2.data(), decrypted2.data());
EXPECT_EQ(input2, std::string(decrypted2.data(), input2.size()));
}
TEST(Crypto, sha1)
{
auto hash1 = tr_sha1("test"sv);
EXPECT_TRUE(hash1);
EXPECT_EQ(
0,
memcmp(
std::data(*hash1),
"\xa9\x4a\x8f\xe5\xcc\xb1\x9b\xa6\x1c\x4c\x08\x73\xd3\x91\xe9\x87\x98\x2f\xbb\xd3",
std::size(*hash1)));
auto hash2 = tr_sha1("test"sv);
EXPECT_TRUE(hash1);
EXPECT_EQ(*hash1, *hash2);
hash1 = tr_sha1("1"sv, "22"sv, "333"sv);
hash2 = tr_sha1("1"sv, "22"sv, "333"sv);
EXPECT_TRUE(hash1);
EXPECT_TRUE(hash2);
EXPECT_EQ(*hash1, *hash2);
EXPECT_EQ(
0,
memcmp(
std::data(*hash1),
"\x1f\x74\x64\x8e\x50\xa6\xa6\x70\x8e\xc5\x4a\xb3\x27\xa1\x63\xd5\x53\x6b\x7c\xed",
std::size(*hash1)));
auto const hash3 = tr_sha1("test"sv);
EXPECT_TRUE(hash3);
EXPECT_EQ("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"sv, tr_sha1_to_string(*hash3));
auto const hash4 = tr_sha1("te"sv, "st"sv);
EXPECT_TRUE(hash4);
EXPECT_EQ("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"sv, tr_sha1_to_string(*hash4));
auto const hash5 = tr_sha1("t"sv, "e"sv, std::string{ "s" }, std::array<char, 1>{ { 't' } });
EXPECT_TRUE(hash5);
EXPECT_EQ("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"sv, tr_sha1_to_string(*hash5));
}
TEST(Crypto, ssha1)
{
struct LocalTest
{
std::string_view plain_text;
std::string_view ssha1;
};
auto constexpr Tests = std::array<LocalTest, 2>{ {
{ "test"sv, "{15ad0621b259a84d24dcd4e75b09004e98a3627bAMbyRHJy"sv },
{ "QNY)(*#$B)!_X$B !_B#($^!)*&$%CV!#)&$C!@$(P*)"sv, "{10e2d7acbb104d970514a147cd16d51dfa40fb3c0OSwJtOL"sv },
} };
auto constexpr HashCount = size_t{ 4 * 1024 };
for (auto const& [plain_text, ssha1] : Tests)
{
auto hashes = std::unordered_set<std::string>{};
hashes.reserve(HashCount);
EXPECT_TRUE(tr_ssha1_matches(ssha1, plain_text));
EXPECT_TRUE(tr_ssha1_matches_(ssha1, plain_text));
for (size_t j = 0; j < HashCount; ++j)
{
auto const hash = (j % 2 == 0) ? tr_ssha1(plain_text) : tr_ssha1_(plain_text);
// phrase matches each of generated hashes
EXPECT_TRUE(tr_ssha1_matches(hash, plain_text));
EXPECT_TRUE(tr_ssha1_matches_(hash, plain_text));
hashes.insert(hash);
}
// confirm all hashes are different
EXPECT_EQ(HashCount, hashes.size());
/* exchange two first chars */
auto phrase = std::string{ plain_text };
phrase[0] ^= phrase[1];
phrase[1] ^= phrase[0];
phrase[0] ^= phrase[1];
for (auto const& hash : hashes)
{
/* changed phrase doesn't match the hashes */
EXPECT_FALSE(tr_ssha1_matches(hash, phrase));
EXPECT_FALSE(tr_ssha1_matches_(hash, phrase));
}
}
/* should work with different salt lengths as well */
EXPECT_TRUE(tr_ssha1_matches("{a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "test"));
EXPECT_TRUE(tr_ssha1_matches("{d209a21d3bc4f8fc4f8faf347e69f3def597eb170pySy4ai1ZPMjeU1", "test"));
}
TEST(Crypto, sha1FromString)
{
// bad lengths
EXPECT_FALSE(tr_sha1_from_string(""));
EXPECT_FALSE(tr_sha1_from_string("a94a8fe5ccb19ba61c4c0873d391e987982fbbd"sv));
EXPECT_FALSE(tr_sha1_from_string("a94a8fe5ccb19ba61c4c0873d391e987982fbbd33"sv));
// nonhex
EXPECT_FALSE(tr_sha1_from_string("a94a8fe5ccb19ba61c4cz873d391e987982fbbd3"sv));
EXPECT_FALSE(tr_sha1_from_string("a94a8fe5ccb19 61c4c0873d391e987982fbbd3"sv));
// lowecase hex
auto const baseline = "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"sv;
auto const lc = tr_sha1_from_string(baseline);
EXPECT_TRUE(lc);
EXPECT_EQ(baseline, tr_sha1_to_string(*lc));
// uppercase hex should yield the same result
auto const uc = tr_sha1_from_string(tr_strupper(baseline));
EXPECT_TRUE(uc);
EXPECT_EQ(*lc, *uc);
}
TEST(Crypto, random)
{
/* test that tr_rand_int() stays in-bounds */
for (int i = 0; i < 100000; ++i)
{
int const val = tr_rand_int(100);
EXPECT_LE(0, val);
EXPECT_LT(val, 100);
}
}
TEST(Crypto, base64)
{
auto raw = std::string_view{ "YOYO!"sv };
auto encoded = tr_base64_encode(raw);
EXPECT_EQ("WU9ZTyE="sv, encoded);
EXPECT_EQ(raw, tr_base64_decode(encoded));
EXPECT_EQ(""sv, tr_base64_encode(""sv));
EXPECT_EQ(""sv, tr_base64_decode(""sv));
static auto constexpr MaxBufSize = size_t{ 1024 };
for (size_t i = 1; i <= MaxBufSize; ++i)
{
auto buf = std::string{};
for (size_t j = 0; j < i; ++j)
{
buf += char(tr_rand_int_weak(256));
}
EXPECT_EQ(buf, tr_base64_decode(tr_base64_encode(buf)));
buf = std::string{};
for (size_t j = 0; j < i; ++j)
{
buf += char(1 + tr_rand_int_weak(255));
}
EXPECT_EQ(buf, tr_base64_decode(tr_base64_encode(buf)));
}
}