Reformat code

This commit is contained in:
Anatolii Cherednichenko 2022-08-30 02:11:28 +03:00
parent 3ddb370718
commit 55534ea002
140 changed files with 46068 additions and 48277 deletions

View file

@ -14,10 +14,8 @@
// Afrikaans localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace afrikaans // language namespace
{
// language name in lowercase
@ -71,9 +69,9 @@ namespace afrikaans // language namespace
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -14,10 +14,8 @@
// Armenian localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace armenian // language namespace
{
// language name in lowercase
@ -205,9 +203,9 @@ namespace armenian // language namespace
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -15,10 +15,8 @@
// Simplified Chinese localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace chinese // language namespace
{
// language name in lowercase
@ -207,9 +205,9 @@ namespace chinese // language namespace
{"", {""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -15,10 +15,8 @@
// English localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace english // language namespace
{
// language name in lowercase
@ -40,9 +38,9 @@ namespace english // language namespace
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -14,10 +14,8 @@
// French localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace french // language namespace
{
// language name in lowercase
@ -201,9 +199,9 @@ namespace french // language namespace
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -14,10 +14,8 @@
// German localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace german // language namespace
{
// language name in lowercase
@ -206,9 +204,9 @@ namespace german // language namespace
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -11,12 +11,9 @@
#include "ClientContext.h"
namespace i2p
{
namespace i18n
{
inline void SetLanguage(const std::string &lang)
{
namespace i2p {
namespace i18n {
inline void SetLanguage(const std::string &lang) {
const auto it = i2p::i18n::languages.find(lang);
if (it == i2p::i18n::languages.end()) // fallback
i2p::client::context.SetLanguage(i2p::i18n::english::GetLocale());
@ -24,21 +21,18 @@ namespace i18n
i2p::client::context.SetLanguage(it->second.LocaleFunc());
}
inline std::string translate (const std::string& arg)
{
inline std::string translate(const std::string &arg) {
return i2p::client::context.GetLanguage()->GetString(arg);
}
inline std::string translate (const std::string& arg, const std::string& arg2, const int& n)
{
inline std::string translate(const std::string &arg, const std::string &arg2, const int &n) {
return i2p::client::context.GetLanguage()->GetPlural(arg, arg2, n);
}
} // i18n
} // i2p
template<typename... TArgs>
std::string tr (TArgs&&... args)
{
std::string tr(TArgs &&... args) {
return i2p::i18n::translate(std::forward<TArgs>(args)...);
}

View file

@ -9,12 +9,9 @@
#ifndef __I18N_LANGS_H__
#define __I18N_LANGS_H__
namespace i2p
{
namespace i18n
{
class Locale
{
namespace i2p {
namespace i18n {
class Locale {
public:
Locale(
const std::string &language,
@ -24,33 +21,25 @@ namespace i18n
) : m_Language(language), m_Strings(strings), m_Plurals(plurals), m_Formula(formula) {};
// Get activated language name for webconsole
std::string GetLanguage() const
{
std::string GetLanguage() const {
return m_Language;
}
std::string GetString (const std::string& arg) const
{
std::string GetString(const std::string &arg) const {
const auto it = m_Strings.find(arg);
if (it == m_Strings.end())
{
if (it == m_Strings.end()) {
return arg;
}
else
{
} else {
return it->second;
}
}
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
{
std::string GetPlural(const std::string &arg, const std::string &arg2, const int &n) const {
const auto it = m_Plurals.find(arg2);
if (it == m_Plurals.end()) // not found, fallback to english
{
return n == 1 ? arg : arg2;
}
else
{
} else {
int form = m_Formula(n);
return it->second[form];
}
@ -63,8 +52,7 @@ namespace i18n
std::function<int(int)> m_Formula;
};
struct langData
{
struct langData {
std::string LocaleName; // localized name
std::string ShortCode; // short language code, like "en"
std::function<std::shared_ptr<const i2p::i18n::Locale>(void)> LocaleFunc;

View file

@ -14,10 +14,8 @@
// Russian localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace russian // language namespace
{
// language name in lowercase
@ -26,7 +24,8 @@ namespace russian // language namespace
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural(int n) {
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)
? 1 : 2;
}
static std::map <std::string, std::string> strings
@ -205,9 +204,9 @@ namespace russian // language namespace
{"", {"", "", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -14,10 +14,8 @@
// Turkmen localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace turkmen // language namespace
{
// language name in lowercase
@ -205,9 +203,9 @@ namespace turkmen // language namespace
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -14,10 +14,8 @@
// Ukrainian localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace ukrainian // language namespace
{
// language name in lowercase
@ -26,7 +24,8 @@ namespace ukrainian // language namespace
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural(int n) {
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)
? 1 : 2;
}
static std::map <std::string, std::string> strings
@ -205,9 +204,9 @@ namespace ukrainian // language namespace
{"", {"", "", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -14,10 +14,8 @@
// Ukrainian localization file
namespace i2p
{
namespace i18n
{
namespace i2p {
namespace i18n {
namespace uzbek // language namespace
{
// language name in lowercase
@ -205,9 +203,9 @@ namespace uzbek // language namespace
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
[](int n) -> int { return plural(n); });
}
} // language

View file

@ -11,10 +11,8 @@
#include "Base.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
static const char T32[32] =
{
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
@ -23,8 +21,7 @@ namespace data
'y', 'z', '2', '3', '4', '5', '6', '7',
};
const char * GetBase32SubstitutionTable ()
{
const char *GetBase32SubstitutionTable() {
return T32;
}
@ -50,8 +47,7 @@ namespace data
'4', '5', '6', '7', '8', '9', '-', '~'
};
const char * GetBase64SubstitutionTable ()
{
const char *GetBase64SubstitutionTable() {
return T64;
}
@ -82,8 +78,7 @@ namespace data
size_t InCount, /* Number of bytes in the input buffer */
char *OutBuffer, /* output buffer */
size_t len /* length of output buffer */
)
{
) {
unsigned char *ps;
unsigned char *pd;
unsigned char acc_1;
@ -104,8 +99,7 @@ namespace data
if (outCount > len) return 0;
pd = (unsigned char *) OutBuffer;
for ( i = 0; i < n; i++ )
{
for (i = 0; i < n; i++) {
acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x30;
acc_1 >>= 2; /* base64 digit #1 */
@ -121,8 +115,7 @@ namespace data
acc_2 &= 0x3f; /* base64 digit #4 */
*pd++ = T64[acc_2];
}
if ( m == 1 )
{
if (m == 1) {
acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */
acc_1 >>= 2; /* base64 digit #1 */
@ -131,9 +124,7 @@ namespace data
*pd++ = P64;
*pd++ = P64;
}
else if ( m == 2 )
{
} else if (m == 2) {
acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f;
acc_1 >>= 2; /* base64 digit #1 */
@ -165,8 +156,7 @@ namespace data
size_t InCount, /* Number of input bytes */
uint8_t *OutBuffer, /* output buffer length */
size_t len /* length of output buffer */
)
{
) {
unsigned char *ps;
unsigned char *pd;
unsigned char acc_1;
@ -197,8 +187,7 @@ namespace data
pd = OutBuffer;
auto endOfOutBuffer = OutBuffer + outCount;
for ( i = 0; i < n; i++ )
{
for (i = 0; i < n; i++) {
acc_1 = iT64[*ps++];
acc_2 = iT64[*ps++];
acc_1 <<= 2;
@ -222,8 +211,7 @@ namespace data
return outCount;
}
size_t Base64EncodingBufferSize (const size_t input_size)
{
size_t Base64EncodingBufferSize(const size_t input_size) {
auto d = div(input_size, 3);
if (d.rem)
d.quot++;
@ -231,8 +219,7 @@ namespace data
return 4 * d.quot;
}
std::string ToBase64Standard (const std::string& in)
{
std::string ToBase64Standard(const std::string &in) {
auto len = Base64EncodingBufferSize(in.length());
char *str = new char[len + 1];
auto l = ByteStreamToBase64((const uint8_t *) in.c_str(), in.length(), str, len);
@ -258,8 +245,7 @@ namespace data
*
*/
static void iT64Build()
{
static void iT64Build() {
int i;
isFirstTime = 0;
for (i = 0; i < 256; i++) iT64[i] = -1;
@ -267,12 +253,10 @@ namespace data
iT64[(int) P64] = 0;
}
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen)
{
size_t Base32ToByteStream(const char *inBuf, size_t len, uint8_t *outBuf, size_t outLen) {
int tmp = 0, bits = 0;
size_t ret = 0;
for (size_t i = 0; i < len; i++)
{
for (size_t i = 0; i < len; i++) {
char ch = inBuf[i];
if (ch >= '2' && ch <= '7') // digit
ch = (ch - '2') + 26; // 26 means a-z
@ -283,8 +267,7 @@ namespace data
tmp |= ch;
bits += 5;
if (bits >= 8)
{
if (bits >= 8) {
if (ret >= outLen) return ret;
outBuf[ret] = tmp >> (bits - 8);
bits -= 8;
@ -295,22 +278,17 @@ namespace data
return ret;
}
size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen)
{
size_t ByteStreamToBase32(const uint8_t *inBuf, size_t len, char *outBuf, size_t outLen) {
size_t ret = 0, pos = 1;
int bits = 8, tmp = inBuf[0];
while (ret < outLen && (bits > 0 || pos < len))
{
if (bits < 5)
{
if (pos < len)
{
while (ret < outLen && (bits > 0 || pos < len)) {
if (bits < 5) {
if (pos < len) {
tmp <<= 8;
tmp |= inBuf[pos] & 0xFF;
pos++;
bits += 8;
}
else // last byte
} else // last byte
{
tmp <<= (5 - bits);
bits = 5;

View file

@ -16,11 +16,15 @@
namespace i2p {
namespace data {
size_t ByteStreamToBase64(const uint8_t *InBuffer, size_t InCount, char *OutBuffer, size_t len);
size_t Base64ToByteStream(const char *InBuffer, size_t InCount, uint8_t *OutBuffer, size_t len);
const char *GetBase32SubstitutionTable();
const char *GetBase64SubstitutionTable();
size_t Base32ToByteStream(const char *inBuf, size_t len, uint8_t *outBuf, size_t outLen);
size_t ByteStreamToBase32(const uint8_t *InBuf, size_t len, char *outBuf, size_t outLen);
/**

View file

@ -20,12 +20,9 @@
#include "Signature.h"
#include "Blinding.h"
namespace i2p
{
namespace data
{
static EC_POINT * BlindPublicKeyECDSA (const EC_GROUP * group, const EC_POINT * pub, const uint8_t * seed)
{
namespace i2p {
namespace data {
static EC_POINT *BlindPublicKeyECDSA(const EC_GROUP *group, const EC_POINT *pub, const uint8_t *seed) {
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
BIGNUM *q = BN_CTX_get(ctx);
@ -43,8 +40,8 @@ namespace data
return p;
}
static void BlindPrivateKeyECDSA (const EC_GROUP * group, const BIGNUM * priv, const uint8_t * seed, BIGNUM * blindedPriv)
{
static void
BlindPrivateKeyECDSA(const EC_GROUP *group, const BIGNUM *priv, const uint8_t *seed, BIGNUM *blindedPriv) {
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
BIGNUM *q = BN_CTX_get(ctx);
@ -60,8 +57,9 @@ namespace data
BN_CTX_free(ctx);
}
static void BlindEncodedPublicKeyECDSA (size_t publicKeyLen, const EC_GROUP * group, const uint8_t * pub, const uint8_t * seed, uint8_t * blindedPub)
{
static void
BlindEncodedPublicKeyECDSA(size_t publicKeyLen, const EC_GROUP *group, const uint8_t *pub, const uint8_t *seed,
uint8_t *blindedPub) {
BIGNUM *x = BN_bin2bn(pub, publicKeyLen / 2, NULL);
BIGNUM *y = BN_bin2bn(pub + publicKeyLen / 2, publicKeyLen / 2, NULL);
EC_POINT *p = EC_POINT_new(group);
@ -72,11 +70,12 @@ namespace data
EC_POINT_free(p1);
i2p::crypto::bn2buf(x, blindedPub, publicKeyLen / 2);
i2p::crypto::bn2buf(y, blindedPub + publicKeyLen / 2, publicKeyLen / 2);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
static void BlindEncodedPrivateKeyECDSA (size_t publicKeyLen, const EC_GROUP * group, const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub)
{
static void BlindEncodedPrivateKeyECDSA(size_t publicKeyLen, const EC_GROUP *group, const uint8_t *priv,
const uint8_t *seed, uint8_t *blindedPriv, uint8_t *blindedPub) {
BIGNUM *a = BN_bin2bn(priv, publicKeyLen / 2, NULL);
BIGNUM *a1 = BN_new();
BlindPrivateKeyECDSA(group, a, seed, a1);
@ -92,31 +91,29 @@ namespace data
EC_POINT_free(p);
i2p::crypto::bn2buf(x, blindedPub, publicKeyLen / 2);
i2p::crypto::bn2buf(y, blindedPub + publicKeyLen / 2, publicKeyLen / 2);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
template<typename Fn, typename...Args>
static size_t BlindECDSA (i2p::data::SigningKeyType sigType, const uint8_t * key, const uint8_t * seed, Fn blind, Args&&...args)
static size_t
BlindECDSA(i2p::data::SigningKeyType sigType, const uint8_t *key, const uint8_t *seed, Fn blind, Args &&...args)
// blind is BlindEncodedPublicKeyECDSA or BlindEncodedPrivateKeyECDSA
{
size_t publicKeyLength = 0;
EC_GROUP *group = nullptr;
switch (sigType)
{
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{
switch (sigType) {
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: {
publicKeyLength = i2p::crypto::ECDSAP256_KEY_LENGTH;
group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
break;
}
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: {
publicKeyLength = i2p::crypto::ECDSAP384_KEY_LENGTH;
group = EC_GROUP_new_by_curve_name(NID_secp384r1);
break;
}
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
{
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: {
publicKeyLength = i2p::crypto::ECDSAP521_KEY_LENGTH;
group = EC_GROUP_new_by_curve_name(NID_secp521r1);
break;
@ -124,8 +121,7 @@ namespace data
default:
LogPrint(eLogError, "Blinding: Signature type ", (int) sigType, " is not ECDSA");
}
if (group)
{
if (group) {
blind(publicKeyLength, group, key, seed, std::forward<Args>(args)...);
EC_GROUP_free(group);
}
@ -139,8 +135,7 @@ namespace data
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
BlindedPublicKey::BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth) :
m_IsClientAuth (clientAuth)
{
m_IsClientAuth(clientAuth) {
if (!identity) return;
auto len = identity->GetSigningPublicKeyLen();
m_PublicKey.resize(len);
@ -157,48 +152,49 @@ namespace data
{
uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream(b33.c_str(), b33.length(), addr, 40);
if (l < 32)
{
if (l < 32) {
LogPrint(eLogError, "Blinding: Malformed b33 ", b33);
return;
}
uint32_t checksum = crc32(0, addr + 3, l - 3);
// checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
addr[0] ^= checksum;
addr[1] ^= (checksum >> 8);
addr[2] ^= (checksum >> 16);
uint8_t flags = addr[0];
size_t offset = 1;
if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures
{
m_SigType = bufbe16toh (addr + offset); offset += 2;
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2;
}
else // one byte sig
m_SigType = bufbe16toh(addr + offset);
offset += 2;
m_BlindedSigType = bufbe16toh(addr + offset);
offset += 2;
} else // one byte sig
{
m_SigType = addr[offset]; offset++;
m_BlindedSigType = addr[offset]; offset++;
m_SigType = addr[offset];
offset++;
m_BlindedSigType = addr[offset];
offset++;
}
m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG;
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier(i2p::data::IdentityEx::CreateVerifier(m_SigType));
if (blindedVerifier)
{
if (blindedVerifier) {
auto len = blindedVerifier->GetPublicKeyLen();
if (offset + len <= l)
{
if (offset + len <= l) {
m_PublicKey.resize(len);
memcpy(m_PublicKey.data(), addr + offset, len);
}
else
LogPrint (eLogError, "Blinding: Public key in b33 address is too short for signature type ", (int)m_SigType);
}
else
} else
LogPrint(eLogError, "Blinding: Public key in b33 address is too short for signature type ",
(int) m_SigType);
} else
LogPrint(eLogError, "Blinding: Unknown signature type ", (int) m_SigType, " in b33");
}
std::string BlindedPublicKey::ToB33 () const
{
std::string BlindedPublicKey::ToB33() const {
if (m_PublicKey.size() > 32) return ""; // assume 25519
uint8_t addr[35]; char str[60]; // TODO: define actual length
uint8_t addr[35];
char str[60]; // TODO: define actual length
uint8_t flags = 0;
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
addr[0] = flags; // flags
@ -207,51 +203,54 @@ namespace data
memcpy(addr + 3, m_PublicKey.data(), m_PublicKey.size());
uint32_t checksum = crc32(0, addr + 3, m_PublicKey.size());
// checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
addr[0] ^= checksum;
addr[1] ^= (checksum >> 8);
addr[2] ^= (checksum >> 16);
auto l = ByteStreamToBase32(addr, m_PublicKey.size() + 3, str, 60);
return std::string(str, str + l);
}
void BlindedPublicKey::GetCredential (uint8_t * credential) const
{
void BlindedPublicKey::GetCredential(uint8_t *credential) const {
// A = destination's signing public key
// stA = signature type of A, 2 bytes big endian
uint16_t stA = htobe16(GetSigType());
// stA1 = signature type of blinded A, 2 bytes big endian
uint16_t stA1 = htobe16(GetBlindedSigType());
// credential = H("credential", A || stA || stA1)
H ("credential", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, credential);
H("credential", {{GetPublicKey(), GetPublicKeyLen()},
{(const uint8_t *) &stA, 2},
{(const uint8_t *) &stA1, 2}}, credential);
}
void BlindedPublicKey::GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const
{
void BlindedPublicKey::GetSubcredential(const uint8_t *blinded, size_t len, uint8_t *subcredential) const {
uint8_t credential[32];
GetCredential(credential);
// subcredential = H("subcredential", credential || blindedPublicKey)
H ("subcredential", { {credential, 32}, {blinded, len} }, subcredential);
H("subcredential", {{credential, 32},
{blinded, len}}, subcredential);
}
void BlindedPublicKey::GenerateAlpha (const char * date, uint8_t * seed) const
{
void BlindedPublicKey::GenerateAlpha(const char *date, uint8_t *seed) const {
uint16_t stA = htobe16(GetSigType()), stA1 = htobe16(GetBlindedSigType());
uint8_t salt[32];
//seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64)
H ("I2PGenerateAlpha", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt);
H("I2PGenerateAlpha", {{GetPublicKey(), GetPublicKeyLen()},
{(const uint8_t *) &stA, 2},
{(const uint8_t *) &stA1, 2}}, salt);
i2p::crypto::HKDF(salt, (const uint8_t *) date, 8, "i2pblinding1", seed);
}
size_t BlindedPublicKey::GetBlindedKey (const char * date, uint8_t * blindedKey) const
{
size_t BlindedPublicKey::GetBlindedKey(const char *date, uint8_t *blindedKey) const {
uint8_t seed[64];
GenerateAlpha(date, seed);
size_t publicKeyLength = 0;
switch (m_SigType)
{
switch (m_SigType) {
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
publicKeyLength = BlindECDSA (m_SigType, GetPublicKey (), seed, BlindEncodedPublicKeyECDSA, blindedKey);
publicKeyLength = BlindECDSA(m_SigType, GetPublicKey(), seed, BlindEncodedPublicKeyECDSA,
blindedKey);
break;
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
@ -264,24 +263,23 @@ namespace data
return publicKeyLength;
}
size_t BlindedPublicKey::BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const
{
size_t BlindedPublicKey::BlindPrivateKey(const uint8_t *priv, const char *date, uint8_t *blindedPriv,
uint8_t *blindedPub) const {
uint8_t seed[64];
GenerateAlpha(date, seed);
size_t publicKeyLength = 0;
switch (m_SigType)
{
switch (m_SigType) {
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
publicKeyLength = BlindECDSA (m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv, blindedPub);
publicKeyLength = BlindECDSA(m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv,
blindedPub);
break;
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
i2p::crypto::GetEd25519()->BlindPrivateKey(priv, seed, blindedPriv, blindedPub);
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break;
case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
{
case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: {
uint8_t exp[64];
i2p::crypto::Ed25519::ExpandPrivateKey(priv, exp);
i2p::crypto::GetEd25519()->BlindPrivateKey(exp, seed, blindedPriv, blindedPub);
@ -294,8 +292,8 @@ namespace data
return publicKeyLength;
}
void BlindedPublicKey::H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const
{
void BlindedPublicKey::H(const std::string &p, const std::vector<std::pair<const uint8_t *, size_t> > &bufs,
uint8_t *hash) const {
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, p.c_str(), p.length());
@ -304,29 +302,25 @@ namespace data
SHA256_Final(hash, &ctx);
}
i2p::data::IdentHash BlindedPublicKey::GetStoreHash (const char * date) const
{
i2p::data::IdentHash BlindedPublicKey::GetStoreHash(const char *date) const {
i2p::data::IdentHash hash;
uint8_t blinded[128];
size_t publicKeyLength = 0;
if (date)
publicKeyLength = GetBlindedKey(date, blinded);
else
{
else {
char currentDate[9];
i2p::util::GetCurrentDate(currentDate);
publicKeyLength = GetBlindedKey(currentDate, blinded);
}
if (publicKeyLength)
{
if (publicKeyLength) {
auto stA1 = htobe16(m_BlindedSigType);
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, (const uint8_t *) &stA1, 2);
SHA256_Update(&ctx, blinded, publicKeyLength);
SHA256_Final((uint8_t *) hash, &ctx);
}
else
} else
LogPrint(eLogError, "Blinding: Blinded key type ", (int) m_BlindedSigType, " is not supported");
return hash;
}

View file

@ -14,34 +14,41 @@
#include <vector>
#include "Identity.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
class BlindedPublicKey // for encrypted LS2
{
public:
BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
BlindedPublicKey(const std::string &b33); // from b33 without .b32.i2p
std::string ToB33() const;
const uint8_t *GetPublicKey() const { return m_PublicKey.data(); };
size_t GetPublicKeyLen() const { return m_PublicKey.size(); };
SigningKeyType GetSigType() const { return m_SigType; };
SigningKeyType GetBlindedSigType() const { return m_BlindedSigType; };
bool IsValid() const { return GetSigType(); }; // signature type 0 means invalid
void GetSubcredential(const uint8_t *blinded, size_t len, uint8_t *subcredential) const; // 32 bytes
size_t GetBlindedKey (const char * date, uint8_t * blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length
size_t BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const; // date is 8 chars "YYYYMMDD", return public key length
i2p::data::IdentHash GetStoreHash (const char * date = nullptr) const; // date is 8 chars "YYYYMMDD", use current if null
size_t GetBlindedKey(const char *date,
uint8_t *blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length
size_t BlindPrivateKey(const uint8_t *priv, const char *date, uint8_t *blindedPriv,
uint8_t *blindedPub) const; // date is 8 chars "YYYYMMDD", return public key length
i2p::data::IdentHash
GetStoreHash(const char *date = nullptr) const; // date is 8 chars "YYYYMMDD", use current if null
private:
void GetCredential(uint8_t *credential) const; // 32 bytes
void GenerateAlpha(const char *date, uint8_t *seed) const; // 64 bytes, date is 8 chars "YYYYMMDD"
void H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const;
void
H(const std::string &p, const std::vector<std::pair<const uint8_t *, size_t> > &bufs, uint8_t *hash) const;
private:

View file

@ -11,31 +11,25 @@
#include <array>
#include <openssl/sha.h>
namespace i2p
{
namespace util
{
namespace i2p {
namespace util {
/** @brief decaying bloom filter implementation */
class DecayingBloomFilter : public IBloomFilter
{
class DecayingBloomFilter : public IBloomFilter {
public:
DecayingBloomFilter(const std::size_t size)
{
DecayingBloomFilter(const std::size_t size) {
m_Size = size;
m_Data = new uint8_t[size];
}
/** @brief implements IBloomFilter::~IBloomFilter */
~DecayingBloomFilter()
{
~DecayingBloomFilter() {
delete[] m_Data;
}
/** @brief implements IBloomFilter::Add */
bool Add(const uint8_t * data, std::size_t len)
{
bool Add(const uint8_t *data, std::size_t len) {
std::size_t idx;
uint8_t mask;
Get(data, len, idx, mask);
@ -45,16 +39,14 @@ namespace util
}
/** @brief implements IBloomFilter::Decay */
void Decay()
{
void Decay() {
// reset bloom filter buffer
memset(m_Data, 0, m_Size);
}
private:
/** @brief get bit index for for data */
void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm)
{
void Get(const uint8_t *data, std::size_t len, std::size_t &idx, uint8_t &bm) {
bm = 1;
uint8_t digest[32];
// TODO: use blake2 because it's faster
@ -69,8 +61,7 @@ namespace util
};
BloomFilterPtr BloomFilter(std::size_t capacity)
{
BloomFilterPtr BloomFilter(std::size_t capacity) {
return std::make_shared<DecayingBloomFilter>(capacity);
}
}

View file

@ -8,22 +8,22 @@
#ifndef BLOOM_FILTER_H_
#define BLOOM_FILTER_H_
#include <memory>
#include <cstdint>
namespace i2p
{
namespace util
{
namespace i2p {
namespace util {
/** @brief interface for bloom filter */
struct IBloomFilter
{
struct IBloomFilter {
/** @brief destructor */
virtual ~IBloomFilter() {};
/** @brief add entry to bloom filter, return false if filter hit otherwise return true */
virtual bool Add(const uint8_t *data, std::size_t len) = 0;
/** @brief optionally decay old entries */
virtual void Decay() = 0;
};

View file

@ -7,9 +7,11 @@
*/
#include "CPU.h"
#if defined(__x86_64__) || defined(__i386__)
#include <cpuid.h>
#endif
#include "Log.h"
#ifndef bit_AES
@ -20,15 +22,12 @@
#endif
namespace i2p
{
namespace cpu
{
namespace i2p {
namespace cpu {
bool aesni = false;
bool avx = false;
void Detect(bool AesSwitch, bool AvxSwitch, bool force)
{
void Detect(bool AesSwitch, bool AvxSwitch, bool force) {
#if defined(__x86_64__) || defined(__i386__)
int info[4];
__cpuid(0, info[0], info[1], info[2], info[3]);

View file

@ -9,10 +9,8 @@
#ifndef LIBI2PD_CPU_H
#define LIBI2PD_CPU_H
namespace i2p
{
namespace cpu
{
namespace i2p {
namespace cpu {
extern bool aesni;
extern bool avx;

View file

@ -13,22 +13,17 @@
#include "ChaCha20.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
namespace chacha
{
void u32t8le(uint32_t v, uint8_t * p)
{
namespace i2p {
namespace crypto {
namespace chacha {
void u32t8le(uint32_t v, uint8_t *p) {
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
}
uint32_t u8t32le(const uint8_t * p)
{
uint32_t u8t32le(const uint8_t *p) {
uint32_t value = p[3];
value = (value << 8) | p[2];
@ -38,35 +33,34 @@ uint32_t u8t32le(const uint8_t * p)
return value;
}
uint32_t rotl32(uint32_t x, int n)
{
uint32_t rotl32(uint32_t x, int n) {
return x << n | (x >> (-n & 31));
}
void quarterround(uint32_t *x, int a, int b, int c, int d)
{
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12);
x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8);
x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7);
void quarterround(uint32_t *x, int a, int b, int c, int d) {
x[a] += x[b];
x[d] = rotl32(x[d] ^ x[a], 16);
x[c] += x[d];
x[b] = rotl32(x[b] ^ x[c], 12);
x[a] += x[b];
x[d] = rotl32(x[d] ^ x[a], 8);
x[c] += x[d];
x[b] = rotl32(x[b] ^ x[c], 7);
}
void Chacha20Block::operator << (const Chacha20State & st)
{
void Chacha20Block::operator<<(const Chacha20State &st) {
int i;
for (i = 0; i < 16; i++)
u32t8le(st.data[i], data + (i << 2));
}
void block (Chacha20State &input, int rounds)
{
void block(Chacha20State &input, int rounds) {
int i;
Chacha20State x;
x.Copy(input);
for (i = rounds; i > 0; i -= 2)
{
for (i = rounds; i > 0; i -= 2) {
quarterround(x.data, 0, 4, 8, 12);
quarterround(x.data, 1, 5, 9, 13);
quarterround(x.data, 2, 6, 10, 14);
@ -80,8 +74,7 @@ void block (Chacha20State &input, int rounds)
input.block << x;
}
void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t * key, uint32_t counter)
{
void Chacha20Init(Chacha20State &state, const uint8_t *nonce, const uint8_t *key, uint32_t counter) {
state.data[0] = 0x61707865;
state.data[1] = 0x3320646e;
state.data[2] = 0x79622d32;
@ -94,16 +87,13 @@ void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t *
state.data[13 + i] = chacha::u8t32le(nonce + i * 4);
}
void Chacha20SetCounter (Chacha20State& state, uint32_t counter)
{
void Chacha20SetCounter(Chacha20State &state, uint32_t counter) {
state.data[12] = htole32 (counter);
state.offset = 0;
}
void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz)
{
if (state.offset > 0)
{
void Chacha20Encrypt(Chacha20State &state, uint8_t *buf, size_t sz) {
if (state.offset > 0) {
// previous block if any
auto s = chacha::blocksize - state.offset;
if (sz < s) s = sz;
@ -114,14 +104,11 @@ void Chacha20Encrypt (Chacha20State& state, uint8_t * buf, size_t sz)
state.offset += s;
if (state.offset >= chacha::blocksize) state.offset = 0;
}
for (size_t i = 0; i < sz; i += chacha::blocksize)
{
for (size_t i = 0; i < sz; i += chacha::blocksize) {
chacha::block(state, chacha::rounds);
state.data[12]++;
for (size_t j = i; j < i + chacha::blocksize; j++)
{
if (j >= sz)
{
for (size_t j = i; j < i + chacha::blocksize; j++) {
if (j >= sz) {
state.offset = j & 0x3F; // % 64
break;
}

View file

@ -10,6 +10,7 @@
*/
#ifndef LIBI2PD_CHACHA20_H
#define LIBI2PD_CHACHA20_H
#include <cstdint>
#include <cstring>
#include <inttypes.h>
@ -17,22 +18,21 @@
#include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
const std::size_t CHACHA20_KEY_BYTES = 32;
const std::size_t CHACHA20_NOUNCE_BYTES = 12;
namespace chacha
{
constexpr std::size_t blocksize = 64;
namespace chacha {
constexpr std::size_t
blocksize = 64;
constexpr int rounds = 20;
struct Chacha20State;
struct Chacha20Block
{
struct Chacha20Block {
Chacha20Block() {};
Chacha20Block(Chacha20Block &&) = delete;
uint8_t data[blocksize];
@ -40,29 +40,30 @@ namespace chacha
void operator<<(const Chacha20State &st);
};
struct Chacha20State
{
struct Chacha20State {
Chacha20State() : offset(0) {};
Chacha20State(Chacha20State &&) = delete;
Chacha20State & operator += (const Chacha20State & other)
{
Chacha20State &operator+=(const Chacha20State &other) {
for (int i = 0; i < 16; i++)
data[i] += other.data[i];
return *this;
}
void Copy(const Chacha20State & other)
{
void Copy(const Chacha20State &other) {
memcpy(data, other.data, sizeof(uint32_t) * 16);
}
uint32_t data[16];
Chacha20Block block;
size_t offset;
};
void Chacha20Init(Chacha20State &state, const uint8_t *nonce, const uint8_t *key, uint32_t counter);
void Chacha20SetCounter(Chacha20State &state, uint32_t counter);
void Chacha20Encrypt(Chacha20State &state, uint8_t *buf, size_t sz); // encrypt buf in place
} // namespace chacha
} // namespace crypto

View file

@ -28,41 +28,61 @@ namespace config {
options_description m_OptionsDesc;
variables_map m_Options;
void Init()
{
void Init() {
options_description general("General options");
general.add_options()
("help", "Show this message")
("version", "Show i2pd version")
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
("tunnelsdir", value<std::string>()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d")
("certsdir", value<std::string>()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates")
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)")
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
("loglevel", value<std::string>()->default_value("warn"), "Set the minimal level of log messages (debug, info, warn, error, none)")
("logclftime", bool_switch()->default_value(false), "Write full CLF-formatted date and time to log (default: disabled, write only time)")
("conf", value<std::string>()->default_value(""),
"Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
("tunconf", value<std::string>()->default_value(""),
"Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
("tunnelsdir", value<std::string>()->default_value(""),
"Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d")
("certsdir", value<std::string>()->default_value(""),
"Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates")
("pidfile", value<std::string>()->default_value(""),
"Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
("log", value<std::string>()->default_value(""),
"Logs destination: stdout, file, syslog (stdout if not set)")
("logfile", value<std::string>()->default_value(""),
"Path to logfile (stdout if not set, autodetect if daemon)")
("loglevel", value<std::string>()->default_value("warn"),
"Set the minimal level of log messages (debug, info, warn, error, none)")
("logclftime", bool_switch()->default_value(false),
"Write full CLF-formatted date and time to log (default: disabled, write only time)")
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
("datadir", value<std::string>()->default_value(""),
"Path to storage of i2pd data (RI, keys, peer profiles, ...)")
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("nat", bool_switch()->default_value(true),
"Should we assume we are behind NAT? (default: enabled)")
("port", value<uint16_t>()->default_value(0),
"Port to listen for incoming connections (default: auto)")
("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("address4", value<std::string>()->default_value(""), "Local address to bind ipv4 transport sockets to")
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
("address6", value<std::string>()->default_value(""), "Local address to bind ipv6 transport sockets to")
("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("address4", value<std::string>()->default_value(""),
"Local address to bind ipv4 transport sockets to")
("ipv6", bool_switch()->default_value(false),
"Enable communication through ipv6 (default: disabled)")
("address6", value<std::string>()->default_value(""),
"Local address to bind ipv6 transport sockets to")
("reservedrange", bool_switch()->default_value(true),
"Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)")
("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)")
("notransit", bool_switch()->default_value(false), "Router will not accept transit tunnels at startup (default: disabled)")
("daemon", bool_switch()->default_value(false),
"Router will go to background after start (default: disabled)")
("service", bool_switch()->default_value(false),
"Router will use system folders like '/var/lib/i2pd' (default: disabled)")
("notransit", bool_switch()->default_value(false),
"Router will not accept transit tunnels at startup (default: disabled)")
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
("bandwidth", value<std::string>()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("bandwidth", value<std::string>()->default_value(""),
"Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100),
"Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
@ -75,13 +95,15 @@ namespace config {
options_description limits("Limits options");
limits.add_options()
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
("limits.coresize", value<uint32_t>()->default_value(0),
"Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0),
"Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint16_t>()->default_value(2500),
"Maximum active transit sessions (default:2500)")
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored")
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored")
;
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored");
options_description httpserver("HTTP Server options");
httpserver.add_options()
@ -90,92 +112,119 @@ namespace config {
("http.port", value<uint16_t>()->default_value(7070), "Webconsole listen port")
("http.auth", value<bool>()->default_value(false), "Enable Basic HTTP auth for webconsole")
("http.user", value<std::string>()->default_value("i2pd"), "Username for basic auth")
("http.pass", value<std::string>()->default_value(""), "Password for basic auth (default: random, see logs)")
("http.pass", value<std::string>()->default_value(""),
"Password for basic auth (default: random, see logs)")
("http.strictheaders", value<bool>()->default_value(true), "Enable strict host checking on WebUI")
("http.hostname", value<std::string>()->default_value("localhost"), "Expected hostname for WebUI")
("http.webroot", value<std::string>()->default_value("/"), "WebUI root path (default: / )")
("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )")
;
("http.lang", value<std::string>()->default_value("english"), "WebUI language (default: english )");
options_description httpproxy("HTTP Proxy options");
httpproxy.add_options()
("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy")
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
("httpproxy.keys", value<std::string>()->default_value("transient-proxy"), "File to persist HTTP Proxy keys. Transient by default")
("httpproxy.keys", value<std::string>()->default_value("transient-proxy"),
"File to persist HTTP Proxy keys. Transient by default")
("httpproxy.signaturetype", value<i2p::data::SigningKeyType>()->
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length")
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
("httpproxy.inbound.lengthVariance", value<std::string>()->default_value("0"), "HTTP proxy inbound tunnels length variance")
("httpproxy.outbound.lengthVariance", value<std::string>()->default_value("0"), "HTTP proxy outbound tunnels length variance")
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519),
"Signature type for new keys. 7 (EdDSA) by default")
("httpproxy.inbound.length", value<std::string>()->default_value("3"),
"HTTP proxy inbound tunnel length")
("httpproxy.outbound.length", value<std::string>()->default_value("3"),
"HTTP proxy outbound tunnel length")
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"),
"HTTP proxy inbound tunnels quantity")
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"),
"HTTP proxy outbound tunnels quantity")
("httpproxy.inbound.lengthVariance", value<std::string>()->default_value("0"),
"HTTP proxy inbound tunnels length variance")
("httpproxy.outbound.lengthVariance", value<std::string>()->default_value("0"),
"HTTP proxy outbound tunnels length variance")
("httpproxy.latency.min", value<std::string>()->default_value("0"),
"HTTP proxy min latency for tunnels")
("httpproxy.latency.max", value<std::string>()->default_value("0"),
"HTTP proxy max latency for tunnels")
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
;
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"),
"Local destination's LeaseSet type")
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"),
"Local destination's LeaseSet encryption type")
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key");
options_description socksproxy("SOCKS Proxy options");
socksproxy.add_options()
("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy")
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"),
"SOCKS Proxy listen address")
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
("socksproxy.keys", value<std::string>()->default_value("transient-proxy"), "File to persist SOCKS Proxy keys. Transient by default")
("socksproxy.keys", value<std::string>()->default_value("transient-proxy"),
"File to persist SOCKS Proxy keys. Transient by default")
("socksproxy.signaturetype", value<i2p::data::SigningKeyType>()->
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length")
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
("socksproxy.inbound.lengthVariance", value<std::string>()->default_value("0"), "SOCKS proxy inbound tunnels length variance")
("socksproxy.outbound.lengthVariance", value<std::string>()->default_value("0"), "SOCKS proxy outbound tunnels length variance")
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
("socksproxy.outproxy.enabled", value<bool>()->default_value(false), "Enable or disable SOCKS outproxy")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
;
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519),
"Signature type for new keys. 7 (EdDSA) by default")
("socksproxy.inbound.length", value<std::string>()->default_value("3"),
"SOCKS proxy inbound tunnel length")
("socksproxy.outbound.length", value<std::string>()->default_value("3"),
"SOCKS proxy outbound tunnel length")
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"),
"SOCKS proxy inbound tunnels quantity")
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"),
"SOCKS proxy outbound tunnels quantity")
("socksproxy.inbound.lengthVariance", value<std::string>()->default_value("0"),
"SOCKS proxy inbound tunnels length variance")
("socksproxy.outbound.lengthVariance", value<std::string>()->default_value("0"),
"SOCKS proxy outbound tunnels length variance")
("socksproxy.latency.min", value<std::string>()->default_value("0"),
"SOCKS proxy min latency for tunnels")
("socksproxy.latency.max", value<std::string>()->default_value("0"),
"SOCKS proxy max latency for tunnels")
("socksproxy.outproxy.enabled", value<bool>()->default_value(false),
"Enable or disable SOCKS outproxy")
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"),
"Upstream outproxy address for SOCKS Proxy")
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050),
"Upstream outproxy port for SOCKS Proxy")
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"),
"Local destination's LeaseSet type")
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"),
"Local destination's LeaseSet encryption type")
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""),
"LeaseSet private key");
options_description sam("SAM bridge options");
sam.add_options()
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
("sam.singlethread", value<bool>()->default_value(true), "Sessions run in the SAM bridge's thread")
;
("sam.singlethread", value<bool>()->default_value(true), "Sessions run in the SAM bridge's thread");
options_description bob("BOB options");
bob.add_options()
("bob.enabled", value<bool>()->default_value(false), "Enable or disable BOB command channel")
("bob.address", value<std::string>()->default_value("127.0.0.1"), "BOB listen address")
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port")
;
("bob.port", value<uint16_t>()->default_value(2827), "BOB listen port");
options_description i2cp("I2CP options");
i2cp.add_options()
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
("i2cp.singlethread", value<bool>()->default_value(true), "Destinations run in the I2CP server's thread")
;
("i2cp.singlethread", value<bool>()->default_value(true),
"Destinations run in the I2CP server's thread");
options_description i2pcontrol("I2PControl options");
i2pcontrol.add_options()
("i2pcontrol.enabled", value<bool>()->default_value(false), "Enable or disable I2P Control Protocol")
("i2pcontrol.enabled", value<bool>()->default_value(false),
"Enable or disable I2P Control Protocol")
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection certificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection certificate key")
;
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"),
"I2PCP connection certificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"),
"I2PCP connection certificate key");
bool upnp_default = false;
#if (defined(USE_UPNP) && (defined(WIN32_APP) || defined(ANDROID)))
@ -183,9 +232,10 @@ namespace config {
#endif
options_description upnp("UPnP options");
upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwarding list")
;
("upnp.enabled", value<bool>()->default_value(upnp_default),
"Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"),
"Name i2pd appears in UPnP forwarding list");
options_description precomputation("Precomputation options");
precomputation.add_options()
@ -195,17 +245,21 @@ namespace config {
#else
value<bool>()->default_value(true),
#endif
"Enable or disable elgamal precomputation table")
;
"Enable or disable elgamal precomputation table");
options_description reseed("Reseed options");
reseed.add_options()
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
("reseed.threshold", value<uint16_t>()->default_value(25), "Minimum number of known routers before requesting reseed")
("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from")
("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from")
("reseed.zipfile", value<std::string>()->default_value(""), "Path to local .zip file to reseed from")
("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks")
("reseed.threshold", value<uint16_t>()->default_value(25),
"Minimum number of known routers before requesting reseed")
("reseed.floodfill", value<std::string>()->default_value(""),
"Path to router info of floodfill to reseed from")
("reseed.file", value<std::string>()->default_value(""),
"Path to local .su3 file or HTTPS URL to reseed from")
("reseed.zipfile", value<std::string>()->default_value(""),
"Path to local .zip file to reseed from")
("reseed.proxy", value<std::string>()->default_value(""),
"url for reseed proxy, supports http/socks")
("reseed.urls", value<std::string>()->default_value(
"https://reseed2.i2p.net/,"
"https://reseed.diva.exchange/,"
@ -225,59 +279,61 @@ namespace config {
"http://[320:8936:ec1a:31f1::216]/,"
"http://[306:3834:97b9:a00a::1]/,"
"http://[316:f9e0:f22e:a74f::216]/"
), "Reseed URLs through the Yggdrasil, separated by comma")
;
), "Reseed URLs through the Yggdrasil, separated by comma");
options_description addressbook("AddressBook options");
addressbook.add_options()
("addressbook.enabled", value<bool>()->default_value(true), "Enable address book lookups and subscritions (default: enabled)")
("addressbook.enabled", value<bool>()->default_value(true),
"Enable address book lookups and subscritions (default: enabled)")
("addressbook.defaulturl", value<std::string>()->default_value(
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
), "AddressBook subscription URL for initial setup")
("addressbook.subscriptions", value<std::string>()->default_value(
"http://reg.i2p/hosts.txt"
), "AddressBook subscriptions URLs, separated by comma")
("addressbook.hostsfile", value<std::string>()->default_value(""), "File to dump addresses in hosts.txt format");
("addressbook.hostsfile", value<std::string>()->default_value(""),
"File to dump addresses in hosts.txt format");
options_description trust("Trust options");
trust.add_options()
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
("trust.family", value<std::string>()->default_value(""), "Router Family to trust for first hops")
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?")
;
("trust.hidden", value<bool>()->default_value(false),
"Should we hide our router from other routers?");
// Save deprecated websocket options for compatibility
options_description websocket("Websocket Options");
websocket.add_options()
("websockets.enabled", value<bool>()->default_value(false), "Deprecated option")
("websockets.address", value<std::string>()->default_value(""), "Deprecated option")
("websockets.port", value<uint16_t>()->default_value(0), "Deprecated option")
;
("websockets.port", value<uint16_t>()->default_value(0), "Deprecated option");
options_description exploratory("Exploratory Options");
exploratory.add_options()
("exploratory.inbound.length", value<int>()->default_value(2), "Exploratory inbound tunnel length")
("exploratory.outbound.length", value<int>()->default_value(2), "Exploratory outbound tunnel length")
("exploratory.inbound.quantity", value<int>()->default_value(3), "Exploratory inbound tunnels quantity")
("exploratory.outbound.quantity", value<int>()->default_value(3), "Exploratory outbound tunnels quantity")
;
("exploratory.outbound.length", value<int>()->default_value(2),
"Exploratory outbound tunnel length")
("exploratory.inbound.quantity", value<int>()->default_value(3),
"Exploratory inbound tunnels quantity")
("exploratory.outbound.quantity", value<int>()->default_value(3),
"Exploratory outbound tunnels quantity");
options_description ntcp2("NTCP2 Options");
ntcp2.add_options()
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.port", value<uint16_t>()->default_value(0),
"Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to publish NTCP2 with")
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
;
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport");
options_description ssu2("SSU2 Options");
ssu2.add_options()
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)")
;
("ssu2.port", value<uint16_t>()->default_value(0),
"Port to listen for incoming SSU2 packets (default: auto)");
options_description nettime("Time sync options");
nettime.add_options()
@ -288,28 +344,31 @@ namespace config {
"2.pool.ntp.org,"
"3.pool.ntp.org"
), "Comma separated list of NTP servers")
("nettime.ntpsyncinterval", value<int>()->default_value(72), "NTP sync interval in hours (default: 72)")
("nettime.frompeers", value<bool>()->default_value(true), "Sync clock from transport peers (default: enabled)")
;
("nettime.ntpsyncinterval", value<int>()->default_value(72),
"NTP sync interval in hours (default: 72)")
("nettime.frompeers", value<bool>()->default_value(true),
"Sync clock from transport peers (default: enabled)");
options_description persist("Network information persisting options");
persist.add_options()
("persist.profiles", value<bool>()->default_value(true), "Persist peer profiles (default: true)")
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
;
("persist.addressbook", value<bool>()->default_value(true),
"Persist full addresses (default: true)");
options_description cpuext("CPU encryption extensions options");
cpuext.add_options()
("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used")
("cpuext.avx", bool_switch()->default_value(true), "Use auto detection for AVX CPU extensions. If false, AVX will be not used")
("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines")
;
("cpuext.aesni", bool_switch()->default_value(true),
"Use auto detection for AESNI CPU extensions. If false, AESNI will be not used")
("cpuext.avx", bool_switch()->default_value(true),
"Use auto detection for AVX CPU extensions. If false, AVX will be not used")
("cpuext.force", bool_switch()->default_value(false),
"Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines");
options_description meshnets("Meshnet transports options");
meshnets.add_options()
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
;
("meshnets.yggdrasil", bool_switch()->default_value(false),
"Support transports through the Yggdrasil (default: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish");
#ifdef __linux__
options_description unix_specific("UNIX-specific options");
@ -347,33 +406,28 @@ namespace config {
;
}
void ParseCmdline(int argc, char* argv[], bool ignoreUnknown)
{
try
{
void ParseCmdline(int argc, char *argv[], bool ignoreUnknown) {
try {
auto style = boost::program_options::command_line_style::unix_style
| boost::program_options::command_line_style::allow_long_disguise;
style &= ~boost::program_options::command_line_style::allow_guessing;
if (ignoreUnknown)
store(command_line_parser(argc, argv).options(m_OptionsDesc).style (style).allow_unregistered().run(), m_Options);
store(command_line_parser(argc, argv).options(m_OptionsDesc).style(
style).allow_unregistered().run(), m_Options);
else
store(parse_command_line(argc, argv, m_OptionsDesc, style), m_Options);
}
catch (boost::program_options::error& e)
{
catch (boost::program_options::error &e) {
ThrowFatal("Error while parsing arguments: ", e.what());
std::cerr << "args: " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
if (!ignoreUnknown && (m_Options.count("help") || m_Options.count("h")))
{
if (!ignoreUnknown && (m_Options.count("help") || m_Options.count("h"))) {
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
std::cout << m_OptionsDesc;
exit(EXIT_SUCCESS);
}
else if (m_Options.count("version"))
{
} else if (m_Options.count("version")) {
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
std::cout << "Boost version "
<< BOOST_VERSION / 100000 << "." // maj. version
@ -391,38 +445,32 @@ namespace config {
}
}
void ParseConfig(const std::string& path)
{
void ParseConfig(const std::string &path) {
if (path == "") return;
std::ifstream config(path, std::ios::in);
if (!config.is_open())
{
if (!config.is_open()) {
ThrowFatal("Missing or unreadable config file: ", path);
std::cerr << "missing/unreadable config file: " << path << std::endl;
exit(EXIT_FAILURE);
}
try
{
try {
store(boost::program_options::parse_config_file(config, m_OptionsDesc), m_Options);
}
catch (boost::program_options::error& e)
{
catch (boost::program_options::error &e) {
ThrowFatal("Error while parsing config file: ", e.what());
std::cerr << e.what() << std::endl;
exit(EXIT_FAILURE);
};
}
void Finalize()
{
void Finalize() {
notify(m_Options);
}
bool IsDefault(const char *name)
{
bool IsDefault(const char *name) {
if (!m_Options.count(name))
throw "try to check non-existent option";
@ -431,16 +479,14 @@ namespace config {
return false;
}
bool GetOptionAsAny(const char *name, boost::any& value)
{
bool GetOptionAsAny(const char *name, boost::any &value) {
if (!m_Options.count(name))
return false;
value = m_Options[name];
return true;
}
bool GetOptionAsAny(const std::string& name, boost::any& value)
{
bool GetOptionAsAny(const std::string &name, boost::any &value) {
return GetOptionAsAny(name.c_str(), value);
}

View file

@ -80,8 +80,7 @@ namespace config {
* Example: uint16_t port; GetOption("sam.port", port);
*/
template<typename T>
bool GetOption(const char *name, T& value)
{
bool GetOption(const char *name, T &value) {
if (!m_Options.count(name))
return false;
value = m_Options[name].as<T>();
@ -89,12 +88,12 @@ namespace config {
}
template<typename T>
bool GetOption(const std::string& name, T& value)
{
bool GetOption(const std::string &name, T &value) {
return GetOption(name.c_str(), value);
}
bool GetOptionAsAny(const char *name, boost::any &value);
bool GetOptionAsAny(const std::string &name, boost::any &value);
/**
@ -106,8 +105,7 @@ namespace config {
* Example: uint16_t port = 2827; SetOption("bob.port", port);
*/
template<typename T>
bool SetOption(const char *name, const T& value)
{
bool SetOption(const char *name, const T &value) {
if (!m_Options.count(name))
return false;
m_Options.at(name).value() = value;

View file

@ -16,22 +16,24 @@
#include <openssl/crypto.h>
#include "TunnelBase.h"
#include <openssl/ssl.h>
#if OPENSSL_HKDF
#include <openssl/kdf.h>
#endif
#if !OPENSSL_AEAD_CHACHA20_POLY1305
#include "ChaCha20.h"
#include "Poly1305.h"
#endif
#include "Crypto.h"
#include "Ed25519.h"
#include "I2PEndian.h"
#include "Log.h"
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
const uint8_t elgp_[256] =
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
@ -86,8 +88,7 @@ namespace crypto
const int rsae_ = 65537;
struct CryptoConstants
{
struct CryptoConstants {
// DH/ElGamal
BIGNUM *elgp;
BIGNUM *elgg;
@ -101,8 +102,7 @@ namespace crypto
BIGNUM *rsae;
CryptoConstants(const uint8_t *elgp_, int elgg_, const uint8_t *dsap_,
const uint8_t * dsaq_, const uint8_t * dsag_, int rsae_)
{
const uint8_t *dsaq_, const uint8_t *dsag_, int rsae_) {
elgp = BN_new();
BN_bin2bn(elgp_, 256, elgp);
elgg = BN_new();
@ -117,20 +117,22 @@ namespace crypto
BN_set_word(rsae, rsae_);
}
~CryptoConstants ()
{
BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae);
~CryptoConstants() {
BN_free(elgp);
BN_free(elgg);
BN_free(dsap);
BN_free(dsaq);
BN_free(dsag);
BN_free(rsae);
}
};
static const CryptoConstants& GetCryptoConstants ()
{
static const CryptoConstants &GetCryptoConstants() {
static CryptoConstants cryptoConstants(elgp_, elgg_, dsap_, dsaq_, dsag_, rsae_);
return cryptoConstants;
}
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len)
{
bool bn2buf(const BIGNUM *bn, uint8_t *buf, size_t len) {
int offset = len - BN_num_bytes(bn);
if (offset < 0) return false;
BN_bn2bin(bn, buf + offset);
@ -140,8 +142,8 @@ namespace crypto
// RSA
#define rsae GetCryptoConstants ().rsae
const BIGNUM * GetRSAE ()
{
const BIGNUM *GetRSAE() {
return rsae;
}
@ -149,8 +151,8 @@ namespace crypto
#define dsap GetCryptoConstants ().dsap
#define dsaq GetCryptoConstants ().dsaq
#define dsag GetCryptoConstants ().dsag
DSA * CreateDSA ()
{
DSA *CreateDSA() {
DSA *dsa = DSA_new();
DSA_set0_pqg(dsa, BN_dup(dsap), BN_dup(dsaq), BN_dup(dsag));
DSA_set0_key(dsa, NULL, NULL);
@ -168,6 +170,7 @@ namespace crypto
#define elgg GetCryptoConstants ().elgg
static BN_MONT_CTX *g_MontCtx = nullptr;
static void PrecalculateElggTable(BIGNUM *table[][255], int len) // table is len's array of array of 255 bignums
{
if (len <= 0) return;
@ -176,15 +179,13 @@ namespace crypto
BN_MONT_CTX_set(g_MontCtx, elgp, ctx);
auto montCtx = BN_MONT_CTX_new();
BN_MONT_CTX_copy(montCtx, g_MontCtx);
for (int i = 0; i < len; i++)
{
for (int i = 0; i < len; i++) {
table[i][0] = BN_new();
if (!i)
BN_to_montgomery(table[0][0], elgg, montCtx, ctx);
else
BN_mod_mul_montgomery(table[i][0], table[i - 1][254], table[i - 1][0], montCtx, ctx);
for (int j = 1; j < 255; j++)
{
for (int j = 1; j < 255; j++) {
table[i][j] = BN_new();
BN_mod_mul_montgomery(table[i][j], table[i][j - 1], table[i][0], montCtx, ctx);
}
@ -193,11 +194,9 @@ namespace crypto
BN_CTX_free(ctx);
}
static void DestroyElggTable (BIGNUM * table[][255], int len)
{
static void DestroyElggTable(BIGNUM *table[][255], int len) {
for (int i = 0; i < len; i++)
for (int j = 0; j < 255; j++)
{
for (int j = 0; j < 255; j++) {
BN_free(table[i][j]);
table[i][j] = nullptr;
}
@ -211,14 +210,11 @@ namespace crypto
auto montCtx = BN_MONT_CTX_new();
BN_MONT_CTX_copy(montCtx, g_MontCtx);
BIGNUM *res = nullptr;
for (int i = 0; i < len; i++)
{
if (res)
{
for (int i = 0; i < len; i++) {
if (res) {
if (exp[i])
BN_mod_mul_montgomery(res, res, table[len - 1 - i][exp[i] - 1], montCtx, ctx);
}
else if (exp[i])
} else if (exp[i])
res = BN_dup(table[len - i - 1][exp[i] - 1]);
}
if (res)
@ -227,8 +223,7 @@ namespace crypto
return res;
}
static BIGNUM * ElggPow (const BIGNUM * exp, BIGNUM * table[][255], BN_CTX * ctx)
{
static BIGNUM *ElggPow(const BIGNUM *exp, BIGNUM *table[][255], BN_CTX *ctx) {
auto len = BN_num_bytes(exp);
uint8_t *buf = new uint8_t[len];
BN_bn2bin(exp, buf);
@ -241,27 +236,23 @@ namespace crypto
// DH
DHKeys::DHKeys ()
{
DHKeys::DHKeys() {
m_DH = DH_new();
DH_set0_pqg(m_DH, BN_dup(elgp), NULL, BN_dup(elgg));
DH_set0_key(m_DH, NULL, NULL);
}
DHKeys::~DHKeys ()
{
DHKeys::~DHKeys() {
DH_free(m_DH);
}
void DHKeys::GenerateKeys ()
{
void DHKeys::GenerateKeys() {
BIGNUM *priv_key = NULL, *pub_key = NULL;
#if !defined(__x86_64__) // use short exponent for non x64
priv_key = BN_new();
BN_rand(priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
#endif
if (g_ElggTable)
{
if (g_ElggTable) {
#if defined(__x86_64__)
priv_key = BN_new ();
BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
@ -270,9 +261,7 @@ namespace crypto
pub_key = ElggPow(priv_key, g_ElggTable, ctx);
DH_set0_key(m_DH, pub_key, priv_key);
BN_CTX_free(ctx);
}
else
{
} else {
DH_set0_key(m_DH, NULL, priv_key);
DH_generate_key(m_DH);
DH_get0_key(m_DH, (const BIGNUM **) &pub_key, (const BIGNUM **) &priv_key);
@ -281,16 +270,14 @@ namespace crypto
bn2buf(pub_key, m_PublicKey, 256);
}
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
{
void DHKeys::Agree(const uint8_t *pub, uint8_t *shared) {
BIGNUM *pk = BN_bin2bn(pub, 256, NULL);
DH_compute_key(shared, pk, m_DH);
BN_free(pk);
}
// x25519
X25519Keys::X25519Keys ()
{
X25519Keys::X25519Keys() {
#if OPENSSL_X25519
m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL);
m_Pkey = nullptr;
@ -299,8 +286,7 @@ namespace crypto
#endif
}
X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub)
{
X25519Keys::X25519Keys(const uint8_t *priv, const uint8_t *pub) {
#if OPENSSL_X25519
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
@ -321,8 +307,7 @@ namespace crypto
#endif
}
X25519Keys::~X25519Keys ()
{
X25519Keys::~X25519Keys() {
#if OPENSSL_X25519
EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey);
@ -331,8 +316,7 @@ namespace crypto
#endif
}
void X25519Keys::GenerateKeys ()
{
void X25519Keys::GenerateKeys() {
#if OPENSSL_X25519
if (m_Pkey)
{
@ -351,8 +335,7 @@ namespace crypto
#endif
}
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
{
bool X25519Keys::Agree(const uint8_t *pub, uint8_t *shared) {
if (!pub || (pub[31] & 0x80)) return false; // not x25519 key
#if OPENSSL_X25519
EVP_PKEY_derive_init (m_Ctx);
@ -368,8 +351,7 @@ namespace crypto
return true;
}
void X25519Keys::GetPrivateKey (uint8_t * priv) const
{
void X25519Keys::GetPrivateKey(uint8_t *priv) const {
#if OPENSSL_X25519
size_t len = 32;
EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len);
@ -378,8 +360,7 @@ namespace crypto
#endif
}
void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic)
{
void X25519Keys::SetPrivateKey(const uint8_t *priv, bool calculatePublic) {
#if OPENSSL_X25519
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey);
@ -398,8 +379,7 @@ namespace crypto
}
// ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted)
{
void ElGamalEncrypt(const uint8_t *key, const uint8_t *data, uint8_t *encrypted) {
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
// everything, but a, because a might come from table
@ -417,8 +397,7 @@ namespace crypto
BIGNUM *a;
if (g_ElggTable)
a = ElggPow(k, g_ElggTable, ctx);
else
{
else {
a = BN_new();
BN_mod_exp(a, elgg, k, elgp, ctx);
}
@ -446,13 +425,13 @@ namespace crypto
BN_CTX_free(ctx);
}
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data)
{
bool ElGamalDecrypt(const uint8_t *key, const uint8_t *encrypted, uint8_t *data) {
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
BIGNUM *x = BN_CTX_get(ctx), *a = BN_CTX_get(ctx), *b = BN_CTX_get(ctx);
BN_bin2bn(key, 256, x);
BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1
BN_sub(x, elgp, x);
BN_sub_word(x, 1); // x = elgp - x- 1
BN_bin2bn(encrypted + 1, 256, a);
BN_bin2bn(encrypted + 258, 256, b);
// m = b*(a^x mod p) mod p
@ -464,8 +443,7 @@ namespace crypto
BN_CTX_free(ctx);
uint8_t hash[32];
SHA256(m + 33, 222, hash);
if (memcmp (m + 1, hash, 32))
{
if (memcmp(m + 1, hash, 32)) {
LogPrint(eLogError, "ElGamal decrypt hash doesn't match");
return false;
}
@ -473,8 +451,7 @@ namespace crypto
return true;
}
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub)
{
void GenerateElGamalKeyPair(uint8_t *priv, uint8_t *pub) {
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
RAND_bytes (priv, 256);
#else
@ -495,8 +472,7 @@ namespace crypto
}
// ECIES
void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted)
{
void ECIESEncrypt(const EC_GROUP *curve, const EC_POINT *key, const uint8_t *data, uint8_t *encrypted) {
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
BIGNUM *q = BN_CTX_get(ctx);
@ -522,7 +498,8 @@ namespace crypto
SHA256(keyBuf, len, shared);
// create buffer
uint8_t m[256];
m[0] = 0xFF; m[255] = 0xFF;
m[0] = 0xFF;
m[255] = 0xFF;
memcpy(m + 33, data, 222);
SHA256(m + 33, 222, m + 1);
// encrypt
@ -536,8 +513,7 @@ namespace crypto
BN_CTX_free(ctx);
}
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data)
{
bool ECIESDecrypt(const EC_GROUP *curve, const BIGNUM *key, const uint8_t *encrypted, uint8_t *data) {
bool ret = true;
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
@ -549,8 +525,7 @@ namespace crypto
BN_bin2bn(encrypted + 1, len, x);
BN_bin2bn(encrypted + 1 + len, len, y);
auto p = EC_POINT_new(curve);
if (EC_POINT_set_affine_coordinates_GFp (curve, p, x, y, nullptr))
{
if (EC_POINT_set_affine_coordinates_GFp(curve, p, x, y, nullptr)) {
auto s = EC_POINT_new(curve);
EC_POINT_mul(curve, s, nullptr, p, key, ctx);
EC_POINT_get_affine_coordinates_GFp(curve, s, x, y, nullptr);
@ -570,14 +545,11 @@ namespace crypto
SHA256(m + 33, 222, hash);
if (!memcmp(m + 1, hash, 32))
memcpy(data, m + 33, 222);
else
{
else {
LogPrint(eLogError, "ECIES decrypt hash doesn't match");
ret = false;
}
}
else
{
} else {
LogPrint(eLogError, "ECIES decrypt point is invalid");
ret = false;
}
@ -588,8 +560,7 @@ namespace crypto
return ret;
}
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub)
{
void GenerateECIESKeyPair(const EC_GROUP *curve, BIGNUM *&priv, EC_POINT *&pub) {
BN_CTX *ctx = BN_CTX_new();
BIGNUM *q = BN_new();
EC_GROUP_get_order(curve, q, ctx);
@ -758,8 +729,7 @@ namespace crypto
"aesenclast 224(%["#sched"]), %%xmm0 \n"
#endif
void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out)
{
void ECBEncryption::Encrypt(const ChipherBlock *in, ChipherBlock *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -797,8 +767,7 @@ namespace crypto
"aesdeclast (%["#sched"]), %%xmm0 \n"
#endif
void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out)
{
void ECBDecryption::Decrypt(const ChipherBlock *in, ChipherBlock *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -824,8 +793,7 @@ namespace crypto
"movaps %%xmm0, "#offset"(%[shed]) \n"
#endif
void ECBEncryption::SetKey (const AESKey& key)
{
void ECBEncryption::SetKey(const AESKey &key) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -838,8 +806,7 @@ namespace crypto
}
}
void ECBDecryption::SetKey (const AESKey& key)
{
void ECBDecryption::SetKey(const AESKey &key) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -870,8 +837,7 @@ namespace crypto
}
}
void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
{
void CBCEncryption::Encrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -898,8 +864,7 @@ namespace crypto
else
#endif
{
for (int i = 0; i < numBlocks; i++)
{
for (int i = 0; i < numBlocks; i++) {
*m_LastBlock.GetChipherBlock() ^= in[i];
m_ECBEncryption.Encrypt(m_LastBlock.GetChipherBlock(), m_LastBlock.GetChipherBlock());
out[i] = *m_LastBlock.GetChipherBlock();
@ -907,16 +872,14 @@ namespace crypto
}
}
void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
{
void CBCEncryption::Encrypt(const uint8_t *in, std::size_t len, uint8_t *out) {
// len/16
int numBlocks = len >> 4;
if (numBlocks > 0)
Encrypt(numBlocks, (const ChipherBlock *) in, (ChipherBlock *) out);
}
void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out)
{
void CBCEncryption::Encrypt(const uint8_t *in, uint8_t *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -939,8 +902,7 @@ namespace crypto
Encrypt(1, (const ChipherBlock *) in, (ChipherBlock *) out);
}
void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
{
void CBCDecryption::Decrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -968,8 +930,7 @@ namespace crypto
else
#endif
{
for (int i = 0; i < numBlocks; i++)
{
for (int i = 0; i < numBlocks; i++) {
ChipherBlock tmp = in[i];
m_ECBDecryption.Decrypt(in + i, out + i);
out[i] ^= *m_IV.GetChipherBlock();
@ -978,15 +939,13 @@ namespace crypto
}
}
void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
{
void CBCDecryption::Decrypt(const uint8_t *in, std::size_t len, uint8_t *out) {
int numBlocks = len >> 4;
if (numBlocks > 0)
Decrypt(numBlocks, (const ChipherBlock *) in, (ChipherBlock *) out);
}
void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out)
{
void CBCDecryption::Decrypt(const uint8_t *in, uint8_t *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -1009,8 +968,7 @@ namespace crypto
Decrypt(1, (const ChipherBlock *) in, (ChipherBlock *) out);
}
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
{
void TunnelEncryption::Encrypt(const uint8_t *in, uint8_t *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -1050,8 +1008,7 @@ namespace crypto
}
}
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
{
void TunnelDecryption::Decrypt(const uint8_t *in, uint8_t *out) {
#ifdef __AES__
if(i2p::cpu::aesni)
{
@ -1094,8 +1051,9 @@ namespace crypto
// AEAD/ChaCha20/Poly1305
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt)
{
bool
AEADChaCha20Poly1305(const uint8_t *msg, size_t msgLen, const uint8_t *ad, size_t adLen, const uint8_t *key,
const uint8_t *nonce, uint8_t *buf, size_t len, bool encrypt) {
if (len < msgLen) return false;
if (encrypt && len < msgLen + 16) return false;
bool ret = true;
@ -1134,13 +1092,12 @@ namespace crypto
// create Poly1305 hash
Poly1305 polyHash(polyKey);
if (!ad) adLen = 0;
uint8_t padding[16]; memset (padding, 0, 16);
if (ad)
{
uint8_t padding[16];
memset(padding, 0, 16);
if (ad) {
polyHash.Update(ad, adLen);// additional authenticated data
auto rem = adLen & 0x0F; // %16
if (rem)
{
if (rem) {
// padding1
rem = 16 - rem;
polyHash.Update(padding, rem);
@ -1150,20 +1107,16 @@ namespace crypto
Chacha20SetCounter(state, 1);
if (buf != msg)
memcpy(buf, msg, msgLen);
if (encrypt)
{
if (encrypt) {
chacha::Chacha20Encrypt(state, buf, msgLen); // encrypt
polyHash.Update(buf, msgLen); // after encryption
}
else
{
} else {
polyHash.Update(buf, msgLen); // before decryption
chacha::Chacha20Encrypt(state, buf, msgLen); // decrypt
}
auto rem = msgLen & 0x0F; // %16
if (rem)
{
if (rem) {
// padding2
rem = 16 - rem;
polyHash.Update(padding, rem);
@ -1176,8 +1129,7 @@ namespace crypto
if (encrypt)
// calculate Poly1305 tag and write in after encrypted data
polyHash.Finish((uint64_t * )(buf + msgLen));
else
{
else {
uint64_t tag[4];
// calculate Poly1305 tag
polyHash.Finish(tag);
@ -1187,8 +1139,8 @@ namespace crypto
return ret;
}
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
{
void AEADChaCha20Poly1305Encrypt(const std::vector<std::pair<uint8_t *, size_t> > &bufs, const uint8_t *key,
const uint8_t *nonce, uint8_t *mac) {
if (bufs.empty()) return;
#if OPENSSL_AEAD_CHACHA20_POLY1305
int outlen = 0;
@ -1212,8 +1164,7 @@ namespace crypto
// encrypt buffers
Chacha20SetCounter(state, 1);
size_t size = 0;
for (const auto& it: bufs)
{
for (const auto &it: bufs) {
chacha::Chacha20Encrypt(state, it.first, it.second);
polyHash.Update(it.first, it.second); // after encryption
size += it.second;
@ -1222,8 +1173,7 @@ namespace crypto
uint8_t padding[16];
memset(padding, 0, 16);
auto rem = size & 0x0F; // %16
if (rem)
{
if (rem) {
// padding2
rem = 16 - rem;
polyHash.Update(padding, rem);
@ -1237,8 +1187,7 @@ namespace crypto
#endif
}
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
{
void ChaCha20(const uint8_t *msg, size_t msgLen, const uint8_t *key, const uint8_t *nonce, uint8_t *out) {
#if OPENSSL_AEAD_CHACHA20_POLY1305
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
uint32_t iv[4];
@ -1257,8 +1206,7 @@ namespace crypto
}
void HKDF(const uint8_t *salt, const uint8_t *key, size_t keyLen, const std::string &info,
uint8_t * out, size_t outLen)
{
uint8_t *out, size_t outLen) {
#if OPENSSL_HKDF
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr);
EVP_PKEY_derive_init (pctx);
@ -1281,14 +1229,17 @@ namespace crypto
EVP_PKEY_derive (pctx, out, &outLen);
EVP_PKEY_CTX_free (pctx);
#else
uint8_t prk[32]; unsigned int len;
uint8_t prk[32];
unsigned int len;
HMAC(EVP_sha256(), salt, 32, key, keyLen, prk, &len);
auto l = info.length();
memcpy (out, info.c_str (), l); out[l] = 0x01;
memcpy(out, info.c_str(), l);
out[l] = 0x01;
HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len);
if (outLen > 32) // 64
{
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
memcpy(out + 32, info.c_str(), l);
out[l + 32] = 0x02;
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
}
#endif
@ -1296,8 +1247,7 @@ namespace crypto
// Noise
void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len)
{
void NoiseSymmetricState::MixHash(const uint8_t *buf, size_t len) {
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, m_H, 32);
@ -1305,8 +1255,7 @@ namespace crypto
SHA256_Final(m_H, &ctx);
}
void NoiseSymmetricState::MixHash (const std::vector<std::pair<uint8_t *, size_t> >& bufs)
{
void NoiseSymmetricState::MixHash(const std::vector<std::pair<uint8_t *, size_t> > &bufs) {
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, m_H, 32);
@ -1315,15 +1264,13 @@ namespace crypto
SHA256_Final(m_H, &ctx);
}
void NoiseSymmetricState::MixKey (const uint8_t * sharedSecret)
{
void NoiseSymmetricState::MixKey(const uint8_t *sharedSecret) {
HKDF(m_CK, sharedSecret, 32, "", m_CK);
// new ck is m_CK[0:31], key is m_CK[32:63]
}
static void InitNoiseState(NoiseSymmetricState &state, const uint8_t *ck,
const uint8_t * hh, const uint8_t * pub)
{
const uint8_t *hh, const uint8_t *pub) {
// pub is Bob's public static key, hh = SHA256(h)
memcpy(state.m_CK, ck, 32);
SHA256_CTX ctx;
@ -1333,58 +1280,68 @@ namespace crypto
SHA256_Final(state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub)
}
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub)
{
void InitNoiseNState(NoiseSymmetricState &state, const uint8_t *pub) {
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
static const uint8_t hh[32] =
{
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d,
0xc1,
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15,
0x54
}; // hh = SHA256(protocol_name || 0)
InitNoiseState(state, (const uint8_t *) protocolName, hh, pub); // ck = protocol_name || 0
}
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub)
{
void InitNoiseXKState(NoiseSymmetricState &state, const uint8_t *pub) {
static const uint8_t protocolNameHash[32] =
{
0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed,
0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71
0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7,
0xed,
0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f,
0x71
}; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256")
static const uint8_t hh[32] =
{
0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5,
0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e
0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05,
0xb5,
0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d,
0x1e
}; // SHA256 (protocolNameHash)
InitNoiseState(state, protocolNameHash, hh, pub);
}
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub)
{
void InitNoiseXKState1(NoiseSymmetricState &state, const uint8_t *pub) {
static const uint8_t protocolNameHash[32] =
{
0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, 0xf4,
0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, 0x32
0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed,
0xf4,
0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81,
0x32
}; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256")
static const uint8_t hh[32] =
{
0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, 0x53,
0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, 0x33
0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4,
0x53,
0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc,
0x33
}; // SHA256 (protocolNameHash)
InitNoiseState(state, protocolNameHash, hh, pub);
}
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub)
{
void InitNoiseIKState(NoiseSymmetricState &state, const uint8_t *pub) {
static const uint8_t protocolNameHash[32] =
{
0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba,
0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c
0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d,
0xba,
0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4,
0x0c
}; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes
static const uint8_t hh[32] =
{
0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32,
0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c
0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52,
0x32,
0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b,
0x5c
}; // SHA256 (protocolNameHash)
InitNoiseState(state, protocolNameHash, hh, pub);
}
@ -1403,8 +1360,7 @@ namespace crypto
}
}*/
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force)
{
void InitCrypto(bool precomputation, bool aesni, bool avx, bool force) {
i2p::cpu::Detect(aesni, avx, force);
#if LEGACY_OPENSSL
SSL_library_init();
@ -1413,8 +1369,7 @@ namespace crypto
for (int i = 0; i < numLocks; i++)
m_OpenSSLMutexes.emplace_back (new std::mutex);
CRYPTO_set_locking_callback (OpensslLockingCallback);*/
if (precomputation)
{
if (precomputation) {
#if defined(__x86_64__)
g_ElggTable = new BIGNUM * [ELGAMAL_FULL_EXPONENT_NUM_BYTES][255];
PrecalculateElggTable (g_ElggTable, ELGAMAL_FULL_EXPONENT_NUM_BYTES);
@ -1425,10 +1380,8 @@ namespace crypto
}
}
void TerminateCrypto ()
{
if (g_ElggTable)
{
void TerminateCrypto() {
if (g_ElggTable) {
DestroyElggTable(g_ElggTable,
#if defined(__x86_64__)
ELGAMAL_FULL_EXPONENT_NUM_BYTES
@ -1436,7 +1389,8 @@ namespace crypto
ELGAMAL_SHORT_EXPONENT_NUM_BYTES
#endif
);
delete[] g_ElggTable; g_ElggTable = nullptr;
delete[] g_ElggTable;
g_ElggTable = nullptr;
}
/* CRYPTO_set_locking_callback (nullptr);
m_OpenSSLMutexes.clear ();*/

View file

@ -50,10 +50,8 @@
# endif
#endif
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
bool bn2buf(const BIGNUM *bn, uint8_t *buf, size_t len);
// DSA
@ -63,15 +61,17 @@ namespace crypto
const BIGNUM *GetRSAE();
// DH
class DHKeys
{
class DHKeys {
public:
DHKeys();
~DHKeys();
void GenerateKeys();
const uint8_t *GetPublicKey() const { return m_PublicKey; };
void Agree(const uint8_t *pub, uint8_t *shared);
private:
@ -81,21 +81,26 @@ namespace crypto
};
// x25519
class X25519Keys
{
class X25519Keys {
public:
X25519Keys();
X25519Keys(const uint8_t *priv, const uint8_t *pub); // if pub is null, derive from priv
~X25519Keys();
void GenerateKeys();
const uint8_t *GetPublicKey() const { return m_PublicKey; };
void GetPrivateKey(uint8_t *priv) const;
void SetPrivateKey(const uint8_t *priv, bool calculatePublic = false);
bool Agree(const uint8_t *pub, uint8_t *shared);
bool IsElligatorIneligible() const { return m_IsElligatorIneligible; }
void SetElligatorIneligible() { m_IsElligatorIneligible = true; }
private:
@ -112,22 +117,26 @@ namespace crypto
};
// ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data
void ElGamalEncrypt(const uint8_t *key, const uint8_t *data,
uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted
bool
ElGamalDecrypt(const uint8_t *key, const uint8_t *encrypted, uint8_t *data); // 514 bytes encrypted, 222 data
void GenerateElGamalKeyPair(uint8_t *priv, uint8_t *pub);
// ECIES
void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data
void ECIESEncrypt(const EC_GROUP *curve, const EC_POINT *key, const uint8_t *data,
uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted
bool ECIESDecrypt(const EC_GROUP *curve, const BIGNUM *key, const uint8_t *encrypted,
uint8_t *data); // 514 bytes encrypted, 222 data
void GenerateECIESKeyPair(const EC_GROUP *curve, BIGNUM *&priv, EC_POINT *&pub);
// HMAC
typedef i2p::data::Tag<32> MACKey;
void HMACMD5Digest(uint8_t *msg, size_t len, const MACKey &key, uint8_t *digest);
// AES
struct ChipherBlock
{
struct ChipherBlock {
uint8_t buf[16];
void operator^=(const ChipherBlock &other) // XOR
@ -136,9 +145,7 @@ namespace crypto
{
for (int i = 0; i < 4; i++)
reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
}
else
{
} else {
for (int i = 0; i < 16; i++)
buf[i] ^= other.buf[i];
}
@ -152,8 +159,7 @@ namespace crypto
{
public:
AESAlignedBuffer ()
{
AESAlignedBuffer() {
m_Buf = m_UnalignedBuffer;
uint8_t rem = ((size_t) m_Buf) & 0x0f;
if (rem)
@ -161,8 +167,11 @@ namespace crypto
}
operator uint8_t *() { return m_Buf; };
operator const uint8_t *() const { return m_Buf; };
ChipherBlock *GetChipherBlock() { return (ChipherBlock *) m_Buf; };
const ChipherBlock *GetChipherBlock() const { return (const ChipherBlock *) m_Buf; };
private:
@ -192,6 +201,7 @@ namespace crypto
#ifdef __AES__
class ECBEncryption: public ECBCryptoAESNI
#else
class ECBEncryption
#endif
{
@ -208,19 +218,21 @@ namespace crypto
#ifdef __AES__
class ECBDecryption: public ECBCryptoAESNI
#else
class ECBDecryption
#endif
{
public:
void SetKey(const AESKey &key);
void Decrypt(const ChipherBlock *in, ChipherBlock *out);
private:
AES_KEY m_Key;
};
class CBCEncryption
{
class CBCEncryption {
public:
CBCEncryption() { memset((uint8_t *) m_LastBlock, 0, 16); };
@ -230,7 +242,9 @@ namespace crypto
void GetIV(uint8_t *iv) const { memcpy(iv, (const uint8_t *) m_LastBlock, 16); };
void Encrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
void Encrypt(const uint8_t *in, std::size_t len, uint8_t *out);
void Encrypt(const uint8_t *in, uint8_t *out); // one block
ECBEncryption &ECB() { return m_ECBEncryption; }
@ -242,8 +256,7 @@ namespace crypto
ECBEncryption m_ECBEncryption;
};
class CBCDecryption
{
class CBCDecryption {
public:
CBCDecryption() { memset((uint8_t *) m_IV, 0, 16); };
@ -253,7 +266,9 @@ namespace crypto
void GetIV(uint8_t *iv) const { memcpy(iv, (const uint8_t *) m_IV, 16); };
void Decrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
void Decrypt(const uint8_t *in, std::size_t len, uint8_t *out);
void Decrypt(const uint8_t *in, uint8_t *out); // one block
ECBDecryption &ECB() { return m_ECBDecryption; }
@ -268,8 +283,7 @@ namespace crypto
{
public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
{
void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
m_LayerEncryption.SetKey(layerKey);
m_IVEncryption.SetKey(ivKey);
}
@ -286,8 +300,7 @@ namespace crypto
{
public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
{
void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
m_LayerDecryption.SetKey(layerKey);
m_IVDecryption.SetKey(ivKey);
}
@ -301,25 +314,30 @@ namespace crypto
};
// AEAD/ChaCha20/Poly1305
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag
bool
AEADChaCha20Poly1305(const uint8_t *msg, size_t msgLen, const uint8_t *ad, size_t adLen, const uint8_t *key,
const uint8_t *nonce, uint8_t *buf, size_t len, bool encrypt); // msgLen is len without tag
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
void AEADChaCha20Poly1305Encrypt(const std::vector<std::pair<uint8_t *, size_t> > &bufs, const uint8_t *key,
const uint8_t *nonce, uint8_t *mac); // encrypt multiple buffers with zero ad
// ChaCha20
void ChaCha20(const uint8_t *msg, size_t msgLen, const uint8_t *key, const uint8_t *nonce, uint8_t *out);
// HKDF
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
void HKDF(const uint8_t *salt, const uint8_t *key, size_t keyLen, const std::string &info, uint8_t *out,
size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
// Noise
struct NoiseSymmetricState
{
struct NoiseSymmetricState {
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
void MixHash(const uint8_t *buf, size_t len);
void MixHash(const std::vector<std::pair<uint8_t *, size_t> > &bufs);
void MixKey(const uint8_t *sharedSecret);
};
@ -330,79 +348,108 @@ namespace crypto
// init and terminate
void InitCrypto(bool precomputation, bool aesni, bool avx, bool force);
void TerminateCrypto();
}
}
// take care about openssl below 1.1.0
#if LEGACY_OPENSSL
// define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
if (d->p) BN_free(d->p);
if (d->q) BN_free(d->q);
if (d->g) BN_free(d->g);
d->p = p; d->q = q; d->g = g; return 1;
d->p = p;
d->q = q;
d->g = g;
return 1;
}
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
{
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) {
if (d->pub_key) BN_free(d->pub_key);
if (d->priv_key) BN_free(d->priv_key);
d->pub_key = pub_key; d->priv_key = priv_key; return 1;
d->pub_key = pub_key;
d->priv_key = priv_key;
return 1;
}
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key) {
*pub_key = d->pub_key;
*priv_key = d->priv_key;
}
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
if (sig->r) BN_free(sig->r);
if (sig->s) BN_free(sig->s);
sig->r = r; sig->s = s; return 1;
sig->r = r;
sig->s = s;
return 1;
}
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
*pr = sig->r;
*ps = sig->s;
}
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
if (sig->r) BN_free(sig->r);
if (sig->s) BN_free(sig->s);
sig->r = r; sig->s = s; return 1;
sig->r = r;
sig->s = s;
return 1;
}
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
{
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
*pr = sig->r;
*ps = sig->s;
}
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
if (r->n) BN_free(r->n);
if (r->e) BN_free(r->e);
if (r->d) BN_free(r->d);
r->n = n; r->e = e; r->d = d; return 1;
r->n = n;
r->e = e;
r->d = d;
return 1;
}
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
{ *n = r->n; *e = r->e; *d = r->d; }
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) {
*n = r->n;
*e = r->e;
*d = r->d;
}
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
if (dh->p) BN_free(dh->p);
if (dh->q) BN_free(dh->q);
if (dh->g) BN_free(dh->g);
dh->p = p; dh->q = q; dh->g = g; return 1;
dh->p = p;
dh->q = q;
dh->g = g;
return 1;
}
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
{
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) {
if (dh->pub_key) BN_free(dh->pub_key);
if (dh->priv_key) BN_free(dh->priv_key);
dh->pub_key = pub_key; dh->priv_key = priv_key; return 1;
dh->pub_key = pub_key;
dh->priv_key = priv_key;
return 1;
}
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
{ return pkey->pkey.rsa; }
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) {
*pub_key = dh->pub_key;
*priv_key = dh->priv_key;
}
inline EVP_MD_CTX *EVP_MD_CTX_new ()
{ return EVP_MD_CTX_create(); }
inline void EVP_MD_CTX_free (EVP_MD_CTX *ctx)
{ EVP_MD_CTX_destroy (ctx); }
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) { return pkey->pkey.rsa; }
inline EVP_MD_CTX *EVP_MD_CTX_new() { return EVP_MD_CTX_create(); }
inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); }
// ssl
#define TLS_method TLSv1_method

View file

@ -11,74 +11,62 @@
#include "Gost.h"
#include "CryptoKey.h"
namespace i2p
{
namespace crypto
{
ElGamalEncryptor::ElGamalEncryptor (const uint8_t * pub)
{
namespace i2p {
namespace crypto {
ElGamalEncryptor::ElGamalEncryptor(const uint8_t *pub) {
memcpy(m_PublicKey, pub, 256);
}
void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted)
{
void ElGamalEncryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) {
ElGamalEncrypt(m_PublicKey, data, encrypted);
}
ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv)
{
ElGamalDecryptor::ElGamalDecryptor(const uint8_t *priv) {
memcpy(m_PrivateKey, priv, 256);
}
bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data)
{
bool ElGamalDecryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) {
return ElGamalDecrypt(m_PrivateKey, encrypted, data);
}
ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub)
{
ECIESP256Encryptor::ECIESP256Encryptor(const uint8_t *pub) {
m_Curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
m_PublicKey = EC_POINT_new(m_Curve);
BIGNUM *x = BN_bin2bn(pub, 32, nullptr);
BIGNUM *y = BN_bin2bn(pub + 32, 32, nullptr);
if (!EC_POINT_set_affine_coordinates_GFp(m_Curve, m_PublicKey, x, y, nullptr))
LogPrint(eLogError, "ECICS P256 invalid public key");
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
ECIESP256Encryptor::~ECIESP256Encryptor ()
{
ECIESP256Encryptor::~ECIESP256Encryptor() {
if (m_Curve) EC_GROUP_free(m_Curve);
if (m_PublicKey) EC_POINT_free(m_PublicKey);
}
void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted)
{
void ECIESP256Encryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) {
if (m_Curve && m_PublicKey)
ECIESEncrypt(m_Curve, m_PublicKey, data, encrypted);
}
ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv)
{
ECIESP256Decryptor::ECIESP256Decryptor(const uint8_t *priv) {
m_Curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
m_PrivateKey = BN_bin2bn(priv, 32, nullptr);
}
ECIESP256Decryptor::~ECIESP256Decryptor ()
{
ECIESP256Decryptor::~ECIESP256Decryptor() {
if (m_Curve) EC_GROUP_free(m_Curve);
if (m_PrivateKey) BN_free(m_PrivateKey);
}
bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data)
{
bool ECIESP256Decryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) {
if (m_Curve && m_PrivateKey)
return ECIESDecrypt(m_Curve, m_PrivateKey, encrypted, data);
return false;
}
void CreateECIESP256RandomKeys (uint8_t * priv, uint8_t * pub)
{
void CreateECIESP256RandomKeys(uint8_t *priv, uint8_t *pub) {
EC_GROUP *curve = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_POINT *p = nullptr;
BIGNUM *key = nullptr;
@ -92,52 +80,47 @@ namespace crypto
bn2buf(y, pub + 32, 32);
RAND_bytes(pub + 64, 192);
EC_POINT_free(p);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
EC_GROUP_free(curve);
}
ECIESGOSTR3410Encryptor::ECIESGOSTR3410Encryptor (const uint8_t * pub)
{
ECIESGOSTR3410Encryptor::ECIESGOSTR3410Encryptor(const uint8_t *pub) {
auto &curve = GetGOSTR3410Curve(eGOSTR3410CryptoProA);
m_PublicKey = EC_POINT_new(curve->GetGroup());
BIGNUM *x = BN_bin2bn(pub, 32, nullptr);
BIGNUM *y = BN_bin2bn(pub + 32, 32, nullptr);
if (!EC_POINT_set_affine_coordinates_GFp(curve->GetGroup(), m_PublicKey, x, y, nullptr))
LogPrint(eLogError, "ECICS GOST R 34.10 invalid public key");
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
ECIESGOSTR3410Encryptor::~ECIESGOSTR3410Encryptor ()
{
ECIESGOSTR3410Encryptor::~ECIESGOSTR3410Encryptor() {
if (m_PublicKey) EC_POINT_free(m_PublicKey);
}
void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted)
{
void ECIESGOSTR3410Encryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) {
if (m_PublicKey)
ECIESEncrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PublicKey, data, encrypted);
}
ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv)
{
ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor(const uint8_t *priv) {
m_PrivateKey = BN_bin2bn(priv, 32, nullptr);
}
ECIESGOSTR3410Decryptor::~ECIESGOSTR3410Decryptor ()
{
ECIESGOSTR3410Decryptor::~ECIESGOSTR3410Decryptor() {
if (m_PrivateKey) BN_free(m_PrivateKey);
}
bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data)
{
bool ECIESGOSTR3410Decryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) {
if (m_PrivateKey)
return ECIESDecrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PrivateKey, encrypted, data);
return false;
}
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub)
{
void CreateECIESGOSTR3410RandomKeys(uint8_t *priv, uint8_t *pub) {
auto &curve = GetGOSTR3410Curve(eGOSTR3410CryptoProA);
EC_POINT *p = nullptr;
BIGNUM *key = nullptr;
@ -151,31 +134,27 @@ namespace crypto
bn2buf(y, pub + 32, 32);
RAND_bytes(pub + 64, 192);
EC_POINT_free(p);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub)
{
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor(const uint8_t *pub) {
memcpy(m_PublicKey, pub, 32);
}
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub)
{
void ECIESX25519AEADRatchetEncryptor::Encrypt(const uint8_t *, uint8_t *pub) {
memcpy(pub, m_PublicKey, 32);
}
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic)
{
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor(const uint8_t *priv, bool calculatePublic) {
m_StaticKeys.SetPrivateKey(priv, calculatePublic);
}
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret)
{
bool ECIESX25519AEADRatchetDecryptor::Decrypt(const uint8_t *epub, uint8_t *sharedSecret) {
return m_StaticKeys.Agree(epub, sharedSecret);
}
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub)
{
void CreateECIESX25519AEADRatchetRandomKeys(uint8_t *priv, uint8_t *pub) {
X25519Keys k;
k.GenerateKeys();
k.GetPrivateKey(priv);

View file

@ -12,24 +12,23 @@
#include <inttypes.h>
#include "Crypto.h"
namespace i2p
{
namespace crypto
{
class CryptoKeyEncryptor
{
namespace i2p {
namespace crypto {
class CryptoKeyEncryptor {
public:
virtual ~CryptoKeyEncryptor() {};
virtual void Encrypt(const uint8_t *data, uint8_t *encrypted) = 0;
};
class CryptoKeyDecryptor
{
class CryptoKeyDecryptor {
public:
virtual ~CryptoKeyDecryptor() {};
virtual bool Decrypt(const uint8_t *encrypted, uint8_t *data) = 0;
virtual size_t GetPublicKeyLen() const = 0; // we need it to set key in LS2
};
@ -39,6 +38,7 @@ namespace crypto
public:
ElGamalEncryptor(const uint8_t *pub);
void Encrypt(const uint8_t *data, uint8_t *encrypted) override; // 222 bytes data, 514 bytes encrypted
private:
@ -51,6 +51,7 @@ namespace crypto
public:
ElGamalDecryptor(const uint8_t *priv);
bool Decrypt(const uint8_t *encrypted, uint8_t *data) override; // 514 bytes encrypted, 222 bytes data
size_t GetPublicKeyLen() const override { return 256; };
@ -61,12 +62,13 @@ namespace crypto
// ECIES P256
class ECIESP256Encryptor: public CryptoKeyEncryptor
{
class ECIESP256Encryptor : public CryptoKeyEncryptor {
public:
ECIESP256Encryptor(const uint8_t *pub);
~ECIESP256Encryptor();
void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
private:
@ -76,13 +78,15 @@ namespace crypto
};
class ECIESP256Decryptor: public CryptoKeyDecryptor
{
class ECIESP256Decryptor : public CryptoKeyDecryptor {
public:
ECIESP256Decryptor(const uint8_t *priv);
~ECIESP256Decryptor();
bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
size_t GetPublicKeyLen() const override { return 64; };
private:
@ -95,12 +99,13 @@ namespace crypto
// ECIES GOST R 34.10
class ECIESGOSTR3410Encryptor: public CryptoKeyEncryptor
{
class ECIESGOSTR3410Encryptor : public CryptoKeyEncryptor {
public:
ECIESGOSTR3410Encryptor(const uint8_t *pub);
~ECIESGOSTR3410Encryptor();
void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
private:
@ -109,13 +114,15 @@ namespace crypto
};
class ECIESGOSTR3410Decryptor: public CryptoKeyDecryptor
{
class ECIESGOSTR3410Decryptor : public CryptoKeyDecryptor {
public:
ECIESGOSTR3410Decryptor(const uint8_t *priv);
~ECIESGOSTR3410Decryptor();
bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
size_t GetPublicKeyLen() const override { return 64; };
private:
@ -127,12 +134,13 @@ namespace crypto
// ECIES-X25519-AEAD-Ratchet
class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor
{
class ECIESX25519AEADRatchetEncryptor : public CryptoKeyEncryptor {
public:
ECIESX25519AEADRatchetEncryptor(const uint8_t *pub);
~ECIESX25519AEADRatchetEncryptor() {};
void Encrypt(const uint8_t *, uint8_t *pub) override;
// copies m_PublicKey to pub
@ -141,15 +149,18 @@ namespace crypto
uint8_t m_PublicKey[32];
};
class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor
{
class ECIESX25519AEADRatchetDecryptor : public CryptoKeyDecryptor {
public:
ECIESX25519AEADRatchetDecryptor(const uint8_t *priv, bool calculatePublic = false);
~ECIESX25519AEADRatchetDecryptor() {};
bool Decrypt(const uint8_t *epub, uint8_t *sharedSecret) override;
// agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen() const override { return 32; };
const uint8_t *GetPubicKey() const { return m_StaticKeys.GetPublicKey(); };
private:

View file

@ -14,13 +14,10 @@
#include "Destination.h"
#include "Datagram.h"
namespace i2p
{
namespace datagram
{
namespace i2p {
namespace datagram {
DatagramDestination::DatagramDestination(std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip) :
m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip)
{
m_Owner(owner), m_Receiver(nullptr), m_RawReceiver(nullptr), m_Gzip(gzip) {
if (m_Gzip)
m_Deflator.reset(new i2p::data::GzipDeflator);
@ -30,80 +27,76 @@ namespace datagram
m_Signature.resize(m_Owner->GetIdentity()->GetSignatureLen());
}
DatagramDestination::~DatagramDestination ()
{
DatagramDestination::~DatagramDestination() {
m_Sessions.clear();
}
void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{
void
DatagramDestination::SendDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &identity,
uint16_t fromPort, uint16_t toPort) {
auto session = ObtainSession(identity);
SendDatagram(session, payload, len, fromPort, toPort);
FlushSendQueue(session);
}
void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{
void
DatagramDestination::SendRawDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &identity,
uint16_t fromPort, uint16_t toPort) {
auto session = ObtainSession(identity);
SendRawDatagram(session, payload, len, fromPort, toPort);
FlushSendQueue(session);
}
std::shared_ptr<DatagramSession> DatagramDestination::GetSession(const i2p::data::IdentHash & ident)
{
std::shared_ptr<DatagramSession> DatagramDestination::GetSession(const i2p::data::IdentHash &ident) {
return ObtainSession(ident);
}
void DatagramDestination::SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{
if (session)
{
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
void
DatagramDestination::SendDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
uint16_t fromPort, uint16_t toPort) {
if (session) {
if (m_Owner->GetIdentity()->GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) {
uint8_t hash[32];
SHA256(payload, len, hash);
m_Owner->Sign(hash, 32, m_Signature.data());
}
else
} else
m_Owner->Sign(payload, len, m_Signature.data());
auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}},
auto msg = CreateDataMessage({{m_From.data(), m_From.size()},
{m_Signature.data(), m_Signature.size()},
{payload, len}},
fromPort, toPort, false, !session->IsRatchets()); // datagram
session->SendMsg(msg);
}
}
void DatagramDestination::SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
{
void DatagramDestination::SendRawDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload,
size_t len, uint16_t fromPort, uint16_t toPort) {
if (session)
session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ())); // raw
session->SendMsg(
CreateDataMessage({{payload, len}}, fromPort, toPort, true, !session->IsRatchets())); // raw
}
void DatagramDestination::FlushSendQueue (std::shared_ptr<DatagramSession> session)
{
void DatagramDestination::FlushSendQueue(std::shared_ptr<DatagramSession> session) {
if (session)
session->FlushSendQueue();
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
{
void DatagramDestination::HandleDatagram(uint16_t fromPort, uint16_t toPort, uint8_t *const &buf, size_t len) {
i2p::data::IdentityEx identity;
size_t identityLen = identity.FromBuffer(buf, len);
const uint8_t *signature = buf + identityLen;
size_t headerLen = identityLen + identity.GetSignatureLen();
bool verified = false;
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
if (identity.GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) {
uint8_t hash[32];
SHA256(buf + headerLen, len - headerLen, hash);
verified = identity.Verify(hash, 32, signature);
}
else
} else
verified = identity.Verify(buf + headerLen, len - headerLen, signature);
if (verified)
{
if (verified) {
auto h = identity.GetIdentHash();
auto session = ObtainSession(h);
session->Ack();
@ -112,21 +105,19 @@ namespace datagram
r(identity, fromPort, toPort, buf + headerLen, len - headerLen);
else
LogPrint(eLogWarning, "DatagramDestination: no receiver for port ", toPort);
}
else
} else
LogPrint(eLogWarning, "Datagram signature verification failed");
}
void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
void
DatagramDestination::HandleRawDatagram(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len) {
if (m_RawReceiver)
m_RawReceiver(fromPort, toPort, buf, len);
else
LogPrint(eLogWarning, "DatagramDestination: no receiver for raw datagram");
}
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
{
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) {
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
Receiver r = m_Receiver;
auto itr = m_ReceiversByPorts.find(port);
@ -135,26 +126,23 @@ namespace datagram
return r;
}
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw)
{
void DatagramDestination::HandleDataMessagePayload(uint16_t fromPort, uint16_t toPort, const uint8_t *buf,
size_t len, bool isRaw) {
// unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate(buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen)
{
if (uncompressedLen) {
if (isRaw)
HandleRawDatagram(fromPort, toPort, uncompressed, uncompressedLen);
else
HandleDatagram(fromPort, toPort, uncompressed, uncompressedLen);
}
else
} else
LogPrint(eLogWarning, "Datagram: decompression failed");
}
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage(
const std::vector<std::pair<const uint8_t *, size_t> > &payloads,
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum)
{
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) {
size_t size;
auto msg = m_I2NPMsgsPool.AcquireShared();
uint8_t *buf = msg->GetPayload();
@ -165,43 +153,37 @@ namespace datagram
else
size = i2p::data::GzipNoCompression(payloads, buf, msg->maxLen - msg->len);
if (size)
{
if (size) {
htobe32buf(msg->GetPayload(), size); // length
htobe16buf(buf + 4, fromPort); // source port
htobe16buf(buf + 6, toPort); // destination port
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW
: i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
msg->len += size + 4;
msg->FillI2NPMessageHeader(eI2NPData, 0, checksum);
}
else
} else
msg = nullptr;
return msg;
}
void DatagramDestination::CleanUp ()
{
void DatagramDestination::CleanUp() {
if (m_Sessions.empty()) return;
auto now = i2p::util::GetMillisecondsSinceEpoch();
LogPrint(eLogDebug, "DatagramDestination: clean up sessions");
std::unique_lock<std::mutex> lock(m_SessionsMutex);
// for each session ...
for (auto it = m_Sessions.begin (); it != m_Sessions.end (); )
{
for (auto it = m_Sessions.begin(); it != m_Sessions.end();) {
// check if expired
if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE)
{
if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE) {
LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32());
it->second->Stop();
it = m_Sessions.erase(it); // we are expired
}
else
} else
it++;
}
}
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity)
{
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash &identity) {
std::shared_ptr<DatagramSession> session = nullptr;
std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto itr = m_Sessions.find(identity);
@ -216,11 +198,10 @@ namespace datagram
return session;
}
std::shared_ptr<DatagramSession::Info> DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash & remote)
{
std::shared_ptr<DatagramSession::Info>
DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash &remote) {
std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( auto & item : m_Sessions)
{
for (auto &item: m_Sessions) {
if (item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
}
return nullptr;
@ -230,21 +211,17 @@ namespace datagram
const i2p::data::IdentHash &remoteIdent) :
m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent),
m_RequestingLS(false)
{
m_RequestingLS(false) {
}
void DatagramSession::Start ()
{
void DatagramSession::Start() {
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
}
void DatagramSession::Stop ()
{
void DatagramSession::Stop() {
}
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
{
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg) {
// we used this session
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
if (msg || m_SendQueue.empty())
@ -254,8 +231,7 @@ namespace datagram
FlushSendQueue();
}
DatagramSession::Info DatagramSession::GetSessionInfo() const
{
DatagramSession::Info DatagramSession::GetSessionInfo() const {
if (!m_RoutingSession)
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
@ -264,21 +240,18 @@ namespace datagram
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto lease = routingPath->remoteLease;
auto tunnel = routingPath->outboundTunnel;
if(lease)
{
if (lease) {
if (tunnel)
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse);
else
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse);
}
else if(tunnel)
} else if (tunnel)
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse);
else
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
}
void DatagramSession::Ack()
{
void DatagramSession::Ack() {
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
auto path = GetSharedRoutingPath();
if (path)
@ -287,24 +260,21 @@ namespace datagram
SendMsg(nullptr); // send empty message in case if we have some data to send
}
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
{
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath() {
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired()) {
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
if (!m_RemoteLeaseSet)
{
if(!m_RequestingLS)
{
if (!m_RemoteLeaseSet) {
if (!m_RequestingLS) {
m_RequestingLS = true;
m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1));
m_LocalDestination->RequestDestination(m_RemoteIdent,
std::bind(&DatagramSession::HandleLeaseSetUpdated, this,
std::placeholders::_1));
}
return nullptr;
}
}
if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ())
{
if (!m_RoutingSession || m_RoutingSession->IsTerminated() || !m_RoutingSession->IsReadyToSend()) {
bool found = false;
for (auto &it: m_PendingRoutingSessions)
if (it->GetOwner() && m_RoutingSession->IsReadyToSend()) // found established session
@ -314,8 +284,7 @@ namespace datagram
found = true;
break;
}
if (!found)
{
if (!found) {
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
if (!m_RoutingSession->GetOwner() || !m_RoutingSession->IsReadyToSend())
m_PendingRoutingSessions.push_back(m_RoutingSession);
@ -324,74 +293,62 @@ namespace datagram
auto path = m_RoutingSession->GetSharedRoutingPath();
if (path && m_RoutingSession->IsRatchets() &&
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)
{
m_LastUse > m_RoutingSession->GetLastActivityTimestamp() * 1000 + DATAGRAM_SESSION_PATH_TIMEOUT) {
m_RoutingSession->SetSharedRoutingPath(nullptr);
path = nullptr;
}
if (path)
{
if (path->outboundTunnel && !path->outboundTunnel->IsEstablished ())
{
if (path) {
if (path->outboundTunnel && !path->outboundTunnel->IsEstablished()) {
// bad outbound tunnel, switch outbound tunnel
path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(path->outboundTunnel);
path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(
path->outboundTunnel);
if (!path->outboundTunnel)
m_RoutingSession->SetSharedRoutingPath(nullptr);
}
if (path->remoteLease && path->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW))
{
if (path->remoteLease && path->remoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
// bad lease, switch to next one
if (m_RemoteLeaseSet)
{
if (m_RemoteLeaseSet) {
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding(
[&](const i2p::data::Lease& l) -> bool
{
[&](const i2p::data::Lease &l) -> bool {
return l.tunnelID == path->remoteLease->tunnelID;
});
auto sz = ls.size();
if (sz)
{
if (sz) {
auto idx = rand() % sz;
path->remoteLease = ls[idx];
}
else
} else
m_RoutingSession->SetSharedRoutingPath(nullptr);
}
else
{
} else {
// no remote lease set?
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ",
m_RemoteIdent.ToBase32());
m_RoutingSession->SetSharedRoutingPath(nullptr);
}
}
}
else
{
} else {
// no current path, make one
path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
if (m_RemoteLeaseSet)
{
if (m_RemoteLeaseSet) {
// pick random next good lease
auto ls = m_RemoteLeaseSet->GetNonExpiredLeases();
auto sz = ls.size();
if (sz)
{
if (sz) {
auto idx = rand() % sz;
path->remoteLease = ls[idx];
}
else
} else
return nullptr;
auto leaseRouter = i2p::data::netdb.FindRouter(path->remoteLease->tunnelGateway);
path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(nullptr,
leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports);
leaseRouter
? leaseRouter->GetCompatibleTransports(
false)
: (i2p::data::RouterInfo::CompatibleTransports) i2p::data::RouterInfo::eAllTransports);
if (!path->outboundTunnel) return nullptr;
}
else
{
} else {
// no remote lease set currently, bail
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
return nullptr;
@ -401,8 +358,7 @@ namespace datagram
return path;
}
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls)
{
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls) {
m_RequestingLS = false;
if (!ls) return;
// only update lease set if found and newer than previous lease set
@ -411,19 +367,18 @@ namespace datagram
if (ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
}
void DatagramSession::FlushSendQueue ()
{
void DatagramSession::FlushSendQueue() {
if (m_SendQueue.empty()) return;
std::vector<i2p::tunnel::TunnelMessageBlock> send;
auto routingPath = GetSharedRoutingPath();
// if we don't have a routing path we will drop all queued messages
if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease)
{
for (const auto & msg : m_SendQueue)
{
if (routingPath && routingPath->outboundTunnel && routingPath->remoteLease) {
for (const auto &msg: m_SendQueue) {
auto m = m_RoutingSession->WrapSingleMessage(msg);
if (m)
send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m});
send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,
routingPath->remoteLease->tunnelGateway,
routingPath->remoteLease->tunnelID, m});
}
routingPath->outboundTunnel->SendTunnelDataMsg(send);
}

View file

@ -20,14 +20,11 @@
#include "I2NPProtocol.h"
#include "Garlic.h"
namespace i2p
{
namespace client
{
namespace i2p {
namespace client {
class ClientDestination;
}
namespace datagram
{
namespace datagram {
// milliseconds for max session idle time
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
// milliseconds for how long we try sticking to a dead routing path before trying to switch
@ -43,14 +40,15 @@ namespace datagram
// max 64 messages buffered in send queue for each datagram session
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
{
class DatagramSession : public std::enable_shared_from_this<DatagramSession> {
public:
DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, const i2p::data::IdentHash & remoteIdent);
DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash &remoteIdent);
void Start();
void Stop();
@ -59,19 +57,21 @@ namespace datagram
/** send an i2np message to remote endpoint for this session */
void SendMsg(std::shared_ptr<I2NPMessage> msg);
void FlushSendQueue();
/** get the last time in milliseconds for when we used this datagram session */
uint64_t LastActivity() const { return m_LastUse; }
bool IsRatchets() const { return m_RoutingSession && m_RoutingSession->IsRatchets(); }
struct Info
{
struct Info {
std::shared_ptr<const i2p::data::IdentHash> IBGW;
std::shared_ptr<const i2p::data::IdentHash> OBEP;
const uint64_t activity;
Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {}
Info(const uint8_t *ibgw, const uint8_t *obep, const uint64_t a) :
activity(a) {
if (ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
@ -104,34 +104,61 @@ namespace datagram
typedef std::shared_ptr<DatagramSession> DatagramSession_ptr;
const size_t MAX_DATAGRAM_SIZE = 32768;
class DatagramDestination
{
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver;
class DatagramDestination {
typedef std::function<void(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort,
const uint8_t *buf, size_t len)> Receiver;
typedef std::function<
void (uint16_t
fromPort,
uint16_t toPort,
const uint8_t *buf, size_t
len)>
RawReceiver;
public:
DatagramDestination(std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip);
~DatagramDestination();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void
SendDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident, uint16_t fromPort = 0,
uint16_t toPort = 0);
void SendRawDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident,
uint16_t fromPort = 0, uint16_t toPort = 0);
// TODO: implement calls from other thread from SAM
std::shared_ptr<DatagramSession> GetSession(const i2p::data::IdentHash &ident);
void SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
void SendDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
uint16_t fromPort, uint16_t toPort);
void SendRawDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
uint16_t fromPort, uint16_t toPort);
void FlushSendQueue(std::shared_ptr<DatagramSession> session);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
void HandleDataMessagePayload(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len,
bool isRaw = false);
void SetReceiver(const Receiver &receiver) { m_Receiver = receiver; };
void ResetReceiver() { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
void SetReceiver(const Receiver &receiver, uint16_t port) {
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
m_ReceiversByPorts[port] = receiver;
};
void ResetReceiver(uint16_t port) {
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
m_ReceiversByPorts.erase(port);
};
void SetRawReceiver(const RawReceiver &receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver() { m_RawReceiver = nullptr; };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash &remote);
@ -143,10 +170,12 @@ namespace datagram
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash &ident);
std::shared_ptr<I2NPMessage> CreateDataMessage (const std::vector<std::pair<const uint8_t *, size_t> >& payloads,
std::shared_ptr<I2NPMessage>
CreateDataMessage(const std::vector<std::pair<const uint8_t *, size_t> > &payloads,
uint16_t fromPort, uint16_t toPort, bool isRaw = false, bool checksum = true);
void HandleDatagram(uint16_t fromPort, uint16_t toPort, uint8_t *const &buf, size_t len);
void HandleRawDatagram(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */

File diff suppressed because it is too large Load diff

View file

@ -28,10 +28,8 @@
#include "Datagram.h"
#include "util.h"
namespace i2p
{
namespace client
{
namespace i2p {
namespace client {
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18;
@ -89,12 +87,11 @@ namespace client
typedef std::function<void(std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
class LeaseSetDestination : public i2p::garlic::GarlicDestination,
public std::enable_shared_from_this<LeaseSetDestination>
{
public std::enable_shared_from_this<LeaseSetDestination> {
typedef std::function<void(std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
// leaseSet = nullptr means not found
struct LeaseSetRequest
{
struct LeaseSetRequest {
LeaseSetRequest(boost::asio::io_service &service) : requestTime(0), requestTimeoutTimer(service) {};
std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
@ -104,8 +101,7 @@ namespace client
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
{
void Complete(std::shared_ptr<i2p::data::LeaseSet> ls) {
for (auto &it: requestComplete) it(ls);
requestComplete.clear();
}
@ -113,71 +109,115 @@ namespace client
public:
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
LeaseSetDestination(boost::asio::io_service &service, bool isPublic,
const std::map<std::string, std::string> *params = nullptr);
~LeaseSetDestination();
const std::string &GetNickname() const { return m_Nickname; };
boost::asio::io_service &GetService() { return m_Service; };
virtual void Start();
virtual void Stop();
/** i2cp reconfigure */
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
bool IsReady() const {
return m_LeaseSet && !m_LeaseSet->IsExpired() && m_Pool->GetOutboundTunnels().size() > 0;
};
std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet(const i2p::data::IdentHash &ident);
bool RequestDestination(const i2p::data::IdentHash &dest, RequestComplete requestComplete = nullptr);
bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete = nullptr);
bool RequestDestinationWithEncryptedLeaseSet(std::shared_ptr<const i2p::data::BlindedPublicKey> dest,
RequestComplete requestComplete = nullptr);
void CancelDestinationRequest(const i2p::data::IdentHash &dest, bool notify = true);
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
void CancelDestinationRequestWithEncryptedLeaseSet(std::shared_ptr<const i2p::data::BlindedPublicKey> dest,
bool notify = true);
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const { return m_Pool; }
// override GarlicDestination
bool SubmitSessionKey(const uint8_t *key, const uint8_t *tag);
void SubmitECIESx25519Key(const uint8_t *key, uint64_t tag);
void ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated();
bool IsPublic() const { return m_IsPublic; };
void SetPublic(bool pub) { m_IsPublic = pub; };
protected:
// implements GarlicDestination
void HandleI2NPMessage(const uint8_t *buf, size_t len);
bool HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID);
void SetLeaseSet(std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType() const { return m_LeaseSetType; };
void SetLeaseSetType(int leaseSetType) { m_LeaseSetType = leaseSetType; };
int GetAuthType() const { return m_AuthType; };
virtual void CleanupDestination() {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage(const uint8_t *buf, size_t len) = 0;
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
virtual void
CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels) = 0;
private:
void UpdateLeaseSet();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt();
void Publish();
void HandlePublishConfirmationTimer(const boost::system::error_code &ecode);
void HandlePublishVerificationTimer(const boost::system::error_code &ecode);
void HandlePublishDelayTimer(const boost::system::error_code &ecode);
void HandleDatabaseStoreMessage(const uint8_t *buf, size_t len);
void HandleDatabaseSearchReplyMessage(const uint8_t *buf, size_t len);
void HandleDeliveryStatusMessage(uint32_t msgID);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void RequestLeaseSet(const i2p::data::IdentHash &dest, RequestComplete requestComplete,
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest(const i2p::data::IdentHash &dest,
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill,
std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer(const boost::system::error_code &ecode, const i2p::data::IdentHash &dest);
void HandleCleanupTimer(const boost::system::error_code &ecode);
void CleanupRemoteLeaseSets();
i2p::data::CryptoKeyType GetPreferredCryptoType() const;
private:
@ -205,21 +245,31 @@ namespace client
// for HTTP only
int GetNumRemoteLeaseSets() const { return m_RemoteLeaseSets.size(); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet () const { return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; };
const decltype(m_RemoteLeaseSets)
&
GetLeaseSets() const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet() const {
return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
};
bool IsPerClientAuth() const { return m_AuthType > 0; };
};
class ClientDestination: public LeaseSetDestination
{
struct EncryptionKey
{
class ClientDestination : public LeaseSetDestination {
struct EncryptionKey {
uint8_t pub[256], priv[256];
i2p::data::CryptoKeyType keyType;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> decryptor;
EncryptionKey (i2p::data::CryptoKeyType t):keyType(t) { memset (pub, 0, 256); memset (priv, 0, 256); };
EncryptionKey(i2p::data::CryptoKeyType t) : keyType(t) {
memset(pub, 0, 256);
memset(priv, 0, 256);
};
void GenerateKeys() { i2p::data::PrivateKeys::GenerateCryptoKeyPair(keyType, priv, pub); };
void CreateDecryptor() { decryptor = i2p::data::PrivateKeys::CreateDecryptor(keyType, priv); };
};
@ -227,51 +277,78 @@ namespace client
ClientDestination(boost::asio::io_service &service, const i2p::data::PrivateKeys &keys,
bool isPublic, const std::map<std::string, std::string> *params = nullptr);
~ClientDestination();
void Start();
void Stop();
const i2p::data::PrivateKeys &GetPrivateKeys() const { return m_Keys; };
void Sign(const uint8_t *buf, int len, uint8_t *signature) const { m_Keys.Sign(buf, len, signature); };
// ref counter
int Acquire() { return ++m_RefCounter; };
int Release() { return --m_RefCounter; };
int GetRefCounter() const { return m_RefCounter; };
// streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination>
CreateStreamingDestination(int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination(int port = 0) const;
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination(int port);
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void
CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash &dest, int port = 0);
void CreateStream(StreamRequestComplete streamRequestComplete,
std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream>
CreateStream(std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing(const i2p::data::IdentHash &to);
void SendPing(std::shared_ptr<const i2p::data::BlindedPublicKey> to);
void AcceptStreams(const i2p::stream::StreamingDestination::Acceptor &acceptor);
void StopAcceptingStreams();
bool IsAcceptingStreams() const;
void AcceptOnce(const i2p::stream::StreamingDestination::Acceptor &acceptor);
int GetStreamingAckDelay() const { return m_StreamingAckDelay; }
bool IsStreamingAnswerPings() const { return m_IsStreamingAnswerPings; }
// datagram
i2p::datagram::DatagramDestination *GetDatagramDestination() const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination *CreateDatagramDestination(bool gzip = true);
// implements LocalDestination
bool Decrypt(const uint8_t *encrypted, uint8_t *data, i2p::data::CryptoKeyType preferredCrypto) const;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity() const { return m_Keys.GetPublic(); };
bool SupportsEncryptionType(i2p::data::CryptoKeyType keyType) const;
const uint8_t *GetEncryptionPublicKey(i2p::data::CryptoKeyType keyType) const;
protected:
void CleanupDestination();
// I2CP
void HandleDataMessage(const uint8_t *buf, size_t len);
void CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels);
private:
@ -279,7 +356,9 @@ namespace client
std::shared_ptr<ClientDestination> GetSharedFromThis() {
return std::static_pointer_cast<ClientDestination>(shared_from_this());
}
void PersistTemporaryKeys(EncryptionKey *keys, bool isSingleKey);
void ReadAuthKey(const std::string &group, const std::map<std::string, std::string> *params);
private:
@ -303,17 +382,20 @@ namespace client
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams() const;
bool DeleteStream(uint32_t recvStreamID);
};
class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination
{
class RunnableClientDestination : private i2p::util::RunnableService, public ClientDestination {
public:
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
RunnableClientDestination(const i2p::data::PrivateKeys &keys, bool isPublic,
const std::map<std::string, std::string> *params = nullptr);
~RunnableClientDestination();
void Start();
void Stop();
};

File diff suppressed because it is too large Load diff

View file

@ -21,10 +21,8 @@
#include "Garlic.h"
#include "Tag.h"
namespace i2p
{
namespace garlic
{
namespace i2p {
namespace garlic {
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after
const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of seconds we receive nothing and should restart if we can
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
@ -39,22 +37,29 @@ namespace garlic
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */
// - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */
class RatchetTagSet
{
class RatchetTagSet {
public:
RatchetTagSet() {};
virtual ~RatchetTagSet() {};
void DHInitialize(const uint8_t *rootKey, const uint8_t *k);
void NextSessionTagRatchet();
uint64_t GetNextSessionTag();
const uint8_t *GetNextRootKey() const { return m_NextRootKey; };
int GetNextIndex() const { return m_NextIndex; };
void GetSymmKey(int index, uint8_t *key);
void DeleteSymmKey(int index);
int GetTagSetID() const { return m_TagSetID; };
void SetTagSetID(int tagsetID) { m_TagSetID = tagsetID; };
private:
@ -68,23 +73,28 @@ namespace garlic
};
class ECIESX25519AEADRatchetSession;
class ReceiveRatchetTagSet : public RatchetTagSet,
public std::enable_shared_from_this<ReceiveRatchetTagSet>
{
public std::enable_shared_from_this<ReceiveRatchetTagSet> {
public:
ReceiveRatchetTagSet(std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false) :
m_Session(session), m_IsNS(isNS) {};
bool IsNS() const { return m_IsNS; };
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession() { return m_Session; };
void SetTrimBehind(int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
int GetTrimBehind() const { return m_TrimBehindIndex; };
void Expire();
bool IsExpired(uint64_t ts) const;
virtual bool IsIndexExpired(int index) const;
virtual bool HandleNextMessage(uint8_t *buf, size_t len, int index);
private:
@ -95,13 +105,13 @@ namespace garlic
uint64_t m_ExpirationTimestamp = 0;
};
class SymmetricKeyTagSet: public ReceiveRatchetTagSet
{
class SymmetricKeyTagSet : public ReceiveRatchetTagSet {
public:
SymmetricKeyTagSet(GarlicDestination *destination, const uint8_t *key);
bool IsIndexExpired(int index) const { return false; };
bool HandleNextMessage(uint8_t *buf, size_t len, int index);
private:
@ -110,8 +120,7 @@ namespace garlic
uint8_t m_Key[32];
};
enum ECIESx25519BlockType
{
enum ECIESx25519BlockType {
eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1,
eECIESx25519BlkTermination = 4,
@ -129,10 +138,8 @@ namespace garlic
class ECIESX25519AEADRatchetSession : public GarlicRoutingSession,
private i2p::crypto::NoiseSymmetricState,
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
{
enum SessionState
{
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession> {
enum SessionState {
eSessionStateNew = 0,
eSessionStateNewSessionReceived,
eSessionStateNewSessionSent,
@ -141,8 +148,7 @@ namespace garlic
eSessionStateOneTime
};
struct DHRatchet
{
struct DHRatchet {
int keyID = 0;
std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key
@ -152,36 +158,55 @@ namespace garlic
public:
ECIESX25519AEADRatchetSession(GarlicDestination *owner, bool attachLeaseSetNS);
~ECIESX25519AEADRatchetSession();
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
bool HandleNextMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage(std::shared_ptr<const I2NPMessage> msg);
const uint8_t *GetRemoteStaticKey() const { return m_RemoteStaticKey; }
void SetRemoteStaticKey(const uint8_t *key) { memcpy(m_RemoteStaticKey, key, 32); }
void Terminate() { m_IsTerminated = true; }
void SetDestination(const i2p::data::IdentHash &dest) // TODO:
{
if (!m_Destination) m_Destination.reset(new i2p::data::IdentHash(dest));
}
bool CheckExpired(uint64_t ts); // true is expired
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; }
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
bool CanBeRestarted(uint64_t ts) const {
return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT;
}
bool IsInactive(uint64_t ts) const {
return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted(ts);
}
bool IsRatchets() const { return true; };
bool IsReadyToSend() const { return m_State != eSessionStateNewSessionSent; };
bool IsTerminated() const { return m_IsTerminated; }
uint64_t GetLastActivityTimestamp() const { return m_LastActivityTimestamp; };
protected:
i2p::crypto::NoiseSymmetricState &GetNoiseState() { return *this; };
void SetNoiseState(const i2p::crypto::NoiseSymmetricState &state) { GetNoiseState() = state; };
void CreateNonce(uint64_t seqn, uint8_t *nonce);
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
void
HandlePayload(const uint8_t *buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet> &receiveTagset,
int index);
private:
@ -189,20 +214,34 @@ namespace garlic
void InitNewSessionTagset(std::shared_ptr<RatchetTagSet> tagsetNsr) const;
bool HandleNewIncomingSession(const uint8_t *buf, size_t len);
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len);
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index);
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
bool HandleNewOutgoingSessionReply(uint8_t *buf, size_t len);
bool
HandleExistingSessionMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
int index);
void
HandleNextKey(const uint8_t *buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet> &receiveTagset);
bool NewOutgoingSessionMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen,
bool isStatic = true);
bool NewSessionReplyMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen);
bool NextNewSessionReplyMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen);
bool NewExistingSessionMessage(const uint8_t *payload, size_t len, uint8_t *out, size_t outLen);
size_t CreatePayload(std::shared_ptr<const I2NPMessage> msg, bool first, uint8_t *payload);
size_t CreateGarlicClove(std::shared_ptr<const I2NPMessage> msg, uint8_t *buf, size_t len);
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len);
size_t CreateLeaseSetClove(std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t *buf,
size_t len);
void GenerateMoreReceiveTags(std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags);
void NewNextSendRatchet();
private:
@ -225,19 +264,20 @@ namespace garlic
// for HTTP only
int GetState() const { return (int) m_State; }
i2p::data::IdentHash GetDestination () const
{
i2p::data::IdentHash GetDestination() const {
return m_Destination ? *m_Destination : i2p::data::IdentHash();
}
};
// single session for all incoming messages
class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession
{
class RouterIncomingRatchetSession : public ECIESX25519AEADRatchetSession {
public:
RouterIncomingRatchetSession(const i2p::crypto::NoiseSymmetricState &initState);
bool HandleNextMessage(const uint8_t *buf, size_t len);
i2p::crypto::NoiseSymmetricState &GetCurrentNoiseState() { return m_CurrentNoiseState; };
private:
@ -245,8 +285,11 @@ namespace garlic
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState;
};
std::shared_ptr<I2NPMessage> WrapECIESX25519Message (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey);
std::shared_ptr<I2NPMessage>
WrapECIESX25519Message(std::shared_ptr<const I2NPMessage> msg, const uint8_t *key, uint64_t tag);
std::shared_ptr<I2NPMessage>
WrapECIESX25519MessageForRouter(std::shared_ptr<const I2NPMessage> msg, const uint8_t *routerPublicKey);
}
}

View file

@ -11,12 +11,9 @@
#include "Crypto.h"
#include "Ed25519.h"
namespace i2p
{
namespace crypto
{
Ed25519::Ed25519 ()
{
namespace i2p {
namespace crypto {
Ed25519::Ed25519() {
BN_CTX *ctx = BN_CTX_new();
BIGNUM *tmp = BN_new();
@ -62,8 +59,7 @@ namespace crypto
// precalculate Bi256 table
Bi256Carry = {Bx, By}; // B
for (int i = 0; i < 32; i++)
{
for (int i = 0; i < 32; i++) {
Bi256[i][0] = Bi256Carry; // first point
for (int j = 1; j < 128; j++)
Bi256[i][j] = Sum(Bi256[i][j - 1], Bi256[i][0], ctx); // (256+j+1)^i*B
@ -76,16 +72,15 @@ namespace crypto
}
Ed25519::Ed25519(const Ed25519 &other) : q(BN_dup(other.q)), l(BN_dup(other.l)),
d (BN_dup (other.d)), I (BN_dup (other.I)), two_252_2 (BN_dup (other.two_252_2)),
Bi256Carry (other.Bi256Carry)
{
d(BN_dup(other.d)), I(BN_dup(other.I)),
two_252_2(BN_dup(other.two_252_2)),
Bi256Carry(other.Bi256Carry) {
for (int i = 0; i < 32; i++)
for (int j = 0; j < 128; j++)
Bi256[i][j] = other.Bi256[i][j];
}
Ed25519::~Ed25519 ()
{
Ed25519::~Ed25519() {
BN_free(q);
BN_free(l);
BN_free(d);
@ -94,23 +89,19 @@ namespace crypto
}
EDDSAPoint Ed25519::GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const
{
EDDSAPoint Ed25519::GeneratePublicKey(const uint8_t *expandedPrivateKey, BN_CTX *ctx) const {
return MulB(expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian
}
EDDSAPoint Ed25519::DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const
{
EDDSAPoint Ed25519::DecodePublicKey(const uint8_t *buf, BN_CTX *ctx) const {
return DecodePoint(buf, ctx);
}
void Ed25519::EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const
{
void Ed25519::EncodePublicKey(const EDDSAPoint &publicKey, uint8_t *buf, BN_CTX *ctx) const {
EncodePoint(Normalize(publicKey, ctx), buf);
}
bool Ed25519::Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const
{
bool Ed25519::Verify(const EDDSAPoint &publicKey, const uint8_t *digest, const uint8_t *signature) const {
BN_CTX *ctx = BN_CTX_new();
BIGNUM *h = DecodeBN<64>(digest);
// signature 0..31 - R, 32..63 - S
@ -130,20 +121,22 @@ namespace crypto
}
void Ed25519::Sign(const uint8_t *expandedPrivateKey, const uint8_t *publicKeyEncoded,
const uint8_t * buf, size_t len, uint8_t * signature) const
{
const uint8_t *buf, size_t len, uint8_t *signature) const {
BN_CTX *bnCtx = BN_CTX_new();
// calculate r
SHA512_CTX ctx;
SHA512_Init(&ctx);
SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
SHA512_Update(&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH,
EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
SHA512_Update(&ctx, buf, len); // data
uint8_t digest[64];
SHA512_Final(digest, &ctx);
BIGNUM *r = DecodeBN<32>(digest); // DecodeBN<64> (digest); // for test vectors
// calculate R
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
uint8_t R[EDDSA25519_SIGNATURE_LENGTH /
2]; // we must use separate buffer because signature might be inside buf
EncodePoint(Normalize(MulB(digest, bnCtx), bnCtx),
R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
// calculate S
SHA512_Init(&ctx);
SHA512_Update(&ctx, R, EDDSA25519_SIGNATURE_LENGTH / 2); // R
@ -157,13 +150,14 @@ namespace crypto
BN_mod_add(h, h, r, l, bnCtx); // %l
memcpy(signature, R, EDDSA25519_SIGNATURE_LENGTH / 2);
EncodeBN(h, signature + EDDSA25519_SIGNATURE_LENGTH / 2, EDDSA25519_SIGNATURE_LENGTH / 2); // S
BN_free (r); BN_free (h); BN_free (a);
BN_free(r);
BN_free(h);
BN_free(a);
BN_CTX_free(bnCtx);
}
void Ed25519::SignRedDSA(const uint8_t *privateKey, const uint8_t *publicKeyEncoded,
const uint8_t * buf, size_t len, uint8_t * signature) const
{
const uint8_t *buf, size_t len, uint8_t *signature) const {
BN_CTX *bnCtx = BN_CTX_new();
// T = 80 random bytes
uint8_t T[80];
@ -180,7 +174,8 @@ namespace crypto
BN_mod(r, r, l, bnCtx); // % l
EncodeBN(r, digest, 32);
// calculate R
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
uint8_t R[EDDSA25519_SIGNATURE_LENGTH /
2]; // we must use separate buffer because signature might be inside buf
EncodePoint(Normalize(MulB(digest, bnCtx), bnCtx), R);
// calculate S
SHA512_Init(&ctx);
@ -195,12 +190,13 @@ namespace crypto
BN_mod_add(h, h, r, l, bnCtx); // %l
memcpy(signature, R, EDDSA25519_SIGNATURE_LENGTH / 2);
EncodeBN(h, signature + EDDSA25519_SIGNATURE_LENGTH / 2, EDDSA25519_SIGNATURE_LENGTH / 2); // S
BN_free (r); BN_free (h); BN_free (a);
BN_free(r);
BN_free(h);
BN_free(a);
BN_CTX_free(bnCtx);
}
EDDSAPoint Ed25519::Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const
{
EDDSAPoint Ed25519::Sum(const EDDSAPoint &p1, const EDDSAPoint &p2, BN_CTX *ctx) const {
// x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2)
// y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2)
// z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2)
@ -212,20 +208,23 @@ namespace crypto
BN_CTX_start(ctx);
BIGNUM *t1 = p1.t, *t2 = p2.t;
if (!t1) { t1 = BN_CTX_get (ctx); BN_mul (t1, p1.x, p1.y, ctx); }
if (!t2) { t2 = BN_CTX_get (ctx); BN_mul (t2, p2.x, p2.y, ctx); }
if (!t1) {
t1 = BN_CTX_get(ctx);
BN_mul(t1, p1.x, p1.y, ctx);
}
if (!t2) {
t2 = BN_CTX_get(ctx);
BN_mul(t2, p2.x, p2.y, ctx);
}
BN_mul(t3, t1, t2, ctx);
BN_mul(t3, t3, d, ctx); // C = d*t1*t2
if (p1.z)
{
if (p1.z) {
if (p2.z)
BN_mul(z3, p1.z, p2.z, ctx); // D = z1*z2
else
BN_copy(z3, p1.z); // D = z1
}
else
{
} else {
if (p2.z)
BN_copy(z3, p2.z); // D = z2
else
@ -252,8 +251,7 @@ namespace crypto
return EDDSAPoint{x3, y3, z3, t3};
}
void Ed25519::Double (EDDSAPoint& p, BN_CTX * ctx) const
{
void Ed25519::Double(EDDSAPoint &p, BN_CTX *ctx) const {
BN_CTX_start(ctx);
BIGNUM *x2 = BN_CTX_get(ctx), *y2 = BN_CTX_get(ctx), *z2 = BN_CTX_get(ctx), *t2 = BN_CTX_get(ctx);
@ -261,8 +259,7 @@ namespace crypto
BN_sqr(y2, p.y, ctx); // y2 = B = y^2
if (p.t)
BN_sqr(t2, p.t, ctx); // t2 = t^2
else
{
else {
BN_mul(t2, p.x, p.y, ctx); // t = x*y
BN_sqr(t2, t2, ctx); // t2 = t^2
}
@ -290,16 +287,14 @@ namespace crypto
BN_CTX_end(ctx);
}
EDDSAPoint Ed25519::Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const
{
EDDSAPoint Ed25519::Mul(const EDDSAPoint &p, const BIGNUM *e, BN_CTX *ctx) const {
BIGNUM *zero = BN_new(), *one = BN_new();
BN_zero (zero); BN_one (one);
BN_zero(zero);
BN_one(one);
EDDSAPoint res{zero, one};
if (!BN_is_zero (e))
{
if (!BN_is_zero(e)) {
int bitCount = BN_num_bits(e);
for (int i = bitCount - 1; i >= 0; i--)
{
for (int i = bitCount - 1; i >= 0; i--) {
Double(res, ctx);
if (BN_is_bit_set(e, i)) res = Sum(res, p, ctx);
}
@ -310,28 +305,23 @@ namespace crypto
EDDSAPoint Ed25519::MulB(const uint8_t *e, BN_CTX *ctx) const // B*e, e is 32 bytes Little Endian
{
BIGNUM *zero = BN_new(), *one = BN_new();
BN_zero (zero); BN_one (one);
BN_zero(zero);
BN_one(one);
EDDSAPoint res{zero, one};
bool carry = false;
for (int i = 0; i < 32; i++)
{
for (int i = 0; i < 32; i++) {
uint8_t x = e[i];
if (carry)
{
if (x < 255)
{
if (carry) {
if (x < 255) {
x++;
carry = false;
}
else
} else
x = 0;
}
if (x > 0)
{
if (x > 0) {
if (x <= 128)
res = Sum(res, Bi256[i][x - 1], ctx);
else
{
else {
res = Sum(res, -Bi256[i][255 - x], ctx); // -Bi[256-x]
carry = true;
}
@ -341,22 +331,18 @@ namespace crypto
return res;
}
EDDSAPoint Ed25519::Normalize (const EDDSAPoint& p, BN_CTX * ctx) const
{
if (p.z)
{
EDDSAPoint Ed25519::Normalize(const EDDSAPoint &p, BN_CTX *ctx) const {
if (p.z) {
BIGNUM *x = BN_new(), *y = BN_new();
BN_mod_inverse(y, p.z, q, ctx);
BN_mod_mul(x, p.x, y, q, ctx); // x = x/z
BN_mod_mul(y, p.y, y, q, ctx); // y = y/z
return EDDSAPoint{x, y};
}
else
} else
return EDDSAPoint{BN_dup(p.x), BN_dup(p.y)};
}
bool Ed25519::IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const
{
bool Ed25519::IsOnCurve(const EDDSAPoint &p, BN_CTX *ctx) const {
BN_CTX_start(ctx);
BIGNUM *x2 = BN_CTX_get(ctx), *y2 = BN_CTX_get(ctx), *tmp = BN_CTX_get(ctx);
BN_sqr(x2, p.x, ctx); // x^2
@ -373,8 +359,7 @@ namespace crypto
return ret;
}
BIGNUM * Ed25519::RecoverX (const BIGNUM * y, BN_CTX * ctx) const
{
BIGNUM *Ed25519::RecoverX(const BIGNUM *y, BN_CTX *ctx) const {
BN_CTX_start(ctx);
BIGNUM *y2 = BN_CTX_get(ctx), *xx = BN_CTX_get(ctx);
BN_sqr(y2, y, ctx); // y^2
@ -398,8 +383,7 @@ namespace crypto
return x;
}
EDDSAPoint Ed25519::DecodePoint (const uint8_t * buf, BN_CTX * ctx) const
{
EDDSAPoint Ed25519::DecodePoint(const uint8_t *buf, BN_CTX *ctx) const {
// buf is 32 bytes Little Endian, convert it to Big Endian
uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH];
for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH / 2; i++) // invert bytes
@ -416,23 +400,22 @@ namespace crypto
if (BN_is_bit_set(x, 0) != isHighestBitSet)
BN_sub(x, q, x); // x = q - x
BIGNUM *z = BN_new(), *t = BN_new();
BN_one (z); BN_mod_mul (t, x, y, q, ctx); // pre-calculate t
BN_one(z);
BN_mod_mul(t, x, y, q, ctx); // pre-calculate t
EDDSAPoint p{x, y, z, t};
if (!IsOnCurve(p, ctx))
LogPrint(eLogError, "Decoded point is not on 25519");
return p;
}
void Ed25519::EncodePoint (const EDDSAPoint& p, uint8_t * buf) const
{
void Ed25519::EncodePoint(const EDDSAPoint &p, uint8_t *buf) const {
EncodeBN(p.y, buf, EDDSA25519_PUBLIC_KEY_LENGTH);
if (BN_is_bit_set(p.x, 0)) // highest bit
buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit
}
template<int len>
BIGNUM * Ed25519::DecodeBN (const uint8_t * buf) const
{
BIGNUM *Ed25519::DecodeBN(const uint8_t *buf) const {
// buf is Little Endian convert it to Big Endian
uint8_t buf1[len];
for (size_t i = 0; i < len / 2; i++) // invert bytes
@ -445,8 +428,7 @@ namespace crypto
return res;
}
void Ed25519::EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const
{
void Ed25519::EncodeBN(const BIGNUM *bn, uint8_t *buf, size_t len) const {
bn2buf(bn, buf, len);
// To Little Endian
for (size_t i = 0; i < len / 2; i++) // invert bytes
@ -458,25 +440,30 @@ namespace crypto
}
#if !OPENSSL_X25519
BIGNUM * Ed25519::ScalarMul (const BIGNUM * u, const BIGNUM * k, BN_CTX * ctx) const
{
BIGNUM *Ed25519::ScalarMul(const BIGNUM *u, const BIGNUM *k, BN_CTX *ctx) const {
BN_CTX_start(ctx);
auto x1 = BN_CTX_get (ctx); BN_copy (x1, u);
auto x2 = BN_CTX_get (ctx); BN_one (x2);
auto z2 = BN_CTX_get (ctx); BN_zero (z2);
auto x3 = BN_CTX_get (ctx); BN_copy (x3, u);
auto z3 = BN_CTX_get (ctx); BN_one (z3);
auto c121666 = BN_CTX_get (ctx); BN_set_word (c121666, 121666);
auto tmp0 = BN_CTX_get (ctx); auto tmp1 = BN_CTX_get (ctx);
auto x1 = BN_CTX_get(ctx);
BN_copy(x1, u);
auto x2 = BN_CTX_get(ctx);
BN_one(x2);
auto z2 = BN_CTX_get(ctx);
BN_zero(z2);
auto x3 = BN_CTX_get(ctx);
BN_copy(x3, u);
auto z3 = BN_CTX_get(ctx);
BN_one(z3);
auto c121666 = BN_CTX_get(ctx);
BN_set_word(c121666, 121666);
auto tmp0 = BN_CTX_get(ctx);
auto tmp1 = BN_CTX_get(ctx);
unsigned int swap = 0;
auto bits = BN_num_bits(k);
while(bits)
{
while (bits) {
--bits;
auto k_t = BN_is_bit_set(k, bits) ? 1 : 0;
swap ^= k_t;
if (swap)
{
if (swap) {
std::swap(x2, x3);
std::swap(z2, z3);
}
@ -500,8 +487,7 @@ namespace crypto
BN_mod_mul(z3, x1, z2, q, ctx);
BN_mod_mul(z2, tmp1, tmp0, q, ctx);
}
if (swap)
{
if (swap) {
std::swap(x2, x3);
std::swap(z2, z3);
}
@ -512,33 +498,40 @@ namespace crypto
return res;
}
void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const
{
void Ed25519::ScalarMul(const uint8_t *p, const uint8_t *e, uint8_t *buf, BN_CTX *ctx) const {
BIGNUM *p1 = DecodeBN<32>(p);
uint8_t k[32];
memcpy(k, e, 32);
k[0] &= 248; k[31] &= 127; k[31] |= 64;
k[0] &= 248;
k[31] &= 127;
k[31] |= 64;
BIGNUM *n = DecodeBN<32>(k);
BIGNUM *q1 = ScalarMul(p1, n, ctx);
EncodeBN(q1, buf, 32);
BN_free (p1); BN_free (n); BN_free (q1);
BN_free(p1);
BN_free(n);
BN_free(q1);
}
void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const
{
BIGNUM *p1 = BN_new (); BN_set_word (p1, 9);
void Ed25519::ScalarMulB(const uint8_t *e, uint8_t *buf, BN_CTX *ctx) const {
BIGNUM *p1 = BN_new();
BN_set_word(p1, 9);
uint8_t k[32];
memcpy(k, e, 32);
k[0] &= 248; k[31] &= 127; k[31] |= 64;
k[0] &= 248;
k[31] &= 127;
k[31] |= 64;
BIGNUM *n = DecodeBN<32>(k);
BIGNUM *q1 = ScalarMul(p1, n, ctx);
EncodeBN(q1, buf, 32);
BN_free (p1); BN_free (n); BN_free (q1);
BN_free(p1);
BN_free(n);
BN_free(q1);
}
#endif
void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded)
{
void Ed25519::BlindPublicKey(const uint8_t *pub, const uint8_t *seed, uint8_t *blinded) {
BN_CTX *ctx = BN_CTX_new();
// calculate alpha = seed mod l
BIGNUM *alpha = DecodeBN<64>(seed); // seed is in Little Endian
@ -552,8 +545,8 @@ namespace crypto
BN_CTX_free(ctx);
}
void Ed25519::BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub)
{
void
Ed25519::BlindPrivateKey(const uint8_t *priv, const uint8_t *seed, uint8_t *blindedPriv, uint8_t *blindedPub) {
BN_CTX *ctx = BN_CTX_new();
// calculate alpha = seed mod l
BIGNUM *alpha = DecodeBN<64>(seed); // seed is in Little Endian
@ -566,20 +559,19 @@ namespace crypto
// A' = DERIVE_PUBLIC(a')
auto A1 = MulB(blindedPriv, ctx);
EncodePublicKey(A1, blindedPub, ctx);
BN_free (alpha); BN_free (p);
BN_free(alpha);
BN_free(p);
BN_CTX_free(ctx);
}
void Ed25519::ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey)
{
void Ed25519::ExpandPrivateKey(const uint8_t *key, uint8_t *expandedKey) {
SHA512(key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey);
expandedKey[0] &= 0xF8; // drop last 3 bits
expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits
expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit
}
void Ed25519::CreateRedDSAPrivateKey (uint8_t * priv)
{
void Ed25519::CreateRedDSAPrivateKey(uint8_t *priv) {
uint8_t seed[32];
RAND_bytes(seed, 32);
BIGNUM *p = DecodeBN<32>(seed);
@ -591,10 +583,9 @@ namespace crypto
}
static std::unique_ptr<Ed25519> g_Ed25519;
std::unique_ptr<Ed25519>& GetEd25519 ()
{
if (!g_Ed25519)
{
std::unique_ptr<Ed25519> &GetEd25519() {
if (!g_Ed25519) {
auto c = new Ed25519();
if (!g_Ed25519) // make sure it was not created already
g_Ed25519.reset(c);

View file

@ -13,59 +13,74 @@
#include <openssl/bn.h>
#include "Crypto.h"
namespace i2p
{
namespace crypto
{
struct EDDSAPoint
{
namespace i2p {
namespace crypto {
struct EDDSAPoint {
BIGNUM *x{nullptr};
BIGNUM *y{nullptr};
BIGNUM *z{nullptr};
BIGNUM *t{nullptr}; // projective coordinates
EDDSAPoint() {}
EDDSAPoint(const EDDSAPoint &other) { *this = other; }
EDDSAPoint(EDDSAPoint &&other) { *this = std::move(other); }
EDDSAPoint(BIGNUM *x1, BIGNUM *y1, BIGNUM *z1 = nullptr, BIGNUM *t1 = nullptr)
: x(x1)
, y(y1)
, z(z1)
, t(t1)
{}
~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); }
: x(x1), y(y1), z(z1), t(t1) {}
EDDSAPoint& operator=(EDDSAPoint&& other)
{
if (this != &other)
{
BN_free (x); x = other.x; other.x = nullptr;
BN_free (y); y = other.y; other.y = nullptr;
BN_free (z); z = other.z; other.z = nullptr;
BN_free (t); t = other.t; other.t = nullptr;
~EDDSAPoint() {
BN_free(x);
BN_free(y);
BN_free(z);
BN_free(t);
}
EDDSAPoint &operator=(EDDSAPoint &&other) {
if (this != &other) {
BN_free(x);
x = other.x;
other.x = nullptr;
BN_free(y);
y = other.y;
other.y = nullptr;
BN_free(z);
z = other.z;
other.z = nullptr;
BN_free(t);
t = other.t;
other.t = nullptr;
}
return *this;
}
EDDSAPoint& operator=(const EDDSAPoint& other)
{
if (this != &other)
{
BN_free (x); x = other.x ? BN_dup (other.x) : nullptr;
BN_free (y); y = other.y ? BN_dup (other.y) : nullptr;
BN_free (z); z = other.z ? BN_dup (other.z) : nullptr;
BN_free (t); t = other.t ? BN_dup (other.t) : nullptr;
EDDSAPoint &operator=(const EDDSAPoint &other) {
if (this != &other) {
BN_free(x);
x = other.x ? BN_dup(other.x) : nullptr;
BN_free(y);
y = other.y ? BN_dup(other.y) : nullptr;
BN_free(z);
z = other.z ? BN_dup(other.z) : nullptr;
BN_free(t);
t = other.t ? BN_dup(other.t) : nullptr;
}
return *this;
}
EDDSAPoint operator-() const
{
EDDSAPoint operator-() const {
BIGNUM *x1 = NULL, *y1 = NULL, *z1 = NULL, *t1 = NULL;
if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); };
if (x) {
x1 = BN_dup(x);
BN_set_negative(x1, !BN_is_negative(x));
};
if (y) y1 = BN_dup(y);
if (z) z1 = BN_dup(z);
if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); };
if (t) {
t1 = BN_dup(t);
BN_set_negative(t1, !BN_is_negative(t));
};
return EDDSAPoint{x1, y1, z1, t1};
}
};
@ -73,51 +88,77 @@ namespace crypto
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
class Ed25519
{
class Ed25519 {
public:
Ed25519();
Ed25519(const Ed25519 &other);
~Ed25519();
EDDSAPoint GeneratePublicKey(const uint8_t *expandedPrivateKey, BN_CTX *ctx) const;
EDDSAPoint DecodePublicKey(const uint8_t *buf, BN_CTX *ctx) const;
void EncodePublicKey(const EDDSAPoint &publicKey, uint8_t *buf, BN_CTX *ctx) const;
#if !OPENSSL_X25519
void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519
void ScalarMul(const uint8_t *p, const uint8_t *e, uint8_t *buf,
BN_CTX *ctx) const; // p is point, e is number for x25519
void ScalarMulB(const uint8_t *e, uint8_t *buf, BN_CTX *ctx) const;
#endif
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void BlindPublicKey(const uint8_t *pub, const uint8_t *seed,
uint8_t *blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void BlindPrivateKey(const uint8_t *priv, const uint8_t *seed, uint8_t *blindedPriv,
uint8_t *blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
bool Verify(const EDDSAPoint &publicKey, const uint8_t *digest, const uint8_t *signature) const;
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const;
void SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const;
static void ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey); // key - 32 bytes, expandedKey - 64 bytes
void
Sign(const uint8_t *expandedPrivateKey, const uint8_t *publicKeyEncoded, const uint8_t *buf, size_t len,
uint8_t *signature) const;
void SignRedDSA(const uint8_t *privateKey, const uint8_t *publicKeyEncoded, const uint8_t *buf, size_t len,
uint8_t *signature) const;
static void
ExpandPrivateKey(const uint8_t *key, uint8_t *expandedKey); // key - 32 bytes, expandedKey - 64 bytes
void CreateRedDSAPrivateKey(uint8_t *priv); // priv is 32 bytes
private:
EDDSAPoint Sum(const EDDSAPoint &p1, const EDDSAPoint &p2, BN_CTX *ctx) const;
void Double(EDDSAPoint &p, BN_CTX *ctx) const;
EDDSAPoint Mul(const EDDSAPoint &p, const BIGNUM *e, BN_CTX *ctx) const;
EDDSAPoint MulB(const uint8_t *e, BN_CTX *ctx) const; // B*e, e is 32 bytes Little Endian
EDDSAPoint Normalize(const EDDSAPoint &p, BN_CTX *ctx) const;
bool IsOnCurve(const EDDSAPoint &p, BN_CTX *ctx) const;
BIGNUM *RecoverX(const BIGNUM *y, BN_CTX *ctx) const;
EDDSAPoint DecodePoint(const uint8_t *buf, BN_CTX *ctx) const;
void EncodePoint(const EDDSAPoint &p, uint8_t *buf) const;
template<int len>
BIGNUM *DecodeBN(const uint8_t *buf) const;
void EncodeBN(const BIGNUM *bn, uint8_t *buf, size_t len) const;
#if !OPENSSL_X25519
// for x25519
BIGNUM *ScalarMul(const BIGNUM *p, const BIGNUM *e, BN_CTX *ctx) const;
#endif
private:

View file

@ -10,24 +10,29 @@
#include "Crypto.h"
#include "Elligator.h"
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
Elligator2::Elligator2 ()
{
Elligator2::Elligator2() {
// TODO: share with Ed22519
p = BN_new();
// 2^255-19
BN_set_bit(p, 255); // 2^255
BN_sub_word(p, 19);
p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8
p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2
p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4
p38 = BN_dup(p);
BN_add_word(p38, 3);
BN_div_word(p38, 8); // (p+3)/8
p12 = BN_dup(p);
BN_sub_word(p12, 1);
BN_div_word(p12, 2); // (p-1)/2
p14 = BN_dup(p);
BN_sub_word(p14, 1);
BN_div_word(p14, 4); // (p-1)/4
A = BN_new (); BN_set_word (A, 486662);
nA = BN_new (); BN_sub (nA, p, A);
A = BN_new();
BN_set_word(A, 486662);
nA = BN_new();
BN_sub(nA, p, A);
BN_CTX *ctx = BN_CTX_new();
// calculate sqrt(-1)
@ -35,21 +40,27 @@ namespace crypto
BN_set_word(sqrtn1, 2);
BN_mod_exp(sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
u = BN_new (); BN_set_word (u, 2);
iu = BN_new (); BN_mod_inverse (iu, u, p, ctx);
u = BN_new();
BN_set_word(u, 2);
iu = BN_new();
BN_mod_inverse(iu, u, p, ctx);
BN_CTX_free(ctx);
}
Elligator2::~Elligator2 ()
{
BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14);
BN_free (sqrtn1); BN_free (A); BN_free (nA);
BN_free (u); BN_free (iu);
Elligator2::~Elligator2() {
BN_free(p);
BN_free(p38);
BN_free(p12);
BN_free(p14);
BN_free(sqrtn1);
BN_free(A);
BN_free(nA);
BN_free(u);
BN_free(iu);
}
bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const
{
bool Elligator2::Encode(const uint8_t *key, uint8_t *encoded, bool highY, bool random) const {
bool ret = true;
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
@ -61,31 +72,28 @@ namespace crypto
key1[31 - i] = key[i];
}
BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x);
BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A
BIGNUM *x = BN_CTX_get(ctx);
BN_bin2bn(key1, 32, x);
BIGNUM *xA = BN_CTX_get(ctx);
BN_add(xA, x, A); // x + A
BN_sub(xA, p, xA); // p - (x + A)
BIGNUM *uxxA = BN_CTX_get(ctx); // u*x*xA
BN_mod_mul(uxxA, u, x, p, ctx);
BN_mod_mul(uxxA, uxxA, xA, p, ctx);
if (Legendre (uxxA, ctx) != -1)
{
if (Legendre(uxxA, ctx) != -1) {
uint8_t randByte = 0; // random highest bits and high y
if (random)
{
if (random) {
RAND_bytes(&randByte, 1);
highY = randByte & 0x01;
}
BIGNUM *r = BN_CTX_get(ctx);
if (highY)
{
if (highY) {
BN_mod_inverse(r, x, p, ctx);
BN_mod_mul(r, r, xA, p, ctx);
}
else
{
} else {
BN_mod_inverse(r, xA, p, ctx);
BN_mod_mul(r, r, x, p, ctx);
}
@ -102,8 +110,7 @@ namespace crypto
encoded[i] = encoded[31 - i];
encoded[31 - i] = tmp;
}
}
else
} else
ret = false;
BN_CTX_end(ctx);
@ -111,8 +118,7 @@ namespace crypto
return ret;
}
bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const
{
bool Elligator2::Decode(const uint8_t *encoded, uint8_t *key) const {
bool ret = true;
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
@ -125,12 +131,14 @@ namespace crypto
}
encoded1[0] &= 0x3F; // drop two highest bits
BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r);
BIGNUM *r = BN_CTX_get(ctx);
BN_bin2bn(encoded1, 32, r);
if (BN_cmp(r, p12) <= 0) // r < (p-1)/2
{
// v = -A/(1+u*r^2)
BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx);
BIGNUM *v = BN_CTX_get(ctx);
BN_mod_sqr(v, r, p, ctx);
BN_mod_mul(v, v, u, p, ctx);
BN_add_word(v, 1);
BN_mod_inverse(v, v, p, ctx);
@ -139,7 +147,8 @@ namespace crypto
BIGNUM *vpA = BN_CTX_get(ctx);
BN_add(vpA, v, A); // v + A
// t = v^3+A*v^2+v = v^2*(v+A)+v
BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx);
BIGNUM *t = BN_CTX_get(ctx);
BN_mod_sqr(t, v, p, ctx);
BN_mod_mul(t, t, vpA, p, ctx);
BN_mod_add(t, t, v, p, ctx);
@ -147,8 +156,7 @@ namespace crypto
BIGNUM *x = BN_CTX_get(ctx);
if (legendre == 1)
BN_copy(x, v);
else
{
else {
BN_sub(x, p, v);
BN_mod_sub(x, x, A, p, ctx);
}
@ -160,8 +168,7 @@ namespace crypto
key[i] = key[31 - i];
key[31 - i] = tmp;
}
}
else
} else
ret = false;
BN_CTX_end(ctx);
@ -170,8 +177,7 @@ namespace crypto
return ret;
}
void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const
{
void Elligator2::SquareRoot(const BIGNUM *x, BIGNUM *r, BN_CTX *ctx) const {
BIGNUM *t = BN_CTX_get(ctx);
BN_mod_exp(t, x, p14, p, ctx); // t = x^((p-1)/4)
BN_mod_exp(r, x, p38, p, ctx); // r = x^((p+3)/8)
@ -184,8 +190,7 @@ namespace crypto
BN_sub(r, p, r);
}
int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const
{
int Elligator2::Legendre(const BIGNUM *a, BN_CTX *ctx) const {
// assume a < p, so don't check for a % p = 0, but a = 0 only
if (BN_is_zero(a)) return 0;
BIGNUM *r = BN_CTX_get(ctx);
@ -198,10 +203,9 @@ namespace crypto
}
static std::unique_ptr<Elligator2> g_Elligator;
std::unique_ptr<Elligator2>& GetElligator ()
{
if (!g_Elligator)
{
std::unique_ptr<Elligator2> &GetElligator() {
if (!g_Elligator) {
auto el = new Elligator2();
if (!g_Elligator) // make sure it was not created already
g_Elligator.reset(el);

View file

@ -13,24 +13,24 @@
#include <memory>
#include <openssl/bn.h>
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
class Elligator2
{
class Elligator2 {
public:
Elligator2();
~Elligator2();
bool Encode(const uint8_t *key, uint8_t *encoded, bool highY = false, bool random = true) const;
bool Decode(const uint8_t *encoded, uint8_t *key) const;
private:
void SquareRoot(const BIGNUM *x, BIGNUM *r, BN_CTX *ctx) const;
int Legendre(const BIGNUM *a, BN_CTX *ctx) const; // a/p
private:

View file

@ -158,15 +158,12 @@ namespace fs {
}
void SetCertsDir(const std::string &cmdline_certsdir) {
if (cmdline_certsdir != "")
{
if (cmdline_certsdir != "") {
if (cmdline_certsdir[cmdline_certsdir.length() - 1] == '/')
certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size() - 1); // strip trailing slash
else
certsDir = cmdline_certsdir;
}
else
{
} else {
certsDir = i2p::fs::DataDirPath("certificates");
}
return;
@ -208,8 +205,7 @@ namespace fs {
return boost::filesystem::exists(path);
}
uint32_t GetLastUpdateTime (const std::string & path)
{
uint32_t GetLastUpdateTime(const std::string &path) {
if (!boost::filesystem::exists(path))
return 0;
boost::system::error_code ec;
@ -223,8 +219,7 @@ namespace fs {
return boost::filesystem::remove(path);
}
bool CreateDirectory (const std::string& path)
{
bool CreateDirectory(const std::string &path) {
if (boost::filesystem::exists(path) && boost::filesystem::is_directory(boost::filesystem::status(path)))
return true;
return boost::filesystem::create_directory(path);
@ -276,8 +271,7 @@ namespace fs {
});
}
void HashedStorage::Iterate(FilenameVisitor v)
{
void HashedStorage::Iterate(FilenameVisitor v) {
boost::filesystem::path p(root);
boost::filesystem::recursive_directory_iterator it(p);
boost::filesystem::recursive_directory_iterator end;

View file

@ -35,8 +35,7 @@ namespace fs {
* std::vector<std::string> files;
* h.Traverse(files); <- finds all files in storage and saves in given vector
*/
class HashedStorage
{
class HashedStorage {
protected:
std::string root; /**< path to storage with it's name included */
@ -48,27 +47,36 @@ namespace fs {
public:
typedef std::function<void(const std::string &)> FilenameVisitor;
HashedStorage(const char *n, const char *p1, const char *p2, const char *s) :
name(n), prefix1(p1), prefix2(p2), suffix(s) {};
/** create subdirs in storage */
bool Init(const char *chars, size_t cnt);
const std::string &GetRoot() const { return root; }
const std::string &GetName() const { return name; }
/** set directory where to place storage directory */
void SetPlace(const std::string &path);
/** path to file with given ident */
std::string Path(const std::string &ident) const;
/** remove file by ident */
void Remove(const std::string &ident);
/** find all files in storage and store list in provided vector */
void Traverse(std::vector <std::string> &files);
/** visit every file in this storage with a visitor */
void Iterate(FilenameVisitor v);
};
/** @brief Returns current application name, default 'i2pd' */
const std::string &GetAppName();
/** @brief Set application name, affects autodetection of datadir */
void SetAppName(const std::string &name);
@ -168,8 +176,7 @@ namespace fs {
}
template<typename Storage, typename... Filename>
std::string StorageRootPath (const Storage& storage, Filename... filenames)
{
std::string StorageRootPath(const Storage &storage, Filename... filenames) {
std::stringstream s("");
s << storage.GetRoot();
_ExpandPath(s, filenames...);

View file

@ -15,68 +15,55 @@
#include "Family.h"
#include "Config.h"
namespace i2p
{
namespace data
{
Families::Families ()
{
namespace i2p {
namespace data {
Families::Families() {
}
Families::~Families ()
{
Families::~Families() {
}
void Families::LoadCertificate (const std::string& filename)
{
void Families::LoadCertificate(const std::string &filename) {
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
int ret = SSL_CTX_use_certificate_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
if (ret)
{
if (ret) {
SSL *ssl = SSL_new(ctx);
X509 *cert = SSL_get_certificate(ssl);
if (cert)
{
if (cert) {
std::shared_ptr<i2p::crypto::Verifier> verifier;
// extract issuer name
char name[100];
X509_NAME_oneline(X509_get_issuer_name(cert), name, 100);
char *cn = strstr(name, "CN=");
if (cn)
{
if (cn) {
cn += 3;
char *family = strstr(cn, ".family");
if (family) family[0] = 0;
}
auto pkey = X509_get_pubkey(cert);
int keyType = EVP_PKEY_base_id(pkey);
switch (keyType)
{
switch (keyType) {
case EVP_PKEY_DSA:
// TODO:
break;
case EVP_PKEY_EC:
{
case EVP_PKEY_EC: {
EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(pkey);
if (ecKey)
{
if (ecKey) {
auto group = EC_KEY_get0_group(ecKey);
if (group)
{
if (group) {
int curve = EC_GROUP_get_curve_name(group);
if (curve == NID_X9_62_prime256v1)
{
if (curve == NID_X9_62_prime256v1) {
uint8_t signingKey[64];
BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp(group,
EC_KEY_get0_public_key(ecKey), x, y, NULL);
i2p::crypto::bn2buf(x, signingKey, 32);
i2p::crypto::bn2buf(y, signingKey + 32, 32);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>();
verifier->SetPublicKey(signingKey);
}
else
} else
LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
EC_KEY_free(ecKey);
@ -91,14 +78,12 @@ namespace data
m_SigningKeys.emplace(cn, std::make_pair(verifier, m_SigningKeys.size() + 1));
}
SSL_free(ssl);
}
else
} else
LogPrint(eLogError, "Family: Can't open certificate file ", filename);
SSL_CTX_free(ctx);
}
void Families::LoadCertificates ()
{
void Families::LoadCertificates() {
std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family";
std::vector<std::string> files;
@ -121,12 +106,10 @@ namespace data
}
bool Families::VerifyFamily(const std::string &family, const IdentHash &ident,
const char * signature, const char * key) const
{
const char *signature, const char *key) const {
uint8_t buf[100], signatureBuf[64];
size_t len = family.length(), signatureLen = strlen(signature);
if (len + 32 > 100)
{
if (len + 32 > 100) {
LogPrint(eLogError, "Family: ", family, " is too long");
return false;
}
@ -142,33 +125,27 @@ namespace data
return true;
}
FamilyID Families::GetFamilyID (const std::string& family) const
{
FamilyID Families::GetFamilyID(const std::string &family) const {
auto it = m_SigningKeys.find(family);
if (it != m_SigningKeys.end())
return it->second.second;
return 0;
}
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident)
{
std::string CreateFamilySignature(const std::string &family, const IdentHash &ident) {
auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
std::string sig;
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
int ret = SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
if (ret)
{
if (ret) {
SSL *ssl = SSL_new(ctx);
EVP_PKEY *pkey = SSL_get_privatekey(ssl);
EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(pkey);
if (ecKey)
{
if (ecKey) {
auto group = EC_KEY_get0_group(ecKey);
if (group)
{
if (group) {
int curve = EC_GROUP_get_curve_name(group);
if (curve == NID_X9_62_prime256v1)
{
if (curve == NID_X9_62_prime256v1) {
uint8_t signingPrivateKey[32], buf[50], signature[64];
i2p::crypto::bn2buf(EC_KEY_get0_private_key(ecKey), signingPrivateKey, 32);
i2p::crypto::ECDSAP256Signer signer(signingPrivateKey);
@ -183,14 +160,12 @@ namespace data
b64[len] = 0;
sig = b64;
delete[] b64;
}
else
} else
LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
}
SSL_free(ssl);
}
else
} else
LogPrint(eLogError, "Family: Can't open keys file: ", filename);
SSL_CTX_free(ctx);
return sig;

View file

@ -15,20 +15,22 @@
#include "Signature.h"
#include "Identity.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
typedef int FamilyID;
class Families
{
class Families {
public:
Families();
~Families();
void LoadCertificates();
bool VerifyFamily(const std::string &family, const IdentHash &ident,
const char *signature, const char *key = nullptr) const;
FamilyID GetFamilyID(const std::string &family) const;
private:

File diff suppressed because it is too large Load diff

View file

@ -22,26 +22,21 @@
#include "Queue.h"
#include "Identity.h"
namespace i2p
{
namespace tunnel
{
namespace i2p {
namespace tunnel {
class OutboundTunnel;
}
namespace garlic
{
namespace garlic {
enum GarlicDeliveryType
{
enum GarlicDeliveryType {
eGarlicDeliveryTypeLocal = 0,
eGarlicDeliveryTypeDestination = 1,
eGarlicDeliveryTypeRouter = 2,
eGarlicDeliveryTypeTunnel = 3
};
struct ElGamalBlock
{
struct ElGamalBlock {
uint8_t sessionKey[32];
uint8_t preIV[32];
uint8_t padding[158];
@ -54,28 +49,33 @@ namespace garlic
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used
struct SessionTag: public i2p::data::Tag<32>
{
struct SessionTag : public i2p::data::Tag<32> {
SessionTag(const uint8_t *buf, uint32_t ts = 0) : Tag<32>(buf), creationTime(ts) {};
SessionTag() = default;
SessionTag(const SessionTag &) = default;
SessionTag &operator=(const SessionTag &) = default;
#ifndef _WIN32
SessionTag(SessionTag &&) = default;
SessionTag &operator=(SessionTag &&) = default;
#endif
uint32_t creationTime; // seconds since epoch
};
// AESDecryption is associated with session tags and store key
class AESDecryption: public i2p::crypto::CBCDecryption
{
class AESDecryption : public i2p::crypto::CBCDecryption {
public:
AESDecryption (const uint8_t * key): m_Key (key)
{
AESDecryption(const uint8_t *key) : m_Key(key) {
SetKey(key);
}
const i2p::crypto::AESKey &GetKey() const { return m_Key; };
private:
@ -83,8 +83,7 @@ namespace garlic
i2p::crypto::AESKey m_Key;
};
struct GarlicRoutingPath
{
struct GarlicRoutingPath {
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease;
int rtt; // RTT
@ -93,12 +92,11 @@ namespace garlic
};
class GarlicDestination;
class GarlicRoutingSession
{
class GarlicRoutingSession {
protected:
enum LeaseSetUpdateStatus
{
enum LeaseSetUpdateStatus {
eLeaseSetUpToDate = 0,
eLeaseSetUpdated,
eLeaseSetSubmitted,
@ -108,37 +106,54 @@ namespace garlic
public:
GarlicRoutingSession(GarlicDestination *owner, bool attachLeaseSet);
GarlicRoutingSession();
virtual ~GarlicRoutingSession();
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg) = 0;
virtual bool CleanupUnconfirmedTags() { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed(uint32_t msgID);
virtual bool IsRatchets() const { return false; };
virtual bool IsReadyToSend() const { return true; };
virtual bool IsTerminated() const { return !GetOwner(); };
virtual uint64_t GetLastActivityTimestamp() const { return 0; }; // non-zero for rathets only
void SetLeaseSetUpdated ()
{
void SetLeaseSetUpdated() {
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
};
bool IsLeaseSetNonConfirmed() const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
bool IsLeaseSetUpdated() const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
uint64_t GetLeaseSetSubmissionTime() const { return m_LeaseSetSubmissionTime; }
void CleanupUnconfirmedLeaseSet(uint64_t ts);
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath();
void SetSharedRoutingPath(std::shared_ptr<GarlicRoutingPath> path);
GarlicDestination *GetOwner() const { return m_Owner; }
void SetOwner(GarlicDestination *owner) { m_Owner = owner; }
protected:
LeaseSetUpdateStatus GetLeaseSetUpdateStatus() const { return m_LeaseSetUpdateStatus; }
void SetLeaseSetUpdateStatus(LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; }
uint32_t GetLeaseSetUpdateMsgID() const { return m_LeaseSetUpdateMsgID; }
void SetLeaseSetUpdateMsgID(uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; }
void SetLeaseSetSubmissionTime(uint64_t ts) { m_LeaseSetSubmissionTime = ts; }
std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg(uint32_t msgID);
@ -158,14 +173,14 @@ namespace garlic
// for HTTP only
virtual size_t GetNumOutgoingTags() const { return 0; };
};
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession>
{
struct UnconfirmedTags
{
class ElGamalAESSession : public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession> {
struct UnconfirmedTags {
UnconfirmedTags(int n) : numTags(n), tagsCreationTime(0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags() { delete[] sessionTags; };
uint32_t msgID;
int numTags;
@ -175,25 +190,33 @@ namespace garlic
public:
ElGamalAESSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
ElGamalAESSession(GarlicDestination *owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination,
int numTags, bool attachLeaseSet);
ElGamalAESSession(const uint8_t *sessionKey, const SessionTag &sessionTag); // one time encryption
~ElGamalAESSession() {};
std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg);
bool MessageConfirmed(uint32_t msgID);
bool CleanupExpiredTags(); // returns true if something left
bool CleanupUnconfirmedTags(); // returns true if something has been deleted
private:
size_t CreateAESBlock(uint8_t *buf, std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
size_t
CreateGarlicPayload(uint8_t *payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags *newTags);
size_t CreateGarlicClove(uint8_t *buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
size_t CreateDeliveryStatusClove(uint8_t *buf, uint32_t msgID);
void TagsConfirmed(uint32_t msgID);
UnconfirmedTags *GenerateSessionTags();
private:
@ -212,33 +235,45 @@ namespace garlic
// for HTTP only
size_t GetNumOutgoingTags() const { return m_SessionTags.size(); };
};
typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr;
class ECIESX25519AEADRatchetSession;
typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr;
class ReceiveRatchetTagSet;
typedef std::shared_ptr<ReceiveRatchetTagSet> ReceiveRatchetTagSetPtr;
struct ECIESX25519AEADRatchetIndexTagset
{
struct ECIESX25519AEADRatchetIndexTagset {
int index;
ReceiveRatchetTagSetPtr tagset;
};
class GarlicDestination: public i2p::data::LocalDestination
{
class GarlicDestination : public i2p::data::LocalDestination {
public:
GarlicDestination();
~GarlicDestination();
void CleanUp();
void SetNumTags(int numTags) { m_NumTags = numTags; };
int GetNumTags() const { return m_NumTags; };
void SetNumRatchetInboundTags(int numTags) { m_NumRatchetInboundTags = numTags; };
int GetNumRatchetInboundTags() const { return m_NumRatchetInboundTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
std::shared_ptr<GarlicRoutingSession>
GetRoutingSession(std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags();
void RemoveDeliveryStatusSession(uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessageForRouter(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg);
@ -247,14 +282,21 @@ namespace garlic
virtual bool SubmitSessionKey(const uint8_t *key, const uint8_t *tag); // from different thread
virtual void SubmitECIESx25519Key(const uint8_t *key, uint64_t tag); // from different thread
void DeliveryStatusSent(GarlicRoutingSessionPtr session, uint32_t msgID);
uint64_t AddECIESx25519SessionNextTag(ReceiveRatchetTagSetPtr tagset);
void AddECIESx25519Session(const uint8_t *staticKey, ECIESX25519AEADRatchetSessionPtr session);
void RemoveECIESx25519Session(const uint8_t *staticKey);
void HandleECIESx25519GarlicClove(const uint8_t *buf, size_t len);
uint8_t *GetPayloadBuffer();
virtual void ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated();
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet() = 0; // TODO
@ -265,17 +307,22 @@ namespace garlic
void AddECIESx25519Key(const uint8_t *key, const uint8_t *tag); // one tag
bool HandleECIESx25519TagMessage(uint8_t *buf, size_t len); // return true if found
virtual void HandleI2NPMessage(const uint8_t *buf, size_t len) = 0; // called from clove only
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0;
virtual bool
HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID) = 0;
void HandleGarlicMessage(std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage(uint32_t msgID);
void SaveTags();
void LoadTags();
private:
void HandleAESBlock(uint8_t *buf, size_t len, std::shared_ptr<AESDecryption> decryption,
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload(uint8_t *buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private:
@ -299,9 +346,17 @@ namespace garlic
// for HTTP only
size_t GetNumIncomingTags() const { return m_Tags.size(); }
size_t GetNumIncomingECIESx25519Tags() const { return m_ECIESx25519Tags.size(); }
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
const decltype(m_ECIESx25519Sessions)& GetECIESx25519Sessions () const { return m_ECIESx25519Sessions; }
const decltype(m_Sessions)
&
GetSessions() const { return m_Sessions; };
const decltype(m_ECIESx25519Sessions)
&
GetECIESx25519Sessions() const { return m_ECIESx25519Sessions; }
};
void CleanUpTagsFiles();

View file

@ -14,15 +14,12 @@
#include "I2PEndian.h"
#include "Gost.h"
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
// GOST R 34.10
GOSTR3410Curve::GOSTR3410Curve (BIGNUM * a, BIGNUM * b, BIGNUM * p, BIGNUM * q, BIGNUM * x, BIGNUM * y)
{
GOSTR3410Curve::GOSTR3410Curve(BIGNUM *a, BIGNUM *b, BIGNUM *p, BIGNUM *q, BIGNUM *x, BIGNUM *y) {
m_KeyLen = BN_num_bytes(p);
BN_CTX *ctx = BN_CTX_new();
m_Group = EC_GROUP_new_curve_GFp(p, a, b, ctx);
@ -34,13 +31,11 @@ namespace crypto
BN_CTX_free(ctx);
}
GOSTR3410Curve::~GOSTR3410Curve ()
{
GOSTR3410Curve::~GOSTR3410Curve() {
EC_GROUP_free(m_Group);
}
EC_POINT * GOSTR3410Curve::MulP (const BIGNUM * n) const
{
EC_POINT *GOSTR3410Curve::MulP(const BIGNUM *n) const {
BN_CTX *ctx = BN_CTX_new();
auto p = EC_POINT_new(m_Group);
EC_POINT_mul(m_Group, p, n, nullptr, nullptr, ctx);
@ -48,20 +43,17 @@ namespace crypto
return p;
}
bool GOSTR3410Curve::GetXY (const EC_POINT * p, BIGNUM * x, BIGNUM * y) const
{
bool GOSTR3410Curve::GetXY(const EC_POINT *p, BIGNUM *x, BIGNUM *y) const {
return EC_POINT_get_affine_coordinates_GFp(m_Group, p, x, y, nullptr);
}
EC_POINT * GOSTR3410Curve::CreatePoint (const BIGNUM * x, const BIGNUM * y) const
{
EC_POINT *GOSTR3410Curve::CreatePoint(const BIGNUM *x, const BIGNUM *y) const {
EC_POINT *p = EC_POINT_new(m_Group);
EC_POINT_set_affine_coordinates_GFp(m_Group, p, x, y, nullptr);
return p;
}
void GOSTR3410Curve::Sign (const BIGNUM * priv, const BIGNUM * digest, BIGNUM * r, BIGNUM * s)
{
void GOSTR3410Curve::Sign(const BIGNUM *priv, const BIGNUM *digest, BIGNUM *r, BIGNUM *s) {
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
BIGNUM *q = BN_CTX_get(ctx);
@ -79,8 +71,7 @@ namespace crypto
BN_CTX_free(ctx);
}
bool GOSTR3410Curve::Verify (const EC_POINT * pub, const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s)
{
bool GOSTR3410Curve::Verify(const EC_POINT *pub, const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s) {
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
BIGNUM *q = BN_CTX_get(ctx);
@ -105,15 +96,14 @@ namespace crypto
return ret;
}
EC_POINT * GOSTR3410Curve::RecoverPublicKey (const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s, bool isNegativeY) const
{
EC_POINT *GOSTR3410Curve::RecoverPublicKey(const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s,
bool isNegativeY) const {
// s*P = r*Q + h*C
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
EC_POINT *C = EC_POINT_new(m_Group); // C = k*P = (rx, ry)
EC_POINT *Q = nullptr;
if (EC_POINT_set_compressed_coordinates_GFp (m_Group, C, r, isNegativeY ? 1 : 0, ctx))
{
if (EC_POINT_set_compressed_coordinates_GFp(m_Group, C, r, isNegativeY ? 1 : 0, ctx)) {
EC_POINT *S = EC_POINT_new(m_Group); // S = s*P
EC_POINT_mul(m_Group, S, s, nullptr, nullptr, ctx);
BIGNUM *q = BN_CTX_get(ctx);
@ -137,8 +127,7 @@ namespace crypto
return Q;
}
static GOSTR3410Curve * CreateGOSTR3410Curve (GOSTR3410ParamSet paramSet)
{
static GOSTR3410Curve *CreateGOSTR3410Curve(GOSTR3410ParamSet paramSet) {
// a, b, p, q, x, y
static const char *params[eGOSTR3410NumParamSets][6] =
{
@ -168,15 +157,19 @@ namespace crypto
BN_hex2bn(&x, params[paramSet][4]);
BN_hex2bn(&y, params[paramSet][5]);
auto curve = new GOSTR3410Curve(a, b, p, q, x, y);
BN_free (a); BN_free (b); BN_free (p); BN_free (q); BN_free (x); BN_free (y);
BN_free(a);
BN_free(b);
BN_free(p);
BN_free(q);
BN_free(x);
BN_free(y);
return curve;
}
static std::array <std::unique_ptr<GOSTR3410Curve>, eGOSTR3410NumParamSets> g_GOSTR3410Curves;
std::unique_ptr<GOSTR3410Curve>& GetGOSTR3410Curve (GOSTR3410ParamSet paramSet)
{
if (!g_GOSTR3410Curves[paramSet])
{
std::unique_ptr <GOSTR3410Curve> &GetGOSTR3410Curve(GOSTR3410ParamSet paramSet) {
if (!g_GOSTR3410Curves[paramSet]) {
auto c = CreateGOSTR3410Curve(paramSet);
if (!g_GOSTR3410Curves[paramSet]) // make sure it was not created already
g_GOSTR3410Curves[paramSet].reset(c);
@ -783,28 +776,24 @@ namespace crypto
uint8_t buf[64];
uint64_t ll[8];
GOST3411Block operator^(const GOST3411Block& other) const
{
GOST3411Block operator^(const GOST3411Block &other) const {
GOST3411Block ret;
for (int i = 0; i < 8; i++)
ret.ll[i] = ll[i] ^ other.ll[i];
return ret;
}
GOST3411Block operator^(const uint64_t * other) const
{
GOST3411Block operator^(const uint64_t *other) const {
GOST3411Block ret;
for (int i = 0; i < 8; i++)
ret.ll[i] = ll[i] ^ other[i];
return ret;
}
GOST3411Block operator+(const GOST3411Block& other) const
{
GOST3411Block operator+(const GOST3411Block &other) const {
GOST3411Block ret;
uint8_t carry = 0;
for (int i = 63; i >= 0; i--)
{
for (int i = 63; i >= 0; i--) {
uint16_t sum = buf[i] + other.buf[i] + carry;
ret.buf[i] = sum;
carry = sum >> 8;
@ -812,10 +801,8 @@ namespace crypto
return ret;
}
void Add (uint32_t c)
{
for (int i = 63; i >= 0; i--)
{
void Add(uint32_t c) {
for (int i = 63; i >= 0; i--) {
if (!c) return;
c += buf[i];
buf[i] = c;
@ -823,11 +810,9 @@ namespace crypto
}
}
void F ()
{
void F() {
uint64_t res[8];
for (int b=0; b<8; b++)
{
for (int b = 0; b < 8; b++) {
uint64_t r;
r = T0[buf[b + 56]];
r ^= T1[buf[b + 48]];
@ -842,12 +827,10 @@ namespace crypto
memcpy(buf, res, 64);
}
GOST3411Block E (const GOST3411Block& m)
{
GOST3411Block E(const GOST3411Block &m) {
GOST3411Block k = *this;
GOST3411Block res = k ^ m;
for (int i = 0; i < 12; i++)
{
for (int i = 0; i < 12; i++) {
res.F();
k = k ^ C_[i];
k.F();
@ -857,8 +840,7 @@ namespace crypto
}
};
static GOST3411Block gN (const GOST3411Block& N, const GOST3411Block& h, const GOST3411Block& m)
{
static GOST3411Block gN(const GOST3411Block &N, const GOST3411Block &h, const GOST3411Block &m) {
GOST3411Block res = N ^ h;
res.F();
res = res.E(m);
@ -867,8 +849,7 @@ namespace crypto
return res;
}
static void H (const uint8_t * iv, const uint8_t * buf, size_t len, uint8_t * digest)
{
static void H(const uint8_t *iv, const uint8_t *buf, size_t len, uint8_t *digest) {
// stage 1
GOST3411Block h, N, s, m;
memcpy(h.buf, iv, 64);
@ -876,8 +857,7 @@ namespace crypto
memset(s.buf, 0, 64);
size_t l = len;
// stage 2
while (l >= 64)
{
while (l >= 64) {
memcpy(m.buf, buf + l - 64, 64); // TODO
h = gN(N, h, m);
N.Add(512);
@ -886,8 +866,7 @@ namespace crypto
}
// stage 3
size_t padding = 64 - l;
if (padding)
{
if (padding) {
memset(m.buf, 0, padding - 1);
m.buf[padding - 1] = 1;
}
@ -905,8 +884,7 @@ namespace crypto
memcpy(digest, h.buf, 64);
}
void GOSTR3411_2012_256 (const uint8_t * buf, size_t len, uint8_t * digest)
{
void GOSTR3411_2012_256(const uint8_t *buf, size_t len, uint8_t *digest) {
uint8_t iv[64];
memset(iv, 1, 64);
uint8_t h[64];
@ -914,33 +892,28 @@ namespace crypto
memcpy(digest, h, 32); // first half
}
void GOSTR3411_2012_512 (const uint8_t * buf, size_t len, uint8_t * digest)
{
void GOSTR3411_2012_512(const uint8_t *buf, size_t len, uint8_t *digest) {
uint8_t iv[64];
memset(iv, 0, 64);
H(iv, buf, len, digest);
}
// reverse order
struct GOSTR3411_2012_CTX
{
struct GOSTR3411_2012_CTX {
GOST3411Block h, N, s, m;
size_t len;
bool is512;
};
GOSTR3411_2012_CTX * GOSTR3411_2012_CTX_new ()
{
GOSTR3411_2012_CTX *GOSTR3411_2012_CTX_new() {
return new GOSTR3411_2012_CTX;
}
void GOSTR3411_2012_CTX_free (GOSTR3411_2012_CTX * ctx)
{
void GOSTR3411_2012_CTX_free(GOSTR3411_2012_CTX *ctx) {
delete ctx;
}
void GOSTR3411_2012_CTX_Init (GOSTR3411_2012_CTX * ctx, bool is512)
{
void GOSTR3411_2012_CTX_Init(GOSTR3411_2012_CTX *ctx, bool is512) {
uint8_t iv[64];
memset(iv, is512 ? 0 : 1, 64);
memcpy(ctx->h.buf, iv, 64);
@ -950,8 +923,7 @@ namespace crypto
ctx->is512 = is512;
}
void GOSTR3411_2012_CTX_Update (const uint8_t * buf, size_t len, GOSTR3411_2012_CTX * ctx)
{
void GOSTR3411_2012_CTX_Update(const uint8_t *buf, size_t len, GOSTR3411_2012_CTX *ctx) {
if (!len) return;
if (ctx->len > 0) // something left from buffer
{
@ -959,17 +931,19 @@ namespace crypto
if (len < l) l = len;
for (size_t i = 0; i < l; i++)
ctx->m.buf[ctx->len + i] = buf[l - i - 1]; // invert
ctx->len += l; len -= l; buf += l;
ctx->len += l;
len -= l;
buf += l;
ctx->h = gN(ctx->N, ctx->h, ctx->m);
ctx->N.Add(512);
ctx->s = ctx->m + ctx->s;
}
while (len >= 64)
{
while (len >= 64) {
for (size_t i = 0; i < 64; i++)
ctx->m.buf[i] = buf[63 - i]; // invert
len -= 64; buf += 64;
len -= 64;
buf += 64;
ctx->h = gN(ctx->N, ctx->h, ctx->m);
ctx->N.Add(512);
ctx->s = ctx->m + ctx->s;
@ -982,12 +956,10 @@ namespace crypto
ctx->len = len;
}
void GOSTR3411_2012_CTX_Finish (uint8_t * digest, GOSTR3411_2012_CTX * ctx)
{
void GOSTR3411_2012_CTX_Finish(uint8_t *digest, GOSTR3411_2012_CTX *ctx) {
GOST3411Block m;
size_t padding = 64 - ctx->len;
if (padding)
{
if (padding) {
memset(m.buf, 0, padding - 1);
m.buf[padding - 1] = 1;
}

View file

@ -12,15 +12,12 @@
#include <memory>
#include <openssl/ec.h>
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
// ГОСТ Р 34.10
enum GOSTR3410ParamSet
{
enum GOSTR3410ParamSet {
eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1
// XchA = A, XchB = C
//eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0
@ -29,21 +26,29 @@ namespace crypto
eGOSTR3410NumParamSets
};
class GOSTR3410Curve
{
class GOSTR3410Curve {
public:
GOSTR3410Curve(BIGNUM *a, BIGNUM *b, BIGNUM *p, BIGNUM *q, BIGNUM *x, BIGNUM *y);
~GOSTR3410Curve();
size_t GetKeyLen() const { return m_KeyLen; };
const EC_GROUP *GetGroup() const { return m_Group; };
EC_POINT *MulP(const BIGNUM *n) const;
bool GetXY(const EC_POINT *p, BIGNUM *x, BIGNUM *y) const;
EC_POINT *CreatePoint(const BIGNUM *x, const BIGNUM *y) const;
void Sign(const BIGNUM *priv, const BIGNUM *digest, BIGNUM *r, BIGNUM *s);
bool Verify(const EC_POINT *pub, const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s);
EC_POINT * RecoverPublicKey (const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s, bool isNegativeY = false) const;
EC_POINT *
RecoverPublicKey(const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s, bool isNegativeY = false) const;
private:
@ -55,14 +60,20 @@ namespace crypto
// Big Endian
void GOSTR3411_2012_256(const uint8_t *buf, size_t len, uint8_t *digest);
void GOSTR3411_2012_512(const uint8_t *buf, size_t len, uint8_t *digest);
// Little Endian
struct GOSTR3411_2012_CTX;
GOSTR3411_2012_CTX *GOSTR3411_2012_CTX_new();
void GOSTR3411_2012_CTX_Init(GOSTR3411_2012_CTX *ctx, bool is512 = true);
void GOSTR3411_2012_CTX_Update(const uint8_t *buf, size_t len, GOSTR3411_2012_CTX *ctx);
void GOSTR3411_2012_CTX_Finish(uint8_t *digest, GOSTR3411_2012_CTX *ctx);
void GOSTR3411_2012_CTX_free(GOSTR3411_2012_CTX *ctx);
}
}

View file

@ -13,40 +13,32 @@
#include "I2PEndian.h"
#include "Gzip.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
const size_t GZIP_CHUNK_SIZE = 16384;
GzipInflator::GzipInflator (): m_IsDirty (false)
{
GzipInflator::GzipInflator() : m_IsDirty(false) {
memset(&m_Inflator, 0, sizeof(m_Inflator));
inflateInit2(&m_Inflator, MAX_WBITS + 16); // gzip
}
GzipInflator::~GzipInflator ()
{
GzipInflator::~GzipInflator() {
inflateEnd(&m_Inflator);
}
size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
size_t GzipInflator::Inflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen) {
if (inLen < 23) return 0;
if (in[10] == 0x01) // non compressed
{
size_t len = bufle16toh(in + 11);
if (len + 23 < inLen)
{
if (len + 23 < inLen) {
LogPrint(eLogError, "Gzip: Incorrect length");
return 0;
}
if (len > outLen) len = outLen;
memcpy(out, in + 15, len);
return len;
}
else
{
} else {
if (m_IsDirty) inflateReset(&m_Inflator);
m_IsDirty = true;
m_Inflator.next_in = const_cast<uint8_t *>(in);
@ -62,59 +54,50 @@ namespace data
}
}
void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os)
{
void GzipInflator::Inflate(const uint8_t *in, size_t inLen, std::ostream &os) {
m_IsDirty = true;
uint8_t *out = new uint8_t[GZIP_CHUNK_SIZE];
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
int ret;
do
{
do {
m_Inflator.next_out = out;
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
ret = inflate(&m_Inflator, Z_NO_FLUSH);
if (ret < 0)
{
if (ret < 0) {
inflateEnd(&m_Inflator);
os.setstate(std::ios_base::failbit);
break;
}
os.write((char *) out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
}
while (!m_Inflator.avail_out); // more data to read
} while (!m_Inflator.avail_out); // more data to read
delete[] out;
}
void GzipInflator::Inflate (std::istream& in, std::ostream& out)
{
void GzipInflator::Inflate(std::istream &in, std::ostream &out) {
uint8_t *buf = new uint8_t[GZIP_CHUNK_SIZE];
while (!in.eof ())
{
while (!in.eof()) {
in.read((char *) buf, GZIP_CHUNK_SIZE);
Inflate(buf, in.gcount(), out);
}
delete[] buf;
}
GzipDeflator::GzipDeflator (): m_IsDirty (false)
{
GzipDeflator::GzipDeflator() : m_IsDirty(false) {
memset(&m_Deflator, 0, sizeof(m_Deflator));
deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
deflateInit2(&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8,
Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
}
GzipDeflator::~GzipDeflator ()
{
GzipDeflator::~GzipDeflator() {
deflateEnd(&m_Deflator);
}
void GzipDeflator::SetCompressionLevel (int level)
{
void GzipDeflator::SetCompressionLevel(int level) {
deflateParams(&m_Deflator, level, Z_DEFAULT_STRATEGY);
}
size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
{
size_t GzipDeflator::Deflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen) {
if (m_IsDirty) deflateReset(&m_Deflator);
m_IsDirty = true;
m_Deflator.next_in = const_cast<uint8_t *>(in);
@ -122,8 +105,7 @@ namespace data
m_Deflator.next_out = out;
m_Deflator.avail_out = outLen;
int err;
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
{
if ((err = deflate(&m_Deflator, Z_FINISH)) == Z_STREAM_END) {
out[9] = 0xff; // OS is always unknown
return outLen - m_Deflator.avail_out;
}
@ -132,24 +114,21 @@ namespace data
return 0;
}
size_t GzipDeflator::Deflate (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen)
{
size_t GzipDeflator::Deflate(const std::vector <std::pair<const uint8_t *, size_t>> &bufs, uint8_t *out,
size_t outLen) {
if (m_IsDirty) deflateReset(&m_Deflator);
m_IsDirty = true;
size_t offset = 0;
int err;
for (const auto& it: bufs)
{
for (const auto &it: bufs) {
m_Deflator.next_in = const_cast<uint8_t *>(it.first);
m_Deflator.avail_in = it.second;
m_Deflator.next_out = out + offset;
m_Deflator.avail_out = outLen - offset;
auto flush = (it == bufs.back()) ? Z_FINISH : Z_NO_FLUSH;
err = deflate(&m_Deflator, flush);
if (err)
{
if (flush && err == Z_STREAM_END)
{
if (err) {
if (flush && err == Z_STREAM_END) {
out[9] = 0xff; // OS is always unknown
return outLen - m_Deflator.avail_out;
}
@ -162,8 +141,7 @@ namespace data
return 0;
}
size_t GzipNoCompression (const uint8_t * in, uint16_t inLen, uint8_t * out, size_t outLen)
{
size_t GzipNoCompression(const uint8_t *in, uint16_t inLen, uint8_t *out, size_t outLen) {
static const uint8_t gzipHeader[11] = {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01};
if (outLen < (size_t) inLen + 23) return 0;
memcpy(out, gzipHeader, 11);
@ -175,14 +153,13 @@ namespace data
return inLen + 23;
}
size_t GzipNoCompression (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen)
{
size_t
GzipNoCompression(const std::vector <std::pair<const uint8_t *, size_t>> &bufs, uint8_t *out, size_t outLen) {
static const uint8_t gzipHeader[11] = {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01};
memcpy(out, gzipHeader, 11);
uint32_t crc = 0;
size_t len = 0, len1;
for (const auto& it: bufs)
{
for (const auto &it: bufs) {
len1 = len;
len += it.second;
if (outLen < len + 23) return 0;

View file

@ -12,20 +12,20 @@
#include <zlib.h>
#include <vector>
namespace i2p
{
namespace data
{
class GzipInflator
{
namespace i2p {
namespace data {
class GzipInflator {
public:
GzipInflator();
~GzipInflator();
size_t Inflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen);
/** @note @a os failbit will be set in case of error */
void Inflate(const uint8_t *in, size_t inLen, std::ostream &os);
void Inflate(std::istream &in, std::ostream &out);
private:
@ -34,15 +34,17 @@ namespace data
bool m_IsDirty;
};
class GzipDeflator
{
class GzipDeflator {
public:
GzipDeflator();
~GzipDeflator();
void SetCompressionLevel(int level);
size_t Deflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen);
size_t Deflate(const std::vector <std::pair<const uint8_t *, size_t>> &bufs, uint8_t *out, size_t outLen);
private:
@ -52,7 +54,8 @@ namespace data
};
size_t GzipNoCompression(const uint8_t *in, uint16_t inLen, uint8_t *out, size_t outLen); // for < 64K
size_t GzipNoCompression (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen); // for total size < 64K
size_t GzipNoCompression(const std::vector <std::pair<const uint8_t *, size_t>> &bufs, uint8_t *out,
size_t outLen); // for total size < 64K
} // data
} // i2p

View file

@ -14,13 +14,12 @@
#include "Base.h"
#include "HTTP.h"
namespace i2p
{
namespace http
{
namespace i2p {
namespace http {
const std::vector <std::string> HTTP_METHODS = {
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods
"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323
"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK",
"SEARCH" // WebDAV methods, for SEARCH see rfc5323
};
const std::vector <std::string> HTTP_VERSIONS = {
"HTTP/1.0", "HTTP/1.1"
@ -55,8 +54,7 @@ namespace http
}
}
static std::pair<std::string, std::string> parse_header_line(const std::string& line)
{
static std::pair <std::string, std::string> parse_header_line(const std::string &line) {
std::size_t pos = 0;
std::size_t len = 1; /*: */
std::size_t max = line.length();
@ -119,8 +117,7 @@ namespace http
auto pos_b = url.find(']', pos_p);
if (pos_b == std::string::npos) return false;
pos_c = url.find_first_of(":/", pos_b);
}
else
} else
pos_c = url.find_first_of(":/", pos_p);
if (pos_c == std::string::npos) {
/* only hostname, without post and path */
@ -224,8 +221,7 @@ namespace http
return out;
}
bool URL::is_i2p() const
{
bool URL::is_i2p() const {
return host.rfind(".i2p") == (host.size() - 4);
}
@ -254,7 +250,9 @@ namespace http
}
int HTTPReq::parse(const std::string &str) {
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
enum {
REQ_LINE, HEADER_LINE
} expect = REQ_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
URL url;
@ -280,9 +278,7 @@ namespace http
uri = tokens[1];
version = tokens[2];
expect = HEADER_LINE;
}
else
{
} else {
std::string line = str.substr(pos, eol - pos);
auto p = parse_header_line(line);
if (p.first.length() > 0)
@ -297,40 +293,33 @@ namespace http
return eoh + strlen(HTTP_EOH);
}
void HTTPReq::write(std::ostream & o)
{
void HTTPReq::write(std::ostream &o) {
o << method << " " << uri << " " << version << CRLF;
for (auto &h: headers)
o << h.first << ": " << h.second << CRLF;
o << CRLF;
}
std::string HTTPReq::to_string()
{
std::string HTTPReq::to_string() {
std::stringstream ss;
write(ss);
return ss.str();
}
void HTTPReq::AddHeader (const std::string& name, const std::string& value)
{
void HTTPReq::AddHeader(const std::string &name, const std::string &value) {
headers.push_back(std::make_pair(name, value));
}
void HTTPReq::UpdateHeader (const std::string& name, const std::string& value)
{
void HTTPReq::UpdateHeader(const std::string &name, const std::string &value) {
for (auto &it: headers)
if (it.first == name)
{
if (it.first == name) {
it.second = value;
break;
}
}
void HTTPReq::RemoveHeader (const std::string& name, const std::string& exempt)
{
for (auto it = headers.begin (); it != headers.end ();)
{
void HTTPReq::RemoveHeader(const std::string &name, const std::string &exempt) {
for (auto it = headers.begin(); it != headers.end();) {
if (!it->first.compare(0, name.length(), name) && it->first != exempt)
it = headers.erase(it);
else
@ -338,16 +327,14 @@ namespace http
}
}
std::string HTTPReq::GetHeader (const std::string& name) const
{
std::string HTTPReq::GetHeader(const std::string &name) const {
for (auto &it: headers)
if (it.first == name)
return it.second;
return "";
}
bool HTTPRes::is_chunked() const
{
bool HTTPRes::is_chunked() const {
auto it = headers.find("Transfer-Encoding");
if (it == headers.end())
return false;
@ -356,8 +343,7 @@ namespace http
return false;
}
bool HTTPRes::is_gzipped(bool includingI2PGzip) const
{
bool HTTPRes::is_gzipped(bool includingI2PGzip) const {
auto it = headers.find("Content-Encoding");
if (it == headers.end())
return false; /* no header */
@ -368,8 +354,7 @@ namespace http
return false;
}
long int HTTPMsg::content_length() const
{
long int HTTPMsg::content_length() const {
unsigned long int length = 0;
auto it = headers.find("Content-Length");
if (it == headers.end())
@ -387,7 +372,9 @@ namespace http
}
int HTTPRes::parse(const std::string &str) {
enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
enum {
RES_LINE, HEADER_LINE
} expect = RES_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0;
@ -450,41 +437,74 @@ namespace http
const char *HTTPCodeToStatus(int code) {
const char *ptr;
switch (code) {
case 105: ptr = "Name Not Resolved"; break;
case 105:
ptr = "Name Not Resolved";
break;
/* success */
case 200: ptr = "OK"; break;
case 206: ptr = "Partial Content"; break;
case 200:
ptr = "OK";
break;
case 206:
ptr = "Partial Content";
break;
/* redirect */
case 301: ptr = "Moved Permanently"; break;
case 302: ptr = "Found"; break;
case 304: ptr = "Not Modified"; break;
case 307: ptr = "Temporary Redirect"; break;
case 301:
ptr = "Moved Permanently";
break;
case 302:
ptr = "Found";
break;
case 304:
ptr = "Not Modified";
break;
case 307:
ptr = "Temporary Redirect";
break;
/* client error */
case 400: ptr = "Bad Request"; break;
case 401: ptr = "Unauthorized"; break;
case 403: ptr = "Forbidden"; break;
case 404: ptr = "Not Found"; break;
case 407: ptr = "Proxy Authentication Required"; break;
case 408: ptr = "Request Timeout"; break;
case 400:
ptr = "Bad Request";
break;
case 401:
ptr = "Unauthorized";
break;
case 403:
ptr = "Forbidden";
break;
case 404:
ptr = "Not Found";
break;
case 407:
ptr = "Proxy Authentication Required";
break;
case 408:
ptr = "Request Timeout";
break;
/* server error */
case 500: ptr = "Internal Server Error"; break;
case 502: ptr = "Bad Gateway"; break;
case 503: ptr = "Not Implemented"; break;
case 504: ptr = "Gateway Timeout"; break;
default: ptr = "Unknown Status"; break;
case 500:
ptr = "Internal Server Error";
break;
case 502:
ptr = "Bad Gateway";
break;
case 503:
ptr = "Not Implemented";
break;
case 504:
ptr = "Gateway Timeout";
break;
default:
ptr = "Unknown Status";
break;
}
return ptr;
}
std::string UrlDecode(const std::string& data, bool allow_null)
{
std::string UrlDecode(const std::string &data, bool allow_null) {
std::string decoded(data);
size_t pos = 0;
while ((pos = decoded.find('%', pos)) != std::string::npos)
{
while ((pos = decoded.find('%', pos)) != std::string::npos) {
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
if (c == '\0' && !allow_null)
{
if (c == '\0' && !allow_null) {
pos += 3;
continue;
}
@ -494,11 +514,9 @@ namespace http
return decoded;
}
bool MergeChunkedResponse (std::istream& in, std::ostream& out)
{
bool MergeChunkedResponse(std::istream &in, std::ostream &out) {
std::string hexLen;
while (!in.eof ())
{
while (!in.eof()) {
std::getline(in, hexLen);
errno = 0;
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
@ -517,8 +535,7 @@ namespace http
return true;
}
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass)
{
std::string CreateBasicAuthorizationString(const std::string &user, const std::string &pass) {
if (user.empty() && pass.empty()) return "";
return "Basic " + i2p::data::ToBase64Standard(user + ":" + pass);
}

View file

@ -16,17 +16,14 @@
#include <string>
#include <vector>
namespace i2p
{
namespace http
{
namespace i2p {
namespace http {
const char CRLF[] = "\r\n"; /**< HTTP line terminator */
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */
extern const std::vector <std::string> HTTP_METHODS; /**< list of valid HTTP methods */
extern const std::vector <std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
struct URL
{
struct URL {
std::string schema;
std::string user;
std::string pass;
@ -43,6 +40,7 @@ namespace http
* @return true on success, false on invalid url
*/
bool parse(const char *str, std::size_t len = 0);
bool parse(const std::string &url);
/**
@ -63,20 +61,20 @@ namespace http
bool is_i2p() const;
};
struct HTTPMsg
{
struct HTTPMsg {
std::map <std::string, std::string> headers;
void add_header(const char *name, std::string &value, bool replace = false);
void add_header(const char *name, const char *value, bool replace = false);
void del_header(const char *name);
/** @brief Returns declared message length or -1 if unknown */
long int content_length() const;
};
struct HTTPReq
{
struct HTTPReq {
std::list <std::pair<std::string, std::string>> headers;
std::string version;
std::string method;
@ -90,16 +88,22 @@ namespace http
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string &buf);
/** @brief Serialize HTTP request to string */
std::string to_string();
void write(std::ostream &o);
void AddHeader(const std::string &name, const std::string &value);
void UpdateHeader(const std::string &name, const std::string &value);
void RemoveHeader (const std::string& name, const std::string& exempt); // remove all headers starting with name, but exempt
void RemoveHeader(const std::string &name,
const std::string &exempt); // remove all headers starting with name, but exempt
void RemoveHeader(const std::string &name) { RemoveHeader(name, ""); };
std::string GetHeader(const std::string &name) const;
};
@ -124,6 +128,7 @@ namespace http
* @note Positive return value is a size of header
*/
int parse(const char *buf, size_t len);
int parse(const std::string &buf);
/**

View file

@ -24,30 +24,24 @@
using namespace i2p::transport;
namespace i2p
{
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
{
namespace i2p {
std::shared_ptr<I2NPMessage> NewI2NPMessage() {
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> >();
}
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ()
{
std::shared_ptr<I2NPMessage> NewI2NPShortMessage() {
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
}
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint)
{
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage(bool endpoint) {
return i2p::tunnel::tunnels.NewI2NPTunnelMessage(endpoint);
}
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
{
std::shared_ptr<I2NPMessage> NewI2NPMessage(size_t len) {
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE - I2NP_HEADER_SIZE - 2) ? NewI2NPShortMessage() : NewI2NPMessage();
}
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID, bool checksum)
{
void I2NPMessage::FillI2NPMessageHeader(I2NPMessageType msgType, uint32_t replyMsgID, bool checksum) {
SetTypeID(msgType);
if (!replyMsgID) RAND_bytes((uint8_t * ) & replyMsgID, 4);
SetMsgID(replyMsgID);
@ -56,23 +50,22 @@ namespace i2p
if (checksum) UpdateChks();
}
void I2NPMessage::RenewI2NPMessageHeader ()
{
void I2NPMessage::RenewI2NPMessageHeader() {
uint32_t msgID;
RAND_bytes((uint8_t * ) & msgID, 4);
SetMsgID(msgID);
SetExpiration(i2p::util::GetMillisecondsSinceEpoch() + I2NP_MESSAGE_EXPIRATION_TIMEOUT);
}
bool I2NPMessage::IsExpired () const
{
bool I2NPMessage::IsExpired() const {
auto ts = i2p::util::GetMillisecondsSinceEpoch();
auto exp = GetExpiration();
return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) || (ts < exp - 3*I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
return (ts > exp + I2NP_MESSAGE_CLOCK_SKEW) ||
(ts < exp - 3 * I2NP_MESSAGE_CLOCK_SKEW); // check if expired or too far in future
}
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
std::shared_ptr<I2NPMessage>
CreateI2NPMessage(I2NPMessageType msgType, const uint8_t *buf, size_t len, uint32_t replyMsgID) {
auto msg = NewI2NPMessage(len);
if (msg->Concat(buf, len) < len)
LogPrint(eLogError, "I2NP: Message length ", len, " exceeds max length ", msg->maxLen);
@ -80,22 +73,19 @@ namespace i2p
return msg;
}
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{
std::shared_ptr<I2NPMessage>
CreateI2NPMessage(const uint8_t *buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) {
auto msg = NewI2NPMessage();
if (msg->offset + len < msg->maxLen)
{
if (msg->offset + len < msg->maxLen) {
memcpy(msg->GetBuffer(), buf, len);
msg->len = msg->offset + len;
msg->from = from;
}
else
} else
LogPrint(eLogError, "I2NP: Message length ", len, " exceeds max length");
return msg;
}
std::shared_ptr<I2NPMessage> CopyI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
std::shared_ptr<I2NPMessage> CopyI2NPMessage(std::shared_ptr<I2NPMessage> msg) {
if (!msg) return nullptr;
auto newMsg = NewI2NPMessage(msg->len);
newMsg->offset = msg->offset;
@ -103,16 +93,13 @@ namespace i2p
return newMsg;
}
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
{
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg(uint32_t msgID) {
auto m = NewI2NPShortMessage();
uint8_t *buf = m->GetPayload();
if (msgID)
{
if (msgID) {
htobe32buf(buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
htobe64buf(buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch());
}
else // for SSU establishment
} else // for SSU establishment
{
RAND_bytes((uint8_t * ) & msgID, 4);
htobe32buf(buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
@ -124,8 +111,8 @@ namespace i2p
}
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg(const uint8_t *key, const uint8_t *from,
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
{
uint32_t replyTunnelID, bool exploratory,
std::set<i2p::data::IdentHash> *excludedPeers) {
auto m = excludedPeers ? NewI2NPMessage() : NewI2NPShortMessage();
uint8_t *buf = m->GetPayload();
memcpy(buf, key, 32); // key
@ -133,31 +120,24 @@ namespace i2p
memcpy(buf, from, 32); // from
buf += 32;
uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP;
if (replyTunnelID)
{
if (replyTunnelID) {
*buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag
htobe32buf(buf + 1, replyTunnelID);
buf += 5;
}
else
{
} else {
*buf = flag; // flag
buf++;
}
if (excludedPeers)
{
if (excludedPeers) {
int cnt = excludedPeers->size();
htobe16buf(buf, cnt);
buf += 2;
for (auto& it: *excludedPeers)
{
for (auto &it: *excludedPeers) {
memcpy(buf, it, 32);
buf += 32;
}
}
else
{
} else {
// nothing to exclude
htobuf16(buf, 0);
buf += 2;
@ -170,9 +150,9 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg(const i2p::data::IdentHash &dest,
const std::set<i2p::data::IdentHash> &excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey,
const uint8_t * replyTag, bool replyECIES)
{
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t *replyKey,
const uint8_t *replyTag, bool replyECIES) {
int cnt = excludedFloodfills.size();
auto m = cnt > 7 ? NewI2NPMessage() : NewI2NPShortMessage();
uint8_t *buf = m->GetPayload();
@ -187,17 +167,14 @@ namespace i2p
buf += 4;
// excluded
if (cnt > 512)
{
if (cnt > 512) {
LogPrint(eLogWarning, "I2NP: Too many peers to exclude ", cnt, " for DatabaseLookup");
cnt = 0;
}
htobe16buf(buf, cnt);
buf += 2;
if (cnt > 0)
{
for (auto& it: excludedFloodfills)
{
if (cnt > 0) {
for (auto &it: excludedFloodfills) {
memcpy(buf, it, 32);
buf += 32;
}
@ -205,13 +182,10 @@ namespace i2p
// encryption
memcpy(buf, replyKey, 32);
buf[32] = 1; // 1 tag
if (replyECIES)
{
if (replyECIES) {
memcpy(buf + 33, replyTag, 8); // 8 bytes tag
buf += 41;
}
else
{
} else {
memcpy(buf + 33, replyTag, 32); // 32 bytes tag
buf += 65;
}
@ -222,8 +196,7 @@ namespace i2p
}
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply(const i2p::data::IdentHash &ident,
std::vector<i2p::data::IdentHash> routers)
{
std::vector<i2p::data::IdentHash> routers) {
auto m = NewI2NPShortMessage();
uint8_t *buf = m->GetPayload();
size_t len = 0;
@ -231,8 +204,7 @@ namespace i2p
len += 32;
buf[len] = routers.size();
len++;
for (const auto& it: routers)
{
for (const auto &it: routers) {
memcpy(buf + len, it, 32);
len += 32;
}
@ -244,13 +216,12 @@ namespace i2p
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg(std::shared_ptr<const i2p::data::RouterInfo> router,
uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
uint32_t replyToken,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) {
if (!router) // we send own RouterInfo
router = context.GetSharedRouterInfo();
if (!router->GetBuffer ())
{
if (!router->GetBuffer()) {
LogPrint(eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore");
return nullptr;
}
@ -262,17 +233,13 @@ namespace i2p
payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo
htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
uint8_t *buf = payload + DATABASE_STORE_HEADER_SIZE;
if (replyToken)
{
if (replyTunnel)
{
if (replyToken) {
if (replyTunnel) {
htobe32buf(buf, replyTunnel->GetNextTunnelID());
buf += 4; // reply tunnelID
memcpy(buf, replyTunnel->GetNextIdentHash(), 32);
buf += 32; // reply tunnel gateway
}
else
{
} else {
memset(buf, 0, 4); // zero tunnelID means direct reply
buf += 4;
memcpy(buf, context.GetIdentHash(), 32);
@ -286,25 +253,22 @@ namespace i2p
size_t size = 0;
if (router->GetBufferLen() + (buf - payload) <= 940) // fits one tunnel message
size = i2p::data::GzipNoCompression(router->GetBuffer(), router->GetBufferLen(), buf, m->maxLen - m->len);
else
{
else {
i2p::data::GzipDeflator deflator;
size = deflator.Deflate(router->GetBuffer(), router->GetBufferLen(), buf, m->maxLen - m->len);
}
if (size)
{
if (size) {
htobe16buf(sizePtr, size); // size
m->len += size;
}
else
} else
m = nullptr;
if (m)
m->FillI2NPMessageHeader(eI2NPDatabaseStore);
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
std::shared_ptr<I2NPMessage>
CreateDatabaseStoreMsg(const i2p::data::IdentHash &storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet) {
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage();
uint8_t *payload = m->GetPayload();
@ -319,8 +283,9 @@ namespace i2p
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
std::shared_ptr<I2NPMessage>
CreateDatabaseStoreMsg(std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) {
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage();
uint8_t *payload = m->GetPayload();
@ -328,16 +293,13 @@ namespace i2p
payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType(); // LeaseSet or LeaseSet2
htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE;
if (replyToken && replyTunnel)
{
if (replyTunnel)
{
if (replyToken && replyTunnel) {
if (replyTunnel) {
htobe32buf(payload + size, replyTunnel->GetNextTunnelID());
size += 4; // reply tunnelID
memcpy(payload + size, replyTunnel->GetNextIdentHash(), 32);
size += 32; // reply tunnel gateway
}
else
} else
htobe32buf(payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
}
memcpy(payload + size, leaseSet->GetBuffer(), leaseSet->GetBufferLen());
@ -347,43 +309,38 @@ namespace i2p
return m;
}
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg)
{
bool IsRouterInfoMsg(std::shared_ptr<I2NPMessage> msg) {
if (!msg || msg->GetTypeID() != eI2NPDatabaseStore) return false;
return !msg->GetPayload()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
}
static uint16_t g_MaxNumTransitTunnels = DEFAULT_MAX_NUM_TRANSIT_TUNNELS; // TODO:
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels)
{
if (maxNumTransitTunnels > 0 && g_MaxNumTransitTunnels != maxNumTransitTunnels)
{
void SetMaxNumTransitTunnels(uint16_t maxNumTransitTunnels) {
if (maxNumTransitTunnels > 0 && g_MaxNumTransitTunnels != maxNumTransitTunnels) {
LogPrint(eLogDebug, "I2NP: Max number of transit tunnels set to ", maxNumTransitTunnels);
g_MaxNumTransitTunnels = maxNumTransitTunnels;
}
}
uint16_t GetMaxNumTransitTunnels ()
{
uint16_t GetMaxNumTransitTunnels() {
return g_MaxNumTransitTunnels;
}
static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
{
for (int i = 0; i < num; i++)
{
static bool HandleBuildRequestRecords(int num, uint8_t *records, uint8_t *clearText) {
for (int i = 0; i < num; i++) {
uint8_t *record = records + i * TUNNEL_BUILD_RECORD_SIZE;
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
if (!memcmp(record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET,
(const uint8_t *) i2p::context.GetRouterInfo().GetIdentHash(), 16)) {
LogPrint(eLogDebug, "I2NP: Build request record ", i, " is ours");
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false;
if (!i2p::context.DecryptTunnelBuildRecord(record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET,
clearText))
return false;
uint8_t retCode = 0;
// replace record to reply
if (i2p::context.AcceptsTunnels() &&
i2p::tunnel::tunnels.GetTransitTunnels().size() <= g_MaxNumTransitTunnels &&
!i2p::transport::transports.IsBandwidthExceeded() &&
!i2p::transport::transports.IsTransitBandwidthExceeded ())
{
!i2p::transport::transports.IsTransitBandwidthExceeded()) {
auto transitTunnel = i2p::tunnel::CreateTransitTunnel(
bufbe32toh(clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
@ -393,31 +350,27 @@ namespace i2p
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
i2p::tunnel::tunnels.AddTransitTunnel(transitTunnel);
}
else
} else
retCode = 30; // always reject with bandwidth reason (30)
memset(record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
// encrypt reply
i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++)
{
for (int j = 0; j < num; j++) {
uint8_t *reply = records + j * TUNNEL_BUILD_RECORD_SIZE;
if (j == i)
{
if (j == i) {
uint8_t nonce[12];
memset(nonce, 0, 12);
auto &noiseState = i2p::context.GetCurrentNoiseState();
if (!i2p::crypto::AEADChaCha20Poly1305(reply, TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
noiseState.m_H, 32, noiseState.m_CK, nonce, reply,
TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{
LogPrint(eLogWarning, "I2NP: Reply AEAD encryption failed");
return false;
}
}
else
{
} else {
encryption.SetKey(clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV(clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
@ -429,132 +382,111 @@ namespace i2p
return false;
}
static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
static void HandleVariableTunnelBuildMsg(uint32_t replyMsgID, uint8_t *buf, size_t len) {
int num = buf[0];
LogPrint(eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
{
if (len < num * TUNNEL_BUILD_RECORD_SIZE + 1) {
LogPrint(eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel(replyMsgID);
if (tunnel)
{
if (tunnel) {
// endpoint of inbound tunnel
LogPrint(eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
if (tunnel->HandleTunnelBuildResponse(buf, len)) {
LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been created");
tunnel->SetState(i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel(tunnel);
}
else
{
} else {
LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been declined");
tunnel->SetState(i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
{
} else {
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
if (HandleBuildRequestRecords(num, buf + 1, clearText)) {
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] &
TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
CreateTunnelGatewayMsg(bufbe32toh(
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
bufbe32toh(clearText +
ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} else
transports.SendMessage(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage(eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
bufbe32toh(clearText +
ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
}
static void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{
static void HandleTunnelBuildMsg(uint8_t *buf, size_t len) {
LogPrint(eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
}
static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort)
{
static void HandleTunnelBuildReplyMsg(uint32_t replyMsgID, uint8_t *buf, size_t len, bool isShort) {
int num = buf[0];
LogPrint(eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
if (len < num*recordSize + 1)
{
if (len < num * recordSize + 1) {
LogPrint(eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel(replyMsgID);
if (tunnel)
{
if (tunnel) {
// reply for outbound tunnel
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
if (tunnel->HandleTunnelBuildResponse(buf, len)) {
LogPrint(eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID(), " has been created");
tunnel->SetState(i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddOutboundTunnel(tunnel);
}
else
{
} else {
LogPrint(eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID(), " has been declined");
tunnel->SetState(i2p::tunnel::eTunnelStateBuildFailed);
}
}
else
} else
LogPrint(eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
}
static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
{
static void HandleShortTunnelBuildMsg(uint32_t replyMsgID, uint8_t *buf, size_t len) {
int num = buf[0];
LogPrint(eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
{
if (len < num * SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) {
LogPrint(eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len);
return;
}
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel(replyMsgID);
if (tunnel)
{
if (tunnel) {
// endpoint of inbound tunnel
LogPrint(eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID());
if (tunnel->HandleTunnelBuildResponse (buf, len))
{
if (tunnel->HandleTunnelBuildResponse(buf, len)) {
LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been created");
tunnel->SetState(i2p::tunnel::eTunnelStateEstablished);
i2p::tunnel::tunnels.AddInboundTunnel(tunnel);
}
else
{
} else {
LogPrint(eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID(), " has been declined");
tunnel->SetState(i2p::tunnel::eTunnelStateBuildFailed);
}
return;
}
const uint8_t *record = buf + 1;
for (int i = 0; i < num; i++)
{
if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
{
for (int i = 0; i < num; i++) {
if (!memcmp(record, (const uint8_t *) i2p::context.GetRouterInfo().GetIdentHash(), 16)) {
LogPrint(eLogDebug, "I2NP: Short request record ", i, " is ours");
uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
{
if (!i2p::context.DecryptTunnelShortRequestRecord(record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET,
clearText)) {
LogPrint(eLogWarning, "I2NP: Can't decrypt short request record ", i);
return;
}
if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
{
LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
LogPrint(eLogWarning, "I2NP: Unknown layer encryption type ",
clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
return;
}
auto &noiseState = i2p::context.GetCurrentNoiseState();
@ -564,12 +496,10 @@ namespace i2p
i2p::crypto::HKDF(noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
memcpy(layerKey, noiseState.m_CK + 32, 32);
bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
if (isEndpoint)
{
if (isEndpoint) {
i2p::crypto::HKDF(noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
memcpy(ivKey, noiseState.m_CK + 32, 32);
}
else
} else
memcpy(ivKey, noiseState.m_CK, 32);
// check if we accept this tunnel
@ -579,8 +509,7 @@ namespace i2p
i2p::transport::transports.IsBandwidthExceeded() ||
i2p::transport::transports.IsTransitBandwidthExceeded())
retCode = 30;
if (!retCode)
{
if (!retCode) {
// create new transit tunnel
auto transitTunnel = i2p::tunnel::CreateTransitTunnel(
bufbe32toh(clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
@ -596,30 +525,28 @@ namespace i2p
uint8_t nonce[12];
memset(nonce, 0, 12);
uint8_t *reply = buf + 1;
for (int j = 0; j < num; j++)
{
for (int j = 0; j < num; j++) {
nonce[4] = j; // nonce is record #
if (j == i)
{
if (j == i) {
memset(reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
if (!i2p::crypto::AEADChaCha20Poly1305(reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
noiseState.m_H, 32, replyKey, nonce, reply,
SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{
LogPrint(eLogWarning, "I2NP: Short reply AEAD encryption failed");
return;
}
}
else
} else
i2p::crypto::ChaCha20(reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
// send reply
if (isEndpoint)
{
if (isEndpoint) {
auto replyMsg = NewI2NPShortMessage();
replyMsg->Concat(buf, len);
replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
replyMsg->FillI2NPMessageHeader(eI2NPShortTunnelBuildReply,
bufbe32toh(clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
if (memcmp((const uint8_t *) i2p::context.GetIdentHash(),
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
{
@ -628,11 +555,11 @@ namespace i2p
memcpy(&tag, noiseState.m_CK, 8);
// we send it to reply tunnel
transports.SendMessage(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
}
else
{
CreateTunnelGatewayMsg(
bufbe32toh(clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
i2p::garlic::WrapECIESX25519Message(replyMsg,
noiseState.m_CK + 32, tag)));
} else {
// IBGW is local
uint32_t tunnelID = bufbe32toh(clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
auto tunnel = i2p::tunnel::tunnels.GetTunnel(tunnelID);
@ -641,27 +568,25 @@ namespace i2p
else
LogPrint(eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
}
}
else
} else
transports.SendMessage(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage(eI2NPShortTunnelBuild, buf, len,
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
bufbe32toh(clearText +
SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
return;
}
record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
}
}
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg(const uint8_t *buf) {
auto msg = NewI2NPTunnelMessage(false);
msg->Concat(buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->FillI2NPMessageHeader(eI2NPTunnelData);
return msg;
}
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
{
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg(uint32_t tunnelID, const uint8_t *payload) {
auto msg = NewI2NPTunnelMessage(false);
htobe32buf(msg->GetPayload(), tunnelID);
msg->len += 4; // tunnelID
@ -670,15 +595,13 @@ namespace i2p
return msg;
}
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint)
{
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg(bool endpoint) {
auto msg = NewI2NPTunnelMessage(endpoint);
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
return msg;
}
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
{
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, const uint8_t *buf, size_t len) {
auto msg = NewI2NPMessage(len);
uint8_t *payload = msg->GetPayload();
htobe32buf(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
@ -690,10 +613,8 @@ namespace i2p
return msg;
}
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
{
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
{
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg) {
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) {
// message is capable to be used without copying
uint8_t *payload = msg->GetBuffer() - TUNNEL_GATEWAY_HEADER_SIZE;
htobe32buf(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
@ -703,14 +624,12 @@ namespace i2p
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + len;
msg->FillI2NPMessageHeader(eI2NPTunnelGateway);
return msg;
}
else
} else
return CreateTunnelGatewayMsg(tunnelID, msg->GetBuffer(), msg->GetLength());
}
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t * buf, size_t len, uint32_t replyMsgID)
{
const uint8_t *buf, size_t len, uint32_t replyMsgID) {
auto msg = NewI2NPMessage(len);
size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
msg->offset += gatewayMsgOffset;
@ -727,26 +646,21 @@ namespace i2p
return msg;
}
size_t GetI2NPMessageLength (const uint8_t * msg, size_t len)
{
if (len < I2NP_HEADER_SIZE_OFFSET + 2)
{
size_t GetI2NPMessageLength(const uint8_t *msg, size_t len) {
if (len < I2NP_HEADER_SIZE_OFFSET + 2) {
LogPrint(eLogError, "I2NP: Message length ", len, " is smaller than header");
return len;
}
auto l = bufbe16toh(msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
if (l > len)
{
if (l > len) {
LogPrint(eLogError, "I2NP: Message length ", l, " exceeds buffer length ", len);
l = len;
}
return l;
}
void HandleI2NPMessage (uint8_t * msg, size_t len)
{
if (len < I2NP_HEADER_SIZE)
{
void HandleI2NPMessage(uint8_t *msg, size_t len) {
if (len < I2NP_HEADER_SIZE) {
LogPrint(eLogError, "I2NP: Message length ", len, " is smaller than header");
return;
}
@ -756,13 +670,11 @@ namespace i2p
uint8_t *buf = msg + I2NP_HEADER_SIZE;
auto size = bufbe16toh(msg + I2NP_HEADER_SIZE_OFFSET);
len -= I2NP_HEADER_SIZE;
if (size > len)
{
if (size > len) {
LogPrint(eLogError, "I2NP: Payload size ", size, " exceeds buffer length ", len);
size = len;
}
switch (typeID)
{
switch (typeID) {
case eI2NPVariableTunnelBuild:
HandleVariableTunnelBuildMsg(msgID, buf, size);
break;
@ -786,22 +698,18 @@ namespace i2p
}
}
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
{
if (msg)
{
void HandleI2NPMessage(std::shared_ptr<I2NPMessage> msg) {
if (msg) {
uint8_t typeID = msg->GetTypeID();
LogPrint(eLogDebug, "I2NP: Handling message with type ", (int) typeID);
switch (typeID)
{
switch (typeID) {
case eI2NPTunnelData:
i2p::tunnel::tunnels.PostTunnelData(msg);
break;
case eI2NPTunnelGateway:
i2p::tunnel::tunnels.PostTunnelData(msg);
break;
case eI2NPGarlic:
{
case eI2NPGarlic: {
if (msg->from && msg->from->GetTunnelPool())
msg->from->GetTunnelPool()->ProcessGarlicMessage(msg);
else
@ -814,8 +722,7 @@ namespace i2p
// forward to netDb
i2p::data::netdb.PostI2NPMsg(msg);
break;
case eI2NPDeliveryStatus:
{
case eI2NPDeliveryStatus: {
if (msg->from && msg->from->GetTunnelPool())
msg->from->GetTunnelPool()->ProcessDeliveryStatus(msg);
else
@ -837,17 +744,13 @@ namespace i2p
}
}
I2NPMessagesHandler::~I2NPMessagesHandler ()
{
I2NPMessagesHandler::~I2NPMessagesHandler() {
Flush();
}
void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage>&& msg)
{
if (msg)
{
switch (msg->GetTypeID ())
{
void I2NPMessagesHandler::PutNextMessage(std::shared_ptr<I2NPMessage> &&msg) {
if (msg) {
switch (msg->GetTypeID()) {
case eI2NPTunnelData:
m_TunnelMsgs.push_back(msg);
break;
@ -860,15 +763,12 @@ namespace i2p
}
}
void I2NPMessagesHandler::Flush ()
{
if (!m_TunnelMsgs.empty ())
{
void I2NPMessagesHandler::Flush() {
if (!m_TunnelMsgs.empty()) {
i2p::tunnel::tunnels.PostTunnelData(m_TunnelMsgs);
m_TunnelMsgs.clear();
}
if (!m_TunnelGatewayMsgs.empty ())
{
if (!m_TunnelGatewayMsgs.empty()) {
i2p::tunnel::tunnels.PostTunnelData(m_TunnelGatewayMsgs);
m_TunnelGatewayMsgs.clear();
}

View file

@ -19,8 +19,7 @@
#include "RouterInfo.h"
#include "LeaseSet.h"
namespace i2p
{
namespace i2p {
// I2NP header
const size_t I2NP_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1;
@ -72,8 +71,10 @@ namespace i2p
const size_t ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET = ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
const size_t ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET = ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET + 3;
const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET =
ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET =
ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET = ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464;
@ -99,8 +100,7 @@ namespace i2p
const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201;
enum I2NPMessageType
{
enum I2NPMessageType {
eI2NPDummyMsg = 0,
eI2NPDatabaseStore = 1,
eI2NPDatabaseLookup = 2,
@ -132,9 +132,9 @@ namespace i2p
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
namespace tunnel
{
namespace tunnel {
class InboundTunnel;
class TunnelPool;
}
@ -143,8 +143,7 @@ namespace tunnel
const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60 * 1000; // 1 minute in milliseconds
struct I2NPMessage
{
struct I2NPMessage {
uint8_t *buf;
size_t len, offset, maxLen;
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
@ -154,19 +153,32 @@ namespace tunnel
// header accessors
uint8_t *GetHeader() { return GetBuffer(); };
const uint8_t *GetHeader() const { return GetBuffer(); };
void SetTypeID(uint8_t typeID) { GetHeader()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
uint8_t GetTypeID() const { return GetHeader()[I2NP_HEADER_TYPEID_OFFSET]; };
void SetMsgID(uint32_t msgID) { htobe32buf(GetHeader() + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint32_t GetMsgID() const { return bufbe32toh(GetHeader() + I2NP_HEADER_MSGID_OFFSET); };
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); };
void SetExpiration(uint64_t expiration) {
htobe64buf(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET, expiration);
};
uint64_t GetExpiration() const { return bufbe64toh(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET); };
void SetSize(uint16_t size) { htobe16buf(GetHeader() + I2NP_HEADER_SIZE_OFFSET, size); };
uint16_t GetSize() const { return bufbe16toh(GetHeader() + I2NP_HEADER_SIZE_OFFSET); };
void UpdateSize() { SetSize(GetPayloadLength()); };
void SetChks(uint8_t chks) { GetHeader()[I2NP_HEADER_CHKS_OFFSET] = chks; };
void UpdateChks ()
{
void UpdateChks() {
uint8_t hash[32];
SHA256(GetPayload(), GetPayloadLength(), hash);
GetHeader()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
@ -174,25 +186,27 @@ namespace tunnel
// payload
uint8_t *GetPayload() { return GetBuffer() + I2NP_HEADER_SIZE; };
const uint8_t *GetPayload() const { return GetBuffer() + I2NP_HEADER_SIZE; };
uint8_t *GetBuffer() { return buf + offset; };
const uint8_t *GetBuffer() const { return buf + offset; };
size_t GetLength() const { return len - offset; };
size_t GetPayloadLength() const { return GetLength() - I2NP_HEADER_SIZE; };
void Align (size_t alignment)
{
void Align(size_t alignment) {
if (len + alignment > maxLen) return;
size_t rem = ((size_t) GetBuffer()) % alignment;
if (rem)
{
if (rem) {
offset += (alignment - rem);
len += (alignment - rem);
}
}
size_t Concat (const uint8_t * buf1, size_t len1)
{
size_t Concat(const uint8_t *buf1, size_t len1) {
// make sure with don't write beyond maxLen
if (len + len1 > maxLen) len1 = maxLen - len;
memcpy(buf + len, buf1, len1);
@ -200,8 +214,7 @@ namespace tunnel
return len1;
}
I2NPMessage& operator=(const I2NPMessage& other)
{
I2NPMessage &operator=(const I2NPMessage &other) {
memcpy(buf + offset, other.buf + other.offset, other.GetLength());
len = offset + other.GetLength();
from = other.from;
@ -210,6 +223,7 @@ namespace tunnel
// for SSU only
uint8_t *GetSSUHeader() { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
void FromSSU(uint32_t msgID) // we have received SSU message and convert it to regular
{
const uint8_t *ssu = GetSSUHeader();
@ -219,21 +233,25 @@ namespace tunnel
SetSize(len - offset - I2NP_HEADER_SIZE);
SetChks(0);
}
uint32_t ToSSU() // return msgID
{
uint8_t header[I2NP_HEADER_SIZE];
memcpy(header, GetHeader(), I2NP_HEADER_SIZE);
uint8_t *ssu = GetSSUHeader();
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
htobe32buf(ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET,
bufbe64toh(header + I2NP_HEADER_EXPIRATION_OFFSET) / 1000LL);
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh(header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh(header + I2NP_HEADER_MSGID_OFFSET);
}
// for NTCP2 only
uint8_t *GetNTCP2Header() { return GetPayload() - I2NP_NTCP2_HEADER_SIZE; };
size_t GetNTCP2Length() const { return GetPayloadLength() + I2NP_NTCP2_HEADER_SIZE; };
void FromNTCP2 ()
{
void FromNTCP2() {
const uint8_t *ntcp2 = GetNTCP2Header();
memcpy(GetHeader() + I2NP_HEADER_TYPEID_OFFSET, ntcp2 + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid
SetExpiration(bufbe32toh(ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET) * 1000LL);
@ -241,67 +259,98 @@ namespace tunnel
SetChks(0);
}
void ToNTCP2 ()
{
void ToNTCP2() {
uint8_t *ntcp2 = GetNTCP2Header();
htobe32buf (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET, bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
htobe32buf(ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET,
bufbe64toh(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET) / 1000LL);
memcpy(ntcp2 + I2NP_HEADER_TYPEID_OFFSET, GetHeader() + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid
}
void FillI2NPMessageHeader(I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true);
void RenewI2NPMessageHeader();
bool IsExpired() const;
};
template<int sz>
struct I2NPMessageBuffer: public I2NPMessage
{
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
struct I2NPMessageBuffer : public I2NPMessage {
I2NPMessageBuffer() {
buf = m_Buffer;
maxLen = sz;
};
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
};
std::shared_ptr<I2NPMessage> NewI2NPMessage();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage();
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage(bool endpoint);
std::shared_ptr<I2NPMessage> NewI2NPMessage(size_t len);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage>
CreateI2NPMessage(I2NPMessageType msgType, const uint8_t *buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage>
CreateI2NPMessage(const uint8_t *buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
std::shared_ptr<I2NPMessage> CopyI2NPMessage(std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg(uint32_t msgID);
std::shared_ptr<I2NPMessage> CreateRouterInfoDatabaseLookupMsg(const uint8_t *key, const uint8_t *from,
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
uint32_t replyTunnelID, bool exploratory = false,
std::set<i2p::data::IdentHash> *excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg(const i2p::data::IdentHash &dest,
const std::set<i2p::data::IdentHash> &excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
const uint8_t *replyKey, const uint8_t *replyTag,
bool replyECIES = false);
std::shared_ptr<I2NPMessage>
CreateDatabaseSearchReply(const i2p::data::IdentHash &ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage>
CreateDatabaseStoreMsg(std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg(const i2p::data::IdentHash &storeHash,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage>
CreateDatabaseStoreMsg(std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg(std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg(const uint8_t *buf);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg(uint32_t tunnelID, const uint8_t *payload);
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg(bool endpoint);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, const uint8_t *buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, I2NPMessageType msgType,
const uint8_t *buf, size_t len, uint32_t replyMsgID = 0);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg(uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
size_t GetI2NPMessageLength(const uint8_t *msg, size_t len);
void HandleI2NPMessage(uint8_t *msg, size_t len);
void HandleI2NPMessage(std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler
{
class I2NPMessagesHandler {
public:
~I2NPMessagesHandler();
void PutNextMessage(std::shared_ptr<I2NPMessage> &&msg);
void Flush();
private:
@ -310,7 +359,9 @@ namespace tunnel
};
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500;
void SetMaxNumTransitTunnels(uint16_t maxNumTransitTunnels);
uint16_t GetMaxNumTransitTunnels();
}

View file

@ -14,39 +14,35 @@
#include "LittleBigEndian.h"
#ifdef NEEDS_LOCAL_ENDIAN
uint16_t htobe16(uint16_t int16)
{
uint16_t htobe16(uint16_t int16) {
BigEndian<uint16_t> u16(int16);
return u16.raw_value;
}
uint32_t htobe32(uint32_t int32)
{
uint32_t htobe32(uint32_t int32) {
BigEndian<uint32_t> u32(int32);
return u32.raw_value;
}
uint64_t htobe64(uint64_t int64)
{
uint64_t htobe64(uint64_t int64) {
BigEndian<uint64_t> u64(int64);
return u64.raw_value;
}
uint16_t be16toh(uint16_t big16)
{
uint16_t be16toh(uint16_t big16) {
LittleEndian<uint16_t> u16(big16);
return u16.raw_value;
}
uint32_t be32toh(uint32_t big32)
{
uint32_t be32toh(uint32_t big32) {
LittleEndian<uint32_t> u32(big32);
return u32.raw_value;
}
uint64_t be64toh(uint64_t big64)
{
uint64_t be64toh(uint64_t big64) {
LittleEndian<uint64_t> u64(big64);
return u64.raw_value;
}
#endif

View file

@ -8,6 +8,7 @@
#ifndef I2PENDIAN_H__
#define I2PENDIAN_H__
#include <inttypes.h>
#include <string.h>
@ -53,13 +54,19 @@
#else
#define NEEDS_LOCAL_ENDIAN
#include <cstdint>
uint16_t htobe16(uint16_t int16);
uint32_t htobe32(uint32_t int32);
uint64_t htobe64(uint64_t int64);
uint16_t be16toh(uint16_t big16);
uint32_t be32toh(uint32_t big32);
uint64_t be64toh(uint64_t big64);
// assume LittleEndine
@ -72,99 +79,81 @@ uint64_t be64toh(uint64_t big64);
#endif
inline uint16_t buf16toh(const void *buf)
{
inline uint16_t buf16toh(const void *buf) {
uint16_t b16;
memcpy(&b16, buf, sizeof(uint16_t));
return b16;
}
inline uint32_t buf32toh(const void *buf)
{
inline uint32_t buf32toh(const void *buf) {
uint32_t b32;
memcpy(&b32, buf, sizeof(uint32_t));
return b32;
}
inline uint64_t buf64toh(const void *buf)
{
inline uint64_t buf64toh(const void *buf) {
uint64_t b64;
memcpy(&b64, buf, sizeof(uint64_t));
return b64;
}
inline uint16_t bufbe16toh(const void *buf)
{
inline uint16_t bufbe16toh(const void *buf) {
return be16toh(buf16toh(buf));
}
inline uint32_t bufbe32toh(const void *buf)
{
inline uint32_t bufbe32toh(const void *buf) {
return be32toh(buf32toh(buf));
}
inline uint64_t bufbe64toh(const void *buf)
{
inline uint64_t bufbe64toh(const void *buf) {
return be64toh(buf64toh(buf));
}
inline void htobuf16(void *buf, uint16_t b16)
{
inline void htobuf16(void *buf, uint16_t b16) {
memcpy(buf, &b16, sizeof(uint16_t));
}
inline void htobuf32(void *buf, uint32_t b32)
{
inline void htobuf32(void *buf, uint32_t b32) {
memcpy(buf, &b32, sizeof(uint32_t));
}
inline void htobuf64(void *buf, uint64_t b64)
{
inline void htobuf64(void *buf, uint64_t b64) {
memcpy(buf, &b64, sizeof(uint64_t));
}
inline void htobe16buf(void *buf, uint16_t big16)
{
inline void htobe16buf(void *buf, uint16_t big16) {
htobuf16(buf, htobe16(big16));
}
inline void htobe32buf(void *buf, uint32_t big32)
{
inline void htobe32buf(void *buf, uint32_t big32) {
htobuf32(buf, htobe32(big32));
}
inline void htobe64buf(void *buf, uint64_t big64)
{
inline void htobe64buf(void *buf, uint64_t big64) {
htobuf64(buf, htobe64(big64));
}
inline void htole16buf(void *buf, uint16_t big16)
{
inline void htole16buf(void *buf, uint16_t big16) {
htobuf16(buf, htole16(big16));
}
inline void htole32buf(void *buf, uint32_t big32)
{
inline void htole32buf(void *buf, uint32_t big32) {
htobuf32(buf, htole32(big32));
}
inline void htole64buf(void *buf, uint64_t big64)
{
inline void htole64buf(void *buf, uint64_t big64) {
htobuf64(buf, htole64(big64));
}
inline uint16_t bufle16toh(const void *buf)
{
inline uint16_t bufle16toh(const void *buf) {
return le16toh(buf16toh(buf));
}
inline uint32_t bufle32toh(const void *buf)
{
inline uint32_t bufle32toh(const void *buf) {
return le32toh(buf32toh(buf));
}
inline uint64_t bufle64toh(const void *buf)
{
inline uint64_t bufle64toh(const void *buf) {
return le64toh(buf64toh(buf));
}

View file

@ -12,12 +12,9 @@
#include "Timestamp.h"
#include "Identity.h"
namespace i2p
{
namespace data
{
Identity& Identity::operator=(const Keys& keys)
{
namespace i2p {
namespace data {
Identity &Identity::operator=(const Keys &keys) {
// copy public and signing keys together
memcpy(publicKey, keys.publicKey, sizeof(publicKey));
memcpy(signingKey, keys.signingKey, sizeof(signingKey));
@ -25,8 +22,7 @@ namespace data
return *this;
}
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
{
size_t Identity::FromBuffer(const uint8_t *buf, size_t len) {
if (len < DEFAULT_IDENTITY_SIZE) {
// buffer too small, don't overflow
return 0;
@ -35,49 +31,40 @@ namespace data
return DEFAULT_IDENTITY_SIZE;
}
IdentHash Identity::Hash () const
{
IdentHash Identity::Hash() const {
IdentHash hash;
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
return hash;
}
IdentityEx::IdentityEx() :
m_ExtendedLen (0)
{
m_ExtendedLen(0) {
}
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
{
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{
IdentityEx::IdentityEx(const uint8_t *publicKey, const uint8_t *signingKey, SigningKeyType type,
CryptoKeyType cryptoType) {
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) {
memcpy(m_StandardIdentity.publicKey, publicKey, 32);
RAND_bytes(m_StandardIdentity.publicKey + 32, 224);
}
else
} else
memcpy(m_StandardIdentity.publicKey, publicKey, 256);
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
if (type != SIGNING_KEY_TYPE_DSA_SHA1) {
size_t excessLen = 0;
uint8_t *excessBuf = nullptr;
switch (type)
{
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{
switch (type) {
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: {
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
RAND_bytes(m_StandardIdentity.signingKey, padding);
memcpy(m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
{
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: {
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
RAND_bytes(m_StandardIdentity.signingKey, padding);
memcpy(m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
{
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: {
memcpy(m_StandardIdentity.signingKey, signingKey, 128);
excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128
excessBuf = new uint8_t[excessLen];
@ -90,23 +77,22 @@ namespace data
LogPrint(eLogError, "Identity: RSA signing key type ", (int) type, " is not supported");
break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
{
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: {
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
RAND_bytes(m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
memcpy(m_StandardIdentity.signingKey + padding, signingKey,
i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256:
{
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: {
// 256
size_t padding = 128 - i2p::crypto::GOSTR3410_256_PUBLIC_KEY_LENGTH; // 64 = 128 - 64
RAND_bytes(m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::GOSTR3410_256_PUBLIC_KEY_LENGTH);
memcpy(m_StandardIdentity.signingKey + padding, signingKey,
i2p::crypto::GOSTR3410_256_PUBLIC_KEY_LENGTH);
break;
}
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
{
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: {
// 512
// no padding, key length is 128
memcpy(m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH);
@ -122,10 +108,8 @@ namespace data
// fill extended buffer
htobe16buf(m_ExtendedBuffer, type);
htobe16buf(m_ExtendedBuffer + 2, cryptoType);
if (excessLen && excessBuf)
{
if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4)
{
if (excessLen && excessBuf) {
if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4) {
LogPrint(eLogError, "Identity: Unexpected excessive signing key len ", excessLen);
excessLen = MAX_EXTENDED_BUFFER_SIZE - 4;
}
@ -134,8 +118,7 @@ namespace data
}
// calculate ident hash
RecalculateIdentHash();
}
else // DSA-SHA1
} else // DSA-SHA1
{
memcpy(m_StandardIdentity.signingKey, signingKey, sizeof(m_StandardIdentity.signingKey));
memset(m_StandardIdentity.certificate, 0, sizeof(m_StandardIdentity.certificate));
@ -145,8 +128,7 @@ namespace data
CreateVerifier();
}
void IdentityEx::RecalculateIdentHash(uint8_t * buf)
{
void IdentityEx::RecalculateIdentHash(uint8_t *buf) {
bool dofree = buf == nullptr;
size_t sz = GetFullLen();
if (!buf)
@ -158,36 +140,30 @@ namespace data
}
IdentityEx::IdentityEx(const uint8_t *buf, size_t len) :
m_ExtendedLen (0)
{
m_ExtendedLen(0) {
FromBuffer(buf, len);
}
IdentityEx::IdentityEx(const IdentityEx &other) :
m_ExtendedLen (0)
{
m_ExtendedLen(0) {
*this = other;
}
IdentityEx::IdentityEx(const Identity &standard) :
m_ExtendedLen (0)
{
m_ExtendedLen(0) {
*this = standard;
}
IdentityEx::~IdentityEx ()
{
IdentityEx::~IdentityEx() {
delete m_Verifier;
}
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
{
IdentityEx &IdentityEx::operator=(const IdentityEx &other) {
memcpy(&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
m_IdentHash = other.m_IdentHash;
m_ExtendedLen = other.m_ExtendedLen;
if (m_ExtendedLen > 0)
{
if (m_ExtendedLen > 0) {
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE;
memcpy(m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen);
}
@ -198,8 +174,7 @@ namespace data
return *this;
}
IdentityEx& IdentityEx::operator=(const Identity& standard)
{
IdentityEx &IdentityEx::operator=(const Identity &standard) {
m_StandardIdentity = standard;
m_IdentHash = m_StandardIdentity.Hash();
@ -211,31 +186,25 @@ namespace data
return *this;
}
size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len)
{
if (len < DEFAULT_IDENTITY_SIZE)
{
size_t IdentityEx::FromBuffer(const uint8_t *buf, size_t len) {
if (len < DEFAULT_IDENTITY_SIZE) {
LogPrint(eLogError, "Identity: Buffer length ", len, " is too small");
return 0;
}
memcpy(&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
m_ExtendedLen = bufbe16toh(m_StandardIdentity.certificate + 1);
if (m_ExtendedLen)
{
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
{
if (m_ExtendedLen) {
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) {
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE;
memcpy(m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
}
else
{
LogPrint (eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
} else {
LogPrint(eLogError, "Identity: Certificate length ", m_ExtendedLen, " exceeds buffer length ",
len - DEFAULT_IDENTITY_SIZE);
m_ExtendedLen = 0;
return 0;
}
}
else
} else
m_ExtendedLen = 0;
SHA256(buf, GetFullLen(), m_IdentHash);
@ -245,8 +214,7 @@ namespace data
return GetFullLen();
}
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
{
size_t IdentityEx::ToBuffer(uint8_t *buf, size_t len) const {
const size_t fullLen = GetFullLen();
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
memcpy(buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
@ -255,16 +223,14 @@ namespace data
return fullLen;
}
size_t IdentityEx::FromBase64(const std::string& s)
{
size_t IdentityEx::FromBase64(const std::string &s) {
const size_t slen = s.length();
std::vector<uint8_t> buf(slen); // binary data can't exceed base64
const size_t len = Base64ToByteStream(s.c_str(), slen, buf.data(), slen);
return FromBuffer(buf.data(), len);
}
std::string IdentityEx::ToBase64 () const
{
std::string IdentityEx::ToBase64() const {
const size_t bufLen = GetFullLen();
const size_t strLen = Base64EncodingBufferSize(bufLen);
std::vector<uint8_t> buf(bufLen);
@ -274,68 +240,59 @@ namespace data
return std::string(str.data(), l1);
}
size_t IdentityEx::GetSigningPublicKeyLen () const
{
size_t IdentityEx::GetSigningPublicKeyLen() const {
if (!m_Verifier) CreateVerifier();
if (m_Verifier)
return m_Verifier->GetPublicKeyLen();
return 128;
}
const uint8_t * IdentityEx::GetSigningPublicKeyBuffer () const
{
const uint8_t *IdentityEx::GetSigningPublicKeyBuffer() const {
auto keyLen = GetSigningPublicKeyLen();
if (keyLen > 128) return nullptr; // P521
return m_StandardIdentity.signingKey + 128 - keyLen;
}
size_t IdentityEx::GetSigningPrivateKeyLen () const
{
size_t IdentityEx::GetSigningPrivateKeyLen() const {
if (!m_Verifier) CreateVerifier();
if (m_Verifier)
return m_Verifier->GetPrivateKeyLen();
return GetSignatureLen() / 2;
}
size_t IdentityEx::GetSignatureLen () const
{
size_t IdentityEx::GetSignatureLen() const {
if (!m_Verifier) CreateVerifier();
if (m_Verifier)
return m_Verifier->GetSignatureLen();
return i2p::crypto::DSA_SIGNATURE_LENGTH;
}
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
bool IdentityEx::Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
if (!m_Verifier) CreateVerifier();
if (m_Verifier)
return m_Verifier->Verify(buf, len, signature);
return false;
}
SigningKeyType IdentityEx::GetSigningKeyType () const
{
SigningKeyType IdentityEx::GetSigningKeyType() const {
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 2)
return bufbe16toh(m_ExtendedBuffer); // signing key
return SIGNING_KEY_TYPE_DSA_SHA1;
}
bool IdentityEx::IsRSA () const
{
bool IdentityEx::IsRSA() const {
auto sigType = GetSigningKeyType();
return sigType <= SIGNING_KEY_TYPE_RSA_SHA512_4096 && sigType >= SIGNING_KEY_TYPE_RSA_SHA256_2048;
}
CryptoKeyType IdentityEx::GetCryptoKeyType () const
{
CryptoKeyType IdentityEx::GetCryptoKeyType() const {
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 4)
return bufbe16toh(m_ExtendedBuffer + 2); // crypto key
return CRYPTO_KEY_TYPE_ELGAMAL;
}
i2p::crypto::Verifier * IdentityEx::CreateVerifier (SigningKeyType keyType)
{
switch (keyType)
{
i2p::crypto::Verifier *IdentityEx::CreateVerifier(SigningKeyType keyType) {
switch (keyType) {
case SIGNING_KEY_TYPE_DSA_SHA1:
return new i2p::crypto::DSAVerifier();
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
@ -363,22 +320,20 @@ namespace data
return nullptr;
}
void IdentityEx::CreateVerifier () const
{
void IdentityEx::CreateVerifier() const {
if (m_Verifier) return; // don't create again
auto verifier = CreateVerifier(GetSigningKeyType());
if (verifier)
{
if (verifier) {
auto keyLen = verifier->GetPublicKeyLen();
if (keyLen <= 128)
verifier->SetPublicKey(m_StandardIdentity.signingKey + 128 - keyLen);
else
{
else {
// for P521
uint8_t *signingKey = new uint8_t[keyLen];
memcpy(signingKey, m_StandardIdentity.signingKey, 128);
size_t excessLen = keyLen - 128;
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
memcpy(signingKey + 128, m_ExtendedBuffer + 4,
excessLen); // right after signing and crypto key types
verifier->SetPublicKey(signingKey);
delete[] signingKey;
}
@ -386,8 +341,7 @@ namespace data
UpdateVerifier(verifier);
}
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
{
void IdentityEx::UpdateVerifier(i2p::crypto::Verifier *verifier) const {
bool del = false;
{
std::lock_guard<std::mutex> l(m_VerifierMutex);
@ -400,8 +354,7 @@ namespace data
delete verifier;
}
void IdentityEx::DropVerifier () const
{
void IdentityEx::DropVerifier() const {
i2p::crypto::Verifier *verifier;
{
std::lock_guard<std::mutex> l(m_VerifierMutex);
@ -411,10 +364,9 @@ namespace data
delete verifier;
}
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> IdentityEx::CreateEncryptor (CryptoKeyType keyType, const uint8_t * key)
{
switch (keyType)
{
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>
IdentityEx::CreateEncryptor(CryptoKeyType keyType, const uint8_t *key) {
switch (keyType) {
case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
break;
@ -434,14 +386,12 @@ namespace data
return nullptr;
}
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> IdentityEx::CreateEncryptor (const uint8_t * key) const
{
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> IdentityEx::CreateEncryptor(const uint8_t *key) const {
if (!key) key = GetEncryptionPublicKey(); // use publicKey
return CreateEncryptor(GetCryptoKeyType(), key);
}
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
{
PrivateKeys &PrivateKeys::operator=(const Keys &keys) {
m_Public = std::make_shared<IdentityEx>(Identity(keys));
memcpy(m_PrivateKey, keys.privateKey, 256); // 256
memcpy(m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen());
@ -453,29 +403,28 @@ namespace data
return *this;
}
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
{
PrivateKeys &PrivateKeys::operator=(const PrivateKeys &other) {
m_Public = std::make_shared<IdentityEx>(*other.m_Public);
memcpy(m_PrivateKey, other.m_PrivateKey, 256); // 256
m_OfflineSignature = other.m_OfflineSignature;
m_TransientSignatureLen = other.m_TransientSignatureLen;
m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen;
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen : m_Public->GetSigningPrivateKeyLen ());
memcpy(m_SigningPrivateKey, other.m_SigningPrivateKey,
m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen
: m_Public->GetSigningPrivateKeyLen());
m_Signer = nullptr;
CreateSigner();
return *this;
}
size_t PrivateKeys::GetFullLen () const
{
size_t PrivateKeys::GetFullLen() const {
size_t ret = m_Public->GetFullLen() + GetPrivateKeyLen() + m_Public->GetSigningPrivateKeyLen();
if (IsOfflineSignature())
ret += m_OfflineSignature.size() + m_TransientSigningPrivateKeyLen;
return ret;
}
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
{
size_t PrivateKeys::FromBuffer(const uint8_t *buf, size_t len) {
m_Public = std::make_shared<IdentityEx>();
size_t ret = m_Public->FromBuffer(buf, len);
auto cryptoKeyLen = GetPrivateKeyLen();
@ -490,25 +439,24 @@ namespace data
// check if signing private key is all zeros
bool allzeros = true;
for (size_t i = 0; i < signingPrivateKeySize; i++)
if (m_SigningPrivateKey[i])
{
if (m_SigningPrivateKey[i]) {
allzeros = false;
break;
}
if (allzeros)
{
if (allzeros) {
// offline information
const uint8_t *offlineInfo = buf + ret;
ret += 4; // expires timestamp
SigningKeyType keyType = bufbe16toh (buf + ret); ret += 2; // key type
SigningKeyType keyType = bufbe16toh(buf + ret);
ret += 2; // key type
std::unique_ptr<i2p::crypto::Verifier> transientVerifier(IdentityEx::CreateVerifier(keyType));
if (!transientVerifier) return 0;
auto keyLen = transientVerifier->GetPublicKeyLen();
if (keyLen + ret > len) return 0;
transientVerifier->SetPublicKey (buf + ret); ret += keyLen;
transientVerifier->SetPublicKey(buf + ret);
ret += keyLen;
if (m_Public->GetSignatureLen() + ret > len) return 0;
if (!m_Public->Verify (offlineInfo, keyLen + 6, buf + ret))
{
if (!m_Public->Verify(offlineInfo, keyLen + 6, buf + ret)) {
LogPrint(eLogError, "Identity: Offline signature verification failed");
return 0;
}
@ -524,14 +472,12 @@ namespace data
memcpy(m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen);
ret += m_TransientSigningPrivateKeyLen;
CreateSigner(keyType);
}
else
} else
CreateSigner(m_Public->GetSigningKeyType());
return ret;
}
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
{
size_t PrivateKeys::ToBuffer(uint8_t *buf, size_t len) const {
size_t ret = m_Public->ToBuffer(buf, len);
auto cryptoKeyLen = GetPrivateKeyLen();
memcpy(buf + ret, m_PrivateKey, cryptoKeyLen);
@ -543,8 +489,7 @@ namespace data
else
memcpy(buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
ret += signingPrivateKeySize;
if (IsOfflineSignature ())
{
if (IsOfflineSignature()) {
// offline signature
auto offlineSignatureLen = m_OfflineSignature.size();
if (ret + offlineSignatureLen > len) return 0;
@ -558,8 +503,7 @@ namespace data
return ret;
}
size_t PrivateKeys::FromBase64(const std::string& s)
{
size_t PrivateKeys::FromBase64(const std::string &s) {
uint8_t *buf = new uint8_t[s.length()];
size_t l = i2p::data::Base64ToByteStream(s.c_str(), s.length(), buf, s.length());
size_t ret = FromBuffer(buf, l);
@ -567,8 +511,7 @@ namespace data
return ret;
}
std::string PrivateKeys::ToBase64 () const
{
std::string PrivateKeys::ToBase64() const {
uint8_t *buf = new uint8_t[GetFullLen()];
char *str = new char[GetFullLen() * 2];
size_t l = ToBuffer(buf, GetFullLen());
@ -580,40 +523,37 @@ namespace data
return ret;
}
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
void PrivateKeys::Sign(const uint8_t *buf, int len, uint8_t *signature) const {
if (!m_Signer)
CreateSigner();
m_Signer->Sign(buf, len, signature);
}
void PrivateKeys::CreateSigner () const
{
void PrivateKeys::CreateSigner() const {
if (IsOfflineSignature())
CreateSigner(bufbe16toh(m_OfflineSignature.data() + 4)); // key type
else
CreateSigner(m_Public->GetSigningKeyType());
}
void PrivateKeys::CreateSigner (SigningKeyType keyType) const
{
void PrivateKeys::CreateSigner(SigningKeyType keyType) const {
if (m_Signer) return;
if (keyType == SIGNING_KEY_TYPE_DSA_SHA1)
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
m_Signer.reset(
new i2p::crypto::DSASigner(m_SigningPrivateKey, m_Public->GetStandardIdentity().signingKey));
else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature())
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check
else
{
m_Signer.reset(new i2p::crypto::EDDSA25519Signer(m_SigningPrivateKey,
m_Public->GetStandardIdentity().certificate -
i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check
else {
// public key is not required
auto signer = CreateSigner(keyType, m_SigningPrivateKey);
if (signer) m_Signer.reset(signer);
}
}
i2p::crypto::Signer * PrivateKeys::CreateSigner (SigningKeyType keyType, const uint8_t * priv)
{
switch (keyType)
{
i2p::crypto::Signer *PrivateKeys::CreateSigner(SigningKeyType keyType, const uint8_t *priv) {
switch (keyType) {
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
return new i2p::crypto::ECDSAP256Signer(priv);
break;
@ -646,36 +586,31 @@ namespace data
return nullptr;
}
size_t PrivateKeys::GetSignatureLen () const
{
size_t PrivateKeys::GetSignatureLen() const {
return IsOfflineSignature() ? m_TransientSignatureLen : m_Public->GetSignatureLen();
}
size_t PrivateKeys::GetPrivateKeyLen () const
{
size_t PrivateKeys::GetPrivateKeyLen() const {
// private key length always 256, but type 4
return (m_Public->GetCryptoKeyType() == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256;
}
uint8_t * PrivateKeys::GetPadding()
{
uint8_t *PrivateKeys::GetPadding() {
if (m_Public->GetSigningKeyType() == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
return m_Public->GetEncryptionPublicKeyBuffer() + 256;
else
return nullptr; // TODO: implement me
}
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> PrivateKeys::CreateDecryptor (const uint8_t * key) const
{
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> PrivateKeys::CreateDecryptor(const uint8_t *key) const {
if (!key) key = m_PrivateKey; // use privateKey
return CreateDecryptor(m_Public->GetCryptoKeyType(), key);
}
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> PrivateKeys::CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key)
{
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor>
PrivateKeys::CreateDecryptor(CryptoKeyType cryptoType, const uint8_t *key) {
if (!key) return nullptr;
switch (cryptoType)
{
switch (cryptoType) {
case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalDecryptor>(key);
break;
@ -695,10 +630,8 @@ namespace data
return nullptr;
}
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType)
{
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
PrivateKeys PrivateKeys::CreateRandomKeys(SigningKeyType type, CryptoKeyType cryptoType) {
if (type != SIGNING_KEY_TYPE_DSA_SHA1) {
PrivateKeys keys;
// signature
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
@ -715,10 +648,8 @@ namespace data
return PrivateKeys(i2p::data::CreateRandomKeys()); // DSA-SHA1
}
void PrivateKeys::GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub)
{
switch (type)
{
void PrivateKeys::GenerateSigningKeyPair(SigningKeyType type, uint8_t *priv, uint8_t *pub) {
switch (type) {
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
i2p::crypto::CreateECDSAP256RandomKeys(priv, pub);
break;
@ -749,15 +680,14 @@ namespace data
i2p::crypto::CreateRedDSA25519RandomKeys(priv, pub);
break;
default:
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
LogPrint(eLogWarning, "Identity: Signing key type ", (int) type,
" is not supported. Create DSA-SHA1");
i2p::crypto::CreateDSARandomKeys(priv, pub); // DSA-SHA1
}
}
void PrivateKeys::GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub)
{
switch (type)
{
void PrivateKeys::GenerateCryptoKeyPair(CryptoKeyType type, uint8_t *priv, uint8_t *pub) {
switch (type) {
case CRYPTO_KEY_TYPE_ELGAMAL:
i2p::crypto::GenerateElGamalKeyPair(priv, pub);
break;
@ -776,20 +706,20 @@ namespace data
}
}
PrivateKeys PrivateKeys::CreateOfflineKeys (SigningKeyType type, uint32_t expires) const
{
PrivateKeys PrivateKeys::CreateOfflineKeys(SigningKeyType type, uint32_t expires) const {
PrivateKeys keys(*this);
std::unique_ptr<i2p::crypto::Verifier> verifier(IdentityEx::CreateVerifier(type));
if (verifier)
{
if (verifier) {
size_t pubKeyLen = verifier->GetPublicKeyLen();
keys.m_TransientSigningPrivateKeyLen = verifier->GetPrivateKeyLen();
keys.m_TransientSignatureLen = verifier->GetSignatureLen();
keys.m_OfflineSignature.resize(pubKeyLen + m_Public->GetSignatureLen() + 6);
htobe32buf(keys.m_OfflineSignature.data(), expires); // expires
htobe16buf(keys.m_OfflineSignature.data() + 4, type); // type
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key
Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature
GenerateSigningKeyPair(type, keys.m_SigningPrivateKey,
keys.m_OfflineSignature.data() + 6); // public key
Sign(keys.m_OfflineSignature.data(), pubKeyLen + 6,
keys.m_OfflineSignature.data() + 6 + pubKeyLen); // signature
// recreate signer
keys.m_Signer = nullptr;
keys.CreateSigner(type);
@ -797,8 +727,7 @@ namespace data
return keys;
}
Keys CreateRandomKeys ()
{
Keys CreateRandomKeys() {
Keys keys;
// encryption
i2p::crypto::GenerateElGamalKeyPair(keys.privateKey, keys.publicKey);
@ -807,8 +736,7 @@ namespace data
return keys;
}
IdentHash CreateRoutingKey (const IdentHash& ident)
{
IdentHash CreateRoutingKey(const IdentHash &ident) {
uint8_t buf[41]; // ident + yyyymmdd
memcpy(buf, (const uint8_t *) ident, 32);
i2p::util::GetCurrentDate((char *) (buf + 32));
@ -817,8 +745,7 @@ namespace data
return key;
}
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
{
XORMetric operator^(const IdentHash &key1, const IdentHash &key2) {
XORMetric m;
#if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600)
if(i2p::cpu::avx)

View file

@ -20,18 +20,15 @@
#include "Signature.h"
#include "CryptoKey.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
typedef Tag<32> IdentHash;
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
{
inline std::string GetIdentHashAbbreviation(const IdentHash &ident) {
return ident.ToBase64().substr(0, 4);
}
struct Keys
{
struct Keys {
uint8_t privateKey[256];
uint8_t signingPrivateKey[20];
uint8_t publicKey[256];
@ -45,16 +42,19 @@ namespace data
const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4;
const uint8_t CERTIFICATE_TYPE_KEY = 5;
struct Identity
{
struct Identity {
uint8_t publicKey[256];
uint8_t signingKey[128];
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
Identity() = default;
Identity(const Keys &keys) { *this = keys; };
Identity &operator=(const Keys &keys);
size_t FromBuffer(const uint8_t *buf, size_t len);
IdentHash Hash() const;
};
@ -85,50 +85,76 @@ namespace data
typedef uint16_t CryptoKeyType;
const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521
class IdentityEx
{
class IdentityEx {
public:
IdentityEx();
IdentityEx(const uint8_t *publicKey, const uint8_t *signingKey,
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1,
CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
IdentityEx(const uint8_t *buf, size_t len);
IdentityEx(const IdentityEx &other);
IdentityEx(const Identity &standard);
~IdentityEx();
IdentityEx &operator=(const IdentityEx &other);
IdentityEx &operator=(const Identity &standard);
size_t FromBuffer(const uint8_t *buf, size_t len);
size_t ToBuffer(uint8_t *buf, size_t len) const;
size_t FromBase64(const std::string &s);
std::string ToBase64() const;
const Identity &GetStandardIdentity() const { return m_StandardIdentity; };
const IdentHash &GetIdentHash() const { return m_IdentHash; };
const uint8_t *GetEncryptionPublicKey() const { return m_StandardIdentity.publicKey; };
uint8_t *GetEncryptionPublicKeyBuffer() { return m_StandardIdentity.publicKey; };
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor(const uint8_t *key) const;
size_t GetFullLen() const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
size_t GetSigningPublicKeyLen() const;
const uint8_t *GetSigningPublicKeyBuffer() const; // returns NULL for P521
size_t GetSigningPrivateKeyLen() const;
size_t GetSignatureLen() const;
bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const;
SigningKeyType GetSigningKeyType() const;
bool IsRSA() const; // signing key type
CryptoKeyType GetCryptoKeyType() const;
void DropVerifier() const; // to save memory
bool operator==(const IdentityEx &other) const { return GetIdentHash() == other.GetIdentHash(); }
void RecalculateIdentHash(uint8_t *buff = nullptr);
static i2p::crypto::Verifier *CreateVerifier(SigningKeyType keyType);
static std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor (CryptoKeyType keyType, const uint8_t * key);
static std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>
CreateEncryptor(CryptoKeyType keyType, const uint8_t *key);
private:
void CreateVerifier() const;
void UpdateVerifier(i2p::crypto::Verifier *verifier) const;
private:
@ -146,44 +172,67 @@ namespace data
public:
PrivateKeys() = default;
PrivateKeys(const PrivateKeys &other) { *this = other; };
PrivateKeys(const Keys &keys) { *this = keys; };
PrivateKeys &operator=(const Keys &keys);
PrivateKeys &operator=(const PrivateKeys &other);
~PrivateKeys() = default;
std::shared_ptr<const IdentityEx> GetPublic() const { return m_Public; };
const uint8_t *GetPrivateKey() const { return m_PrivateKey; };
const uint8_t *GetSigningPrivateKey() const { return m_SigningPrivateKey; };
size_t GetSignatureLen() const; // might not match identity
bool IsOfflineSignature() const { return m_TransientSignatureLen > 0; };
uint8_t *GetPadding();
void RecalculateIdentHash(uint8_t *buf = nullptr) { m_Public->RecalculateIdentHash(buf); }
void Sign(const uint8_t *buf, int len, uint8_t *signature) const;
size_t GetFullLen() const;
size_t FromBuffer(const uint8_t *buf, size_t len);
size_t ToBuffer(uint8_t *buf, size_t len) const;
size_t FromBase64(const std::string &s);
std::string ToBase64() const;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor(const uint8_t *key) const;
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key);
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor>
CreateDecryptor(CryptoKeyType cryptoType, const uint8_t *key);
static PrivateKeys CreateRandomKeys(SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1,
CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
static void GenerateSigningKeyPair(SigningKeyType type, uint8_t *priv, uint8_t *pub);
static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long
static void
GenerateCryptoKeyPair(CryptoKeyType type, uint8_t *priv, uint8_t *pub); // priv and pub are 256 bytes long
static i2p::crypto::Signer *CreateSigner(SigningKeyType keyType, const uint8_t *priv);
// offline keys
PrivateKeys CreateOfflineKeys(SigningKeyType type, uint32_t expires) const;
const std::vector<uint8_t> &GetOfflineSignature() const { return m_OfflineSignature; };
private:
void CreateSigner() const;
void CreateSigner(SigningKeyType keyType) const;
size_t GetPrivateKeyLen() const;
private:
@ -198,49 +247,59 @@ namespace data
};
// kademlia
struct XORMetric
{
union
{
struct XORMetric {
union {
uint8_t metric[32];
uint64_t metric_ll[4];
};
void SetMin() { memset(metric, 0, 32); };
void SetMax() { memset(metric, 0xFF, 32); };
bool operator<(const XORMetric &other) const { return memcmp(metric, other.metric, 32) < 0; };
};
IdentHash CreateRoutingKey(const IdentHash &ident);
XORMetric operator^(const IdentHash &key1, const IdentHash &key2);
// destination for delivery instructions
class RoutingDestination
{
class RoutingDestination {
public:
RoutingDestination() {};
virtual ~RoutingDestination() {};
virtual std::shared_ptr<const IdentityEx> GetIdentity() const = 0;
virtual void Encrypt(const uint8_t *data, uint8_t *encrypted) const = 0; // encrypt data for
virtual bool IsDestination() const = 0; // for garlic
const IdentHash &GetIdentHash() const { return GetIdentity()->GetIdentHash(); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
virtual CryptoKeyType
GetEncryptionType() const { return GetIdentity()->GetCryptoKeyType(); }; // override in LeaseSet2
};
class LocalDestination
{
class LocalDestination {
public:
virtual ~LocalDestination() {};
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
virtual bool Decrypt(const uint8_t *encrypted, uint8_t *data,
CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
virtual std::shared_ptr<const IdentityEx> GetIdentity() const = 0;
const IdentHash &GetIdentHash() const { return GetIdentity()->GetIdentHash(); };
virtual bool SupportsEncryptionType (CryptoKeyType keyType) const { return GetIdentity ()->GetCryptoKeyType () == keyType; }; // override for LeaseSet
virtual const uint8_t * GetEncryptionPublicKey (CryptoKeyType keyType) const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet
virtual bool SupportsEncryptionType(CryptoKeyType keyType) const {
return GetIdentity()->GetCryptoKeyType() == keyType;
}; // override for LeaseSet
virtual const uint8_t *GetEncryptionPublicKey(
CryptoKeyType keyType) const { return GetIdentity()->GetEncryptionPublicKey(); }; // override for LeaseSet
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -19,19 +19,15 @@
#include "I2PEndian.h"
#include "Blinding.h"
namespace i2p
{
namespace i2p {
namespace tunnel
{
namespace tunnel {
class InboundTunnel;
}
namespace data
{
namespace data {
const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds
struct Lease
{
struct Lease {
IdentHash tunnelGateway;
uint32_t tunnelID;
uint64_t endDate; // 0 means invalid
@ -45,10 +41,8 @@ namespace data
}
};
struct LeaseCmp
{
bool operator() (std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const
{
struct LeaseCmp {
bool operator()(std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const {
if (l1->tunnelID != l2->tunnelID)
return l1->tunnelID < l2->tunnelID;
else
@ -64,57 +58,91 @@ namespace data
const uint8_t MAX_NUM_LEASES = 16;
const uint8_t NETDB_STORE_TYPE_LEASESET = 1;
class LeaseSet: public RoutingDestination
{
class LeaseSet : public RoutingDestination {
public:
LeaseSet(const uint8_t *buf, size_t len, bool storeLeases = true);
virtual ~LeaseSet () { delete[] m_EncryptionKey; delete[] m_Buffer; };
virtual ~LeaseSet() {
delete[] m_EncryptionKey;
delete[] m_Buffer;
};
virtual void Update(const uint8_t *buf, size_t len, bool verifySignature = true);
virtual bool IsNewer(const uint8_t *buf, size_t len) const;
void PopulateLeases(); // from buffer
const uint8_t *GetBuffer() const { return m_Buffer; };
size_t GetBufferLen() const { return m_BufferLen; };
bool IsValid() const { return m_IsValid; };
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases(bool withThreshold = true) const;
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const;
const std::vector<std::shared_ptr<const Lease> >
GetNonExpiredLeasesExcluding(LeaseInspectFunc exclude, bool withThreshold = true) const;
bool HasExpiredLeases() const;
bool IsExpired() const;
bool IsEmpty() const { return m_Leases.empty(); };
uint64_t GetExpirationTime() const { return m_ExpirationTime; };
bool ExpiresSoon(const uint64_t dlt = 1000 * 5, const uint64_t fudge = 0) const;
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
bool operator==(const LeaseSet &other) const {
return m_BufferLen == other.m_BufferLen && !memcmp(m_Buffer, other.m_Buffer, m_BufferLen);
};
virtual uint8_t GetStoreType() const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint32_t GetPublishedTimestamp() const { return 0; }; // should be set for LeaseSet2 only
virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier() const { return nullptr; };
virtual bool IsPublishedEncrypted() const { return false; };
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity() const { return m_Identity; };
void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
bool IsDestination() const { return true; };
protected:
void UpdateLeasesBegin();
void UpdateLeasesEnd();
void UpdateLease(const Lease &lease, uint64_t ts);
// called from LeaseSet2
LeaseSet(bool storeLeases);
void SetBuffer(const uint8_t *buf, size_t len);
void SetBufferLen(size_t len);
void SetIdentity(std::shared_ptr<const IdentityEx> identity) { m_Identity = identity; };
void SetExpirationTime(uint64_t t) { m_ExpirationTime = t; };
void SetIsValid(bool isValid) { m_IsValid = isValid; };
bool IsStoreLeases() const { return m_StoreLeases; };
private:
void ReadFromBuffer(bool readIdentity = true, bool verifySignature = true);
virtual uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const; // returns max expiration time
virtual uint64_t
ExtractExpirationTimestamp(const uint8_t *buf, size_t len) const; // returns max expiration time
private:
@ -141,37 +169,55 @@ namespace data
const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002;
const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004;
class LeaseSet2: public LeaseSet
{
class LeaseSet2 : public LeaseSet {
public:
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
LeaseSet2(uint8_t storeType, const uint8_t *buf, size_t len, bool storeLeases = true,
CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
LeaseSet2(const uint8_t *buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
const uint8_t *secret = nullptr,
CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
uint8_t GetStoreType() const { return m_StoreType; };
uint32_t GetPublishedTimestamp() const { return m_PublishedTimestamp; };
bool IsPublic() const { return m_IsPublic; };
bool IsPublishedEncrypted() const { return m_IsPublishedEncrypted; };
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier() const { return m_TransientVerifier; };
void Update(const uint8_t *buf, size_t len, bool verifySignature);
bool IsNewer(const uint8_t *buf, size_t len) const;
// implements RoutingDestination
void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
CryptoKeyType GetEncryptionType() const { return m_EncryptionType; };
private:
void ReadFromBuffer(const uint8_t *buf, size_t len, bool readIdentity = true, bool verifySignature = true);
void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret);
void ReadFromBufferEncrypted(const uint8_t *buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
const uint8_t *secret);
size_t ReadStandardLS2TypeSpecificPart(const uint8_t *buf, size_t len);
size_t ReadMetaLS2TypeSpecificPart(const uint8_t *buf, size_t len);
template<typename Verifier>
bool VerifySignature(Verifier &verifier, const uint8_t *buf, size_t len, size_t signatureOffset);
uint64_t ExtractExpirationTimestamp(const uint8_t *buf, size_t len) const;
uint64_t ExtractPublishedTimestamp(const uint8_t *buf, size_t len, uint64_t &expiration) const;
size_t ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const; // subcredential is subcredential + timestamp, return length of autData without flag
size_t
ExtractClientAuthData(const uint8_t *buf, size_t len, const uint8_t *secret, const uint8_t *subcredential,
uint8_t *authCookie) const; // subcredential is subcredential + timestamp, return length of autData without flag
private:
@ -185,18 +231,21 @@ namespace data
// also called from Streaming.cpp
template<typename Verifier>
std::shared_ptr<i2p::crypto::Verifier> ProcessOfflineSignature (const Verifier& verifier, const uint8_t * buf, size_t len, size_t& offset)
{
std::shared_ptr<i2p::crypto::Verifier>
ProcessOfflineSignature(const Verifier &verifier, const uint8_t *buf, size_t len, size_t &offset) {
if (offset + 6 >= len) return nullptr;
const uint8_t *signedData = buf + offset;
uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp
uint32_t expiresTimestamp = bufbe32toh(buf + offset);
offset += 4; // expires timestamp
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch()) return nullptr;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2;
uint16_t keyType = bufbe16toh(buf + offset);
offset += 2;
std::shared_ptr<i2p::crypto::Verifier> transientVerifier(i2p::data::IdentityEx::CreateVerifier(keyType));
if (!transientVerifier) return nullptr;
auto keyLen = transientVerifier->GetPublicKeyLen();
if (offset + keyLen >= len) return nullptr;
transientVerifier->SetPublicKey (buf + offset); offset += keyLen;
transientVerifier->SetPublicKey(buf + offset);
offset += keyLen;
if (offset + verifier->GetSignatureLen() >= len) return nullptr;
if (!verifier->Verify(signedData, keyLen + 6, buf + offset)) return nullptr;
offset += verifier->GetSignatureLen();
@ -204,31 +253,47 @@ namespace data
}
//------------------------------------------------------------------------------------
class LocalLeaseSet
{
class LocalLeaseSet {
public:
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *buf, size_t len);
virtual ~LocalLeaseSet() { delete[] m_Buffer; };
virtual uint8_t *GetBuffer() const { return m_Buffer; };
uint8_t *GetSignature() { return GetBuffer() + GetBufferLen() - GetSignatureLen(); };
virtual size_t GetBufferLen() const { return m_BufferLen; };
size_t GetSignatureLen() const { return m_Identity->GetSignatureLen(); };
uint8_t *GetLeases() { return m_Leases; };
const IdentHash &GetIdentHash() const { return m_Identity->GetIdentHash(); };
std::shared_ptr<const IdentityEx> GetIdentity() const { return m_Identity; };
bool IsExpired() const;
uint64_t GetExpirationTime() const { return m_ExpirationTime; };
void SetExpirationTime(uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
bool operator== (const LeaseSet& other) const
{ return GetBufferLen () == other.GetBufferLen () && !memcmp (GetBuffer (), other.GetBuffer (), GetBufferLen ()); };
bool operator==(const LeaseSet &other) const {
return GetBufferLen() == other.GetBufferLen() && !memcmp(GetBuffer(), other.GetBuffer(),
GetBufferLen());
};
virtual uint8_t GetStoreType() const { return NETDB_STORE_TYPE_LEASESET; };
virtual const IdentHash& GetStoreHash () const { return GetIdentHash (); }; // differ from ident hash for encrypted LeaseSet2
virtual std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return nullptr; }; // non-null for encrypted LeaseSet2
virtual const IdentHash &
GetStoreHash() const { return GetIdentHash(); }; // differ from ident hash for encrypted LeaseSet2
virtual std::shared_ptr<const LocalLeaseSet>
GetInnerLeaseSet() const { return nullptr; }; // non-null for encrypted LeaseSet2
private:
@ -238,12 +303,10 @@ namespace data
size_t m_BufferLen;
};
class LocalLeaseSet2: public LocalLeaseSet
{
class LocalLeaseSet2 : public LocalLeaseSet {
public:
struct KeySection
{
struct KeySection {
uint16_t keyType, keyLen;
const uint8_t *encryptionPublicKey;
};
@ -254,18 +317,22 @@ namespace data
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
LocalLeaseSet2(uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t *buf,
size_t len); // from I2CP
virtual ~LocalLeaseSet2() { delete[] m_Buffer; };
uint8_t *GetBuffer() const { return m_Buffer + 1; };
size_t GetBufferLen() const { return m_BufferLen; };
uint8_t GetStoreType() const { return m_Buffer[0]; };
protected:
LocalLeaseSet2 (std::shared_ptr<const IdentityEx> identity): LocalLeaseSet (identity, nullptr, 0), m_Buffer (nullptr), m_BufferLen(0) {}; // called from LocalEncryptedLeaseSet2
LocalLeaseSet2(std::shared_ptr<const IdentityEx> identity) : LocalLeaseSet(identity, nullptr, 0),
m_Buffer(nullptr), m_BufferLen(
0) {}; // called from LocalEncryptedLeaseSet2
protected:
@ -280,20 +347,25 @@ namespace data
typedef i2p::data::Tag<32> AuthPublicKey;
class LocalEncryptedLeaseSet2: public LocalLeaseSet2
{
class LocalEncryptedLeaseSet2 : public LocalLeaseSet2 {
public:
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr);
LocalEncryptedLeaseSet2(std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys &keys,
int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE,
std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr);
LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
LocalEncryptedLeaseSet2(std::shared_ptr<const IdentityEx> identity, const uint8_t *buf,
size_t len); // from I2CP
const IdentHash &GetStoreHash() const { return m_StoreHash; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet() const { return m_InnerLeaseSet; };
private:
void CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const;
void CreateClientAuthData(const uint8_t *subcredential, int authType,
std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t *authCookie,
uint8_t *authData) const;
private:

View file

@ -34,41 +34,35 @@ struct BigEndian;
// Little-Endian template
#pragma pack(push, 1)
template<typename T>
struct LittleEndian
{
union
{
struct LittleEndian {
union {
unsigned char bytes[sizeof(T)];
T raw_value;
};
LittleEndian(T t = T())
{
LittleEndian(T t = T()) {
operator=(t);
}
LittleEndian(const LittleEndian<T> & t)
{
LittleEndian(const LittleEndian<T> &t) {
raw_value = t.raw_value;
}
LittleEndian(const BigEndian<T> & t)
{
LittleEndian(const BigEndian<T> &t) {
for (unsigned i = 0; i < sizeof(T); i++)
bytes[i] = t.bytes[sizeof(T) - 1 - i];
}
operator const T() const
{
operator const T() const {
T t = T();
for (unsigned i = 0; i < sizeof(T); i++)
t |= T(bytes[i]) << (i << 3);
return t;
}
const T operator = (const T t)
{
const T operator=(const T t) {
for (unsigned i = 0; i < sizeof(T); i++)
bytes[sizeof(T) - 1 - i] = static_cast<unsigned char>(t >> (i << 3));
return t;
@ -76,42 +70,34 @@ struct LittleEndian
// operators
const T operator += (const T t)
{
const T operator+=(const T t) {
return (*this = *this + t);
}
const T operator -= (const T t)
{
const T operator-=(const T t) {
return (*this = *this - t);
}
const T operator *= (const T t)
{
const T operator*=(const T t) {
return (*this = *this * t);
}
const T operator /= (const T t)
{
const T operator/=(const T t) {
return (*this = *this / t);
}
const T operator %= (const T t)
{
const T operator%=(const T t) {
return (*this = *this % t);
}
LittleEndian<T> operator ++ (int)
{
LittleEndian<T> operator++(int) {
LittleEndian<T> tmp(*this);
operator++();
return tmp;
}
LittleEndian<T> & operator ++ ()
{
for (unsigned i = 0; i < sizeof(T); i++)
{
LittleEndian<T> &operator++() {
for (unsigned i = 0; i < sizeof(T); i++) {
++bytes[i];
if (bytes[i] != 0)
break;
@ -119,17 +105,14 @@ struct LittleEndian
return (*this);
}
LittleEndian<T> operator -- (int)
{
LittleEndian<T> operator--(int) {
LittleEndian<T> tmp(*this);
operator--();
return tmp;
}
LittleEndian<T> & operator -- ()
{
for (unsigned i = 0; i < sizeof(T); i++)
{
LittleEndian<T> &operator--() {
for (unsigned i = 0; i < sizeof(T); i++) {
--bytes[i];
if (bytes[i] != (T) (-1))
break;
@ -137,46 +120,41 @@ struct LittleEndian
return (*this);
}
};
#pragma pack(pop)
// Big-Endian template
#pragma pack(push, 1)
template<typename T>
struct BigEndian
{
union
{
struct BigEndian {
union {
unsigned char bytes[sizeof(T)];
T raw_value;
};
BigEndian(T t = T())
{
BigEndian(T t = T()) {
operator=(t);
}
BigEndian(const BigEndian<T> & t)
{
BigEndian(const BigEndian<T> &t) {
raw_value = t.raw_value;
}
BigEndian(const LittleEndian<T> & t)
{
BigEndian(const LittleEndian<T> &t) {
for (unsigned i = 0; i < sizeof(T); i++)
bytes[i] = t.bytes[sizeof(T) - 1 - i];
}
operator const T() const
{
operator const T() const {
T t = T();
for (unsigned i = 0; i < sizeof(T); i++)
t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3);
return t;
}
const T operator = (const T t)
{
const T operator=(const T t) {
for (unsigned i = 0; i < sizeof(T); i++)
bytes[sizeof(T) - 1 - i] = t >> (i << 3);
return t;
@ -184,42 +162,34 @@ struct BigEndian
// operators
const T operator += (const T t)
{
const T operator+=(const T t) {
return (*this = *this + t);
}
const T operator -= (const T t)
{
const T operator-=(const T t) {
return (*this = *this - t);
}
const T operator *= (const T t)
{
const T operator*=(const T t) {
return (*this = *this * t);
}
const T operator /= (const T t)
{
const T operator/=(const T t) {
return (*this = *this / t);
}
const T operator %= (const T t)
{
const T operator%=(const T t) {
return (*this = *this % t);
}
BigEndian<T> operator ++ (int)
{
BigEndian<T> operator++(int) {
BigEndian<T> tmp(*this);
operator++();
return tmp;
}
BigEndian<T> & operator ++ ()
{
for (unsigned i = 0; i < sizeof(T); i++)
{
BigEndian<T> &operator++() {
for (unsigned i = 0; i < sizeof(T); i++) {
++bytes[sizeof(T) - 1 - i];
if (bytes[sizeof(T) - 1 - i] != 0)
break;
@ -227,17 +197,14 @@ struct BigEndian
return (*this);
}
BigEndian<T> operator -- (int)
{
BigEndian<T> operator--(int) {
BigEndian<T> tmp(*this);
operator--();
return tmp;
}
BigEndian<T> & operator -- ()
{
for (unsigned i = 0; i < sizeof(T); i++)
{
BigEndian<T> &operator--() {
for (unsigned i = 0; i < sizeof(T); i++) {
--bytes[sizeof(T) - 1 - i];
if (bytes[sizeof(T) - 1 - i] != (T) (-1))
break;
@ -245,6 +212,7 @@ struct BigEndian
return (*this);
}
};
#pragma pack(pop)
#endif // LITTLEBIGENDIAN_H

View file

@ -45,6 +45,7 @@ namespace log {
#endif
#ifndef _WIN32
/**
* @brief Maps our log levels to syslog one
* @return syslog priority LOG_*, as defined in syslog.h
@ -52,42 +53,49 @@ namespace log {
static inline int GetSyslogPrio(enum LogLevel l) {
int priority = LOG_DEBUG;
switch (l) {
case eLogNone : priority = LOG_CRIT; break;
case eLogError : priority = LOG_ERR; break;
case eLogWarning : priority = LOG_WARNING; break;
case eLogInfo : priority = LOG_INFO; break;
case eLogDebug : priority = LOG_DEBUG; break;
default : priority = LOG_DEBUG; break;
case eLogNone :
priority = LOG_CRIT;
break;
case eLogError :
priority = LOG_ERR;
break;
case eLogWarning :
priority = LOG_WARNING;
break;
case eLogInfo :
priority = LOG_INFO;
break;
case eLogDebug :
priority = LOG_DEBUG;
break;
default :
priority = LOG_DEBUG;
break;
}
return priority;
}
#endif
Log::Log() :
m_Destination(eLogStdout), m_MinLevel(eLogInfo),
m_LogStream(nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"),
m_IsRunning (false), m_Thread (nullptr)
{
m_IsRunning(false), m_Thread(nullptr) {
}
Log::~Log ()
{
Log::~Log() {
delete m_Thread;
}
void Log::Start ()
{
if (!m_IsRunning)
{
void Log::Start() {
if (!m_IsRunning) {
m_IsRunning = true;
m_Thread = new std::thread(std::bind(&Log::Run, this));
}
}
void Log::Stop ()
{
switch (m_Destination)
{
void Log::Stop() {
switch (m_Destination) {
#ifndef _WIN32
case eLogSyslog :
closelog();
@ -103,8 +111,7 @@ namespace log {
}
m_IsRunning = false;
m_Queue.WakeUp();
if (m_Thread)
{
if (m_Thread) {
m_Thread->join();
delete m_Thread;
m_Thread = nullptr;
@ -148,8 +155,7 @@ namespace log {
* Unfortunately, with current startup process with late fork() this
* will give us nothing but pain. Maybe later. See in NetDb as example.
*/
void Log::Process(std::shared_ptr<LogMsg> msg)
{
void Log::Process(std::shared_ptr <LogMsg> msg) {
if (!msg) return;
std::hash <std::thread::id> hasher;
unsigned short short_tid;
@ -172,19 +178,18 @@ namespace log {
default:
std::cout << TimeAsString(msg->timestamp)
<< "@" << short_tid
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level]
<< LogMsgColors[eNumLogLevels]
<< " - " << msg->text << std::endl;
break;
} // switch
}
void Log::Run ()
{
void Log::Run() {
i2p::util::SetThreadName("Logging");
Reopen();
while (m_IsRunning)
{
while (m_IsRunning) {
std::shared_ptr <LogMsg> msg;
while ((msg = m_Queue.Get()))
Process(msg);
@ -194,18 +199,15 @@ namespace log {
}
}
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
{
void Log::Append(std::shared_ptr <i2p::log::LogMsg> &msg) {
m_Queue.Put(msg);
}
void Log::SendTo (const std::string& path)
{
void Log::SendTo(const std::string &path) {
if (m_LogStream) m_LogStream = nullptr; // close previous
auto flags = std::ofstream::out | std::ofstream::app;
auto os = std::make_shared<std::ofstream>(path, flags);
if (os->is_open ())
{
if (os->is_open()) {
m_HasColors = false;
m_Logfile = path;
m_Destination = eLogFile;
@ -222,6 +224,7 @@ namespace log {
}
#ifndef _WIN32
void Log::SendTo(const char *name, int facility) {
if (m_MinLevel == eLogNone) return;
m_HasColors = false;
@ -229,6 +232,7 @@ namespace log {
m_LogStream = nullptr;
openlog(name, LOG_CONS | LOG_PID, facility);
}
#endif
void Log::Reopen() {
@ -241,7 +245,9 @@ namespace log {
}
static ThrowFunction g_ThrowFunction;
ThrowFunction GetThrowFunction() { return g_ThrowFunction; }
void SetThrowFunction(ThrowFunction f) { g_ThrowFunction = f; }
} // log

View file

@ -21,11 +21,12 @@
#include "Queue.h"
#ifndef _WIN32
#include <syslog.h>
#endif
enum LogLevel
{
enum LogLevel {
eLogNone = 0,
eLogError,
eLogWarning,
@ -48,8 +49,7 @@ namespace log {
struct LogMsg; /* forward declaration */
class Log
{
class Log {
private:
enum LogType m_Destination;
@ -58,7 +58,8 @@ namespace log {
std::string m_Logfile;
std::time_t m_LastTimestamp;
char m_LastDateTime[64];
i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue;
i2p::util::Queue<std::shared_ptr < LogMsg> >
m_Queue;
bool m_HasColors;
std::string m_TimeFormat;
volatile bool m_IsRunning;
@ -68,9 +69,11 @@ namespace log {
/** prevent making copies */
Log(const Log &);
const Log &operator=(const Log &);
void Run();
void Process(std::shared_ptr <LogMsg> msg);
/**
@ -84,12 +87,15 @@ namespace log {
public:
Log();
~Log();
LogType GetLogType() { return m_Destination; };
LogLevel GetLogLevel() { return m_MinLevel; };
void Start();
void Stop();
/**
@ -117,12 +123,14 @@ namespace log {
void SetTimeFormat(std::string format) { m_TimeFormat = format; };
#ifndef _WIN32
/**
* @brief Sets log destination to syslog
* @param name Wanted program name
* @param facility Wanted log category
*/
void SendTo(const char *name, int facility);
#endif
/**
@ -154,25 +162,35 @@ namespace log {
Log &Logger();
typedef std::function<void(const std::string &)> ThrowFunction;
ThrowFunction GetThrowFunction();
void SetThrowFunction(ThrowFunction f);
} // log
} // i2p
/** internal usage only -- folding args array to single string */
template<typename TValue>
void LogPrint (std::stringstream& s, TValue&& arg) noexcept
void LogPrint(std::stringstream &s, TValue &&arg)
noexcept
{
s << std::forward<TValue>(arg);
s <<
std::forward<TValue>(arg);
}
#if (__cplusplus < 201703L) // below C++ 17
/** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
void LogPrint(std::stringstream &s, TValue &&arg, TArgs &&... args)
noexcept
{
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
LogPrint (s, std::forward<TValue>(arg)
);
LogPrint (s, std::forward<TArgs>(args)
...);
}
#endif
@ -182,10 +200,16 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
* @param args Array of message parts
*/
template<typename... TArgs>
void LogPrint (LogLevel level, TArgs&&... args) noexcept
void LogPrint(LogLevel level, TArgs &&... args)
noexcept
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
if (level > log.
GetLogLevel()
)
return;
// fold message to single string
@ -194,12 +218,15 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
LogPrint (ss, std::forward<TArgs>(args)
...);
#endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id();
log.Append(msg);
msg->
tid = std::this_thread::get_id();
log.
Append(msg);
}
/**
@ -207,7 +234,9 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
* @param args Array of message parts
*/
template<typename... TArgs>
void ThrowFatal (TArgs&&... args) noexcept
void ThrowFatal(TArgs &&... args)
noexcept
{
auto f = i2p::log::GetThrowFunction();
if (!f) return;
@ -216,9 +245,15 @@ void ThrowFatal (TArgs&&... args) noexcept
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
LogPrint (ss, std::forward<TArgs>(args)
...);
#endif
f (ss.str ());
f (ss
.
str()
);
}
#endif // LOG_H__

File diff suppressed because it is too large Load diff

View file

@ -22,10 +22,8 @@
#include "RouterInfo.h"
#include "TransportSession.h"
namespace i2p
{
namespace transport
{
namespace i2p {
namespace transport {
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287;
@ -43,8 +41,7 @@ namespace transport
const int NTCP2_CLOCK_SKEW = 60; // in seconds
const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up
enum NTCP2BlockType
{
enum NTCP2BlockType {
eNTCP2BlkDateTime = 0,
eNTCP2BlkOptions, // 1
eNTCP2BlkRouterInfo, // 2
@ -53,8 +50,7 @@ namespace transport
eNTCP2BlkPadding = 254
};
enum NTCP2TerminationReason
{
enum NTCP2TerminationReason {
eNTCP2NormalClose = 0,
eNTCP2TerminationReceived, // 1
eNTCP2IdleTimeout, // 2
@ -78,38 +74,53 @@ namespace transport
// RouterInfo flags
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
struct NTCP2Establisher: private i2p::crypto::NoiseSymmetricState
{
struct NTCP2Establisher : private i2p::crypto::NoiseSymmetricState {
NTCP2Establisher();
~NTCP2Establisher();
const uint8_t *GetPub() const { return m_EphemeralKeys->GetPublicKey(); };
const uint8_t *GetRemotePub() const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
uint8_t *GetRemotePub() { return m_RemoteEphemeralPublicKey; }; // to set
const uint8_t *GetK() const { return m_CK + 32; };
const uint8_t *GetCK() const { return m_CK; };
const uint8_t *GetH() const { return m_H; };
void KDF1Alice();
void KDF1Bob();
void KDF2Alice();
void KDF2Bob();
void KDF3Alice(); // for SessionConfirmed part 2
void KDF3Bob();
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
void KeyDerivationFunction1(const uint8_t *pub, i2p::crypto::X25519Keys &priv, const uint8_t *rs,
const uint8_t *epub); // for SessionRequest, (pub, priv) for DH
void KeyDerivationFunction2(const uint8_t *sessionRequest, size_t sessionRequestLen,
const uint8_t *epub); // for SessionCreate
void CreateEphemeralKey();
void CreateSessionRequestMessage();
void CreateSessionCreatedMessage();
void CreateSessionConfirmedMessagePart1(const uint8_t *nonce);
void CreateSessionConfirmedMessagePart2(const uint8_t *nonce);
bool ProcessSessionRequestMessage(uint16_t &paddingLen, bool &clockSkew);
bool ProcessSessionCreatedMessage(uint16_t &paddingLen);
bool ProcessSessionConfirmedMessagePart1(const uint8_t *nonce);
bool ProcessSessionConfirmedMessagePart2(const uint8_t *nonce, uint8_t *m3p2Buf);
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
@ -125,24 +136,32 @@ namespace transport
};
class NTCP2Server;
class NTCP2Session: public TransportSession, public std::enable_shared_from_this<NTCP2Session>
{
class NTCP2Session : public TransportSession, public std::enable_shared_from_this<NTCP2Session> {
public:
NTCP2Session(NTCP2Server &server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
~NTCP2Session();
void Terminate();
void TerminateByTimeout();
void Done();
void Close() { m_Socket.close(); }; // for accept
void DeleteNextReceiveBuffer(uint64_t ts);
boost::asio::ip::tcp::socket &GetSocket() { return m_Socket; };
const boost::asio::ip::tcp::endpoint &GetRemoteEndpoint() { return m_RemoteEndpoint; };
void SetRemoteEndpoint(const boost::asio::ip::tcp::endpoint &ep) { m_RemoteEndpoint = ep; };
bool IsEstablished() const { return m_IsEstablished; };
bool IsTerminated() const { return m_IsTerminated; };
void ClientLogin(); // Alice
@ -156,41 +175,70 @@ namespace transport
void Established();
void CreateNonce(uint64_t seqn, uint8_t *nonce);
void CreateNextReceivedBuffer(size_t size);
void KeyDerivationFunctionDataPhase();
void SetSipKeys(const uint8_t *sendSipKey, const uint8_t *receiveSipKey);
// establish
void SendSessionRequest();
void SendSessionCreated();
void SendSessionConfirmed();
void HandleSessionRequestSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionRequestReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void
HandleSessionRequestPaddingReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionCreatedSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionCreatedReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void
HandleSessionCreatedPaddingReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void HandleSessionConfirmedReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
// data
void ReceiveLength();
void HandleReceivedLength(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void Receive();
void HandleReceived(const boost::system::error_code &ecode, std::size_t bytes_transferred);
void ProcessNextFrame(const uint8_t *frame, size_t len);
void SetNextSentFrameLength(size_t frameLen, uint8_t *lengthBuf);
void SendI2NPMsgs(std::vector<std::shared_ptr<I2NPMessage> > &msgs);
void HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
void HandleI2NPMsgsSent(const boost::system::error_code &ecode, std::size_t bytes_transferred,
std::vector<std::shared_ptr<I2NPMessage> > msgs);
void EncryptAndSendNextBuffer(size_t payloadLen);
void HandleNextFrameSent(const boost::system::error_code &ecode, std::size_t bytes_transferred);
size_t CreatePaddingBlock(size_t msgLen, uint8_t *buf, size_t len);
void SendQueue();
void SendRouterInfo();
void SendTermination(NTCP2TerminationReason reason);
void SendTerminationAndTerminate(NTCP2TerminationReason reason);
void PostI2NPMessages(std::vector<std::shared_ptr<I2NPMessage> > msgs);
private:
@ -212,8 +260,7 @@ namespace transport
uint16_t m_NextReceivedLen;
uint8_t *m_NextReceivedBuffer, *m_NextSendBuffer;
size_t m_NextReceivedBufferSize;
union
{
union {
uint8_t buf[8];
uint16_t key;
} m_ReceiveIV, m_SendIV;
@ -229,47 +276,60 @@ namespace transport
int m_NextPaddingSize;
};
class NTCP2Server: private i2p::util::RunnableServiceWithWork
{
class NTCP2Server : private i2p::util::RunnableServiceWithWork {
public:
enum ProxyType
{
enum ProxyType {
eNoProxy,
eSocksProxy,
eHTTPProxy
};
NTCP2Server();
~NTCP2Server();
void Start();
void Stop();
boost::asio::io_service &GetService() { return GetIOService(); };
bool AddNTCP2Session(std::shared_ptr<NTCP2Session> session, bool incoming = false);
void RemoveNTCP2Session(std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session(const i2p::data::IdentHash &ident);
void ConnectWithProxy(std::shared_ptr<NTCP2Session> conn);
void Connect(std::shared_ptr<NTCP2Session> conn);
bool UsingProxy() const { return m_ProxyType != eNoProxy; };
void UseProxy(ProxyType proxy, const std::string& address, uint16_t port, const std::string& user, const std::string& pass);
void UseProxy(ProxyType proxy, const std::string &address, uint16_t port, const std::string &user,
const std::string &pass);
void SetLocalAddress(const boost::asio::ip::address &localAddress);
private:
void HandleAccept(std::shared_ptr<NTCP2Session> conn, const boost::system::error_code &error);
void HandleAcceptV6(std::shared_ptr<NTCP2Session> conn, const boost::system::error_code &error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleConnect(const boost::system::error_code &ecode, std::shared_ptr<NTCP2Session> conn,
std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code &ecode, std::shared_ptr<NTCP2Session> conn,
std::shared_ptr<boost::asio::deadline_timer> timer);
void
AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
// timer
void ScheduleTermination();
void HandleTerminationTimer(const boost::system::error_code &ecode);
private:
@ -289,7 +349,10 @@ namespace transport
public:
// for HTTP/I2PControl
const decltype(m_NTCP2Sessions)& GetNTCP2Sessions () const { return m_NTCP2Sessions; };
const decltype(m_NTCP2Sessions)
&
GetNTCP2Sessions() const { return m_NTCP2Sessions; };
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -32,10 +32,8 @@
#include "version.h"
#include "util.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
const int NETDB_MIN_ROUTERS = 90;
const int NETDB_MIN_FLOODFILLS = 5;
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds
@ -59,46 +57,81 @@ namespace data
/** function for visiting a router info and determining if we want to use it */
typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter;
class NetDb
{
class NetDb {
public:
NetDb();
~NetDb();
void Start();
void Stop();
std::shared_ptr<const RouterInfo> AddRouterInfo(const uint8_t *buf, int len);
bool AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len);
bool AddLeaseSet(const IdentHash &ident, const uint8_t *buf, int len);
bool AddLeaseSet2(const IdentHash &ident, const uint8_t *buf, int len, uint8_t storeType);
std::shared_ptr<RouterInfo> FindRouter(const IdentHash &ident) const;
std::shared_ptr<LeaseSet> FindLeaseSet(const IdentHash &destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile(const IdentHash &ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true);
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestDestination(const IdentHash &destination,
RequestedDestination::RequestComplete requestComplete = nullptr,
bool direct = true);
void RequestDestinationFrom(const IdentHash &destination, const IdentHash &from, bool exploritory,
RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg(std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseSearchReplyMsg(std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg(std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg(std::shared_ptr<const I2NPMessage> m);
void HandleDeliveryStatusMsg(std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter() const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo>
GetRandomRouter(std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo>
GetHighBandwidthRandomRouter(std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
std::shared_ptr<const RouterInfo>
GetRandomPeerTestRouter(bool v4, const std::set<IdentHash> &excluded) const;
std::shared_ptr<const RouterInfo>
GetRandomSSU2PeerTestRouter(bool v4, const std::set<IdentHash> &excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router() const; // TODO: change to v6 peer test later
std::shared_ptr<const RouterInfo> GetRandomIntroducer(bool v4, const std::set<IdentHash> &excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo>
GetRandomSSU2Introducer(bool v4, const std::set<IdentHash> &excluded) const;
std::shared_ptr<const RouterInfo>
GetClosestFloodfill(const IdentHash &destination, const std::set<IdentHash> &excluded,
bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetClosestFloodfills(const IdentHash &destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
std::set<IdentHash> &excluded,
bool closeThanUsOnly = false) const;
std::shared_ptr<const RouterInfo>
GetClosestNonFloodfill(const IdentHash &destination, const std::set<IdentHash> &excluded) const;
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(FamilyID fam) const;
void SetUnreachable(const IdentHash &ident, bool unreachable);
void PostI2NPMsg(std::shared_ptr<const I2NPMessage> msg);
@ -107,25 +140,35 @@ namespace data
void SetHidden(bool hide);
void Reseed();
Families &GetFamilies() { return m_Families; };
// for web interface
int GetNumRouters() const { return m_RouterInfos.size(); };
int GetNumFloodfills() const { return m_Floodfills.size(); };
int GetNumLeaseSets() const { return m_LeaseSets.size(); };
/** visit all lease sets we currently store */
void VisitLeaseSets(LeaseSetVisitor v);
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
void VisitStoredRouterInfos(RouterInfoVisitor v);
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
void VisitRouterInfos(RouterInfoVisitor v);
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos() { m_RouterInfos.clear(); };
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
std::shared_ptr<RouterInfo::Buffer>
NewRouterInfoBuffer() { return m_RouterInfoBuffersPool.AcquireSharedMt(); };
void PopulateRouterInfoBuffer(std::shared_ptr<RouterInfo> r);
std::shared_ptr<Lease> NewLease(const Lease &lease) { return m_LeasesPool.AcquireSharedMt(lease); };
uint32_t GetPublishReplyToken() const { return m_PublishReplyToken; };
@ -133,19 +176,28 @@ namespace data
private:
void Load();
bool LoadRouterInfo(const std::string &path, uint64_t ts);
void SaveUpdated();
void Run(); // exploratory thread
void Explore(int numDestinations);
void Publish();
void Flood(const IdentHash &ident, std::shared_ptr<I2NPMessage> floodMsg);
void ManageLeaseSets();
void ManageRequests();
void ReseedFromFloodfill(const RouterInfo &ri, int numRouters = 40, int numFloodfills = 20);
std::shared_ptr<const RouterInfo> AddRouterInfo(const uint8_t *buf, int len, bool &updated);
std::shared_ptr<const RouterInfo> AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated);
std::shared_ptr<const RouterInfo>
AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len, bool &updated);
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter(Filter filter) const;
@ -169,6 +221,7 @@ namespace data
i2p::fs::HashedStorage m_Storage;
friend class NetDbRequests;
NetDbRequests m_Requests;
bool m_PersistProfiles;

View file

@ -12,70 +12,64 @@
#include "NetDb.hpp"
#include "NetDbRequests.h"
namespace i2p
{
namespace data
{
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
namespace i2p {
namespace data {
std::shared_ptr<I2NPMessage>
RequestedDestination::CreateRequestMessage(std::shared_ptr<const RouterInfo> router,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) {
std::shared_ptr<I2NPMessage> msg;
if (replyTunnel)
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
replyTunnel->GetNextIdentHash(),
replyTunnel->GetNextTunnelID(), m_IsExploratory,
&m_ExcludedPeers);
else
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0,
m_IsExploratory, &m_ExcludedPeers);
if (router)
m_ExcludedPeers.insert(router->GetIdentHash());
m_CreationTime = i2p::util::GetSecondsSinceEpoch();
return msg;
}
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
{
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage(const IdentHash &floodfill) {
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
i2p::context.GetRouterInfo().GetIdentHash(), 0, false,
&m_ExcludedPeers);
m_ExcludedPeers.insert(floodfill);
m_CreationTime = i2p::util::GetSecondsSinceEpoch();
return msg;
}
void RequestedDestination::ClearExcludedPeers ()
{
void RequestedDestination::ClearExcludedPeers() {
m_ExcludedPeers.clear();
}
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
{
if (m_RequestComplete)
{
void RequestedDestination::Success(std::shared_ptr<RouterInfo> r) {
if (m_RequestComplete) {
m_RequestComplete(r);
m_RequestComplete = nullptr;
}
}
void RequestedDestination::Fail ()
{
if (m_RequestComplete)
{
void RequestedDestination::Fail() {
if (m_RequestComplete) {
m_RequestComplete(nullptr);
m_RequestComplete = nullptr;
}
}
void NetDbRequests::Start ()
{
void NetDbRequests::Start() {
}
void NetDbRequests::Stop ()
{
void NetDbRequests::Stop() {
m_RequestedDestinations.clear();
}
std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete)
{
std::shared_ptr<RequestedDestination>
NetDbRequests::CreateRequest(const IdentHash &destination, bool isExploratory,
RequestedDestination::RequestComplete requestComplete) {
// request RouterInfo directly
auto dest = std::make_shared<RequestedDestination>(destination, isExploratory);
dest->SetRequestComplete(requestComplete);
@ -87,20 +81,17 @@ namespace data
return dest;
}
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
{
void NetDbRequests::RequestComplete(const IdentHash &ident, std::shared_ptr<RouterInfo> r) {
std::shared_ptr<RequestedDestination> request;
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find(ident);
if (it != m_RequestedDestinations.end ())
{
if (it != m_RequestedDestinations.end()) {
request = it->second;
m_RequestedDestinations.erase(it);
}
}
if (request)
{
if (request) {
if (r)
request->Success(r);
else
@ -108,8 +99,7 @@ namespace data
}
}
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
{
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest(const IdentHash &ident) const {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find(ident);
if (it != m_RequestedDestinations.end())
@ -117,12 +107,10 @@ namespace data
return nullptr;
}
void NetDbRequests::ManageRequests ()
{
void NetDbRequests::ManageRequests() {
uint64_t ts = i2p::util::GetSecondsSinceEpoch();
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
{
for (auto it = m_RequestedDestinations.begin(); it != m_RequestedDestinations.end();) {
auto &dest = it->second;
bool done = false;
if (ts < dest->GetCreationTime() + 60) // request is worthless after 1 minute
@ -130,32 +118,29 @@ namespace data
if (ts > dest->GetCreationTime() + 5) // no response for 5 seconds
{
auto count = dest->GetExcludedPeers().size();
if (!dest->IsExploratory () && count < 7)
{
if (!dest->IsExploratory() && count < 7) {
auto pool = i2p::tunnel::tunnels.GetExploratoryPool();
auto outbound = pool->GetNextOutboundTunnel();
auto inbound = pool->GetNextInboundTunnel();
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
auto nextFloodfill = netdb.GetClosestFloodfill(dest->GetDestination(),
dest->GetExcludedPeers());
if (nextFloodfill && outbound && inbound)
outbound->SendTunnelDataMsg(nextFloodfill->GetIdentHash(), 0,
dest->CreateRequestMessage(nextFloodfill, inbound));
else
{
else {
done = true;
if (!inbound) LogPrint(eLogWarning, "NetDbReq: No inbound tunnels");
if (!outbound) LogPrint(eLogWarning, "NetDbReq: No outbound tunnels");
if (!nextFloodfill) LogPrint(eLogWarning, "NetDbReq: No more floodfills");
}
}
else
{
} else {
if (!dest->IsExploratory())
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
LogPrint(eLogWarning, "NetDbReq: ", dest->GetDestination().ToBase64(),
" not found after 7 attempts");
done = true;
}
}
}
else // delete obsolete request
} else // delete obsolete request
done = true;
if (done)

View file

@ -15,33 +15,43 @@
#include "Identity.h"
#include "RouterInfo.h"
namespace i2p
{
namespace data
{
class RequestedDestination
{
namespace i2p {
namespace data {
class RequestedDestination {
public:
typedef std::function<void(std::shared_ptr<RouterInfo>)> RequestComplete;
RequestedDestination(const IdentHash &destination, bool isExploratory = false) :
m_Destination(destination), m_IsExploratory(isExploratory), m_CreationTime(0) {};
~RequestedDestination() { if (m_RequestComplete) m_RequestComplete(nullptr); };
const IdentHash &GetDestination() const { return m_Destination; };
int GetNumExcludedPeers() const { return m_ExcludedPeers.size(); };
const std::set<IdentHash> &GetExcludedPeers() { return m_ExcludedPeers; };
void ClearExcludedPeers();
bool IsExploratory() const { return m_IsExploratory; };
bool IsExcluded(const IdentHash &ident) const { return m_ExcludedPeers.count(ident); };
uint64_t GetCreationTime() const { return m_CreationTime; };
std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
std::shared_ptr<I2NPMessage> CreateRequestMessage(std::shared_ptr<const RouterInfo>,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
std::shared_ptr<I2NPMessage> CreateRequestMessage(const IdentHash &floodfill);
void SetRequestComplete(const RequestComplete &requestComplete) { m_RequestComplete = requestComplete; };
bool IsRequestComplete() const { return m_RequestComplete != nullptr; };
void Success(std::shared_ptr<RouterInfo> r);
void Fail();
private:
@ -53,16 +63,20 @@ namespace data
RequestComplete m_RequestComplete;
};
class NetDbRequests
{
class NetDbRequests {
public:
void Start();
void Stop();
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr);
std::shared_ptr<RequestedDestination> CreateRequest(const IdentHash &destination, bool isExploratory,
RequestedDestination::RequestComplete requestComplete = nullptr);
void RequestComplete(const IdentHash &ident, std::shared_ptr<RouterInfo> r);
std::shared_ptr<RequestedDestination> FindRequest(const IdentHash &ident) const;
void ManageRequests();
private:

View file

@ -9,12 +9,9 @@
#include "Poly1305.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz)
{
namespace i2p {
namespace crypto {
void Poly1305HMAC(uint64_t *out, const uint64_t *key, const uint8_t *buf, std::size_t sz) {
Poly1305 p(key);
p.Update(buf, sz);
p.Finish(out);

View file

@ -8,52 +8,44 @@
#ifndef LIBI2PD_POLY1305_H
#define LIBI2PD_POLY1305_H
#include <cstdint>
#include <cstring>
#include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
const std::size_t POLY1305_DIGEST_BYTES = 16;
const std::size_t POLY1305_DIGEST_DWORDS = 4;
const std::size_t POLY1305_KEY_BYTES = 32;
const std::size_t POLY1305_KEY_DWORDS = 8;
const std::size_t POLY1305_BLOCK_BYTES = 16;
namespace poly1305
{
struct LongBlock
{
namespace poly1305 {
struct LongBlock {
unsigned long data[17];
operator unsigned long * ()
{
operator unsigned long *() {
return data;
}
};
struct Block
{
struct Block {
unsigned char data[17];
void Zero()
{
void Zero() {
memset(data, 0, sizeof(data));
}
operator uint8_t * ()
{
operator uint8_t *() {
return data;
}
Block & operator += (const Block & other)
{
Block &operator+=(const Block &other) {
unsigned short u;
unsigned int i;
for(u = 0, i = 0; i < 17; i++)
{
for (u = 0, i = 0; i < 17; i++) {
u += (unsigned short) data[i] + (unsigned short) other.data[i];
data[i] = (unsigned char) u & 0xff;
u >>= 8;
@ -61,8 +53,7 @@ namespace crypto
return *this;
}
Block & operator %=(const LongBlock & other)
{
Block &operator%=(const LongBlock &other) {
unsigned long u;
unsigned int i;
u = 0;
@ -84,14 +75,12 @@ namespace crypto
return *this;
}
Block & operator = (const Block & other)
{
Block &operator=(const Block &other) {
memcpy(data, other.data, sizeof(data));
return *this;
}
Block & operator ~ ()
{
Block &operator~() {
static const Block minusp = {
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -109,8 +98,7 @@ namespace crypto
return *this;
}
void PutKey(const uint64_t * key_l)
{
void PutKey(const uint64_t *key_l) {
const uint8_t *key = (const uint8_t *) key_l;
data[0] = key[0] & 0xff;
data[1] = key[1] & 0xff;
@ -132,28 +120,23 @@ namespace crypto
}
template<typename Int_t>
void Put(const Int_t * d, uint8_t last=0)
{
void Put(const Int_t *d, uint8_t last = 0) {
memcpy(data, d, 16);
data[16] = last;
}
};
struct Buffer
{
struct Buffer {
uint8_t data[POLY1305_BLOCK_BYTES];
operator uint8_t * ()
{
operator uint8_t *() {
return data;
}
};
}
struct Poly1305
{
Poly1305(const uint64_t * key)
{
struct Poly1305 {
Poly1305(const uint64_t *key) {
m_Leftover = 0;
m_H.Zero();
m_Final = 0;
@ -161,11 +144,9 @@ namespace crypto
m_Pad.Put(key + 2);
}
void Update(const uint8_t * buf, size_t sz)
{
void Update(const uint8_t *buf, size_t sz) {
// process leftover
if(m_Leftover)
{
if (m_Leftover) {
size_t want = POLY1305_BLOCK_BYTES - m_Leftover;
if (want > sz) want = sz;
memcpy(m_Buffer + m_Leftover, buf, want);
@ -177,23 +158,20 @@ namespace crypto
m_Leftover = 0;
}
// process blocks
if(sz >= POLY1305_BLOCK_BYTES)
{
if (sz >= POLY1305_BLOCK_BYTES) {
size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1));
Blocks(buf, want);
buf += want;
sz -= want;
}
// leftover
if(sz)
{
if (sz) {
memcpy(m_Buffer + m_Leftover, buf, sz);
m_Leftover += sz;
}
}
void Blocks(const uint8_t * buf, size_t sz)
{
void Blocks(const uint8_t *buf, size_t sz) {
const unsigned char hi = m_Final ^ 1;
while (sz >= POLY1305_BLOCK_BYTES) {
unsigned long u;
@ -222,11 +200,9 @@ namespace crypto
}
}
void Finish(uint64_t * out)
{
void Finish(uint64_t *out) {
// process leftovers
if(m_Leftover)
{
if (m_Leftover) {
size_t idx = m_Leftover;
m_Buffer[idx++] = 1;
for (; idx < POLY1305_BLOCK_BYTES; idx++)

View file

@ -14,31 +14,25 @@
#include "Log.h"
#include "Profiling.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
RouterProfile::RouterProfile() :
m_LastUpdateTime(boost::posix_time::second_clock::local_time()),
m_NumTunnelsAgreed(0), m_NumTunnelsDeclined(0), m_NumTunnelsNonReplied(0),
m_NumTimesTaken (0), m_NumTimesRejected (0)
{
m_NumTimesTaken(0), m_NumTimesRejected(0) {
}
boost::posix_time::ptime RouterProfile::GetTime () const
{
boost::posix_time::ptime RouterProfile::GetTime() const {
return boost::posix_time::second_clock::local_time();
}
void RouterProfile::UpdateTime ()
{
void RouterProfile::UpdateTime() {
m_LastUpdateTime = GetTime();
}
void RouterProfile::Save (const IdentHash& identHash)
{
void RouterProfile::Save(const IdentHash &identHash) {
// fill sections
boost::property_tree::ptree participation;
participation.put(PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
@ -65,70 +59,59 @@ namespace data
}
}
void RouterProfile::Load (const IdentHash& identHash)
{
void RouterProfile::Load(const IdentHash &identHash) {
std::string ident = identHash.ToBase64();
std::string path = m_ProfilesStorage.Path(ident);
boost::property_tree::ptree pt;
if (!i2p::fs::Exists(path))
{
if (!i2p::fs::Exists(path)) {
LogPrint(eLogWarning, "Profiling: No profile yet for ", ident);
return;
}
try
{
try {
boost::property_tree::read_ini(path, pt);
} catch (std::exception& ex)
{
} catch (std::exception &ex) {
/* boost exception verbose enough */
LogPrint(eLogError, "Profiling: ", ex.what());
return;
}
try
{
try {
auto t = pt.get(PEER_PROFILE_LAST_UPDATE_TIME, "");
if (t.length() > 0)
m_LastUpdateTime = boost::posix_time::time_from_string(t);
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT)
{
try
{
if ((GetTime() - m_LastUpdateTime).hours() < PEER_PROFILE_EXPIRATION_TIMEOUT) {
try {
// read participations
auto participations = pt.get_child(PEER_PROFILE_SECTION_PARTICIPATION);
m_NumTunnelsAgreed = participations.get(PEER_PROFILE_PARTICIPATION_AGREED, 0);
m_NumTunnelsDeclined = participations.get(PEER_PROFILE_PARTICIPATION_DECLINED, 0);
m_NumTunnelsNonReplied = participations.get(PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident);
catch (boost::property_tree::ptree_bad_path &ex) {
LogPrint(eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION,
" in profile for ", ident);
}
try
{
try {
// read usage
auto usage = pt.get_child(PEER_PROFILE_SECTION_USAGE);
m_NumTimesTaken = usage.get(PEER_PROFILE_USAGE_TAKEN, 0);
m_NumTimesRejected = usage.get(PEER_PROFILE_USAGE_REJECTED, 0);
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident);
catch (boost::property_tree::ptree_bad_path &ex) {
LogPrint(eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_USAGE,
" in profile for ", ident);
}
}
else
} else
*this = RouterProfile();
}
catch (std::exception& ex)
{
catch (std::exception &ex) {
LogPrint(eLogError, "Profiling: Can't read profile ", ident, " :", ex.what());
}
}
void RouterProfile::TunnelBuildResponse (uint8_t ret)
{
void RouterProfile::TunnelBuildResponse(uint8_t ret) {
UpdateTime();
if (ret > 0)
m_NumTunnelsDeclined++;
@ -136,28 +119,23 @@ namespace data
m_NumTunnelsAgreed++;
}
void RouterProfile::TunnelNonReplied ()
{
void RouterProfile::TunnelNonReplied() {
m_NumTunnelsNonReplied++;
UpdateTime();
}
bool RouterProfile::IsLowPartcipationRate () const
{
bool RouterProfile::IsLowPartcipationRate() const {
return 4 * m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
}
bool RouterProfile::IsLowReplyRate () const
{
bool RouterProfile::IsLowReplyRate() const {
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
return m_NumTunnelsNonReplied > 10 * (total + 1);
}
bool RouterProfile::IsBad ()
{
bool RouterProfile::IsBad() {
auto isBad = IsAlwaysDeclining() || IsLowPartcipationRate() /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{
if (isBad && m_NumTimesRejected > 10 * (m_NumTimesTaken + 1)) {
// reset profile
m_NumTunnelsAgreed = 0;
m_NumTunnelsDeclined = 0;
@ -168,21 +146,18 @@ namespace data
return isBad;
}
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
{
std::shared_ptr<RouterProfile> GetRouterProfile(const IdentHash &identHash) {
auto profile = std::make_shared<RouterProfile>();
profile->Load(identHash); // if possible
return profile;
}
void InitProfilesStorage ()
{
void InitProfilesStorage() {
m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
}
void DeleteObsoleteProfiles ()
{
void DeleteObsoleteProfiles() {
struct stat st;
std::time_t now = std::time(nullptr);

View file

@ -13,10 +13,8 @@
#include <boost/date_time/posix_time/posix_time.hpp>
#include "Identity.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
// sections
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
const char PEER_PROFILE_SECTION_USAGE[] = "usage";
@ -32,28 +30,33 @@ namespace data
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours)
class RouterProfile
{
class RouterProfile {
public:
RouterProfile();
RouterProfile &operator=(const RouterProfile &) = default;
void Save(const IdentHash &identHash);
void Load(const IdentHash &identHash);
bool IsBad();
void TunnelBuildResponse(uint8_t ret);
void TunnelNonReplied();
private:
boost::posix_time::ptime GetTime() const;
void UpdateTime();
bool IsAlwaysDeclining() const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate() const;
bool IsLowReplyRate() const;
private:
@ -69,7 +72,9 @@ namespace data
};
std::shared_ptr<RouterProfile> GetRouterProfile(const IdentHash &identHash);
void InitProfilesStorage();
void DeleteObsoleteProfiles();
}
}

View file

@ -17,27 +17,21 @@
#include <functional>
#include <utility>
namespace i2p
{
namespace util
{
namespace i2p {
namespace util {
template<typename Element>
class Queue
{
class Queue {
public:
void Put (Element e)
{
void Put(Element e) {
std::unique_lock <std::mutex> l(m_QueueMutex);
m_Queue.push(std::move(e));
m_NonEmpty.notify_one();
}
template<template<typename, typename...> class Container, typename... R>
void Put (const Container<Element, R...>& vec)
{
if (!vec.empty ())
{
void Put(const Container<Element, R...> &vec) {
if (!vec.empty()) {
std::unique_lock <std::mutex> l(m_QueueMutex);
for (const auto &it: vec)
m_Queue.push(std::move(it));
@ -45,74 +39,63 @@ namespace util
}
}
Element GetNext ()
{
Element GetNext() {
std::unique_lock <std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe();
if (!el)
{
if (!el) {
m_NonEmpty.wait(l);
el = GetNonThreadSafe();
}
return el;
}
Element GetNextWithTimeout (int usec)
{
Element GetNextWithTimeout(int usec) {
std::unique_lock <std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe();
if (!el)
{
if (!el) {
m_NonEmpty.wait_for(l, std::chrono::milliseconds(usec));
el = GetNonThreadSafe();
}
return el;
}
void Wait ()
{
void Wait() {
std::unique_lock <std::mutex> l(m_QueueMutex);
m_NonEmpty.wait(l);
}
bool Wait (int sec, int usec)
{
bool Wait(int sec, int usec) {
std::unique_lock <std::mutex> l(m_QueueMutex);
return m_NonEmpty.wait_for (l, std::chrono::seconds (sec) + std::chrono::milliseconds (usec)) != std::cv_status::timeout;
return m_NonEmpty.wait_for(l, std::chrono::seconds(sec) + std::chrono::milliseconds(usec)) !=
std::cv_status::timeout;
}
bool IsEmpty ()
{
bool IsEmpty() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return m_Queue.empty();
}
int GetSize ()
{
int GetSize() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return m_Queue.size();
}
void WakeUp() { m_NonEmpty.notify_all(); };
Element Get ()
{
Element Get() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return GetNonThreadSafe();
}
Element Peek ()
{
Element Peek() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return GetNonThreadSafe(true);
}
private:
Element GetNonThreadSafe (bool peek = false)
{
if (!m_Queue.empty ())
{
Element GetNonThreadSafe(bool peek = false) {
if (!m_Queue.empty()) {
auto el = m_Queue.front();
if (!peek)
m_Queue.pop();

View file

@ -27,48 +27,40 @@
#include "util.h"
#include "Config.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
Reseeder::Reseeder()
{
Reseeder::Reseeder() {
}
Reseeder::~Reseeder()
{
Reseeder::~Reseeder() {
}
/**
@brief tries to bootstrap into I2P network (from local files and servers, with respect of options)
*/
void Reseeder::Bootstrap ()
{
std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName);
std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName);
void Reseeder::Bootstrap() {
std::string su3FileName;
i2p::config::GetOption("reseed.file", su3FileName);
std::string zipFileName;
i2p::config::GetOption("reseed.zipfile", zipFileName);
if (su3FileName.length() > 0) // bootstrap from SU3 file or URL
{
int num;
if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://")
{
if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") {
num = ReseedFromSU3Url(su3FileName); // from https URL
}
else
{
} else {
num = ProcessSU3File(su3FileName.c_str());
}
if (num == 0)
LogPrint(eLogWarning, "Reseed: Failed to reseed from ", su3FileName);
}
else if (zipFileName.length() > 0) // bootstrap from ZIP file
} else if (zipFileName.length() > 0) // bootstrap from ZIP file
{
int num = ProcessZIPFile(zipFileName.c_str());
if (num == 0)
LogPrint(eLogWarning, "Reseed: Failed to reseed from ", zipFileName);
}
else // bootstrap from reseed servers
} else // bootstrap from reseed servers
{
int num = ReseedFromServers();
if (num == 0)
@ -80,38 +72,38 @@ namespace data
* @brief bootstrap from random server, retry 10 times
* @return number of entries added to netDb
*/
int Reseeder::ReseedFromServers ()
{
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool yggdrasil; i2p::config::GetOption("meshnets.yggdrasil", yggdrasil);
int Reseeder::ReseedFromServers() {
bool ipv6;
i2p::config::GetOption("ipv6", ipv6);
bool ipv4;
i2p::config::GetOption("ipv4", ipv4);
bool yggdrasil;
i2p::config::GetOption("meshnets.yggdrasil", yggdrasil);
std::vector<std::string> httpsReseedHostList;
if (ipv4 || ipv6)
{
std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs);
if (ipv4 || ipv6) {
std::string reseedURLs;
i2p::config::GetOption("reseed.urls", reseedURLs);
if (!reseedURLs.empty())
boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on);
}
std::vector<std::string> yggReseedHostList;
if (yggdrasil && !i2p::util::net::GetYggdrasilAddress ().is_unspecified ())
{
if (yggdrasil && !i2p::util::net::GetYggdrasilAddress().is_unspecified()) {
LogPrint(eLogInfo, "Reseed: Yggdrasil is supported");
std::string yggReseedURLs; i2p::config::GetOption("reseed.yggurls", yggReseedURLs);
std::string yggReseedURLs;
i2p::config::GetOption("reseed.yggurls", yggReseedURLs);
if (!yggReseedURLs.empty())
boost::split(yggReseedHostList, yggReseedURLs, boost::is_any_of(","), boost::token_compress_on);
}
if (httpsReseedHostList.empty () && yggReseedHostList.empty())
{
if (httpsReseedHostList.empty() && yggReseedHostList.empty()) {
LogPrint(eLogWarning, "Reseed: No reseed servers specified");
return 0;
}
int reseedRetries = 0;
while (reseedRetries < 10)
{
while (reseedRetries < 10) {
auto ind = rand() % (httpsReseedHostList.size() + yggReseedHostList.size());
bool isHttps = ind < httpsReseedHostList.size();
std::string reseedUrl = isHttps ? httpsReseedHostList[ind] :
@ -130,58 +122,47 @@ namespace data
* @param url
* @return number of entries added to netDb
*/
int Reseeder::ReseedFromSU3Url (const std::string& url, bool isHttps)
{
int Reseeder::ReseedFromSU3Url(const std::string &url, bool isHttps) {
LogPrint(eLogInfo, "Reseed: Downloading SU3 from ", url);
std::string su3 = isHttps ? HttpsRequest(url) : YggdrasilRequest(url);
if (su3.length () > 0)
{
if (su3.length() > 0) {
std::stringstream s(su3);
return ProcessSU3Stream(s);
}
else
{
} else {
LogPrint(eLogWarning, "Reseed: SU3 download failed");
return 0;
}
}
int Reseeder::ProcessSU3File (const char * filename)
{
int Reseeder::ProcessSU3File(const char *filename) {
std::ifstream s(filename, std::ifstream::binary);
if (s.is_open())
return ProcessSU3Stream(s);
else
{
else {
LogPrint(eLogError, "Reseed: Can't open file ", filename);
return 0;
}
}
int Reseeder::ProcessZIPFile (const char * filename)
{
int Reseeder::ProcessZIPFile(const char *filename) {
std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ())
{
if (s.is_open()) {
s.seekg(0, std::ios::end);
auto len = s.tellg();
s.seekg(0, std::ios::beg);
return ProcessZIPStream(s, len);
}
else
{
} else {
LogPrint(eLogError, "Reseed: Can't open file ", filename);
return 0;
}
}
const char SU3_MAGIC_NUMBER[] = "I2Psu3";
int Reseeder::ProcessSU3Stream (std::istream& s)
{
int Reseeder::ProcessSU3Stream(std::istream &s) {
char magicNumber[7];
s.read(magicNumber, 7); // magic number and zero byte 6
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
{
if (strcmp(magicNumber, SU3_MAGIC_NUMBER)) {
LogPrint(eLogError, "Reseed: Unexpected SU3 magic number");
return 0;
}
@ -224,16 +205,14 @@ namespace data
s.read(signerID, signerIDLength); // signerID
signerID[signerIDLength] = 0;
bool verify; i2p::config::GetOption("reseed.verify", verify);
if (verify)
{
bool verify;
i2p::config::GetOption("reseed.verify", verify);
if (verify) {
//try to verify signature
auto it = m_SigningKeys.find(signerID);
if (it != m_SigningKeys.end ())
{
if (it != m_SigningKeys.end()) {
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
{
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) {
size_t pos = s.tellg();
size_t tbsLen = pos + contentLength;
uint8_t *tbs = new uint8_t[tbsLen];
@ -261,18 +240,17 @@ namespace data
else
verify = false; // verified
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_free(s);
BN_free(n);
BN_CTX_free(bnctx);
}
delete[] signature;
delete[] tbs;
s.seekg(pos, std::ios::beg);
}
else
} else
LogPrint(eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
}
else
} else
LogPrint(eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
}
@ -289,17 +267,15 @@ namespace data
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008;
int Reseeder::ProcessZIPStream (std::istream& s, uint64_t contentLength)
{
int Reseeder::ProcessZIPStream(std::istream &s, uint64_t contentLength) {
int numFiles = 0;
size_t contentPos = s.tellg();
while (!s.eof ())
{
while (!s.eof()) {
uint32_t signature;
s.read((char *) &signature, 4);
signature = le32toh (signature);
if (signature == ZIP_HEADER_SIGNATURE)
{
if (signature == ZIP_HEADER_SIGNATURE) {
// next local file
s.seekg(2, std::ios::cur); // version
uint16_t bitFlag;
@ -332,18 +308,17 @@ namespace data
localFileName[fileNameLength] = 0;
s.seekg(extraFieldLength, std::ios::cur);
// take care about data descriptor if presented
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
{
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) {
size_t pos = s.tellg();
if (!FindZipDataDescriptor (s))
{
if (!FindZipDataDescriptor(s)) {
LogPrint(eLogError, "Reseed: SU3 archive data descriptor not found");
return numFiles;
}
s.read((char *) &crc_32, 4);
crc_32 = le32toh (crc_32);
s.read((char *) &compressedSize, 4);
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
compressedSize = le32toh (compressedSize) +
4; // ??? we must consider signature as part of compressed data
s.read((char *) &uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize);
@ -352,8 +327,7 @@ namespace data
}
LogPrint(eLogDebug, "Reseed: Processing file ", localFileName, " ", compressedSize, " bytes");
if (!compressedSize)
{
if (!compressedSize) {
LogPrint(eLogWarning, "Reseed: Unexpected size 0. Skipped");
continue;
}
@ -371,23 +345,18 @@ namespace data
inflator.next_out = uncompressed;
inflator.avail_out = uncompressedSize;
int err;
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0)
{
if ((err = inflate(&inflator, Z_SYNC_FLUSH)) >= 0) {
uncompressedSize -= inflator.avail_out;
if (crc32 (0, uncompressed, uncompressedSize) == crc_32)
{
if (crc32(0, uncompressed, uncompressedSize) == crc_32) {
i2p::data::netdb.AddRouterInfo(uncompressed, uncompressedSize);
numFiles++;
}
else
} else
LogPrint(eLogError, "Reseed: CRC32 verification failed");
}
else
} else
LogPrint(eLogError, "Reseed: SU3 decompression error ", err);
delete[] uncompressed;
inflateEnd(&inflator);
}
else // no compression
} else // no compression
{
i2p::data::netdb.AddRouterInfo(compressed, compressedSize);
numFiles++;
@ -395,9 +364,7 @@ namespace data
delete[] compressed;
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
s.seekg(12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
}
else
{
} else {
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
LogPrint(eLogWarning, "Reseed: Missing zip central directory header");
break; // no more files
@ -411,11 +378,12 @@ namespace data
auto ts = i2p::util::GetMillisecondsSinceEpoch();
int numOutdated = 0;
i2p::data::netdb.VisitRouterInfos(
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r)
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r) {
if (r && ts > r->GetTimestamp() +
10 * i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT * 1000LL) // 270 hours
{
if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours
{
LogPrint (eLogError, "Reseed: Router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours");
LogPrint(eLogError, "Reseed: Router ", r->GetIdentHash().ToBase64(), " is outdated by ",
(ts - r->GetTimestamp()) / 1000LL / 3600LL, " hours");
numOutdated++;
}
});
@ -438,42 +406,35 @@ namespace data
}
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = {0x50, 0x4B, 0x07, 0x08};
bool Reseeder::FindZipDataDescriptor (std::istream& s)
{
bool Reseeder::FindZipDataDescriptor(std::istream &s) {
size_t nextInd = 0;
while (!s.eof ())
{
while (!s.eof()) {
uint8_t nextByte;
s.read((char *) &nextByte, 1);
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd])
{
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) {
nextInd++;
if (nextInd >= sizeof(ZIP_DATA_DESCRIPTOR_SIGNATURE))
return true;
}
else
} else
nextInd = 0;
}
return false;
}
void Reseeder::LoadCertificate (const std::string& filename)
{
void Reseeder::LoadCertificate(const std::string &filename) {
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
int ret = SSL_CTX_use_certificate_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
if (ret)
{
if (ret) {
SSL *ssl = SSL_new(ctx);
X509 *cert = SSL_get_certificate(ssl);
// verify
if (cert)
{
if (cert) {
// extract issuer name
char name[100];
X509_NAME_oneline(X509_get_issuer_name(cert), name, 100);
char *cn = strstr(name, "CN=");
if (cn)
{
if (cn) {
cn += 3;
char *terminator = strchr(cn, '/');
if (terminator) terminator[0] = 0;
@ -490,14 +451,12 @@ namespace data
LogPrint(eLogError, "Reseed: Can't find CN field in ", filename);
}
SSL_free(ssl);
}
else
} else
LogPrint(eLogError, "Reseed: Can't open certificate file ", filename);
SSL_CTX_free(ctx);
}
void Reseeder::LoadCertificates ()
{
void Reseeder::LoadCertificates() {
std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed";
std::vector<std::string> files;
@ -519,10 +478,10 @@ namespace data
LogPrint(eLogInfo, "Reseed: ", numCertificates, " certificates loaded");
}
std::string Reseeder::HttpsRequest (const std::string& address)
{
std::string Reseeder::HttpsRequest(const std::string &address) {
i2p::http::URL proxyUrl;
std::string proxy; i2p::config::GetOption("reseed.proxy", proxy);
std::string proxy;
i2p::config::GetOption("reseed.proxy", proxy);
// check for proxy url
if (proxy.size()) {
// parse
@ -558,19 +517,15 @@ namespace data
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
boost::asio::ssl::stream <boost::asio::ip::tcp::socket> s(service, ctx);
if(proxyUrl.schema.size())
{
if (proxyUrl.schema.size()) {
// proxy connection
auto it = boost::asio::ip::tcp::resolver(service).resolve(
boost::asio::ip::tcp::resolver::query(proxyUrl.host, std::to_string(proxyUrl.port)), ecode);
if(!ecode)
{
if (!ecode) {
s.lowest_layer().connect(*it, ecode);
if(!ecode)
{
if (!ecode) {
auto &sock = s.next_layer();
if(proxyUrl.schema == "http")
{
if (proxyUrl.schema == "http") {
i2p::http::HTTPReq proxyReq;
i2p::http::HTTPRes proxyRes;
proxyReq.method = "CONNECT";
@ -585,48 +540,42 @@ namespace data
out << proxyReq.to_string();
boost::asio::write(sock, writebuf.data(), boost::asio::transfer_all(), ecode);
if (ecode)
{
if (ecode) {
sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT write error: ", ecode.message());
return "";
}
boost::asio::read_until(sock, readbuf, "\r\n\r\n", ecode);
if (ecode)
{
if (ecode) {
sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message());
return "";
}
if(proxyRes.parse(boost::asio::buffer_cast<const char *>(readbuf.data()), readbuf.size()) <= 0)
{
if (proxyRes.parse(boost::asio::buffer_cast<const char *>(readbuf.data()),
readbuf.size()) <= 0) {
sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply");
return "";
}
if(proxyRes.code != 200)
{
if (proxyRes.code != 200) {
sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT got bad status: ", proxyRes.code);
return "";
}
}
else
{
} else {
// assume socks if not http, is checked before this for other types
// TODO: support username/password auth etc
uint8_t hs_writebuf[3] = {0x05, 0x01, 0x00};
uint8_t hs_readbuf[2];
boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(), ecode);
if(ecode)
{
boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(),
ecode);
if (ecode) {
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake write failed: ", ecode.message());
return "";
}
boost::asio::read(sock, boost::asio::buffer(hs_readbuf, 2), ecode);
if(ecode)
{
if (ecode) {
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake read failed: ", ecode.message());
return "";
@ -640,8 +589,7 @@ namespace data
buf[3] = 0x03;
sz += 4;
size_t hostsz = url.host.size();
if(1 + 2 + hostsz + sz > sizeof(buf))
{
if (1 + 2 + hostsz + sz > sizeof(buf)) {
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed, hostname too big: ", url.host);
return "";
@ -652,21 +600,18 @@ namespace data
htobe16buf(buf + sz, url.port);
sz += 2;
boost::asio::write(sock, boost::asio::buffer(buf, sz), boost::asio::transfer_all(), ecode);
if(ecode)
{
if (ecode) {
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed writing: ", ecode.message());
return "";
}
boost::asio::read(sock, boost::asio::buffer(buf, 10), ecode);
if(ecode)
{
if (ecode) {
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed reading: ", ecode.message());
return "";
}
if(buf[1] != 0x00)
{
if (buf[1] != 0x00) {
sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake bad reply code: ", std::to_string(buf[1]));
return "";
@ -674,58 +619,46 @@ namespace data
}
}
}
}
else
{
} else {
// direct connection
auto it = boost::asio::ip::tcp::resolver(service).resolve(
boost::asio::ip::tcp::resolver::query(url.host, std::to_string(url.port)), ecode);
if (!ecode)
{
if (!ecode) {
bool connected = false;
boost::asio::ip::tcp::resolver::iterator end;
while (it != end)
{
while (it != end) {
boost::asio::ip::tcp::endpoint ep = *it;
if ((ep.address().is_v4() && i2p::context.SupportsV4()) ||
(ep.address ().is_v6 () && i2p::context.SupportsV6 ()))
{
(ep.address().is_v6() && i2p::context.SupportsV6())) {
s.lowest_layer().connect(ep, ecode);
if (!ecode)
{
if (!ecode) {
connected = true;
break;
}
}
it++;
}
if (!connected)
{
if (!connected) {
LogPrint(eLogError, "Reseed: Failed to connect to ", url.host);
return "";
}
}
}
if (!ecode)
{
if (!ecode) {
SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str());
s.handshake(boost::asio::ssl::stream_base::client, ecode);
if (!ecode)
{
if (!ecode) {
LogPrint(eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
return ReseedRequest(s, url.to_string());
}
else
} else
LogPrint(eLogError, "Reseed: SSL handshake failed: ", ecode.message());
}
else
} else
LogPrint(eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message());
return "";
}
template<typename Stream>
std::string Reseeder::ReseedRequest (Stream& s, const std::string& uri)
{
std::string Reseeder::ReseedRequest(Stream &s, const std::string &uri) {
boost::system::error_code ecode;
i2p::http::HTTPReq req;
req.uri = uri;
@ -734,7 +667,8 @@ namespace data
s.write_some(boost::asio::buffer(req.to_string()));
// read response
std::stringstream rs;
char recv_buf[1024]; size_t l = 0;
char recv_buf[1024];
size_t l = 0;
do {
l = s.read_some(boost::asio::buffer(recv_buf, sizeof(recv_buf)), ecode);
if (l) rs.write(recv_buf, l);
@ -765,11 +699,9 @@ namespace data
return data;
}
std::string Reseeder::YggdrasilRequest (const std::string& address)
{
std::string Reseeder::YggdrasilRequest(const std::string &address) {
i2p::http::URL url;
if (!url.parse(address))
{
if (!url.parse(address)) {
LogPrint(eLogError, "Reseed: Failed to parse url: ", address);
return "";
}
@ -784,12 +716,10 @@ namespace data
auto host = url.host.substr(1, url.host.length() - 2);
LogPrint(eLogDebug, "Reseed: Connecting to Yggdrasil ", url.host, ":", url.port);
s.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v6::from_string(host), url.port), ecode);
if (!ecode)
{
if (!ecode) {
LogPrint(eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port);
return ReseedRequest(s, url.to_string());
}
else
} else
LogPrint(eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message());
return "";

View file

@ -16,22 +16,24 @@
#include "Identity.h"
#include "Crypto.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
class Reseeder
{
class Reseeder {
typedef Tag<512> PublicKey;
public:
Reseeder();
~Reseeder();
void Bootstrap();
int ReseedFromServers();
int ProcessSU3File(const char *filename);
int ProcessZIPFile(const char *filename);
void LoadCertificates();
@ -39,15 +41,19 @@ namespace data
private:
int ReseedFromSU3Url(const std::string &url, bool isHttps = true);
void LoadCertificate(const std::string &filename);
int ProcessSU3Stream(std::istream &s);
int ProcessZIPStream(std::istream &s, uint64_t contentLength);
bool FindZipDataDescriptor(std::istream &s);
std::string HttpsRequest(const std::string &address);
std::string YggdrasilRequest(const std::string &address);
template<typename Stream>
std::string ReseedRequest(Stream &s, const std::string &uri);

File diff suppressed because it is too large Load diff

View file

@ -19,10 +19,8 @@
#include "RouterInfo.h"
#include "Garlic.h"
namespace i2p
{
namespace garlic
{
namespace i2p {
namespace garlic {
class RouterIncomingRatchetSession;
}
@ -32,8 +30,7 @@ namespace garlic
const char SSU2_KEYS[] = "ssu2.keys";
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
enum RouterStatus
{
enum RouterStatus {
eRouterStatusOK = 0,
eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2,
@ -43,27 +40,23 @@ namespace garlic
eRouterStatusMesh = 6
};
enum RouterError
{
enum RouterError {
eRouterErrorNone = 0,
eRouterErrorClockSkew = 1,
eRouterErrorOffline = 2,
eRouterErrorSymmetricNAT = 3
};
class RouterContext: public i2p::garlic::GarlicDestination
{
class RouterContext : public i2p::garlic::GarlicDestination {
private:
struct NTCP2PrivateKeys
{
struct NTCP2PrivateKeys {
uint8_t staticPublicKey[32];
uint8_t staticPrivateKey[32];
uint8_t iv[16];
};
struct SSU2PrivateKeys
{
struct SSU2PrivateKeys {
uint8_t staticPublicKey[32];
uint8_t staticPrivateKey[32];
uint8_t intro[32];
@ -72,120 +65,192 @@ namespace garlic
public:
RouterContext();
void Init();
const i2p::data::PrivateKeys &GetPrivateKeys() const { return m_Keys; };
i2p::data::LocalRouterInfo &GetRouterInfo() { return m_RouterInfo; };
std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo ()
{
std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo() {
return std::shared_ptr<i2p::data::RouterInfo>(&m_RouterInfo,
[](i2p::data::RouterInfo *) {});
}
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
{
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination() {
return std::shared_ptr<i2p::garlic::GarlicDestination>(this,
[](i2p::garlic::GarlicDestination *) {});
}
const uint8_t *GetNTCP2StaticPublicKey() const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; };
const uint8_t *GetNTCP2StaticPrivateKey() const {
return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr;
};
const uint8_t *GetNTCP2IV() const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
i2p::crypto::X25519Keys &GetNTCP2StaticKeys();
const uint8_t *GetSSU2StaticPublicKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; };
const uint8_t *GetSSU2StaticPrivateKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; };
const uint8_t *GetSSU2IntroKey() const { return m_SSU2Keys ? m_SSU2Keys->intro : nullptr; };
i2p::crypto::X25519Keys &GetSSU2StaticKeys();
uint32_t GetUptime() const; // in seconds
uint64_t GetLastUpdateTime() const { return m_LastUpdateTime; };
uint64_t GetBandwidthLimit() const { return m_BandwidthLimit; };
uint64_t GetTransitBandwidthLimit() const { return (m_BandwidthLimit * m_ShareRatio) / 100LL; };
RouterStatus GetStatus() const { return m_Status; };
void SetStatus(RouterStatus status);
void SetStatusSSU2(RouterStatus status);
RouterError GetError() const { return m_Error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
void SetError(RouterError error) {
m_Status = eRouterStatusError;
m_Error = error;
};
RouterStatus GetStatusV6() const { return m_StatusV6; };
void SetStatusV6(RouterStatus status);
void SetStatusV6SSU2(RouterStatus status);
RouterError GetErrorV6() const { return m_ErrorV6; };
void SetErrorV6 (RouterError error) { m_StatusV6 = eRouterStatusError; m_ErrorV6 = error; };
void SetErrorV6(RouterError error) {
m_StatusV6 = eRouterStatusError;
m_ErrorV6 = error;
};
int GetNetID() const { return m_NetID; };
void SetNetID(int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord(const uint8_t *encrypted, uint8_t *data);
bool DecryptTunnelShortRequestRecord(const uint8_t *encrypted, uint8_t *data);
void UpdatePort(int port); // called from Daemon
void UpdateAddress(const boost::asio::ip::address &host); // called from SSU or Daemon
void PublishNTCP2Address(int port, bool publish, bool v4, bool v6, bool ygg);
void UpdateNTCP2Address(bool enable);
void PublishSSU2Address(int port, bool publish, bool v4, bool v6);
void UpdateSSU2Address(bool enable);
void RemoveNTCPAddress(bool v4only = true); // delete NTCP address for older routers. TODO: remove later
void RemoveSSUAddress(); // delete SSU address for older routers
bool AddIntroducer(const i2p::data::RouterInfo::Introducer &introducer);
void RemoveIntroducer(const boost::asio::ip::udp::endpoint &e);
bool AddSSU2Introducer(const i2p::data::RouterInfo::Introducer &introducer, bool v4);
void RemoveSSU2Introducer(const i2p::data::IdentHash &h, bool v4);
void ClearSSU2Introducers(bool v4);
bool IsUnreachable() const;
void SetUnreachable(bool v4, bool v6);
void SetUnreachableSSU2(bool v4, bool v6);
void SetReachable(bool v4, bool v6);
bool IsFloodfill() const { return m_IsFloodfill; };
void SetFloodfill(bool floodfill);
void SetFamily(const std::string &family);
std::string GetFamily() const;
void SetBandwidth(int limit); /* in kilobytes */
void SetBandwidth(char L); /* by letter */
void SetShareRatio(int percents); // 0 - 100
bool AcceptsTunnels() const { return m_AcceptsTunnels; };
void SetAcceptsTunnels(bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
bool SupportsV6() const { return m_RouterInfo.IsV6(); };
bool SupportsV4() const { return m_RouterInfo.IsV4(); };
bool SupportsMesh() const { return m_RouterInfo.IsMesh(); };
void SetSupportsV6(bool supportsV6);
void SetSupportsV4(bool supportsV4);
void SetSupportsMesh(bool supportsmesh, const boost::asio::ip::address_v6 &host);
void SetMTU(int mtu, bool v4);
i2p::crypto::NoiseSymmetricState &GetCurrentNoiseState() { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address(const boost::asio::ip::address &host); // called from Daemon. TODO: remove
void UpdateStats();
void UpdateTimestamp(uint64_t ts); // in seconds, called from NetDb before publishing
void CleanupDestination(); // garlic destination
// implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity() const { return m_Keys.GetPublic(); };
bool Decrypt(const uint8_t *encrypted, uint8_t *data, i2p::data::CryptoKeyType preferredCrypto) const;
void Sign(const uint8_t *buf, int len, uint8_t *signature) const { m_Keys.Sign(buf, len, signature); };
void SetLeaseSetUpdated() {};
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet() { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const;
// override GarlicDestination
void ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg);
protected:
// implements GarlicDestination
void HandleI2NPMessage(const uint8_t *buf, size_t len);
bool HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID);
private:
void CreateNewRouter();
void NewRouterInfo();
void UpdateRouterInfo();
void NewNTCP2Keys();
void NewSSU2Keys();
bool IsSSU2Only() const; // SSU2 and no SSU
bool Load();
void SaveKeys();
uint16_t SelectRandomPort() const;
bool DecryptECIESTunnelBuildRecord(const uint8_t *encrypted, uint8_t *data, size_t clearTextSize);

File diff suppressed because it is too large Load diff

View file

@ -21,10 +21,8 @@
#include "Profiling.h"
#include "Family.h"
namespace i2p
{
namespace data
{
namespace i2p {
namespace data {
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
const char ROUTER_INFO_PROPERTY_NETID[] = "netId";
@ -58,12 +56,10 @@ namespace data
const uint8_t COST_SSU2_NON_PUBLISHED = 15;
const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later
class RouterInfo: public RoutingDestination
{
class RouterInfo : public RoutingDestination {
public:
enum SupportedTransports
{
enum SupportedTransports {
eNTCP2V4 = 0x01,
eNTCP2V6 = 0x02,
eSSUV4 = 0x04,
@ -75,8 +71,7 @@ namespace data
};
typedef uint8_t CompatibleTransports;
enum Caps
{
enum Caps {
eFloodfill = 0x01,
eHighBandwidth = 0x02,
eExtraBandwidth = 0x04,
@ -85,16 +80,14 @@ namespace data
eUnreachable = 0x20
};
enum AddressCaps
{
enum AddressCaps {
eV4 = 0x01,
eV6 = 0x02,
eSSUTesting = 0x04,
eSSUIntroducer = 0x08
};
enum TransportStyle
{
enum TransportStyle {
eTransportUnknown = 0,
eTransportNTCP,
eTransportSSU,
@ -102,8 +95,7 @@ namespace data
};
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer
{
struct Introducer {
Introducer() : iPort(0), iExp(0) {};
boost::asio::ip::address iHost;
int iPort;
@ -112,14 +104,12 @@ namespace data
uint32_t iExp;
};
struct SSUExt
{
struct SSUExt {
int mtu;
std::vector<Introducer> introducers;
};
struct Address
{
struct Address {
TransportStyle transportStyle;
boost::asio::ip::address host;
Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU
@ -129,133 +119,210 @@ namespace data
bool published = false;
std::unique_ptr<SSUExt> ssu; // not null for SSU
bool IsCompatible (const boost::asio::ip::address& other) const
{
bool IsCompatible(const boost::asio::ip::address &other) const {
return (IsV4() && other.is_v4()) ||
(IsV6() && other.is_v6());
}
bool operator==(const Address& other) const
{
bool operator==(const Address &other) const {
return transportStyle == other.transportStyle &&
host == other.host && port == other.port;
}
bool operator!=(const Address& other) const
{
bool operator!=(const Address &other) const {
return !(*this == other);
}
bool IsNTCP2() const { return transportStyle == eTransportNTCP; };
bool IsSSU2() const { return transportStyle == eTransportSSU2; };
bool IsPublishedNTCP2() const { return IsNTCP2() && published; };
bool IsReachableSSU() const { return (bool) ssu && (published || UsesIntroducer()); };
bool UsesIntroducer() const { return (bool) ssu && !ssu->introducers.empty(); };
bool IsIntroducer() const { return caps & eSSUIntroducer; };
bool IsPeerTesting() const { return caps & eSSUTesting; };
bool IsV4() const { return (caps & AddressCaps::eV4) || (host.is_v4() && !host.is_unspecified()); };
bool IsV6() const { return (caps & AddressCaps::eV6) || (host.is_v6() && !host.is_unspecified()); };
};
class Buffer: public std::array<uint8_t, MAX_RI_BUFFER_SIZE>
{
class Buffer : public std::array<uint8_t, MAX_RI_BUFFER_SIZE> {
public:
Buffer() = default;
Buffer(const uint8_t *buf, size_t len);
};
typedef std::vector<std::shared_ptr<Address> > Addresses;
RouterInfo(const std::string &fullPath);
RouterInfo(const RouterInfo &) = default;
RouterInfo &operator=(const RouterInfo &) = default;
RouterInfo(std::shared_ptr<Buffer> &&buf, size_t len);
RouterInfo(const uint8_t *buf, size_t len);
virtual ~RouterInfo();
std::shared_ptr<const IdentityEx> GetRouterIdentity() const { return m_RouterIdentity; };
void SetRouterIdentity(std::shared_ptr<const IdentityEx> identity);
std::string GetIdentHashBase64() const { return GetIdentHash().ToBase64(); };
uint64_t GetTimestamp() const { return m_Timestamp; };
int GetVersion() const { return m_Version; };
virtual void SetProperty(const std::string &key, const std::string &value) {};
virtual void ClearProperties() {};
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
Addresses &
GetAddresses() { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey(const uint8_t *key) const;
std::shared_ptr<const Address> GetSSU2AddressWithStaticKey(const uint8_t *key, bool isV6) const;
std::shared_ptr<const Address> GetPublishedNTCP2V4Address() const;
std::shared_ptr<const Address> GetPublishedNTCP2V6Address() const;
std::shared_ptr<const Address> GetSSUAddress(bool v4only = true) const;
std::shared_ptr<const Address> GetSSUV6Address() const;
std::shared_ptr<const Address> GetYggdrasilAddress() const;
std::shared_ptr<const Address> GetSSU2V4Address() const;
std::shared_ptr<const Address> GetSSU2V6Address() const;
std::shared_ptr<const Address> GetSSU2Address(bool v4) const;
void AddSSUAddress(const char *host, int port, const uint8_t *key, int mtu = 0);
void AddNTCP2Address(const uint8_t *staticKey, const uint8_t *iv,
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
const boost::asio::ip::address &host = boost::asio::ip::address(), int port = 0,
uint8_t caps = 0);
void AddSSU2Address(const uint8_t *staticKey, const uint8_t *introKey, uint8_t caps = 0); // non published
void AddSSU2Address(const uint8_t *staticKey, const uint8_t *introKey,
const boost::asio::ip::address &host, int port); // published
bool AddIntroducer(const Introducer &introducer);
bool RemoveIntroducer(const boost::asio::ip::udp::endpoint &e);
void SetUnreachableAddressesTransportCaps(uint8_t transports); // bitmask of AddressCaps
void UpdateSupportedTransports();
bool IsFloodfill() const { return m_Caps & Caps::eFloodfill; };
bool IsReachable() const { return m_Caps & Caps::eReachable; };
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
bool IsECIES() const {
return m_RouterIdentity->GetCryptoKeyType() == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
};
bool IsSSU(bool v4only = true) const;
bool IsSSUV6() const { return m_SupportedTransports & eSSUV6; };
bool IsNTCP2(bool v4only = true) const;
bool IsNTCP2V6() const { return m_SupportedTransports & eNTCP2V6; };
bool IsSSU2V4() const { return m_SupportedTransports & eSSU2V4; };
bool IsSSU2V6() const { return m_SupportedTransports & eSSU2V6; };
bool IsV6() const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); };
bool IsV4() const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); };
bool IsMesh() const { return m_SupportedTransports & eNTCP2V6Mesh; };
void EnableV6();
void DisableV6();
void EnableV4();
void DisableV4();
void EnableMesh();
void DisableMesh();
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; };
bool IsCompatible(const RouterInfo &other) const {
return m_SupportedTransports & other.m_SupportedTransports;
};
bool IsReachableFrom(const RouterInfo &other) const {
return m_ReachableTransports & other.m_SupportedTransports;
};
bool IsReachableBy(CompatibleTransports transports) const { return m_ReachableTransports & transports; };
CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; };
CompatibleTransports GetCompatibleTransports(bool incoming) const {
return incoming ? m_ReachableTransports : m_SupportedTransports;
};
bool HasValidAddresses() const { return m_SupportedTransports; };
bool IsHidden() const { return m_Caps & eHidden; };
bool IsHighBandwidth() const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth() const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill() const;
bool IsPeerTesting(bool v4) const;
bool IsSSU2PeerTesting(bool v4) const;
bool IsIntroducer(bool v4) const;
bool IsSSU2Introducer(bool v4) const;
uint8_t GetCaps() const { return m_Caps; };
void SetCaps(uint8_t caps) { m_Caps = caps; };
void SetUnreachable(bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable() const { return m_IsUnreachable; };
const uint8_t *GetBuffer() const { return m_Buffer->data(); };
const uint8_t *LoadBuffer(const std::string &fullPath); // load if necessary
size_t GetBufferLen() const { return m_BufferLen; };
bool IsUpdated() const { return m_IsUpdated; };
void SetUpdated(bool updated) { m_IsUpdated = updated; };
bool SaveToFile(const std::string &fullPath);
std::shared_ptr<RouterProfile> GetProfile() const;
void SaveProfile() { if (m_Profile) m_Profile->Save(GetIdentHash()); };
void Update(const uint8_t *buf, size_t len);
void DeleteBuffer() { m_Buffer = nullptr; };
bool IsNewer(const uint8_t *buf, size_t len) const;
/** return true if we are in a router family and the signature is valid */
@ -263,6 +330,7 @@ namespace data
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity() const { return m_RouterIdentity; };
void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
bool IsDestination() const { return false; };
@ -270,25 +338,40 @@ namespace data
protected:
RouterInfo();
uint8_t *GetBufferPointer(size_t offset = 0) { return m_Buffer->data() + offset; };
void UpdateBuffer(const uint8_t *buf, size_t len);
void SetBufferLen(size_t len) { m_BufferLen = len; };
void RefreshTimestamp();
const Addresses &GetAddresses() const { return *m_Addresses; };
CompatibleTransports GetReachableTransports() const { return m_ReachableTransports; };
void SetReachableTransports(CompatibleTransports transports) { m_ReachableTransports = transports; };
private:
bool LoadFile(const std::string &fullPath);
void ReadFromFile(const std::string &fullPath);
void ReadFromStream(std::istream &s);
void ReadFromBuffer(bool verifySignature);
size_t ReadString(char *str, size_t len, std::istream &s) const;
void ExtractCaps(const char *value);
uint8_t ExtractAddressCaps(const char *value) const;
template<typename Filter>
std::shared_ptr<const Address> GetAddress(Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer() const;
private:
@ -306,27 +389,35 @@ namespace data
mutable std::shared_ptr<RouterProfile> m_Profile;
};
class LocalRouterInfo: public RouterInfo
{
class LocalRouterInfo : public RouterInfo {
public:
LocalRouterInfo() = default;
void CreateBuffer(const PrivateKeys &privateKeys);
void UpdateCaps(uint8_t caps);
void SetProperty(const std::string &key, const std::string &value) override;
void DeleteProperty(const std::string &key);
std::string GetProperty(const std::string &key) const;
void ClearProperties() override { m_Properties.clear(); };
bool AddSSU2Introducer(const Introducer &introducer, bool v4);
bool RemoveSSU2Introducer(const IdentHash &h, bool v4);
private:
void WriteToStream(std::ostream &s) const;
void UpdateCapsProperty();
void WriteString(const std::string &str, std::ostream &s) const;
std::shared_ptr<Buffer> NewBuffer() const override;
private:

File diff suppressed because it is too large Load diff

View file

@ -25,10 +25,8 @@
#include "I2NPProtocol.h"
#include "SSUSession.h"
namespace i2p
{
namespace transport
{
namespace i2p {
namespace transport {
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
@ -38,92 +36,138 @@ namespace transport
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
struct SSUPacket
{
struct SSUPacket {
i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size
boost::asio::ip::udp::endpoint from;
size_t len;
};
class SSUServer
{
class SSUServer {
public:
SSUServer(int port);
~SSUServer();
void Start();
void Stop();
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
bool CreateSession(std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false,
bool v4only = false);
bool CreateSession(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
void CreateDirectSession(std::shared_ptr<const i2p::data::RouterInfo> router,
boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
std::shared_ptr<SSUSession> FindSession(const boost::asio::ip::udp::endpoint &e) const;
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session(std::shared_ptr<const SSUSession> excluded);
std::shared_ptr<SSUSession> GetRandomEstablishedV6Session(std::shared_ptr<const SSUSession> excluded);
void DeleteSession(std::shared_ptr<SSUSession> session);
void DeleteAllSessions();
boost::asio::io_service &GetService() { return m_Service; };
i2p::util::MemoryPool<Fragment> &GetFragmentsPool() { return m_FragmentsPool; };
i2p::util::MemoryPool<IncompleteMessage> &GetIncompleteMessagesPool() { return m_IncompleteMessagesPool; };
i2p::util::MemoryPool<SentMessage> &GetSentMessagesPool() { return m_SentMessagesPool; };
uint16_t GetPort() const { return m_Endpoint.port(); };
bool IsSyncClockFromPeers() const { return m_IsSyncClockFromPeers; };
void SetLocalAddress(const boost::asio::ip::address &localAddress);
void Send(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &to);
void AddRelay(uint32_t tag, std::shared_ptr<SSUSession> relay);
void RemoveRelay(uint32_t tag);
std::shared_ptr<SSUSession> FindRelaySession(uint32_t tag);
void RescheduleIntroducersUpdateTimer();
void RescheduleIntroducersUpdateTimerV6();
void NewPeerTest(uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
PeerTestParticipant GetPeerTestParticipant(uint32_t nonce);
std::shared_ptr<SSUSession> GetPeerTestSession(uint32_t nonce);
void UpdatePeerTest(uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest(uint32_t nonce);
private:
void OpenSocket();
void OpenSocketV6();
void Run();
void RunReceivers();
void RunReceiversV6();
void Receive();
void ReceiveV6();
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
void HandleReceivedFrom(const boost::system::error_code &ecode, std::size_t bytes_transferred,
SSUPacket *packet);
void HandleReceivedFromV6(const boost::system::error_code &ecode, std::size_t bytes_transferred,
SSUPacket *packet);
void HandleReceivedPackets(std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > *sessions);
void CreateSessionThroughIntroducer(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
std::shared_ptr<const i2p::data::RouterInfo::Address> address,
bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session(Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session(Filter filter);
std::list<std::shared_ptr<SSUSession> > FindIntroducers (int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash>& excluded);
std::list<std::shared_ptr<SSUSession> >
FindIntroducers(int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash> &excluded);
void ScheduleIntroducersUpdateTimer();
void ScheduleIntroducersUpdateTimerV6();
void HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4);
void SchedulePeerTestsCleanupTimer();
void HandlePeerTestsCleanupTimer(const boost::system::error_code &ecode);
// timer
void ScheduleTermination();
void HandleTerminationTimer(const boost::system::error_code &ecode);
void ScheduleTerminationV6();
void HandleTerminationTimerV6(const boost::system::error_code &ecode);
private:
struct PeerTest
{
struct PeerTest {
uint64_t creationTime;
PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice
@ -150,8 +194,14 @@ namespace transport
public:
// for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; };
const decltype(m_Sessions)
&
GetSessions() const { return m_Sessions; };
const decltype(m_SessionsV6)
&
GetSessionsV6() const { return m_SessionsV6; };
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -13,10 +13,8 @@
#include "util.h"
#include "SSU2Session.h"
namespace i2p
{
namespace transport
{
namespace i2p {
namespace transport {
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
@ -26,67 +24,89 @@ namespace transport
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
class SSU2Server: private i2p::util::RunnableServiceWithWork
{
struct Packet
{
class SSU2Server : private i2p::util::RunnableServiceWithWork {
struct Packet {
uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len;
boost::asio::ip::udp::endpoint from;
};
class ReceiveService: public i2p::util::RunnableService
{
class ReceiveService : public i2p::util::RunnableService {
public:
ReceiveService(const std::string &name) : RunnableService(name) {};
boost::asio::io_service &GetService() { return GetIOService(); };
void Start() { StartIOService(); };
void Stop() { StopIOService(); };
};
public:
SSU2Server();
~SSU2Server() {};
void Start();
void Stop();
boost::asio::io_service &GetService() { return GetIOService(); };
void SetLocalAddress(const boost::asio::ip::address &localAddress);
bool IsSupported(const boost::asio::ip::address &addr) const;
uint16_t GetPort(bool v4) const;
bool IsSyncClockFromPeers() const { return m_IsSyncClockFromPeers; };
void AddSession(std::shared_ptr<SSU2Session> session);
void RemoveSession(uint64_t connID);
void AddSessionByRouterHash(std::shared_ptr<SSU2Session> session);
bool AddPendingOutgoingSession(std::shared_ptr<SSU2Session> session);
void RemovePendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep);
std::shared_ptr<SSU2Session> FindSession(const i2p::data::IdentHash &ident) const;
std::shared_ptr<SSU2Session> FindPendingOutgoingSession(const boost::asio::ip::udp::endpoint &ep) const;
std::shared_ptr<SSU2Session> GetRandomSession(i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash &excluded) const;
void AddRelay(uint32_t tag, std::shared_ptr<SSU2Session> relay);
void RemoveRelay(uint32_t tag);
std::shared_ptr<SSU2Session> FindRelaySession(uint32_t tag);
void Send(const uint8_t *header, size_t headerLen, const uint8_t *payload, size_t payloadLen,
const boost::asio::ip::udp::endpoint &to);
void Send(const uint8_t *header, size_t headerLen, const uint8_t *headerX, size_t headerXLen,
const uint8_t *payload, size_t payloadLen, const boost::asio::ip::udp::endpoint &to);
bool CreateSession(std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
bool StartPeerTest(std::shared_ptr<const i2p::data::RouterInfo> router, bool v4);
void UpdateOutgoingToken(const boost::asio::ip::udp::endpoint &ep, uint64_t token, uint32_t exp);
uint64_t FindOutgoingToken(const boost::asio::ip::udp::endpoint &ep) const;
uint64_t GetIncomingToken(const boost::asio::ip::udp::endpoint &ep);
std::pair<uint64_t, uint32_t> NewIncomingToken(const boost::asio::ip::udp::endpoint &ep);
void RescheduleIntroducersUpdateTimer();
void RescheduleIntroducersUpdateTimerV6();
i2p::util::MemoryPool<SSU2SentPacket> &GetSentPacketsPool() { return m_SentPacketsPool; };
@ -94,25 +114,38 @@ namespace transport
private:
boost::asio::ip::udp::socket &OpenSocket(const boost::asio::ip::udp::endpoint &localEndpoint);
void Receive(boost::asio::ip::udp::socket &socket);
void HandleReceivedFrom(const boost::system::error_code &ecode, size_t bytes_transferred,
Packet *packet, boost::asio::ip::udp::socket &socket);
void HandleReceivedPacket(Packet *packet);
void HandleReceivedPackets(std::vector<Packet *> packets);
void ProcessNextPacket(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
void ScheduleTermination();
void HandleTerminationTimer(const boost::system::error_code &ecode);
void ScheduleResend();
void HandleResendTimer(const boost::system::error_code &ecode);
void ConnectThroughIntroducer(std::shared_ptr<SSU2Session> session);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers(int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const;
bool v4,
const std::set<i2p::data::IdentHash> &excluded) const;
void UpdateIntroducers(bool v4);
void ScheduleIntroducersUpdateTimer();
void HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6();
private:
@ -137,7 +170,10 @@ namespace transport
public:
// for HTTP/I2PControl
const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; };
const decltype(m_Sessions)
&
GetSSU2Sessions() const { return m_Sessions; };
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -20,10 +20,8 @@
#include "RouterContext.h"
#include "TransportSession.h"
namespace i2p
{
namespace transport
{
namespace i2p {
namespace transport {
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU2_CLOCK_SKEW = 60; // in seconds
@ -48,8 +46,7 @@ namespace transport
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
enum SSU2MessageType
{
enum SSU2MessageType {
eSSU2SessionRequest = 0,
eSSU2SessionCreated = 1,
eSSU2SessionConfirmed = 2,
@ -60,8 +57,7 @@ namespace transport
eSSU2HolePunch = 11
};
enum SSU2BlockType
{
enum SSU2BlockType {
eSSU2BlkDateTime = 0,
eSSU2BlkOptions, // 1
eSSU2BlkRouterInfo, // 2
@ -86,8 +82,7 @@ namespace transport
eSSU2BlkPadding = 254
};
enum SSU2SessionState
{
enum SSU2SessionState {
eSSU2SessionStateUnknown,
eSSU2SessionStateTokenReceived,
eSSU2SessionStateSessionRequestSent,
@ -105,8 +100,7 @@ namespace transport
eSSU2SessionStateTokenRequestReceived
};
enum SSU2PeerTestCode
{
enum SSU2PeerTestCode {
eSSU2PeerTestCodeAccept = 0,
eSSU2PeerTestCodeBobReasonUnspecified = 1,
eSSU2PeerTestCodeBobNoCharlieAvailable = 2,
@ -122,8 +116,7 @@ namespace transport
eSSU2PeerTestCodeUnspecified = 128
};
enum SSU2RelayResponseCode
{
enum SSU2RelayResponseCode {
eSSU2RelayResponseCodeAccept = 0,
eSSU2RelayResponseCodeBobRelayTagNotFound = 5,
eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65,
@ -131,8 +124,7 @@ namespace transport
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70
};
enum SSU2TerminationReason
{
enum SSU2TerminationReason {
eSSU2TerminationReasonNormalClose = 0,
eSSU2TerminationReasonTerminationReceived = 1,
eSSU2TerminationReasonIdleTimeout = 2,
@ -158,10 +150,8 @@ namespace transport
eSSU2TerminationReasonReplacedByNewSession = 22
};
struct SSU2IncompleteMessage
{
struct Fragment
{
struct SSU2IncompleteMessage {
struct Fragment {
uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len;
bool isLast;
@ -175,8 +165,7 @@ namespace transport
void AttachNextFragment(const uint8_t *fragment, size_t fragmentSize);
};
struct SSU2SentPacket
{
struct SSU2SentPacket {
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = 0;
uint64_t sendTime; // in milliseconds
@ -188,14 +177,12 @@ namespace transport
const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
class SSU2Server;
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
{
union Header
{
class SSU2Session : public TransportSession, public std::enable_shared_from_this<SSU2Session> {
union Header {
uint64_t ll[2];
uint8_t buf[16];
struct
{
struct {
uint64_t connID;
uint32_t packetNum;
uint8_t type;
@ -203,8 +190,7 @@ namespace transport
} h;
};
struct HandshakePacket
{
struct HandshakePacket {
Header header;
uint8_t headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE * 2];
@ -219,101 +205,179 @@ namespace transport
SSU2Session(SSU2Server &server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
~SSU2Session();
void SetRemoteEndpoint(const boost::asio::ip::udp::endpoint &ep) { m_RemoteEndpoint = ep; };
const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() const { return m_RemoteEndpoint; };
i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports() const { return m_RemoteTransports; };
std::shared_ptr<const i2p::data::RouterInfo::Address> GetAddress() const { return m_Address; };
void SetOnEstablished(OnEstablished e) { m_OnEstablished = e; };
OnEstablished GetOnEstablished() const { return m_OnEstablished; };
void Connect();
bool Introduce(std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction();
void SendPeerTest(); // Alice, Data message
void SendKeepAlive();
void RequestTermination(SSU2TerminationReason reason);
void CleanUp(uint64_t ts);
void FlushData();
void Done() override;
void SendLocalRouterInfo(bool update) override;
void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs) override;
uint32_t GetRelayTag() const override { return m_RelayTag; };
void Resend(uint64_t ts);
bool IsEstablished() const { return m_State == eSSU2SessionStateEstablished; };
uint64_t GetConnID() const { return m_SourceConnID; };
SSU2SessionState GetState() const { return m_State; };
void SetState(SSU2SessionState state) { m_State = state; };
bool ProcessFirstIncomingMessage(uint64_t connID, uint8_t *buf, size_t len);
bool ProcessSessionCreated(uint8_t *buf, size_t len);
bool ProcessSessionConfirmed(uint8_t *buf, size_t len);
bool ProcessRetry(uint8_t *buf, size_t len);
bool ProcessHolePunch(uint8_t *buf, size_t len);
bool ProcessPeerTest(uint8_t *buf, size_t len);
void ProcessData(uint8_t *buf, size_t len);
private:
void Terminate();
void Established();
void ScheduleConnectTimer();
void HandleConnectTimer(const boost::system::error_code &ecode);
void PostI2NPMessages(std::vector<std::shared_ptr<I2NPMessage> > msgs);
bool SendQueue(); // returns true if ack block was sent
bool SendFragmentedMessage(std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket();
void ConnectAfterIntroduction();
void ProcessSessionRequest(Header &header, uint8_t *buf, size_t len);
void ProcessTokenRequest(Header &header, uint8_t *buf, size_t len);
void SendSessionRequest(uint64_t token = 0);
void SendSessionCreated(const uint8_t *X);
void SendSessionConfirmed(const uint8_t *Y);
void KDFDataPhase(uint8_t *keydata_ab, uint8_t *keydata_ba);
void SendTokenRequest();
void SendRetry();
uint32_t SendData(const uint8_t *buf, size_t len); // returns packet num
void SendQuickAck();
void SendTermination();
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
void SendHolePunch(uint32_t nonce, const boost::asio::ip::udp::endpoint &ep, const uint8_t *introKey,
uint64_t token);
void SendPeerTest(uint8_t msg, const uint8_t *signedData, size_t signedDataLen,
const uint8_t *introKey); // PeerTest message
void SendPathResponse(const uint8_t *data, size_t len);
void HandlePayload(const uint8_t *buf, size_t len);
void HandleDateTime(const uint8_t *buf, size_t len);
void HandleAck(const uint8_t *buf, size_t len);
void HandleAckRange(uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress(const uint8_t *buf, size_t len);
bool ExtractEndpoint(const uint8_t *buf, size_t size, boost::asio::ip::udp::endpoint &ep);
size_t CreateEndpoint(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &ep);
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress() const;
void AdjustMaxPayloadSize();
RouterStatus GetRouterStatus() const;
void SetRouterStatus(RouterStatus status) const;
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo(const uint8_t *buf, size_t size);
void CreateNonce(uint64_t seqn, uint8_t *nonce);
bool UpdateReceivePacketNum(uint32_t packetNum); // for Ack, returns false if duplicate
void HandleFirstFragment(const uint8_t *buf, size_t len);
void HandleFollowOnFragment(const uint8_t *buf, size_t len);
bool ConcatOutOfSequenceFragments(std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
void HandleRelayRequest(const uint8_t *buf, size_t len);
void HandleRelayIntro(const uint8_t *buf, size_t len);
void HandleRelayResponse(const uint8_t *buf, size_t len);
void HandlePeerTest(const uint8_t *buf, size_t len);
size_t CreateAddressBlock(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &ep);
size_t CreateRouterInfoBlock(uint8_t *buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
size_t CreateAckBlock(uint8_t *buf, size_t len);
size_t CreatePaddingBlock(uint8_t *buf, size_t len, size_t minSize = 0);
size_t CreateI2NPBlock(uint8_t *buf, size_t len, std::shared_ptr<I2NPMessage> &&msg);
size_t CreateFirstFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
size_t CreateFollowOnFragmentBlock(uint8_t *buf, size_t len, std::shared_ptr<I2NPMessage> msg,
uint8_t &fragmentNum, uint32_t msgID);
size_t CreateRelayIntroBlock(uint8_t *buf, size_t len, const uint8_t *introData, size_t introDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
size_t CreateRelayResponseBlock(uint8_t *buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce,
uint64_t token, bool v4);
size_t
CreatePeerTestBlock(uint8_t *buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t *routerHash,
const uint8_t *signedData, size_t signedDataLen);
size_t CreatePeerTestBlock(uint8_t *buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock(uint8_t *buf, size_t len);
@ -347,8 +411,7 @@ namespace transport
size_t m_MaxPayloadSize;
};
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)
{
inline uint64_t CreateHeaderMask(const uint8_t *kh, const uint8_t *nonce) {
uint64_t data = 0;
i2p::crypto::ChaCha20((uint8_t * ) & data, 8, kh, nonce, (uint8_t * ) & data);
return data;

View file

@ -13,14 +13,10 @@
#include "SSU.h"
#include "SSUData.h"
namespace i2p
{
namespace transport
{
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
{
if (msg->len + fragmentSize > msg->maxLen)
{
namespace i2p {
namespace transport {
void IncompleteMessage::AttachNextFragment(const uint8_t *fragment, size_t fragmentSize) {
if (msg->len + fragmentSize > msg->maxLen) {
LogPrint(eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
auto newMsg = NewI2NPMessage();
*newMsg = *msg;
@ -34,74 +30,60 @@ namespace transport
SSUData::SSUData(SSUSession &session) :
m_Session(session), m_ResendTimer(session.GetService()),
m_MaxPacketSize(session.IsV6() ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0)
{
m_PacketSize(m_MaxPacketSize), m_LastMessageReceivedTime(0) {
}
SSUData::~SSUData ()
{
SSUData::~SSUData() {
}
void SSUData::Start ()
{
void SSUData::Start() {
}
void SSUData::Stop ()
{
void SSUData::Stop() {
m_ResendTimer.cancel();
m_IncompleteMessages.clear();
m_SentMessages.clear();
m_ReceivedMessages.clear();
}
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
{
void SSUData::AdjustPacketSize(std::shared_ptr<const i2p::data::RouterInfo> remoteRouter) {
if (!remoteRouter) return;
auto ssuAddress = remoteRouter->GetSSUAddress();
if (ssuAddress && ssuAddress->ssu->mtu)
{
if (ssuAddress && ssuAddress->ssu->mtu) {
if (m_Session.IsV6())
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
else
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
if (m_PacketSize > 0)
{
if (m_PacketSize > 0) {
// make sure packet size multiple of 16
m_PacketSize >>= 4;
m_PacketSize <<= 4;
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
LogPrint(eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
}
else
{
} else {
LogPrint(eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
m_PacketSize = m_MaxPacketSize;
}
}
}
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
{
void SSUData::UpdatePacketSize(const i2p::data::IdentHash &remoteIdent) {
auto routerInfo = i2p::data::netdb.FindRouter(remoteIdent);
if (routerInfo)
AdjustPacketSize(routerInfo);
}
void SSUData::ProcessSentMessageAck (uint32_t msgID)
{
void SSUData::ProcessSentMessageAck(uint32_t msgID) {
auto it = m_SentMessages.find(msgID);
if (it != m_SentMessages.end ())
{
if (it != m_SentMessages.end()) {
m_SentMessages.erase(it);
if (m_SentMessages.empty())
m_ResendTimer.cancel();
}
}
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
{
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{
void SSUData::ProcessAcks(uint8_t *&buf, uint8_t flag) {
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) {
// explicit ACKs
uint8_t numAcks = *buf;
buf++;
@ -109,33 +91,27 @@ namespace transport
ProcessSentMessageAck(bufbe32toh(buf + i * 4));
buf += numAcks * 4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
{
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) {
// explicit ACK bitfields
uint8_t numBitfields = *buf;
buf++;
for (int i = 0; i < numBitfields; i++)
{
for (int i = 0; i < numBitfields; i++) {
uint32_t msgID = bufbe32toh(buf);
buf += 4; // msgID
auto it = m_SentMessages.find(msgID);
// process individual Ack bitfields
bool isNonLast = false;
int fragment = 0;
do
{
do {
uint8_t bitfield = *buf;
isNonLast = bitfield & 0x80;
bitfield &= 0x7F; // clear MSB
if (bitfield && it != m_SentMessages.end ())
{
if (bitfield && it != m_SentMessages.end()) {
int numSentFragments = it->second->fragments.size();
// process bits
uint8_t mask = 0x01;
for (int j = 0; j < 7; j++)
{
if (bitfield & mask)
{
for (int j = 0; j < 7; j++) {
if (bitfield & mask) {
if (fragment < numSentFragments)
it->second->fragments[fragment] = nullptr;
}
@ -144,18 +120,15 @@ namespace transport
}
}
buf++;
}
while (isNonLast);
} while (isNonLast);
}
}
}
void SSUData::ProcessFragments (uint8_t * buf)
{
void SSUData::ProcessFragments(uint8_t *buf) {
uint8_t numFragments = *buf; // number of fragments
buf++;
for (int i = 0; i < numFragments; i++)
{
for (int i = 0; i < numFragments; i++) {
uint32_t msgID = bufbe32toh(buf); // message ID
buf += 4;
uint8_t frag[4] = {0};
@ -165,21 +138,20 @@ namespace transport
uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
{
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) {
LogPrint(eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
return;
}
// find message with msgID
auto it = m_IncompleteMessages.find(msgID);
if (it == m_IncompleteMessages.end ())
{
if (it == m_IncompleteMessages.end()) {
// create new message
auto msg = NewI2NPShortMessage();
msg->len -= I2NP_SHORT_HEADER_SIZE;
it = m_IncompleteMessages.insert(std::make_pair(msgID,
m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first;
m_Session.GetServer().GetIncompleteMessagesPool().AcquireShared(
std::move(msg)))).first;
}
auto &incompleteMessage = it->second;
// mark fragment as received
@ -189,49 +161,46 @@ namespace transport
LogPrint(eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
// handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum)
{
if (fragmentNum == incompleteMessage->nextFragmentNum) {
// expected fragment
incompleteMessage->AttachNextFragment(buf, fragmentSize);
if (!isLast && !incompleteMessage->savedFragments.empty ())
{
if (!isLast && !incompleteMessage->savedFragments.empty()) {
// try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
{
for (auto it1 = incompleteMessage->savedFragments.begin();
it1 != incompleteMessage->savedFragments.end();) {
auto &savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
{
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) {
incompleteMessage->AttachNextFragment(savedFragment->buf, savedFragment->len);
isLast = savedFragment->isLast;
incompleteMessage->savedFragments.erase(it1++);
}
else
} else
break;
}
if (isLast)
LogPrint(eLogDebug, "SSU: Message ", msgID, " complete");
}
}
else
{
} else {
if (fragmentNum < incompleteMessage->nextFragmentNum)
// duplicate fragment
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored");
else
{
LogPrint(eLogWarning, "SSU: Duplicate fragment ", (int) fragmentNum, " of message ", msgID,
", ignored");
else {
// missing fragment
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast);
LogPrint(eLogWarning, "SSU: Missing fragments from ", (int) incompleteMessage->nextFragmentNum,
" to ", fragmentNum - 1, " of message ", msgID);
auto savedFragment = m_Session.GetServer().GetFragmentsPool().AcquireShared(fragmentNum, buf,
fragmentSize,
isLast);
if (incompleteMessage->savedFragments.insert(savedFragment).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch();
else
LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
LogPrint(eLogWarning, "SSU: Fragment ", (int) fragmentNum, " of message ", msgID,
" already saved");
}
isLast = false;
}
if (isLast)
{
if (isLast) {
// delete incomplete message
auto msg = incompleteMessage->msg;
incompleteMessage->msg = nullptr;
@ -239,47 +208,35 @@ namespace transport
// process message
SendMsgAck(msgID);
msg->FromSSU(msgID);
if (m_Session.GetState () == eSessionStateEstablished)
{
if (!m_ReceivedMessages.count (msgID))
{
if (m_Session.GetState() == eSessionStateEstablished) {
if (!m_ReceivedMessages.count(msgID)) {
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch();
m_ReceivedMessages.emplace(msgID, m_LastMessageReceivedTime);
if (!msg->IsExpired ())
{
if (!msg->IsExpired()) {
m_Handler.PutNextMessage(std::move(msg));
}
else
} else
LogPrint(eLogDebug, "SSU: message expired");
}
else
} else
LogPrint(eLogWarning, "SSU: Message ", msgID, " already received");
}
else
{
} else {
// we expect DeliveryStatus
if (msg->GetTypeID () == eI2NPDeliveryStatus)
{
if (msg->GetTypeID() == eI2NPDeliveryStatus) {
LogPrint(eLogDebug, "SSU: session established");
m_Session.Established();
}
else
} else
LogPrint(eLogError, "SSU: unexpected message ", (int) msg->GetTypeID());
}
}
else
} else
SendFragmentAck(msgID, incompleteMessage->receivedFragmentsBits);
buf += fragmentSize;
}
}
void SSUData::FlushReceivedMessage ()
{
void SSUData::FlushReceivedMessage() {
m_Handler.Flush();
}
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{
void SSUData::ProcessMessage(uint8_t *buf, size_t len) {
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
@ -288,8 +245,7 @@ namespace transport
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks(buf, flag);
// extended data if presented
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
{
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) {
uint8_t extendedDataSize = *buf;
buf++; // size
LogPrint(eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
@ -299,11 +255,9 @@ namespace transport
ProcessFragments(buf);
}
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{
void SSUData::Send(std::shared_ptr<i2p::I2NPMessage> msg) {
uint32_t msgID = msg->ToSSU();
if (m_SentMessages.find (msgID) != m_SentMessages.end())
{
if (m_SentMessages.find(msgID) != m_SentMessages.end()) {
LogPrint(eLogWarning, "SSU: message ", msgID, " already sent");
return;
}
@ -312,19 +266,18 @@ namespace transport
auto ret = m_SentMessages.emplace(msgID, m_Session.GetServer().GetSentMessagesPool().AcquireShared());
auto &sentMessage = ret.first->second;
if (ret.second)
{
if (ret.second) {
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch() + RESEND_INTERVAL;
sentMessage->numResends = 0;
}
auto &fragments = sentMessage->fragments;
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t payloadSize =
m_PacketSize - sizeof(SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
size_t len = msg->GetLength();
uint8_t *msgBuf = msg->GetSSUHeader();
uint32_t fragmentNum = 0;
while (len > 0 && fragmentNum <= 127)
{
while (len > 0 && fragmentNum <= 127) {
auto fragment = m_Session.GetServer().GetFragmentsPool().AcquireShared();
fragment->fragmentNum = fragmentNum;
uint8_t *payload = fragment->buf + sizeof(SSUHeader);
@ -360,27 +313,22 @@ namespace transport
// encrypt message with session key
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
try
{
try {
m_Session.Send(buf, size);
}
catch (boost::system::system_error& ec)
{
catch (boost::system::system_error &ec) {
LogPrint(eLogWarning, "SSU: Can't send data fragment ", ec.what());
}
if (!isLast)
{
if (!isLast) {
len -= payloadSize;
msgBuf += payloadSize;
}
else
} else
len = 0;
fragmentNum++;
}
}
void SSUData::SendMsgAck (uint32_t msgID)
{
void SSUData::SendMsgAck(uint32_t msgID) {
uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16
uint8_t *payload = buf + sizeof(SSUHeader);
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
@ -396,8 +344,7 @@ namespace transport
m_Session.Send(buf, 48);
}
void SSUData::SendFragmentAck (uint32_t msgID, uint64_t bits)
{
void SSUData::SendFragmentAck(uint32_t msgID, uint64_t bits) {
if (!bits) return;
uint8_t buf[64 + 18] = {0};
uint8_t *payload = buf + sizeof(SSUHeader);
@ -409,12 +356,12 @@ namespace transport
*(uint32_t * )(payload) = htobe32(msgID); // msgID
payload += 4;
size_t len = 0;
while (bits)
{
while (bits) {
*payload = (bits & 0x7F); // next 7 bits
bits >>= 7;
if (bits) *payload &= 0x80; // 0x80 means non-last
payload++; len++;
payload++;
len++;
}
*payload = 0; // number of fragments
len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4
@ -423,88 +370,73 @@ namespace transport
m_Session.Send(buf, len);
}
void SSUData::ScheduleResend()
{
void SSUData::ScheduleResend() {
m_ResendTimer.cancel();
m_ResendTimer.expires_from_now(boost::posix_time::seconds(RESEND_INTERVAL));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleResendTimer (ecode); });
m_ResendTimer.async_wait(
[s](const boost::system::error_code &ecode) { s->m_Data.HandleResendTimer(ecode); });
}
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
void SSUData::HandleResendTimer(const boost::system::error_code &ecode) {
if (ecode != boost::asio::error::operation_aborted) {
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
uint32_t ts = i2p::util::GetSecondsSinceEpoch();
int numResent = 0;
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
{
if (ts >= it->second->nextResendTime)
{
if (it->second->numResends < MAX_NUM_RESENDS)
{
for (auto it = m_SentMessages.begin(); it != m_SentMessages.end();) {
if (ts >= it->second->nextResendTime) {
if (it->second->numResends < MAX_NUM_RESENDS) {
for (auto &f: it->second->fragments)
if (f)
{
try
{
if (f) {
try {
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
m_Session.Send(buf, f->len); // resend
numResent++;
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ());
catch (boost::system::system_error &ec) {
LogPrint(eLogWarning, "SSU: Can't resend message ", it->first,
" data fragment: ", ec.what());
}
}
it->second->numResends++;
it->second->nextResendTime += it->second->numResends * RESEND_INTERVAL;
++it;
}
else
{
LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
} else {
LogPrint(eLogInfo, "SSU: message ", it->first, " has not been ACKed after ",
MAX_NUM_RESENDS, " attempts, deleted");
it = m_SentMessages.erase(it);
}
}
else
} else
++it;
}
if (m_SentMessages.empty()) return; // nothing to resend
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
ScheduleResend();
else
{
else {
LogPrint(eLogError, "SSU: resend window exceeds max size. Session terminated");
m_Session.Close();
}
}
}
void SSUData::CleanUp (uint64_t ts)
{
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
{
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
{
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
void SSUData::CleanUp(uint64_t ts) {
for (auto it = m_IncompleteMessages.begin(); it != m_IncompleteMessages.end();) {
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) {
LogPrint(eLogWarning, "SSU: message ", it->first, " was not completed in ",
INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
it = m_IncompleteMessages.erase(it);
}
else
} else
++it;
}
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL)
if (m_ReceivedMessages.size() > MAX_NUM_RECEIVED_MESSAGES ||
ts > m_LastMessageReceivedTime + DECAY_INTERVAL)
// decay
m_ReceivedMessages.clear();
else
{
else {
// delete old received messages
for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();)
{
for (auto it = m_ReceivedMessages.begin(); it != m_ReceivedMessages.end();) {
if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT)
it = m_ReceivedMessages.erase(it);
else

View file

@ -21,10 +21,8 @@
#include "RouterInfo.h"
#include "TransportSession.h"
namespace i2p
{
namespace transport
{
namespace i2p {
namespace transport {
const size_t SSU_MTU_V4 = 1484;
const size_t SSU_MTU_V6 = 1488;
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
@ -44,28 +42,25 @@ namespace transport
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
struct Fragment
{
struct Fragment {
int fragmentNum;
size_t len;
bool isLast;
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
Fragment() = default;
Fragment(int n, const uint8_t *b, int l, bool last) :
fragmentNum(n), len(l), isLast(last) { memcpy(buf, b, len); };
};
struct FragmentCmp
{
bool operator() (const std::shared_ptr<Fragment>& f1, const std::shared_ptr<Fragment>& f2) const
{
struct FragmentCmp {
bool operator()(const std::shared_ptr<Fragment> &f1, const std::shared_ptr<Fragment> &f2) const {
return f1->fragmentNum < f2->fragmentNum;
};
};
struct IncompleteMessage
{
struct IncompleteMessage {
std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds
@ -73,45 +68,57 @@ namespace transport
std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage(std::shared_ptr<I2NPMessage> &&m) : msg(m), nextFragmentNum(0),
lastFragmentInsertTime (0), receivedFragmentsBits (0) {};
lastFragmentInsertTime(0),
receivedFragmentsBits(0) {};
void AttachNextFragment(const uint8_t *fragment, size_t fragmentSize);
};
struct SentMessage
{
struct SentMessage {
std::vector<std::shared_ptr<Fragment> > fragments;
uint32_t nextResendTime; // in seconds
int numResends;
};
class SSUSession;
class SSUData
{
class SSUData {
public:
SSUData(SSUSession &session);
~SSUData();
void Start();
void Stop();
void CleanUp(uint64_t ts);
void ProcessMessage(uint8_t *buf, size_t len);
void FlushReceivedMessage();
void Send(std::shared_ptr<i2p::I2NPMessage> msg);
void AdjustPacketSize(std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
void UpdatePacketSize(const i2p::data::IdentHash &remoteIdent);
private:
void SendMsgAck(uint32_t msgID);
void SendFragmentAck(uint32_t msgID, uint64_t bits);
void ProcessAcks(uint8_t *&buf, uint8_t flag);
void ProcessFragments(uint8_t *buf);
void ProcessSentMessageAck(uint32_t msgID);
void ScheduleResend();
void HandleResendTimer(const boost::system::error_code &ecode);
private:

File diff suppressed because it is too large Load diff

View file

@ -17,19 +17,18 @@
#include "TransportSession.h"
#include "SSUData.h"
namespace i2p
{
namespace transport
{
namespace i2p {
namespace transport {
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
struct SSUHeader
{
struct SSUHeader {
uint8_t mac[16];
uint8_t iv[16];
uint8_t flag;
uint8_t time[4];
uint8_t GetPayloadType() const { return flag >> 4; };
bool IsExtendedOptions() const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
};
@ -53,8 +52,7 @@ namespace transport
// extended options
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
enum SessionState
{
enum SessionState {
eSessionStateUnknown,
eSessionStateIntroduced,
eSessionStateEstablished,
@ -62,8 +60,7 @@ namespace transport
eSessionStateFailed
};
enum PeerTestParticipant
{
enum PeerTestParticipant {
ePeerTestParticipantUnknown = 0,
ePeerTestParticipantAlice1,
ePeerTestParticipantAlice2,
@ -72,82 +69,127 @@ namespace transport
};
class SSUServer;
class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession>
{
class SSUSession : public TransportSession, public std::enable_shared_from_this<SSUSession> {
public:
SSUSession(SSUServer &server, boost::asio::ip::udp::endpoint &remoteEndpoint,
std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, bool peerTest = false);
void ProcessNextMessage(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
~SSUSession();
void Connect();
void WaitForConnect();
void Introduce(const i2p::data::RouterInfo::Introducer &introducer,
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
void WaitForIntroduction();
void Close();
void Done();
void Failed();
const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() { return m_RemoteEndpoint; };
SSUServer &GetServer() { return m_Server; };
bool IsV6() const { return m_RemoteEndpoint.address().is_v6(); };
void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs);
void SendPeerTest(); // Alice
SessionState GetState() const { return m_State; };
size_t GetNumSentBytes() const { return m_NumSentBytes; };
size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
void SendKeepAlive();
uint32_t GetRelayTag() const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey &GetIntroKey() const { return m_IntroKey; };
void FlushData();
void CleanUp(uint64_t ts);
private:
boost::asio::io_service &GetService();
void CreateAESandMacKey(const uint8_t *pubKey);
size_t GetSSUHeaderSize(const uint8_t *buf) const;
void PostI2NPMessages(std::vector<std::shared_ptr<I2NPMessage> > msgs);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessMessage(uint8_t *buf, size_t len,
const boost::asio::ip::udp::endpoint &senderEndpoint); // call for established session
void ProcessSessionRequest(const uint8_t *buf, size_t len);
void SendSessionRequest();
void SendRelayRequest(const i2p::data::RouterInfo::Introducer &introducer, uint32_t nonce);
void ProcessSessionCreated(uint8_t *buf, size_t len);
void SendSessionCreated(const uint8_t *x, bool sendRelayTag = true);
void ProcessSessionConfirmed(const uint8_t *buf, size_t len);
void SendSessionConfirmed(const uint8_t *y, const uint8_t *ourAddress, size_t ourAddressLen);
void ProcessRelayRequest(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &from);
void SendRelayResponse(uint32_t nonce, const boost::asio::ip::udp::endpoint &from,
const uint8_t *introKey, const boost::asio::ip::udp::endpoint &to);
void SendRelayIntro(std::shared_ptr<SSUSession> session, const boost::asio::ip::udp::endpoint &from);
void ProcessRelayResponse(const uint8_t *buf, size_t len);
void ProcessRelayIntro(const uint8_t *buf, size_t len);
void Established();
void ScheduleConnectTimer();
void HandleConnectTimer(const boost::system::error_code &ecode);
void ProcessPeerTest(const uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true);
void SendPeerTest(uint32_t nonce, const boost::asio::ip::address &address, uint16_t port,
const uint8_t *introKey, bool toAddress = true, bool sendAddress = true);
void ProcessData(uint8_t *buf, size_t len);
void SendSessionDestroyed();
void Send(uint8_t type, const uint8_t *payload, size_t len); // with session key
void Send(const uint8_t *buf, size_t size);
void FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *buf, size_t len, const i2p::crypto::AESKey &aesKey,
const uint8_t *iv, const i2p::crypto::MACKey &macKey, uint8_t flag = 0);
void FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *buf, size_t len); // with session key
void FillHeaderAndEncrypt(uint8_t payloadType, uint8_t *in, size_t len, uint8_t *out); // with session key
void Decrypt(uint8_t *buf, size_t len, const i2p::crypto::AESKey &aesKey);
void DecryptSessionKey(uint8_t *buf, size_t len);
bool Validate(uint8_t *buf, size_t len, const i2p::crypto::MACKey &macKey);
void Reset();
static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size
static size_t ExtractIPAddressAndPort(const uint8_t *buf, size_t len, boost::asio::ip::address &ip,
uint16_t &port); // returns actual buf size
private:

View file

@ -10,10 +10,8 @@
#include "Log.h"
#include "Signature.h"
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
#if OPENSSL_EDDSA
EDDSA25519Verifier::EDDSA25519Verifier ()
{
@ -38,24 +36,21 @@ namespace crypto
}
#else
EDDSA25519Verifier::EDDSA25519Verifier ()
{
EDDSA25519Verifier::EDDSA25519Verifier() {
}
EDDSA25519Verifier::~EDDSA25519Verifier ()
{
EDDSA25519Verifier::~EDDSA25519Verifier() {
}
void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey)
{
void EDDSA25519Verifier::SetPublicKey(const uint8_t *signingKey) {
memcpy(m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
BN_CTX *ctx = BN_CTX_new();
m_PublicKey = GetEd25519()->DecodePublicKey(m_PublicKeyEncoded, ctx);
BN_CTX_free(ctx);
}
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
bool EDDSA25519Verifier::Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
uint8_t digest[64];
SHA512_CTX ctx;
SHA512_Init(&ctx);
@ -66,10 +61,11 @@ namespace crypto
return GetEd25519()->Verify(m_PublicKey, digest, signature);
}
#endif
EDDSA25519SignerCompat::EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
{
EDDSA25519SignerCompat::EDDSA25519SignerCompat(const uint8_t *signingPrivateKey,
const uint8_t *signingPublicKey) {
// expand key
Ed25519::ExpandPrivateKey(signingPrivateKey, m_ExpandedPrivateKey);
// generate and encode public key
@ -77,8 +73,7 @@ namespace crypto
auto publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx);
GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx);
if (signingPublicKey && memcmp (m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH))
{
if (signingPublicKey && memcmp(m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) {
// keys don't match, it means older key with 0x1F
LogPrint(eLogWarning, "Older EdDSA key detected");
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit
@ -88,12 +83,10 @@ namespace crypto
BN_CTX_free(ctx);
}
EDDSA25519SignerCompat::~EDDSA25519SignerCompat ()
{
EDDSA25519SignerCompat::~EDDSA25519SignerCompat() {
}
void EDDSA25519SignerCompat::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
void EDDSA25519SignerCompat::Sign(const uint8_t *buf, int len, uint8_t *signature) const {
GetEd25519()->Sign(m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
}

View file

@ -19,60 +19,59 @@
#include "Ed25519.h"
#include "Gost.h"
namespace i2p
{
namespace crypto
{
class Verifier
{
namespace i2p {
namespace crypto {
class Verifier {
public:
virtual ~Verifier() {};
virtual bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const = 0;
virtual size_t GetPublicKeyLen() const = 0;
virtual size_t GetSignatureLen() const = 0;
virtual size_t GetPrivateKeyLen() const { return GetSignatureLen() / 2; };
virtual void SetPublicKey(const uint8_t *signingKey) = 0;
};
class Signer
{
class Signer {
public:
virtual ~Signer() {};
virtual void Sign(const uint8_t *buf, int len, uint8_t *signature) const = 0;
};
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
const size_t DSA_SIGNATURE_LENGTH = 40;
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH / 2;
class DSAVerifier: public Verifier
{
class DSAVerifier : public Verifier {
public:
DSAVerifier ()
{
DSAVerifier() {
m_PublicKey = CreateDSA();
}
void SetPublicKey (const uint8_t * signingKey)
{
void SetPublicKey(const uint8_t *signingKey) {
DSA_set0_key(m_PublicKey, BN_bin2bn(signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
}
~DSAVerifier ()
{
~DSAVerifier() {
DSA_free(m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
// calculate SHA1 digest
uint8_t digest[20];
SHA1(buf, len, digest);
// signature
DSA_SIG *sig = DSA_SIG_new();
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
DSA_SIG_set0(sig, BN_bin2bn(signature, DSA_SIGNATURE_LENGTH / 2, NULL),
BN_bin2bn(signature + DSA_SIGNATURE_LENGTH / 2, DSA_SIGNATURE_LENGTH / 2, NULL));
// DSA verification
int ret = DSA_do_verify(digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
@ -80,6 +79,7 @@ namespace crypto
}
size_t GetPublicKeyLen() const { return DSA_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen() const { return DSA_SIGNATURE_LENGTH; };
private:
@ -87,24 +87,22 @@ namespace crypto
DSA *m_PublicKey;
};
class DSASigner: public Signer
{
class DSASigner : public Signer {
public:
DSASigner(const uint8_t *signingPrivateKey, const uint8_t *signingPublicKey)
// openssl 1.1 always requires DSA public key even for signing
{
m_PrivateKey = CreateDSA();
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
DSA_set0_key(m_PrivateKey, BN_bin2bn(signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL),
BN_bin2bn(signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
}
~DSASigner ()
{
~DSASigner() {
DSA_free(m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
uint8_t digest[20];
SHA1(buf, len, digest);
DSA_SIG *sig = DSA_do_sign(digest, 20, m_PrivateKey);
@ -120,8 +118,7 @@ namespace crypto
DSA *m_PrivateKey;
};
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void CreateDSARandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
DSA *dsa = CreateDSA();
DSA_generate_key(dsa);
const BIGNUM *pub_key, *priv_key;
@ -131,62 +128,58 @@ namespace crypto
DSA_free(dsa);
}
struct SHA256Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
struct SHA256Hash {
static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
SHA256(buf, len, digest);
}
enum { hashLen = 32 };
enum {
hashLen = 32
};
};
struct SHA384Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
struct SHA384Hash {
static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
SHA384(buf, len, digest);
}
enum { hashLen = 48 };
enum {
hashLen = 48
};
};
struct SHA512Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
struct SHA512Hash {
static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
SHA512(buf, len, digest);
}
enum { hashLen = 64 };
enum {
hashLen = 64
};
};
// EcDSA
template<typename Hash, int curve, size_t keyLen>
class ECDSAVerifier: public Verifier
{
class ECDSAVerifier : public Verifier {
public:
ECDSAVerifier ()
{
ECDSAVerifier() {
m_PublicKey = EC_KEY_new_by_curve_name(curve);
}
void SetPublicKey (const uint8_t * signingKey)
{
void SetPublicKey(const uint8_t *signingKey) {
BIGNUM *x = BN_bin2bn(signingKey, keyLen / 2, NULL);
BIGNUM *y = BN_bin2bn(signingKey + keyLen / 2, keyLen / 2, NULL);
EC_KEY_set_public_key_affine_coordinates(m_PublicKey, x, y);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
~ECDSAVerifier ()
{
~ECDSAVerifier() {
EC_KEY_free(m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
uint8_t digest[Hash::hashLen];
Hash::CalculateHash(buf, len, digest);
ECDSA_SIG *sig = ECDSA_SIG_new();
@ -200,6 +193,7 @@ namespace crypto
}
size_t GetPublicKeyLen() const { return keyLen; };
size_t GetSignatureLen() const { return keyLen; }; // signature length = key length
@ -209,23 +203,19 @@ namespace crypto
};
template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer
{
class ECDSASigner : public Signer {
public:
ECDSASigner (const uint8_t * signingPrivateKey)
{
ECDSASigner(const uint8_t *signingPrivateKey) {
m_PrivateKey = EC_KEY_new_by_curve_name(curve);
EC_KEY_set_private_key(m_PrivateKey, BN_bin2bn(signingPrivateKey, keyLen / 2, NULL));
}
~ECDSASigner ()
{
~ECDSASigner() {
EC_KEY_free(m_PrivateKey);
}
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
uint8_t digest[Hash::hashLen];
Hash::CalculateHash(buf, len, digest);
ECDSA_SIG *sig = ECDSA_do_sign(digest, Hash::hashLen, m_PrivateKey);
@ -242,8 +232,8 @@ namespace crypto
EC_KEY *m_PrivateKey;
};
inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void
CreateECDSARandomKeys(int curve, size_t keyLen, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
EC_KEY *signingKey = EC_KEY_new_by_curve_name(curve);
EC_KEY_generate_key(signingKey);
bn2buf(EC_KEY_get0_private_key(signingKey), signingPrivateKey, keyLen / 2);
@ -252,7 +242,8 @@ namespace crypto
EC_KEY_get0_public_key(signingKey), x, y, NULL);
bn2buf(x, signingPublicKey, keyLen / 2);
bn2buf(y, signingPublicKey + keyLen / 2, keyLen / 2);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
EC_KEY_free(signingKey);
}
@ -261,8 +252,7 @@ namespace crypto
typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void CreateECDSAP256RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
CreateECDSARandomKeys(NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
@ -271,8 +261,7 @@ namespace crypto
typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void CreateECDSAP384RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
CreateECDSARandomKeys(NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
@ -281,24 +270,25 @@ namespace crypto
typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void CreateECDSAP521RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
CreateECDSARandomKeys(NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// EdDSA
class EDDSA25519Verifier: public Verifier
{
class EDDSA25519Verifier : public Verifier {
public:
EDDSA25519Verifier();
void SetPublicKey(const uint8_t *signingKey);
~EDDSA25519Verifier();
bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const;
size_t GetPublicKeyLen() const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen() const { return EDDSA25519_SIGNATURE_LENGTH; };
private:
@ -311,15 +301,16 @@ namespace crypto
#endif
};
class EDDSA25519SignerCompat: public Signer
{
class EDDSA25519SignerCompat : public Signer {
public:
EDDSA25519SignerCompat(const uint8_t *signingPrivateKey, const uint8_t *signingPublicKey = nullptr);
// we pass signingPublicKey to check if it matches private key
~EDDSA25519SignerCompat();
void Sign(const uint8_t *buf, int len, uint8_t *signature) const;
const uint8_t *GetPublicKey() const { return m_PublicKeyEncoded; }; // for keys creation
private:
@ -350,8 +341,7 @@ namespace crypto
#endif
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void CreateEDDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
#if OPENSSL_EDDSA
EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, NULL);
@ -372,24 +362,24 @@ namespace crypto
// ГОСТ Р 34.11
struct GOSTR3411_256_Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
struct GOSTR3411_256_Hash {
static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
GOSTR3411_2012_256(buf, len, digest);
}
enum { hashLen = 32 };
enum {
hashLen = 32
};
};
struct GOSTR3411_512_Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
struct GOSTR3411_512_Hash {
static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
GOSTR3411_2012_512(buf, len, digest);
}
enum { hashLen = 64 };
enum {
hashLen = 64
};
};
// ГОСТ Р 34.10
@ -397,42 +387,44 @@ namespace crypto
const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128;
template<typename Hash>
class GOSTR3410Verifier: public Verifier
{
class GOSTR3410Verifier : public Verifier {
public:
enum { keyLen = Hash::hashLen };
enum {
keyLen = Hash::hashLen
};
GOSTR3410Verifier(GOSTR3410ParamSet paramSet) :
m_ParamSet (paramSet), m_PublicKey (nullptr)
{
m_ParamSet(paramSet), m_PublicKey(nullptr) {
}
void SetPublicKey (const uint8_t * signingKey)
{
void SetPublicKey(const uint8_t *signingKey) {
BIGNUM *x = BN_bin2bn(signingKey, GetPublicKeyLen() / 2, NULL);
BIGNUM *y = BN_bin2bn(signingKey + GetPublicKeyLen() / 2, GetPublicKeyLen() / 2, NULL);
m_PublicKey = GetGOSTR3410Curve(m_ParamSet)->CreatePoint(x, y);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
~GOSTR3410Verifier ()
{
~GOSTR3410Verifier() {
if (m_PublicKey) EC_POINT_free(m_PublicKey);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const {
uint8_t digest[Hash::hashLen];
Hash::CalculateHash(buf, len, digest);
BIGNUM *d = BN_bin2bn(digest, Hash::hashLen, nullptr);
BIGNUM *r = BN_bin2bn(signature, GetSignatureLen() / 2, NULL);
BIGNUM *s = BN_bin2bn(signature + GetSignatureLen() / 2, GetSignatureLen() / 2, NULL);
bool ret = GetGOSTR3410Curve(m_ParamSet)->Verify(m_PublicKey, d, r, s);
BN_free (d); BN_free (r); BN_free (s);
BN_free(d);
BN_free(r);
BN_free(s);
return ret;
}
size_t GetPublicKeyLen() const { return keyLen * 2; }
size_t GetSignatureLen() const { return keyLen * 2; }
private:
@ -442,21 +434,21 @@ namespace crypto
};
template<typename Hash>
class GOSTR3410Signer: public Signer
{
class GOSTR3410Signer : public Signer {
public:
enum { keyLen = Hash::hashLen };
enum {
keyLen = Hash::hashLen
};
GOSTR3410Signer(GOSTR3410ParamSet paramSet, const uint8_t *signingPrivateKey) :
m_ParamSet (paramSet)
{
m_ParamSet(paramSet) {
m_PrivateKey = BN_bin2bn(signingPrivateKey, keyLen, nullptr);
}
~GOSTR3410Signer() { BN_free(m_PrivateKey); }
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
uint8_t digest[Hash::hashLen];
Hash::CalculateHash(buf, len, digest);
BIGNUM *d = BN_bin2bn(digest, Hash::hashLen, nullptr);
@ -464,7 +456,9 @@ namespace crypto
GetGOSTR3410Curve(m_ParamSet)->Sign(m_PrivateKey, d, r, s);
bn2buf(r, signature, keyLen);
bn2buf(s, signature + keyLen, keyLen);
BN_free (d); BN_free (r); BN_free (s);
BN_free(d);
BN_free(r);
BN_free(s);
}
private:
@ -473,8 +467,8 @@ namespace crypto
BIGNUM *m_PrivateKey;
};
inline void CreateGOSTR3410RandomKeys (GOSTR3410ParamSet paramSet, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void
CreateGOSTR3410RandomKeys(GOSTR3410ParamSet paramSet, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
const auto &curve = GetGOSTR3410Curve(paramSet);
auto keyLen = curve->GetKeyLen();
RAND_bytes(signingPrivateKey, keyLen);
@ -487,7 +481,8 @@ namespace crypto
EC_POINT_free(pub);
bn2buf(x, signingPublicKey, keyLen);
bn2buf(y, signingPublicKey + keyLen, keyLen);
BN_free (x); BN_free (y);
BN_free(x);
BN_free(y);
}
typedef GOSTR3410Verifier<GOSTR3411_256_Hash> GOSTR3410_256_Verifier;
@ -497,22 +492,21 @@ namespace crypto
// RedDSA
typedef EDDSA25519Verifier RedDSA25519Verifier;
class RedDSA25519Signer: public Signer
{
class RedDSA25519Signer : public Signer {
public:
RedDSA25519Signer (const uint8_t * signingPrivateKey)
{
RedDSA25519Signer(const uint8_t *signingPrivateKey) {
memcpy(m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
BN_CTX *ctx = BN_CTX_new();
auto publicKey = GetEd25519()->GeneratePublicKey(m_PrivateKey, ctx);
GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx);
BN_CTX_free(ctx);
}
~RedDSA25519Signer() {};
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
GetEd25519()->SignRedDSA(m_PrivateKey, m_PublicKeyEncoded, buf, len, signature);
}
@ -524,8 +518,7 @@ namespace crypto
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
inline void CreateRedDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
inline void CreateRedDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
GetEd25519()->CreateRedDSAPrivateKey(signingPrivateKey);
RedDSA25519Signer signer(signingPrivateKey);
memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH);

View file

@ -12,32 +12,26 @@
#include "Crypto.h"
#if !OPENSSL_SIPHASH
namespace i2p
{
namespace crypto
{
namespace siphash
{
namespace i2p {
namespace crypto {
namespace siphash {
constexpr int crounds = 2;
constexpr int drounds = 4;
inline uint64_t rotl(const uint64_t & x, int b)
{
inline uint64_t rotl(const uint64_t &x, int b) {
uint64_t ret = x << b;
ret |= x >> (64 - b);
return ret;
}
inline void u32to8le(const uint32_t & v, uint8_t * p)
{
inline void u32to8le(const uint32_t &v, uint8_t *p) {
p[0] = (uint8_t) v;
p[1] = (uint8_t)(v >> 8);
p[2] = (uint8_t)(v >> 16);
p[3] = (uint8_t)(v >> 24);
}
inline void u64to8le(const uint64_t & v, uint8_t * p)
{
inline void u64to8le(const uint64_t &v, uint8_t *p) {
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
@ -48,20 +42,17 @@ namespace crypto
p[7] = (v >> 56) & 0xff;
}
inline uint64_t u8to64le(const uint8_t * p)
{
inline uint64_t u8to64le(const uint8_t *p) {
uint64_t i = 0;
int idx = 0;
while(idx < 8)
{
while (idx < 8) {
i |= ((uint64_t) p[idx]) << (idx * 8);
++idx;
}
return i;
}
inline void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3)
{
inline void round(uint64_t &_v0, uint64_t &_v1, uint64_t &_v2, uint64_t &_v3) {
_v0 += _v1;
_v1 = rotl(_v1, 13);
_v1 ^= _v0;
@ -81,8 +72,7 @@ namespace crypto
/** hashsz must be 8 or 16 */
template<std::size_t hashsz>
inline void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key)
{
inline void Siphash(uint8_t *h, const uint8_t *buf, std::size_t bufsz, const uint8_t *key) {
uint64_t v0 = 0x736f6d6570736575ULL;
uint64_t v1 = 0x646f72616e646f6dULL;
uint64_t v2 = 0x6c7967656e657261ULL;
@ -101,8 +91,7 @@ namespace crypto
if (hashsz == 16) v1 ^= 0xee;
while(buf != end)
{
while (buf != end) {
msg = siphash::u8to64le(buf);
v3 ^= msg;
for (i = 0; i < siphash::crounds; ++i)
@ -112,8 +101,7 @@ namespace crypto
buf += 8;
}
while(left)
{
while (left) {
--left;
b |= ((uint64_t)(buf[left])) << (left * 8);
}

File diff suppressed because it is too large Load diff

View file

@ -27,14 +27,11 @@
#include "Tunnel.h"
#include "util.h" // MemoryPool
namespace i2p
{
namespace client
{
namespace i2p {
namespace client {
class ClientDestination;
}
namespace stream
{
namespace stream {
const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001;
const uint16_t PACKET_FLAG_CLOSE = 0x0002;
const uint16_t PACKET_FLAG_RESET = 0x0004;
@ -64,81 +61,102 @@ namespace stream
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
struct Packet
{
struct Packet {
size_t len, offset;
uint8_t buf[MAX_PACKET_SIZE];
uint64_t sendTime;
Packet() : len(0), offset(0), sendTime(0) {};
uint8_t *GetBuffer() { return buf + offset; };
size_t GetLength() const { return len - offset; };
uint32_t GetSendStreamID() const { return bufbe32toh(buf); };
uint32_t GetReceiveStreamID() const { return bufbe32toh(buf + 4); };
uint32_t GetSeqn() const { return bufbe32toh(buf + 8); };
uint32_t GetAckThrough() const { return bufbe32toh(buf + 12); };
uint8_t GetNACKCount() const { return buf[16]; };
uint32_t GetNACK(int i) const { return bufbe32toh(buf + 17 + 4 * i); };
const uint8_t *GetOption() const { return buf + 17 + GetNACKCount() * 4 + 3; }; // 3 = resendDelay + flags
uint16_t GetFlags() const { return bufbe16toh(GetOption() - 2); };
uint16_t GetOptionSize() const { return bufbe16toh(GetOption()); };
const uint8_t *GetOptionData() const { return GetOption() + 2; };
const uint8_t *GetPayload() const { return GetOptionData() + GetOptionSize(); };
bool IsSYN() const { return GetFlags() & PACKET_FLAG_SYNCHRONIZE; };
bool IsNoAck() const { return GetFlags() & PACKET_FLAG_NO_ACK; };
bool IsEcho() const { return GetFlags() & PACKET_FLAG_ECHO; };
};
struct PacketCmp
{
bool operator() (const Packet * p1, const Packet * p2) const
{
struct PacketCmp {
bool operator()(const Packet *p1, const Packet *p2) const {
return p1->GetSeqn() < p2->GetSeqn();
};
};
typedef std::function<void(const boost::system::error_code &ecode)> SendHandler;
struct SendBuffer
{
struct SendBuffer {
uint8_t *buf;
size_t len, offset;
SendHandler handler;
SendBuffer(const uint8_t *b, size_t l, SendHandler h) :
len(l), offset (0), handler(h)
{
len(l), offset(0), handler(h) {
buf = new uint8_t[len];
memcpy(buf, b, len);
}
SendBuffer(size_t l) : // create empty buffer
len(l), offset (0)
{
len(l), offset(0) {
buf = new uint8_t[len];
}
~SendBuffer ()
{
~SendBuffer() {
delete[] buf;
if (handler) handler(boost::system::error_code());
}
size_t GetRemainingSize() const { return len - offset; };
const uint8_t *GetRemaningBuffer() const { return buf + offset; };
void Cancel () { if (handler) handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); handler = nullptr; };
void Cancel() {
if (handler)
handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted));
handler = nullptr;
};
};
class SendBufferQueue
{
class SendBufferQueue {
public:
SendBufferQueue() : m_Size(0) {};
~SendBufferQueue() { CleanUp(); };
void Add(const uint8_t *buf, size_t len, SendHandler handler);
void Add(std::shared_ptr<SendBuffer> buf);
size_t Get(uint8_t *buf, size_t len);
size_t GetSize() const { return m_Size; };
bool IsEmpty() const { return m_Buffers.empty(); };
void CleanUp();
private:
@ -147,8 +165,7 @@ namespace stream
size_t m_Size;
};
enum StreamStatus
{
enum StreamStatus {
eStreamStatusNew = 0,
eStreamStatusOpen,
eStreamStatusReset,
@ -158,8 +175,8 @@ namespace stream
};
class StreamingDestination;
class Stream: public std::enable_shared_from_this<Stream>
{
class Stream : public std::enable_shared_from_this<Stream> {
public:
Stream(boost::asio::io_service &service, StreamingDestination &local,
@ -167,37 +184,57 @@ namespace stream
Stream(boost::asio::io_service &service, StreamingDestination &local); // incoming
~Stream();
uint32_t GetSendStreamID() const { return m_SendStreamID; };
uint32_t GetRecvStreamID() const { return m_RecvStreamID; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet() const { return m_RemoteLeaseSet; };
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity() const { return m_RemoteIdentity; };
bool IsOpen() const { return m_Status == eStreamStatusOpen; };
bool IsEstablished() const { return m_SendStreamID; };
StreamStatus GetStatus() const { return m_Status; };
StreamingDestination &GetLocalDestination() { return m_LocalDestination; };
void HandleNextPacket(Packet *packet);
void HandlePing(Packet *packet);
size_t Send(const uint8_t *buf, size_t len);
void AsyncSend(const uint8_t *buf, size_t len, SendHandler handler);
void SendPing();
template<typename Buffer, typename ReceiveHandler>
void AsyncReceive(const Buffer &buffer, ReceiveHandler handler, int timeout = 0);
size_t ReadSome(uint8_t *buf, size_t len) { return ConcatenatePackets(buf, len); };
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); };
/** only call close from destination thread, use Stream::AsyncClose for other threads */
void Close();
void Cancel() { m_ReceiveTimer.cancel(); };
size_t GetNumSentBytes() const { return m_NumSentBytes; };
size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
size_t GetSendQueueSize() const { return m_SentPackets.size(); };
size_t GetReceiveQueueSize() const { return m_ReceiveQueue.size(); };
size_t GetSendBufferSize() const { return m_SendBuffer.GetSize(); };
int GetWindowSize() const { return m_WindowSize; };
int GetRTT() const { return m_RTT; };
void Terminate(bool deleteFromDestination = true);
@ -207,25 +244,38 @@ namespace stream
void CleanUp();
void SendBuffer();
void SendQuickAck();
void SendClose();
bool SendPacket(Packet *packet);
void SendPackets(const std::vector<Packet *> &packets);
void SendUpdatedLeaseSet();
void SavePacket(Packet *packet);
void ProcessPacket(Packet *packet);
bool ProcessOptions(uint16_t flags, Packet *packet);
void ProcessAck(Packet *packet);
size_t ConcatenatePackets(uint8_t *buf, size_t len);
void UpdateCurrentRemoteLease(bool expired = false);
template<typename Buffer, typename ReceiveHandler>
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
void
HandleReceiveTimer(const boost::system::error_code &ecode, const Buffer &buffer, ReceiveHandler handler,
int remainingTimeout);
void ScheduleResend();
void HandleResendTimer(const boost::system::error_code &ecode);
void HandleAckSendTimer(const boost::system::error_code &ecode);
private:
@ -257,42 +307,60 @@ namespace stream
size_t m_MTU;
};
class StreamingDestination: public std::enable_shared_from_this<StreamingDestination>
{
class StreamingDestination : public std::enable_shared_from_this<StreamingDestination> {
public:
typedef std::function<void(std::shared_ptr<Stream>)> Acceptor;
StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0, bool gzip = false);
StreamingDestination(std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0,
bool gzip = false);
~StreamingDestination();
void Start();
void Stop();
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
std::shared_ptr<Stream>
CreateNewOutgoingStream(std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing(std::shared_ptr<const i2p::data::LeaseSet> remote);
void DeleteStream(std::shared_ptr<Stream> stream);
bool DeleteStream(uint32_t recvStreamID);
void SetAcceptor(const Acceptor &acceptor);
void ResetAcceptor();
bool IsAcceptorSet() const { return m_Acceptor != nullptr; };
void AcceptOnce(const Acceptor &acceptor);
void AcceptOnceAcceptor(std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
std::shared_ptr<i2p::client::ClientDestination> GetOwner() const { return m_Owner; };
void SetOwner(std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
uint16_t GetLocalPort() const { return m_LocalPort; };
void HandleDataMessagePayload(const uint8_t *buf, size_t len);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true);
std::shared_ptr<I2NPMessage>
CreateDataMessage(const uint8_t *payload, size_t len, uint16_t toPort, bool checksum = true);
Packet *NewPacket() { return m_PacketsPool.Acquire(); }
void DeletePacket(Packet *p) { return m_PacketsPool.Release(p); }
private:
void HandleNextPacket(Packet *packet);
std::shared_ptr<Stream> CreateNewIncomingStream(uint32_t receiveStreamID);
void HandlePendingIncomingTimer(const boost::system::error_code &ecode);
private:
@ -318,28 +386,28 @@ namespace stream
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
// for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
const decltype(m_Streams)
&
GetStreams() const { return m_Streams; };
};
//-------------------------------------------------
template<typename Buffer, typename ReceiveHandler>
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
{
void Stream::AsyncReceive(const Buffer &buffer, ReceiveHandler handler, int timeout) {
auto s = shared_from_this();
m_Service.post ([s, buffer, handler, timeout](void)
{
m_Service.post([s, buffer, handler, timeout](void) {
if (!s->m_ReceiveQueue.empty() || s->m_Status == eStreamStatusReset)
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0);
else
{
s->HandleReceiveTimer(boost::asio::error::make_error_code(boost::asio::error::operation_aborted),
buffer, handler, 0);
else {
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
s->m_ReceiveTimer.expires_from_now(boost::posix_time::seconds(t));
int left = timeout - t;
auto self = s->shared_from_this();
self->m_ReceiveTimer.async_wait(
[self, buffer, handler, left](const boost::system::error_code & ec)
{
[self, buffer, handler, left](const boost::system::error_code &ec) {
self->HandleReceiveTimer(ec, buffer, handler, left);
});
}
@ -347,26 +415,24 @@ namespace stream
}
template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
{
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
void
Stream::HandleReceiveTimer(const boost::system::error_code &ecode, const Buffer &buffer, ReceiveHandler handler,
int remainingTimeout) {
size_t received = ConcatenatePackets(boost::asio::buffer_cast<uint8_t *>(buffer),
boost::asio::buffer_size(buffer));
if (received > 0)
handler(boost::system::error_code(), received);
else if (ecode == boost::asio::error::operation_aborted)
{
else if (ecode == boost::asio::error::operation_aborted) {
// timeout not expired
if (m_Status == eStreamStatusReset)
handler(boost::asio::error::make_error_code(boost::asio::error::connection_reset), 0);
else
handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted), 0);
}
else
{
} else {
// timeout expired
if (remainingTimeout <= 0)
handler(boost::asio::error::make_error_code(boost::asio::error::timed_out), received);
else
{
else {
// itermediate interrupt
SendUpdatedLeaseSet(); // send our leaseset if applicable
AsyncReceive(buffer, handler, remainingTimeout);

View file

@ -17,66 +17,65 @@
namespace i2p {
namespace data {
template<size_t sz>
class Tag
{
BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes");
class Tag {
BOOST_STATIC_ASSERT_MSG(sz
% 8 == 0, "Tag size must be multiple of 8 bytes");
public:
Tag() = default;
Tag(const uint8_t *buf) { memcpy(m_Buf, buf, sz); }
bool operator==(const Tag &other) const { return !memcmp(m_Buf, other.m_Buf, sz); }
bool operator!=(const Tag &other) const { return !(*this == other); }
bool operator<(const Tag &other) const { return memcmp(m_Buf, other.m_Buf, sz) < 0; }
uint8_t *operator()() { return m_Buf; }
const uint8_t *operator()() const { return m_Buf; }
operator uint8_t *() { return m_Buf; }
operator const uint8_t *() const { return m_Buf; }
const uint8_t *data() const { return m_Buf; }
const uint64_t *GetLL() const { return ll; }
bool IsZero () const
{
bool IsZero() const {
for (size_t i = 0; i < sz / 8; ++i)
if (ll[i]) return false;
return true;
}
void Fill(uint8_t c)
{
void Fill(uint8_t c) {
memset(m_Buf, c, sz);
}
void Randomize()
{
void Randomize() {
RAND_bytes(m_Buf, sz);
}
std::string ToBase64 (size_t len = sz) const
{
std::string ToBase64(size_t len = sz) const {
char str[sz * 2];
size_t l = i2p::data::ByteStreamToBase64(m_Buf, len, str, sz * 2);
return std::string(str, str + l);
}
std::string ToBase32 (size_t len = sz) const
{
std::string ToBase32(size_t len = sz) const {
char str[sz * 2];
size_t l = i2p::data::ByteStreamToBase32(m_Buf, len, str, sz * 2);
return std::string(str, str + l);
}
size_t FromBase32 (const std::string& s)
{
size_t FromBase32(const std::string &s) {
return i2p::data::Base32ToByteStream(s.c_str(), s.length(), m_Buf, sz);
}
size_t FromBase64 (const std::string& s)
{
size_t FromBase64(const std::string &s) {
return i2p::data::Base64ToByteStream(s.c_str(), s.length(), m_Buf, sz);
}
@ -91,13 +90,11 @@ namespace data {
} // data
} // i2p
namespace std
{
namespace std {
// hash for std::unordered_map
template<size_t sz> struct hash<i2p::data::Tag<sz> >
{
size_t operator()(const i2p::data::Tag<sz>& s) const
{
template<size_t sz>
struct hash<i2p::data::Tag<sz> > {
size_t operator()(const i2p::data::Tag<sz> &s) const {
return s.GetLL()[0];
}
};

View file

@ -27,85 +27,67 @@
#endif
#endif
namespace i2p
{
namespace util
{
static uint64_t GetLocalMillisecondsSinceEpoch ()
{
namespace i2p {
namespace util {
static uint64_t GetLocalMillisecondsSinceEpoch() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
static uint64_t GetLocalSecondsSinceEpoch ()
{
static uint64_t GetLocalSecondsSinceEpoch() {
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
static uint32_t GetLocalMinutesSinceEpoch ()
{
static uint32_t GetLocalMinutesSinceEpoch() {
return std::chrono::duration_cast<std::chrono::minutes>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
static uint32_t GetLocalHoursSinceEpoch ()
{
static uint32_t GetLocalHoursSinceEpoch() {
return std::chrono::duration_cast<std::chrono::hours>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
static int64_t g_TimeOffset = 0; // in seconds
static void SyncTimeWithNTP (const std::string& address)
{
static void SyncTimeWithNTP(const std::string &address) {
LogPrint(eLogInfo, "Timestamp: NTP request to ", address);
boost::asio::io_service service;
boost::system::error_code ec;
auto it = boost::asio::ip::udp::resolver(service).resolve(
boost::asio::ip::udp::resolver::query(address, "ntp"), ec);
if (!ec)
{
if (!ec) {
bool found = false;
boost::asio::ip::udp::resolver::iterator end;
boost::asio::ip::udp::endpoint ep;
while (it != end)
{
while (it != end) {
ep = *it;
if (!ep.address ().is_unspecified ())
{
if (ep.address ().is_v4 ())
{
if (!ep.address().is_unspecified()) {
if (ep.address().is_v4()) {
if (i2p::context.SupportsV4()) found = true;
}
else if (ep.address ().is_v6 ())
{
if (i2p::util::net::IsYggdrasilAddress (ep.address ()))
{
} else if (ep.address().is_v6()) {
if (i2p::util::net::IsYggdrasilAddress(ep.address())) {
if (i2p::context.SupportsMesh()) found = true;
}
else if (i2p::context.SupportsV6 ()) found = true;
} else if (i2p::context.SupportsV6()) found = true;
}
}
if (found) break;
it++;
}
if (!found)
{
if (!found) {
LogPrint(eLogError, "Timestamp: can't find compatible address for ", address);
return;
}
boost::asio::ip::udp::socket socket(service);
socket.open(ep.protocol(), ec);
if (!ec)
{
if (!ec) {
uint8_t buf[48];// 48 bytes NTP request/response
memset(buf, 0, 48);
htobe32buf(buf, (3 << 27) | (3 << 24)); // RFC 4330
size_t len = 0;
try
{
try {
socket.send_to(boost::asio::buffer(buf, 48), ep);
int i = 0;
while (!socket.available() && i < 10) // 10 seconds max
@ -116,129 +98,106 @@ namespace util
if (socket.available())
len = socket.receive_from(boost::asio::buffer(buf, 48), ep);
}
catch (std::exception& e)
{
catch (std::exception &e) {
LogPrint(eLogError, "Timestamp: NTP error: ", e.what());
}
if (len >= 8)
{
if (len >= 8) {
auto ourTs = GetLocalSecondsSinceEpoch();
uint32_t ts = bufbe32toh(buf + 32);
if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900
g_TimeOffset = ts - ourTs;
LogPrint (eLogInfo, "Timestamp: ", address, " time offset from system time is ", g_TimeOffset, " seconds");
LogPrint(eLogInfo, "Timestamp: ", address, " time offset from system time is ", g_TimeOffset,
" seconds");
}
}
else
} else
LogPrint(eLogError, "Timestamp: Couldn't open UDP socket");
}
else
} else
LogPrint(eLogError, "Timestamp: Couldn't resolve address ", address);
}
NTPTimeSync::NTPTimeSync (): m_IsRunning (false), m_Timer (m_Service)
{
NTPTimeSync::NTPTimeSync() : m_IsRunning(false), m_Timer(m_Service) {
i2p::config::GetOption("nettime.ntpsyncinterval", m_SyncInterval);
std::string ntpservers; i2p::config::GetOption("nettime.ntpservers", ntpservers);
std::string ntpservers;
i2p::config::GetOption("nettime.ntpservers", ntpservers);
boost::split(m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on);
}
NTPTimeSync::~NTPTimeSync ()
{
NTPTimeSync::~NTPTimeSync() {
Stop();
}
void NTPTimeSync::Start()
{
if (m_NTPServersList.size () > 0)
{
void NTPTimeSync::Start() {
if (m_NTPServersList.size() > 0) {
m_IsRunning = true;
LogPrint(eLogInfo, "Timestamp: NTP time sync starting");
m_Service.post(std::bind(&NTPTimeSync::Sync, this));
m_Thread.reset(new std::thread(std::bind(&NTPTimeSync::Run, this)));
}
else
} else
LogPrint(eLogWarning, "Timestamp: No NTP server found");
}
void NTPTimeSync::Stop ()
{
if (m_IsRunning)
{
void NTPTimeSync::Stop() {
if (m_IsRunning) {
LogPrint(eLogInfo, "Timestamp: NTP time sync stopping");
m_IsRunning = false;
m_Timer.cancel();
m_Service.stop();
if (m_Thread)
{
if (m_Thread) {
m_Thread->join();
m_Thread.reset(nullptr);
}
}
}
void NTPTimeSync::Run ()
{
void NTPTimeSync::Run() {
i2p::util::SetThreadName("Timesync");
while (m_IsRunning)
{
try
{
while (m_IsRunning) {
try {
m_Service.run();
}
catch (std::exception& ex)
{
catch (std::exception &ex) {
LogPrint(eLogError, "Timestamp: NTP time sync exception: ", ex.what());
}
}
}
void NTPTimeSync::Sync ()
{
void NTPTimeSync::Sync() {
if (m_NTPServersList.size() > 0)
SyncTimeWithNTP(m_NTPServersList[rand() % m_NTPServersList.size()]);
else
m_IsRunning = false;
if (m_IsRunning)
{
if (m_IsRunning) {
m_Timer.expires_from_now(boost::posix_time::hours(m_SyncInterval));
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
{
m_Timer.async_wait([this](const boost::system::error_code &ecode) {
if (ecode != boost::asio::error::operation_aborted)
Sync();
});
}
}
uint64_t GetMillisecondsSinceEpoch ()
{
uint64_t GetMillisecondsSinceEpoch() {
return GetLocalMillisecondsSinceEpoch() + g_TimeOffset * 1000;
}
uint64_t GetSecondsSinceEpoch ()
{
uint64_t GetSecondsSinceEpoch() {
return GetLocalSecondsSinceEpoch() + g_TimeOffset;
}
uint32_t GetMinutesSinceEpoch ()
{
uint32_t GetMinutesSinceEpoch() {
return GetLocalMinutesSinceEpoch() + g_TimeOffset / 60;
}
uint32_t GetHoursSinceEpoch ()
{
uint32_t GetHoursSinceEpoch() {
return GetLocalHoursSinceEpoch() + g_TimeOffset / 3600;
}
void GetCurrentDate (char * date)
{
void GetCurrentDate(char *date) {
GetDateString(GetSecondsSinceEpoch(), date);
}
void GetDateString (uint64_t timestamp, char * date)
{
void GetDateString(uint64_t timestamp, char *date) {
using clock = std::chrono::system_clock;
auto t = clock::to_time_t(clock::time_point(std::chrono::seconds(timestamp)));
struct tm tm;
@ -251,8 +210,7 @@ namespace util
#endif
}
void AdjustTimeOffset (int64_t offset)
{
void AdjustTimeOffset(int64_t offset) {
g_TimeOffset += offset;
}
}

View file

@ -15,32 +15,36 @@
#include <string>
#include <boost/asio.hpp>
namespace i2p
{
namespace util
{
namespace i2p {
namespace util {
uint64_t GetMillisecondsSinceEpoch();
uint64_t GetSecondsSinceEpoch();
uint32_t GetMinutesSinceEpoch();
uint32_t GetHoursSinceEpoch();
void GetCurrentDate(char *date); // returns date as YYYYMMDD string, 9 bytes
void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
void GetDateString(uint64_t timestamp,
char *date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
void AdjustTimeOffset(int64_t offset); // in seconds from current
class NTPTimeSync
{
class NTPTimeSync {
public:
NTPTimeSync();
~NTPTimeSync();
void Start();
void Stop();
private:
void Run();
void Sync();
private:

View file

@ -15,30 +15,24 @@
#include "Transports.h"
#include "TransitTunnel.h"
namespace i2p
{
namespace tunnel
{
namespace i2p {
namespace tunnel {
TransitTunnel::TransitTunnel(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey) :
TunnelBase (receiveTunnelID, nextTunnelID, nextIdent)
{
TunnelBase(receiveTunnelID, nextTunnelID, nextIdent) {
m_Encryption.SetKeys(layerKey, ivKey);
}
void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
{
void TransitTunnel::EncryptTunnelMsg(std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) {
m_Encryption.Encrypt(in->GetPayload() + 4, out->GetPayload() + 4);
i2p::transport::transports.UpdateTotalTransitTransmittedBytes(TUNNEL_DATA_MSG_SIZE);
}
TransitTunnelParticipant::~TransitTunnelParticipant ()
{
TransitTunnelParticipant::~TransitTunnelParticipant() {
}
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg)
{
void TransitTunnelParticipant::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
EncryptTunnelMsg(tunnelMsg, tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength();
@ -47,10 +41,8 @@ namespace tunnel
m_TunnelDataMsgs.push_back(tunnelMsg);
}
void TransitTunnelParticipant::FlushTunnelDataMsgs ()
{
if (!m_TunnelDataMsgs.empty ())
{
void TransitTunnelParticipant::FlushTunnelDataMsgs() {
if (!m_TunnelDataMsgs.empty()) {
auto num = m_TunnelDataMsgs.size();
if (num > 1)
LogPrint(eLogDebug, "TransitTunnel: ", GetTunnelID(), "->", GetNextTunnelID(), " ", num);
@ -59,18 +51,15 @@ namespace tunnel
}
}
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
void TransitTunnel::SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg) {
LogPrint(eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID());
}
void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg)
{
void TransitTunnel::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
LogPrint(eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID());
}
void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
void TransitTunnelGateway::SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg) {
TunnelMessageBlock block;
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
@ -78,14 +67,12 @@ namespace tunnel
m_Gateway.PutTunnelDataMsg(block);
}
void TransitTunnelGateway::FlushTunnelDataMsgs ()
{
void TransitTunnelGateway::FlushTunnelDataMsgs() {
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendBuffer();
}
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg)
{
void TransitTunnelEndpoint::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
auto newMsg = CreateEmptyTunnelDataMsg(true);
EncryptTunnelMsg(tunnelMsg, newMsg);
@ -96,22 +83,19 @@ namespace tunnel
std::shared_ptr<TransitTunnel> CreateTransitTunnel(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey,
bool isGateway, bool isEndpoint)
{
if (isEndpoint)
{
bool isGateway, bool isEndpoint) {
if (isEndpoint) {
LogPrint(eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
else if (isGateway)
{
return std::make_shared<TransitTunnelEndpoint>(receiveTunnelID, nextIdent, nextTunnelID, layerKey,
ivKey);
} else if (isGateway) {
LogPrint(eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created");
return std::make_shared<TransitTunnelGateway> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
else
{
return std::make_shared<TransitTunnelGateway>(receiveTunnelID, nextIdent, nextTunnelID, layerKey,
ivKey);
} else {
LogPrint(eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
return std::make_shared<TransitTunnelParticipant>(receiveTunnelID, nextIdent, nextTunnelID, layerKey,
ivKey);
}
}
}

View file

@ -19,12 +19,9 @@
#include "TunnelGateway.h"
#include "TunnelBase.h"
namespace i2p
{
namespace tunnel
{
class TransitTunnel: public TunnelBase
{
namespace i2p {
namespace tunnel {
class TransitTunnel : public TunnelBase {
public:
TransitTunnel(uint32_t receiveTunnelID,
@ -35,15 +32,17 @@ namespace tunnel
// implements TunnelBase
void SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg);
void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
void EncryptTunnelMsg(std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
private:
i2p::crypto::TunnelEncryption m_Encryption;
};
class TransitTunnelParticipant: public TransitTunnel
{
class TransitTunnelParticipant : public TransitTunnel {
public:
TransitTunnelParticipant(uint32_t receiveTunnelID,
@ -51,10 +50,13 @@ namespace tunnel
const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes(0) {};
~TransitTunnelParticipant();
size_t GetNumTransmittedBytes() const { return m_NumTransmittedBytes; };
void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
void FlushTunnelDataMsgs();
private:
@ -63,8 +65,7 @@ namespace tunnel
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
};
class TransitTunnelGateway: public TransitTunnel
{
class TransitTunnelGateway : public TransitTunnel {
public:
TransitTunnelGateway(uint32_t receiveTunnelID,
@ -74,7 +75,9 @@ namespace tunnel
layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg);
void FlushTunnelDataMsgs();
size_t GetNumTransmittedBytes() const { return m_Gateway.GetNumSentBytes(); };
private:
@ -83,8 +86,7 @@ namespace tunnel
TunnelGateway m_Gateway;
};
class TransitTunnelEndpoint: public TransitTunnel
{
class TransitTunnelEndpoint : public TransitTunnel {
public:
TransitTunnelEndpoint(uint32_t receiveTunnelID,
@ -96,6 +98,7 @@ namespace tunnel
void Cleanup() { m_Endpoint.Cleanup(); }
void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
size_t GetNumTransmittedBytes() const { return m_Endpoint.GetNumReceivedBytes(); }
private:

View file

@ -20,47 +20,39 @@
#include "I2NPProtocol.h"
#include "Timestamp.h"
namespace i2p
{
namespace transport
{
namespace i2p {
namespace transport {
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
class SignedData
{
class SignedData {
public:
SignedData() {}
SignedData (const SignedData& other)
{
SignedData(const SignedData &other) {
m_Stream << other.m_Stream.rdbuf();
}
void Reset ()
{
void Reset() {
m_Stream.str("");
}
void Insert (const uint8_t * buf, size_t len)
{
void Insert(const uint8_t *buf, size_t len) {
m_Stream.write((char *) buf, len);
}
template<typename T>
void Insert (T t)
{
void Insert(T t) {
m_Stream.write((char *) &t, sizeof(T));
}
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
{
bool Verify(std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t *signature) const {
return ident->Verify((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature);
}
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
{
void Sign(const i2p::data::PrivateKeys &keys, uint8_t *signature) const {
keys.Sign((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature);
}
@ -69,49 +61,58 @@ namespace transport
std::stringstream m_Stream;
};
class TransportSession
{
class TransportSession {
public:
TransportSession(std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout) :
m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ())
{
m_NumSentBytes(0), m_NumReceivedBytes(0), m_IsOutgoing(router),
m_TerminationTimeout(terminationTimeout),
m_LastActivityTimestamp(i2p::util::GetSecondsSinceEpoch()) {
if (router)
m_RemoteIdentity = router->GetRouterIdentity();
m_CreationTime = m_LastActivityTimestamp;
}
virtual ~TransportSession() {};
virtual void Done() = 0;
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
std::string GetIdentHashBase64() const {
return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : "";
}
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity ()
{
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity() {
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
return m_RemoteIdentity;
}
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident)
{
void SetRemoteIdentity(std::shared_ptr<const i2p::data::IdentityEx> ident) {
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
m_RemoteIdentity = ident;
}
size_t GetNumSentBytes() const { return m_NumSentBytes; };
size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
bool IsOutgoing() const { return m_IsOutgoing; };
int GetTerminationTimeout() const { return m_TerminationTimeout; };
void SetTerminationTimeout(int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
bool IsTerminationTimeoutExpired(uint64_t ts) const {
return ts >= m_LastActivityTimestamp + GetTerminationTimeout();
};
uint32_t GetCreationTime() const { return m_CreationTime; };
void SetCreationTime(uint32_t ts) { m_CreationTime = ts; }; // for introducers
virtual uint32_t GetRelayTag() const { return 0; };
virtual void SendLocalRouterInfo(bool update = false) { SendI2NPMessages({CreateDatabaseStoreMsg()}); };
virtual void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs) = 0;
protected:

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more