2022-01-20 18:27:56 +00:00
|
|
|
// This file Copyright © 2007-2022 Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2014-12-04 11:27:38 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <array>
|
2022-01-13 02:13:58 +00:00
|
|
|
#include <cctype>
|
2022-01-08 12:46:25 +00:00
|
|
|
#include <cstring> // memmove(), memset()
|
|
|
|
#include <iterator>
|
|
|
|
#include <random>
|
2021-12-21 22:14:15 +00:00
|
|
|
#include <string>
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <string_view>
|
2014-12-04 11:27:38 +00:00
|
|
|
|
2021-09-12 03:47:29 +00:00
|
|
|
#include <arc4.h>
|
2021-09-12 17:41:49 +00:00
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
2015-01-01 21:16:36 +00:00
|
|
|
#include <b64/cdecode.h>
|
|
|
|
#include <b64/cencode.h>
|
2021-09-12 17:41:49 +00:00
|
|
|
}
|
2015-01-01 21:16:36 +00:00
|
|
|
|
2014-12-04 11:27:38 +00:00
|
|
|
#include "transmission.h"
|
|
|
|
#include "crypto-utils.h"
|
2017-06-08 07:24:12 +00:00
|
|
|
#include "tr-assert.h"
|
2014-12-04 11:27:38 +00:00
|
|
|
#include "utils.h"
|
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2014-12-04 11:27:38 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void tr_dh_align_key(uint8_t* key_buffer, size_t key_size, size_t buffer_size)
|
#4400, #5462: Move DH helpers to crypto-utils
On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing DH key exchange to crypto-utils.{c,h}. OpenSSL-related
functionality (DH context management) is moved to crypto-utils-openssl.c.
Since we know in advance that DH secret key management code will be the
same for most of backends, implement common functionality in separate
crypto-utils-fallback.c.
Add new tr_dh_ctx_t and tr_dh_secret_t types and functions to be
implemented by crypto backends:
* tr_dh_new - allocate DH context,
* tr_dh_free - free the context,
* tr_dh_make_key - generate private/public keypair,
* tr_dh_agree - perform DH key exchange and generate secret key,
* tr_dh_secret_derive - calculate secret key hash,
* tr_dh_secret_free - free the secret key,
* tr_dh_align_key - align some DH key in the buffer allocated for it.
Make DH secret key not accessible in plain form outside the crypto
backend. This allows for implementations where the key is managed by
the underlying library and is not even exposed to our backend.
2014-12-04 19:18:08 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(key_size <= buffer_size);
|
#4400, #5462: Move DH helpers to crypto-utils
On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing DH key exchange to crypto-utils.{c,h}. OpenSSL-related
functionality (DH context management) is moved to crypto-utils-openssl.c.
Since we know in advance that DH secret key management code will be the
same for most of backends, implement common functionality in separate
crypto-utils-fallback.c.
Add new tr_dh_ctx_t and tr_dh_secret_t types and functions to be
implemented by crypto backends:
* tr_dh_new - allocate DH context,
* tr_dh_free - free the context,
* tr_dh_make_key - generate private/public keypair,
* tr_dh_agree - perform DH key exchange and generate secret key,
* tr_dh_secret_derive - calculate secret key hash,
* tr_dh_secret_free - free the secret key,
* tr_dh_align_key - align some DH key in the buffer allocated for it.
Make DH secret key not accessible in plain form outside the crypto
backend. This allows for implementations where the key is managed by
the underlying library and is not even exposed to our backend.
2014-12-04 19:18:08 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* DH can generate key sizes that are smaller than the size of
|
|
|
|
key buffer with exponentially decreasing probability, in which case
|
|
|
|
the msb's of key buffer need to be zeroed appropriately. */
|
|
|
|
if (key_size < buffer_size)
|
#4400, #5462: Move DH helpers to crypto-utils
On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing DH key exchange to crypto-utils.{c,h}. OpenSSL-related
functionality (DH context management) is moved to crypto-utils-openssl.c.
Since we know in advance that DH secret key management code will be the
same for most of backends, implement common functionality in separate
crypto-utils-fallback.c.
Add new tr_dh_ctx_t and tr_dh_secret_t types and functions to be
implemented by crypto backends:
* tr_dh_new - allocate DH context,
* tr_dh_free - free the context,
* tr_dh_make_key - generate private/public keypair,
* tr_dh_agree - perform DH key exchange and generate secret key,
* tr_dh_secret_derive - calculate secret key hash,
* tr_dh_secret_free - free the secret key,
* tr_dh_align_key - align some DH key in the buffer allocated for it.
Make DH secret key not accessible in plain form outside the crypto
backend. This allows for implementations where the key is managed by
the underlying library and is not even exposed to our backend.
2014-12-04 19:18:08 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const offset = buffer_size - key_size;
|
2017-04-19 12:04:45 +00:00
|
|
|
memmove(key_buffer + offset, key_buffer, key_size);
|
|
|
|
memset(key_buffer, 0, offset);
|
#4400, #5462: Move DH helpers to crypto-utils
On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing DH key exchange to crypto-utils.{c,h}. OpenSSL-related
functionality (DH context management) is moved to crypto-utils-openssl.c.
Since we know in advance that DH secret key management code will be the
same for most of backends, implement common functionality in separate
crypto-utils-fallback.c.
Add new tr_dh_ctx_t and tr_dh_secret_t types and functions to be
implemented by crypto backends:
* tr_dh_new - allocate DH context,
* tr_dh_free - free the context,
* tr_dh_make_key - generate private/public keypair,
* tr_dh_agree - perform DH key exchange and generate secret key,
* tr_dh_secret_derive - calculate secret key hash,
* tr_dh_secret_free - free the secret key,
* tr_dh_align_key - align some DH key in the buffer allocated for it.
Make DH secret key not accessible in plain form outside the crypto
backend. This allows for implementations where the key is managed by
the underlying library and is not even exposed to our backend.
2014-12-04 19:18:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
int tr_rand_int(int upper_bound)
|
2014-12-04 11:27:38 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(upper_bound > 0);
|
2014-12-04 11:27:38 +00:00
|
|
|
|
2021-12-17 05:47:51 +00:00
|
|
|
if (unsigned int noise = 0; tr_rand_buffer(&noise, sizeof(noise)))
|
2014-12-04 11:27:38 +00:00
|
|
|
{
|
2020-05-12 01:34:03 +00:00
|
|
|
return noise % upper_bound;
|
2014-12-04 11:27:38 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* fall back to a weaker implementation... */
|
|
|
|
return tr_rand_int_weak(upper_bound);
|
2014-12-04 11:27:38 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
int tr_rand_int_weak(int upper_bound)
|
2014-12-04 11:27:38 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(upper_bound > 0);
|
2014-12-04 11:27:38 +00:00
|
|
|
|
2021-11-11 19:03:33 +00:00
|
|
|
thread_local auto random_engine = std::mt19937{ std::random_device{}() };
|
|
|
|
using distribution_type = std::uniform_int_distribution<>;
|
|
|
|
thread_local distribution_type distribution;
|
2017-06-13 02:24:09 +00:00
|
|
|
|
2021-11-11 19:03:33 +00:00
|
|
|
// Upper bound is inclusive in std::uniform_int_distribution.
|
|
|
|
return distribution(random_engine, distribution_type::param_type{ 0, upper_bound - 1 });
|
2014-12-04 11:27:38 +00:00
|
|
|
}
|
2014-12-04 19:58:34 +00:00
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
namespace
|
2014-12-04 20:45:18 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
auto constexpr DigestStringSize = TR_SHA1_DIGEST_STRLEN;
|
|
|
|
auto constexpr SaltedPrefix = "{"sv;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
std::string tr_salt(std::string_view plaintext, std::string_view salt)
|
|
|
|
{
|
|
|
|
static_assert(DigestStringSize == 40);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
// build a sha1 digest of the original content and the salt
|
|
|
|
auto const digest = tr_sha1(plaintext, salt);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
// convert it to a string. string holds three parts:
|
|
|
|
// DigestPrefix, stringified digest of plaintext + salt, and the salt.
|
|
|
|
return tr_strvJoin(SaltedPrefix, tr_sha1_to_string(*digest), salt);
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
} // namespace
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
std::string tr_ssha1(std::string_view plaintext)
|
|
|
|
{
|
|
|
|
// build an array of random Salter chars
|
|
|
|
auto constexpr Salter = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./"sv;
|
|
|
|
static_assert(std::size(Salter) == 64);
|
|
|
|
auto constexpr SaltSize = size_t{ 8 };
|
|
|
|
auto salt = std::array<char, SaltSize>{};
|
|
|
|
tr_rand_buffer(std::data(salt), std::size(salt));
|
|
|
|
std::transform(
|
|
|
|
std::begin(salt),
|
|
|
|
std::end(salt),
|
|
|
|
std::begin(salt),
|
|
|
|
[&Salter](auto ch) { return Salter[ch % std::size(Salter)]; });
|
|
|
|
|
|
|
|
return tr_salt(plaintext, std::string_view{ std::data(salt), std::size(salt) });
|
2014-12-04 20:45:18 +00:00
|
|
|
}
|
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
bool tr_ssha1_test(std::string_view text)
|
2014-12-04 20:45:18 +00:00
|
|
|
{
|
2021-12-21 22:14:15 +00:00
|
|
|
return tr_strvStartsWith(text, SaltedPrefix) && std::size(text) >= std::size(SaltedPrefix) + DigestStringSize;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
bool tr_ssha1_matches(std::string_view ssha1, std::string_view plaintext)
|
|
|
|
{
|
|
|
|
if (!tr_ssha1_test(ssha1))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2014-12-04 20:45:18 +00:00
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
auto const salt = ssha1.substr(std::size(SaltedPrefix) + DigestStringSize);
|
|
|
|
return tr_salt(plaintext, salt) == ssha1;
|
2014-12-04 20:45:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2022-01-08 12:46:25 +00:00
|
|
|
static size_t base64_alloc_size(std::string_view input)
|
2014-12-04 19:58:34 +00:00
|
|
|
{
|
2022-01-08 12:46:25 +00:00
|
|
|
size_t ret_length = 4 * ((std::size(input) + 2) / 3);
|
2015-01-03 21:35:20 +00:00
|
|
|
#ifdef USE_SYSTEM_B64
|
2022-01-08 12:46:25 +00:00
|
|
|
// Additional space is needed for newlines if we're using unpatched libb64
|
|
|
|
ret_length += ret_length / 72 + 1;
|
2015-01-03 21:35:20 +00:00
|
|
|
#endif
|
2022-01-08 12:46:25 +00:00
|
|
|
return ret_length * 8;
|
2014-12-04 19:58:34 +00:00
|
|
|
}
|
|
|
|
|
2022-01-08 12:46:25 +00:00
|
|
|
std::string tr_base64_encode(std::string_view input)
|
2014-12-04 19:58:34 +00:00
|
|
|
{
|
2022-01-08 12:46:25 +00:00
|
|
|
auto buf = std::vector<char>(base64_alloc_size(input));
|
|
|
|
auto state = base64_encodestate{};
|
|
|
|
base64_init_encodestate(&state);
|
|
|
|
size_t len = base64_encode_block(std::data(input), std::size(input), std::data(buf), &state);
|
|
|
|
len += base64_encode_blockend(std::data(buf) + len, &state);
|
|
|
|
auto str = std::string{};
|
|
|
|
std::copy_if(
|
|
|
|
std::data(buf),
|
|
|
|
std::data(buf) + len,
|
|
|
|
std::back_inserter(str),
|
|
|
|
[](auto ch) { return !tr_strvContains("\r\n"sv, ch); });
|
|
|
|
return str;
|
2014-12-04 19:58:34 +00:00
|
|
|
}
|
2021-11-10 00:13:47 +00:00
|
|
|
|
2022-01-08 12:46:25 +00:00
|
|
|
std::string tr_base64_decode(std::string_view input)
|
2021-11-15 03:54:48 +00:00
|
|
|
{
|
2022-01-08 12:46:25 +00:00
|
|
|
auto buf = std::vector<char>(std::size(input) + 8);
|
|
|
|
auto state = base64_decodestate{};
|
|
|
|
base64_init_decodestate(&state);
|
|
|
|
size_t const len = base64_decode_block(std::data(input), std::size(input), std::data(buf), &state);
|
|
|
|
return std::string{ std::data(buf), len };
|
2021-11-15 03:54:48 +00:00
|
|
|
}
|
|
|
|
|
2021-11-10 00:13:47 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
|
|
|
static void tr_binary_to_hex(void const* vinput, void* voutput, size_t byte_length)
|
|
|
|
{
|
2021-12-21 22:14:15 +00:00
|
|
|
static char constexpr Hex[] = "0123456789abcdef";
|
2021-11-10 00:13:47 +00:00
|
|
|
|
|
|
|
auto const* input = static_cast<uint8_t const*>(vinput);
|
|
|
|
auto* output = static_cast<char*>(voutput);
|
|
|
|
|
|
|
|
/* go from back to front to allow for in-place conversion */
|
|
|
|
input += byte_length;
|
|
|
|
output += byte_length * 2;
|
|
|
|
|
|
|
|
*output = '\0';
|
|
|
|
|
|
|
|
while (byte_length-- > 0)
|
|
|
|
{
|
|
|
|
unsigned int const val = *(--input);
|
2021-12-21 22:14:15 +00:00
|
|
|
*(--output) = Hex[val & 0xf];
|
|
|
|
*(--output) = Hex[val >> 4];
|
2021-11-10 00:13:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
std::string tr_sha1_to_string(tr_sha1_digest_t const& digest)
|
|
|
|
{
|
|
|
|
auto str = std::string(std::size(digest) * 2, '?');
|
|
|
|
tr_binary_to_hex(std::data(digest), std::data(str), std::size(digest));
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* tr_sha1_to_string(tr_sha1_digest_t const& digest, char* strbuf)
|
2021-11-10 00:13:47 +00:00
|
|
|
{
|
2021-12-21 22:14:15 +00:00
|
|
|
tr_binary_to_hex(std::data(digest), strbuf, std::size(digest));
|
|
|
|
return strbuf + (std::size(digest) * 2);
|
2021-11-10 00:13:47 +00:00
|
|
|
}
|
|
|
|
|
2021-12-21 22:14:15 +00:00
|
|
|
static void tr_hex_to_binary(char const* input, void* voutput, size_t byte_length)
|
2021-11-10 00:13:47 +00:00
|
|
|
{
|
2021-12-21 22:14:15 +00:00
|
|
|
static char constexpr Hex[] = "0123456789abcdef";
|
2021-11-10 00:13:47 +00:00
|
|
|
|
|
|
|
auto* output = static_cast<uint8_t*>(voutput);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < byte_length; ++i)
|
|
|
|
{
|
2021-12-21 22:14:15 +00:00
|
|
|
int const hi = strchr(Hex, tolower(*input++)) - Hex;
|
|
|
|
int const lo = strchr(Hex, tolower(*input++)) - Hex;
|
2021-11-10 00:13:47 +00:00
|
|
|
*output++ = (uint8_t)((hi << 4) | lo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-13 04:16:55 +00:00
|
|
|
std::optional<tr_sha1_digest_t> tr_sha1_from_string(std::string_view hex)
|
2021-11-10 00:13:47 +00:00
|
|
|
{
|
2022-02-13 04:16:55 +00:00
|
|
|
if (std::size(hex) != TR_SHA1_DIGEST_STRLEN)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!std::all_of(std::begin(hex), std::end(hex), [](unsigned char ch) { return isxdigit(ch); }))
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
2021-12-21 22:14:15 +00:00
|
|
|
|
|
|
|
auto digest = tr_sha1_digest_t{};
|
|
|
|
tr_hex_to_binary(std::data(hex), std::data(digest), std::size(digest));
|
|
|
|
return digest;
|
2021-11-10 00:13:47 +00:00
|
|
|
}
|