refactor: tr_ssha1(), tr_ssha1_matches() (#2148)

* refactor: make tr_ssha1 and tr_ssha1_matches string_view-friendly
This commit is contained in:
Charles Kerr 2021-11-13 20:03:01 -06:00 committed by GitHub
parent 7ff6756ac5
commit 99aeaa0eaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 44 deletions

View File

@ -118,10 +118,8 @@ int tr_rand_int_weak(int upper_bound)
****
***/
char* tr_ssha1(char const* plain_text)
std::string tr_ssha1(std::string_view plain_text)
{
TR_ASSERT(plain_text != nullptr);
auto constexpr SaltvalLen = int{ 8 };
auto constexpr SalterLen = int{ 64 };
@ -142,24 +140,21 @@ char* tr_ssha1(char const* plain_text)
ch = salter[ch % SalterLen];
}
tr_sha1(sha, plain_text, (int)strlen(plain_text), salt, SaltvalLen, nullptr);
tr_sha1(sha, std::data(plain_text), std::size(plain_text), salt, SaltvalLen, nullptr);
tr_sha1_to_hex(&buf[1], sha);
memcpy(&buf[1 + 2 * SHA_DIGEST_LENGTH], &salt, SaltvalLen);
buf[1 + 2 * SHA_DIGEST_LENGTH + SaltvalLen] = '\0';
buf[0] = '{'; /* signal that this is a hash. this makes saving/restoring easier */
return tr_strdup(buf);
return std::string{ buf };
}
bool tr_ssha1_matches(char const* ssha1, char const* plain_text)
bool tr_ssha1_matches(std::string_view ssha1, std::string_view plain_text)
{
TR_ASSERT(ssha1 != nullptr);
TR_ASSERT(plain_text != nullptr);
size_t const brace_len = 1;
size_t const brace_and_hash_len = brace_len + 2 * SHA_DIGEST_LENGTH;
size_t const source_len = strlen(ssha1);
size_t const source_len = std::size(ssha1);
if (source_len < brace_and_hash_len || ssha1[0] != '{')
{
@ -167,16 +162,16 @@ bool tr_ssha1_matches(char const* ssha1, char const* plain_text)
}
/* extract the salt */
char const* const salt = ssha1 + brace_and_hash_len;
char const* const salt = std::data(ssha1) + brace_and_hash_len;
size_t const salt_len = source_len - brace_and_hash_len;
uint8_t buf[SHA_DIGEST_LENGTH * 2 + 1];
/* hash pass + salt */
tr_sha1(buf, plain_text, (int)strlen(plain_text), salt, (int)salt_len, nullptr);
tr_sha1(buf, std::data(plain_text), std::size(plain_text), salt, (int)salt_len, nullptr);
tr_sha1_to_hex((char*)buf, buf);
return strncmp(ssha1 + brace_len, (char const*)buf, SHA_DIGEST_LENGTH * 2) == 0;
return strncmp(std::data(ssha1) + brace_len, (char const*)buf, SHA_DIGEST_LENGTH * 2) == 0;
}
/***

View File

@ -9,9 +9,11 @@
#ifndef TR_CRYPTO_UTILS_H
#define TR_CRYPTO_UTILS_H
#include <cinttypes>
#include <cstddef>
#include <optional>
#include <inttypes.h>
#include <stddef.h>
#include <string>
#include <string_view>
#include "transmission.h" /* SHA_DIGEST_LENGTH */
#include "tr-macros.h"
@ -143,12 +145,12 @@ bool tr_rand_buffer(void* buffer, size_t length);
/**
* @brief Generate a SSHA password from its plaintext source.
*/
char* tr_ssha1(char const* plain_text) TR_GNUC_MALLOC;
std::string tr_ssha1(std::string_view plain_text);
/**
* @brief Validate a test password against the a ssha1 password.
*/
bool tr_ssha1_matches(char const* ssha1, char const* plain_text);
bool tr_ssha1_matches(std::string_view ssha1, std::string_view plain_text);
/**
* @brief Translate a block of bytes into base64.

View File

@ -64,7 +64,7 @@ struct tr_rpc_server
int start_retry_counter;
tr_session* session;
char* username;
char* password;
std::string password;
std::string whitelistStr;
std::list<std::string> whitelist;
std::list<std::string> hostWhitelist;
@ -1012,23 +1012,21 @@ char const* tr_rpcGetUsername(tr_rpc_server const* server)
void tr_rpcSetPassword(tr_rpc_server* server, char const* password)
{
tr_free(server->password);
if (*password != '{')
{
server->password = tr_ssha1(password);
}
else
{
server->password = strdup(password);
server->password = password;
}
dbgmsg("setting our Password to [%s]", server->password);
dbgmsg("setting our Password to [%s]", server->password.c_str());
}
char const* tr_rpcGetPassword(tr_rpc_server const* server)
{
return server->password != nullptr ? server->password : "";
return server->password.c_str();
}
void tr_rpcSetPasswordEnabled(tr_rpc_server* server, bool isEnabled)
@ -1088,7 +1086,6 @@ static void closeServer(void* vserver)
tr_free(server->url);
tr_free(server->username);
tr_free(server->password);
delete server;
}

View File

@ -20,6 +20,8 @@
#include <string>
#include <unordered_set>
using namespace std::literals;
TEST(Crypto, torrentHash)
{
auto a = tr_crypto{};
@ -124,43 +126,41 @@ TEST(Crypto, ssha1)
{
struct LocalTest
{
char const* const plain_text;
char const* const ssha1;
std::string_view plain_text;
std::string_view ssha1;
};
auto const tests = std::array<LocalTest, 2>{
LocalTest{ "test", "{15ad0621b259a84d24dcd4e75b09004e98a3627bAMbyRHJy" },
{ "QNY)(*#$B)!_X$B !_B#($^!)*&$%CV!#)&$C!@$(P*)", "{10e2d7acbb104d970514a147cd16d51dfa40fb3c0OSwJtOL" },
};
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& test : tests)
for (auto const& [plain_text, ssha1] : Tests)
{
std::unordered_set<std::string> hashes;
auto hashes = std::unordered_set<std::string>{};
hashes.reserve(HashCount);
char* const phrase = tr_strdup(test.plain_text);
EXPECT_TRUE(tr_ssha1_matches(test.ssha1, phrase));
EXPECT_TRUE(tr_ssha1_matches_(test.ssha1, phrase));
EXPECT_TRUE(tr_ssha1_matches(ssha1, plain_text));
EXPECT_TRUE(tr_ssha1_matches_(ssha1, plain_text));
for (size_t j = 0; j < HashCount; ++j)
{
char* hash = (j % 2 == 0) ? tr_ssha1(phrase) : tr_ssha1_(phrase);
EXPECT_NE(nullptr, hash);
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, phrase));
EXPECT_TRUE(tr_ssha1_matches_(hash, phrase));
EXPECT_TRUE(tr_ssha1_matches(hash, plain_text));
EXPECT_TRUE(tr_ssha1_matches_(hash, plain_text));
hashes.insert(hash);
tr_free(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];
@ -168,11 +168,9 @@ TEST(Crypto, ssha1)
for (auto const& hash : hashes)
{
/* changed phrase doesn't match the hashes */
EXPECT_FALSE(tr_ssha1_matches(hash.c_str(), phrase));
EXPECT_FALSE(tr_ssha1_matches_(hash.c_str(), phrase));
EXPECT_FALSE(tr_ssha1_matches(hash, phrase));
EXPECT_FALSE(tr_ssha1_matches_(hash, phrase));
}
tr_free(phrase);
}
/* should work with different salt lengths as well */