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,22 +14,20 @@
// Afrikaans localization file
namespace i2p
{
namespace i18n
{
namespace afrikaans // language namespace
{
namespace i2p {
namespace i18n {
namespace afrikaans // language namespace
{
// language name in lowercase
static std::string language = "afrikaans";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"failed", "Het misluk"},
{"unknown", "onbekend"},
@ -62,7 +60,7 @@ namespace afrikaans // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"dag", "dae"}},
{"hours", {"uur", "ure"}},
@ -71,11 +69,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -14,22 +14,20 @@
// Armenian localization file
namespace i2p
{
namespace i18n
{
namespace armenian // language namespace
{
namespace i2p {
namespace i18n {
namespace armenian // language namespace
{
// language name in lowercase
static std::string language = "armenian";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"KiB", "ԿիԲ"},
{"MiB", "ՄիԲ"},
@ -196,7 +194,7 @@ namespace armenian // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"օր", "օր"}},
{"hours", {"ժամ", "ժամ"}},
@ -205,11 +203,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -15,22 +15,20 @@
// Simplified Chinese localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace chinese // language namespace
{
namespace i2p {
namespace i18n {
namespace chinese // language namespace
{
// language name in lowercase
static std::string language = "chinese";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
@ -198,7 +196,7 @@ namespace chinese // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {""}},
{"hours", {""}},
@ -207,11 +205,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -15,36 +15,34 @@
// English localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace english // language namespace
{
namespace i2p {
namespace i18n {
namespace english // language namespace
{
// language name in lowercase
static std::string language = "english";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"", {"", ""}},
};
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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -14,22 +14,20 @@
// French localization file
namespace i2p
{
namespace i18n
{
namespace french // language namespace
{
namespace i2p {
namespace i18n {
namespace french // language namespace
{
// language name in lowercase
static std::string language = "french";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"KiB", "Kio"},
{"MiB", "Mio"},
@ -192,7 +190,7 @@ namespace french // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"jour", "jours"}},
{"hours", {"heure", "heures"}},
@ -201,11 +199,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -14,22 +14,20 @@
// German localization file
namespace i2p
{
namespace i18n
{
namespace german // language namespace
{
namespace i2p {
namespace i18n {
namespace german // language namespace
{
// language name in lowercase
static std::string language = "german";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"Purple I2P Webconsole", "Purple-I2P-Webkonsole"},
{"KiB", "KiB"},
@ -197,7 +195,7 @@ namespace german // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"Tag", "Tage"}},
{"hours", {"Stunde", "Stunden"}},
@ -206,11 +204,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -11,34 +11,28 @@
#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());
i2p::client::context.SetLanguage(i2p::i18n::english::GetLocale());
else
i2p::client::context.SetLanguage (it->second.LocaleFunc());
i2p::client::context.SetLanguage(it->second.LocaleFunc());
}
inline std::string translate (const std::string& arg)
{
return i2p::client::context.GetLanguage ()->GetString (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)
{
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, 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
} // 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,48 +9,37 @@
#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,
const std::map<std::string, std::string>& strings,
const std::map<std::string, std::vector<std::string>>& plurals,
Locale(
const std::string &language,
const std::map <std::string, std::string> &strings,
const std::map <std::string, std::vector<std::string>> &plurals,
std::function<int(int)> formula
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
) : 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];
}
@ -58,48 +47,47 @@ namespace i18n
private:
const std::string m_Language;
const std::map<std::string, std::string> m_Strings;
const std::map<std::string, std::vector<std::string>> m_Plurals;
const std::map <std::string, std::string> m_Strings;
const std::map <std::string, std::vector<std::string>> m_Plurals;
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;
std::function<std::shared_ptr<const i2p::i18n::Locale>(void)> LocaleFunc;
};
// Add localization here with language name as namespace
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
/**
* That map contains international language name lower-case, name in it's language and it's code
*/
static std::map<std::string, langData> languages
static std::map <std::string, langData> languages
{
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} },
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },
{"afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale}},
{"armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale}},
{"chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale}},
{"english", {"English", "en", i2p::i18n::english::GetLocale}},
{"french", {"Français", "fr", i2p::i18n::french::GetLocale}},
{"german", {"Deutsch", "de", i2p::i18n::german::GetLocale}},
{"russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale}},
{"turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale}},
{"ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale}},
{"uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale}},
};
} // i18n
} // i18n
} // i2p
#endif // __I18N_LANGS_H__

View file

@ -14,22 +14,21 @@
// Russian localization file
namespace i2p
{
namespace i18n
{
namespace russian // language namespace
{
namespace i2p {
namespace i18n {
namespace russian // language namespace
{
// language name in lowercase
static std::string language = "russian";
// 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;
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;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"KiB", "КиБ"},
{"MiB", "МиБ"},
@ -196,7 +195,7 @@ namespace russian // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"день", "дня", "дней"}},
{"hours", {"час", "часа", "часов"}},
@ -205,11 +204,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -14,22 +14,20 @@
// Turkmen localization file
namespace i2p
{
namespace i18n
{
namespace turkmen // language namespace
{
namespace i2p {
namespace i18n {
namespace turkmen // language namespace
{
// language name in lowercase
static std::string language = "turkmen";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
@ -196,7 +194,7 @@ namespace turkmen // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"gün", "gün"}},
{"hours", {"sagat", "sagat"}},
@ -205,11 +203,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -14,22 +14,21 @@
// Ukrainian localization file
namespace i2p
{
namespace i18n
{
namespace ukrainian // language namespace
{
namespace i2p {
namespace i18n {
namespace ukrainian // language namespace
{
// language name in lowercase
static std::string language = "ukrainian";
// 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;
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;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"KiB", "КіБ"},
{"MiB", "МіБ"},
@ -196,7 +195,7 @@ namespace ukrainian // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"день", "дня", "днів"}},
{"hours", {"годину", "години", "годин"}},
@ -205,11 +204,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

View file

@ -14,22 +14,20 @@
// Ukrainian localization file
namespace i2p
{
namespace i18n
{
namespace uzbek // language namespace
{
namespace i2p {
namespace i18n {
namespace uzbek // language namespace
{
// language name in lowercase
static std::string language = "uzbek";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
static int plural(int n) {
return n > 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
static std::map <std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
@ -196,7 +194,7 @@ namespace uzbek // language namespace
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
static std::map <std::string, std::vector<std::string>> plurals
{
{"days", {"kun", "kun"}},
{"hours", {"soat", "soat"}},
@ -205,11 +203,11 @@ 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
} // i18n
} // language
} // i18n
} // i2p

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;
}
@ -77,15 +73,14 @@ namespace data
*
*/
size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */
const uint8_t * InBuffer, /* Input buffer, binary data */
size_t ByteStreamToBase64( /* Number of bytes in the encoded buffer */
const uint8_t *InBuffer, /* Input buffer, binary data */
size_t InCount, /* Number of bytes in the input buffer */
char * OutBuffer, /* output buffer */
char *OutBuffer, /* output buffer */
size_t len /* length of output buffer */
)
{
unsigned char * ps;
unsigned char * pd;
) {
unsigned char *ps;
unsigned char *pd;
unsigned char acc_1;
unsigned char acc_2;
int i;
@ -93,7 +88,7 @@ namespace data
int m;
size_t outCount;
ps = (unsigned char *)InBuffer;
ps = (unsigned char *) InBuffer;
n = InCount / 3;
m = InCount % 3;
if (!m)
@ -103,9 +98,8 @@ namespace data
if (outCount > len) return 0;
pd = (unsigned char *)OutBuffer;
for ( i = 0; i < n; i++ )
{
pd = (unsigned char *) OutBuffer;
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 */
@ -160,15 +151,14 @@ namespace data
*
*/
size_t Base64ToByteStream ( /* Number of output bytes */
const char * InBuffer, /* BASE64 encoded buffer */
size_t Base64ToByteStream( /* Number of output bytes */
const char *InBuffer, /* BASE64 encoded buffer */
size_t InCount, /* Number of input bytes */
uint8_t * OutBuffer, /* output buffer length */
uint8_t *OutBuffer, /* output buffer length */
size_t len /* length of output buffer */
)
{
unsigned char * ps;
unsigned char * pd;
) {
unsigned char *ps;
unsigned char *pd;
unsigned char acc_1;
unsigned char acc_2;
int i;
@ -187,18 +177,17 @@ namespace data
else
return 0;
ps = (unsigned char *)(InBuffer + InCount - 1);
while ( *ps-- == P64 )
ps = (unsigned char *) (InBuffer + InCount - 1);
while (*ps-- == P64)
outCount--;
ps = (unsigned char *)InBuffer;
ps = (unsigned char *) InBuffer;
if (outCount > len)
return 0;
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,20 +211,18 @@ namespace data
return outCount;
}
size_t Base64EncodingBufferSize (const size_t input_size)
{
auto d = div (input_size, 3);
size_t Base64EncodingBufferSize(const size_t input_size) {
auto d = div(input_size, 3);
if (d.rem)
d.quot++;
return 4 * d.quot;
}
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);
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);
str[l] = 0;
// replace '-' by '+' and '~' by '/'
for (size_t i = 0; i < l; i++)
@ -258,21 +245,18 @@ namespace data
*
*/
static void iT64Build()
{
static void iT64Build() {
int i;
isFirstTime = 0;
for ( i = 0; i < 256; i++ ) iT64[i] = -1;
for ( i = 0; i < 64; i++ ) iT64[(int)T64[i]] = i;
iT64[(int)P64] = 0;
for (i = 0; i < 256; i++) iT64[i] = -1;
for (i = 0; i < 64; i++) iT64[(int) T64[i]] = i;
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;
@ -324,5 +302,5 @@ namespace data
}
return ret;
}
}
}
}

View file

@ -14,23 +14,27 @@
#include <iostream>
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 ();
namespace data {
size_t ByteStreamToBase64(const uint8_t *InBuffer, size_t InCount, char *OutBuffer, size_t len);
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);
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);
/**
* Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
*/
size_t Base64EncodingBufferSize(const size_t input_size);
std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization
std::string ToBase64Standard(const std::string &in); // using standard table, for Proxy-Authorization
} // data
} // data
} // i2p
#endif

View file

@ -20,114 +20,110 @@
#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)
{
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
BIGNUM * q = BN_CTX_get (ctx);
EC_GROUP_get_order (group, q, ctx);
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);
EC_GROUP_get_order(group, q, ctx);
// calculate alpha = seed mod q
BIGNUM * alpha = BN_CTX_get (ctx);
BN_bin2bn (seed, 64, alpha); // seed is in BigEndian
BN_mod (alpha, alpha, q, ctx); // % q
BIGNUM *alpha = BN_CTX_get(ctx);
BN_bin2bn(seed, 64, alpha); // seed is in BigEndian
BN_mod(alpha, alpha, q, ctx); // % q
// A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha)
auto p = EC_POINT_new (group);
EC_POINT_mul (group, p, alpha, nullptr, nullptr, ctx); // B*alpha
EC_POINT_add (group, p, pub, p, ctx); // pub + B*alpha
BN_CTX_end (ctx);
BN_CTX_free (ctx);
auto p = EC_POINT_new(group);
EC_POINT_mul(group, p, alpha, nullptr, nullptr, ctx); // B*alpha
EC_POINT_add(group, p, pub, p, ctx); // pub + B*alpha
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return p;
}
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);
EC_GROUP_get_order (group, q, ctx);
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);
EC_GROUP_get_order(group, q, ctx);
// calculate alpha = seed mod q
BIGNUM * alpha = BN_CTX_get (ctx);
BN_bin2bn (seed, 64, alpha); // seed is in BigEndian
BN_mod (alpha, alpha, q, ctx); // % q
BN_add (alpha, alpha, priv); // alpha = alpha + priv
BIGNUM *alpha = BN_CTX_get(ctx);
BN_bin2bn(seed, 64, alpha); // seed is in BigEndian
BN_mod(alpha, alpha, q, ctx); // % q
BN_add(alpha, alpha, priv); // alpha = alpha + priv
// a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod q
BN_mod (blindedPriv, alpha, q, ctx); // % q
BN_CTX_end (ctx);
BN_CTX_free (ctx);
BN_mod(blindedPriv, alpha, q, ctx); // % q
BN_CTX_end(ctx);
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)
{
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);
EC_POINT_set_affine_coordinates_GFp (group, p, x, y, NULL);
EC_POINT * p1 = BlindPublicKeyECDSA (group, p, seed);
EC_POINT_free (p);
EC_POINT_get_affine_coordinates_GFp (group, p1, x, y, NULL);
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);
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);
EC_POINT_set_affine_coordinates_GFp(group, p, x, y, NULL);
EC_POINT *p1 = BlindPublicKeyECDSA(group, p, seed);
EC_POINT_free(p);
EC_POINT_get_affine_coordinates_GFp(group, p1, x, y, NULL);
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);
}
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);
BN_free (a);
i2p::crypto::bn2buf (a1, blindedPriv, publicKeyLen/2);
auto p = EC_POINT_new (group);
BN_CTX * ctx = BN_CTX_new ();
EC_POINT_mul (group, p, a1, nullptr, nullptr, ctx); // B*a1
BN_CTX_free (ctx);
BN_free (a1);
BIGNUM * x = BN_new(), * y = BN_new();
EC_POINT_get_affine_coordinates_GFp (group, p, x, y, NULL);
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);
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);
BN_free(a);
i2p::crypto::bn2buf(a1, blindedPriv, publicKeyLen / 2);
auto p = EC_POINT_new(group);
BN_CTX *ctx = BN_CTX_new();
EC_POINT_mul(group, p, a1, nullptr, nullptr, ctx); // B*a1
BN_CTX_free(ctx);
BN_free(a1);
BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp(group, p, x, y, NULL);
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);
}
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:
{
EC_GROUP *group = nullptr;
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);
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);
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);
group = EC_GROUP_new_by_curve_name(NID_secp521r1);
break;
}
default:
LogPrint (eLogError, "Blinding: Signature type ", (int)sigType, " is not ECDSA");
LogPrint(eLogError, "Blinding: Signature type ", (int) sigType, " is not ECDSA");
}
if (group)
{
blind (publicKeyLength, group, key, seed, std::forward<Args>(args)...);
EC_GROUP_free (group);
if (group) {
blind(publicKeyLength, group, key, seed, std::forward<Args>(args)...);
EC_GROUP_free(group);
}
return publicKeyLength;
}
@ -138,198 +134,196 @@ namespace data
const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth):
m_IsClientAuth (clientAuth)
{
BlindedPublicKey::BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth) :
m_IsClientAuth(clientAuth) {
if (!identity) return;
auto len = identity->GetSigningPublicKeyLen ();
m_PublicKey.resize (len);
memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len);
m_SigType = identity->GetSigningKeyType ();
auto len = identity->GetSigningPublicKeyLen();
m_PublicKey.resize(len);
memcpy(m_PublicKey.data(), identity->GetSigningPublicKeyBuffer(), len);
m_SigType = identity->GetSigningKeyType();
if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11
else
m_BlindedSigType = m_SigType;
}
BlindedPublicKey::BlindedPublicKey (const std::string& b33):
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later
BlindedPublicKey::BlindedPublicKey(const std::string &b33) :
m_SigType(0) // 0 means invalid, we can't blind DSA, set it later
{
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)
{
LogPrint (eLogError, "Blinding: Malformed b33 ", b33);
size_t l = i2p::data::Base32ToByteStream(b33.c_str(), b33.length(), addr, 40);
if (l < 32) {
LogPrint(eLogError, "Blinding: Malformed b33 ", b33);
return;
}
uint32_t checksum = crc32 (0, addr + 3, l - 3);
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)
{
auto len = blindedVerifier->GetPublicKeyLen ();
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
LogPrint (eLogError, "Blinding: Unknown signature type ", (int)m_SigType, " in b33");
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier(i2p::data::IdentityEx::CreateVerifier(m_SigType));
if (blindedVerifier) {
auto len = blindedVerifier->GetPublicKeyLen();
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
LogPrint(eLogError, "Blinding: Unknown signature type ", (int) m_SigType, " in b33");
}
std::string BlindedPublicKey::ToB33 () const
{
if (m_PublicKey.size () > 32) return ""; // assume 25519
uint8_t addr[35]; char str[60]; // TODO: define actual length
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 flags = 0;
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
addr[0] = flags; // flags
addr[1] = m_SigType; // sig type
addr[2] = m_BlindedSigType; // blinded sig type
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ());
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ());
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);
auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60);
return std::string (str, str + l);
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 ());
uint16_t stA = htobe16(GetSigType());
// stA1 = signature type of blinded A, 2 bytes big endian
uint16_t stA1 = htobe16 (GetBlindedSigType ());
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);
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
{
uint16_t stA = htobe16 (GetSigType ()), stA1 = htobe16 (GetBlindedSigType ());
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);
i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed);
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);
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:
i2p::crypto::GetEd25519 ()->BlindPublicKey (GetPublicKey (), seed, blindedKey);
i2p::crypto::GetEd25519()->BlindPublicKey(GetPublicKey(), seed, blindedKey);
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break;
default:
LogPrint (eLogError, "Blinding: Can't blind signature type ", (int)m_SigType);
LogPrint(eLogError, "Blinding: Can't blind signature type ", (int) m_SigType);
}
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);
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);
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);
i2p::crypto::Ed25519::ExpandPrivateKey(priv, exp);
i2p::crypto::GetEd25519()->BlindPrivateKey(exp, seed, blindedPriv, blindedPub);
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break;
}
default:
LogPrint (eLogError, "Blinding: Can't blind signature type ", (int)m_SigType);
LogPrint(eLogError, "Blinding: Can't blind signature type ", (int) m_SigType);
}
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 ());
for (const auto& it: bufs)
SHA256_Update (&ctx, it.first, it.second);
SHA256_Final (hash, &ctx);
SHA256_Init(&ctx);
SHA256_Update(&ctx, p.c_str(), p.length());
for (const auto &it: bufs)
SHA256_Update(&ctx, it.first, it.second);
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
{
publicKeyLength = GetBlindedKey(date, blinded);
else {
char currentDate[9];
i2p::util::GetCurrentDate (currentDate);
publicKeyLength = GetBlindedKey (currentDate, blinded);
i2p::util::GetCurrentDate(currentDate);
publicKeyLength = GetBlindedKey(currentDate, blinded);
}
if (publicKeyLength)
{
auto stA1 = htobe16 (m_BlindedSigType);
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
LogPrint (eLogError, "Blinding: Blinded key type ", (int)m_BlindedSigType, " is not supported");
SHA256_Init(&ctx);
SHA256_Update(&ctx, (const uint8_t *) &stA1, 2);
SHA256_Update(&ctx, blinded, publicKeyLength);
SHA256_Final((uint8_t *) hash, &ctx);
} 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;
BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
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
BlindedPublicKey(const std::string &b33); // from b33 without .b32.i2p
std::string ToB33() const;
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
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
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 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;
private:
@ -49,7 +56,7 @@ namespace data
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
bool m_IsClientAuth = false;
};
}
}
}
#endif

View file

@ -11,50 +11,42 @@
#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()
{
delete [] m_Data;
~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);
if(m_Data[idx] & mask) return false; // filter hit
if (m_Data[idx] & mask) return false; // filter hit
m_Data[idx] |= mask;
return true;
}
/** @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
@ -64,14 +56,13 @@ namespace util
bm <<= (i % 8);
}
uint8_t * m_Data;
uint8_t *m_Data;
std::size_t m_Size;
};
BloomFilterPtr BloomFilter(std::size_t capacity)
{
BloomFilterPtr BloomFilter(std::size_t capacity) {
return std::make_shared<DecayingBloomFilter>(capacity);
}
}
}
}

View file

@ -8,32 +8,32 @@
#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;
virtual bool Add(const uint8_t *data, std::size_t len) = 0;
/** @brief optionally decay old entries */
virtual void Decay() = 0;
};
typedef std::shared_ptr<IBloomFilter> BloomFilterPtr;
typedef std::shared_ptr <IBloomFilter> BloomFilterPtr;
/** @brief create bloom filter */
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8);
}
}
}
#endif

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]);
@ -54,5 +53,5 @@ namespace cpu
LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled"));
}
}
}
}

View file

@ -9,15 +9,13 @@
#ifndef LIBI2PD_CPU_H
#define LIBI2PD_CPU_H
namespace i2p
{
namespace cpu
{
namespace i2p {
namespace cpu {
extern bool aesni;
extern bool avx;
void Detect(bool AesSwitch, bool AvxSwitch, bool force);
}
}
}
#endif

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];
@ -36,37 +31,36 @@ uint32_t u8t32le(const uint8_t * p)
value = (value << 8) | p[0];
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);
@ -78,10 +72,9 @@ void block (Chacha20State &input, int rounds)
}
x += input;
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;
@ -92,18 +85,15 @@ void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t *
state.data[12] = htole32 (counter);
for (size_t i = 0; i < 3; i++)
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,24 +104,21 @@ 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;
}
buf[j] ^= state.block.data[j - i];
}
}
}
}
} // namespace chacha
} // namespace crypto
} // namespace chacha
} // namespace crypto
} // namespace i2p
#endif

View file

@ -10,6 +10,7 @@
*/
#ifndef LIBI2PD_CHACHA20_H
#define LIBI2PD_CHACHA20_H
#include <cstdint>
#include <cstring>
#include <inttypes.h>
@ -17,55 +18,55 @@
#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
{
Chacha20Block () {};
Chacha20Block (Chacha20Block &&) = delete;
struct Chacha20Block {
Chacha20Block() {};
Chacha20Block(Chacha20Block &&) = delete;
uint8_t data[blocksize];
void operator << (const Chacha20State & st);
void operator<<(const Chacha20State &st);
};
struct Chacha20State
{
Chacha20State (): offset (0) {};
Chacha20State (Chacha20State &&) = delete;
struct Chacha20State {
Chacha20State() : offset(0) {};
Chacha20State & operator += (const Chacha20State & other)
{
for(int i = 0; i < 16; i++)
Chacha20State(Chacha20State &&) = delete;
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
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
} // namespace i2p
#endif

View file

@ -24,45 +24,65 @@
using namespace boost::program_options;
namespace i2p {
namespace config {
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;
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)
{
ThrowFatal ("Error while parsing arguments: ", e.what());
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())
{
ThrowFatal ("Missing or unreadable config file: ", path);
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)
{
ThrowFatal ("Error while parsing config file: ", e.what());
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,18 +479,16 @@ 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)
{
return GetOptionAsAny (name.c_str (), value);
bool GetOptionAsAny(const std::string &name, boost::any &value) {
return GetOptionAsAny(name.c_str(), value);
}
} // namespace config
} // namespace config
} // namespace i2p

View file

@ -25,7 +25,7 @@
*/
namespace i2p {
namespace config {
namespace config {
extern boost::program_options::variables_map m_Options;
/**
@ -49,7 +49,7 @@ namespace config {
*
* Other exceptions will be passed to higher level.
*/
void ParseCmdline(int argc, char* argv[], bool ignoreUnknown = false);
void ParseCmdline(int argc, char *argv[], bool ignoreUnknown = false);
/**
* @brief Load and parse given config file
@ -64,7 +64,7 @@ namespace config {
*
* Other exceptions will be passed to higher level.
*/
void ParseConfig(const std::string& path);
void ParseConfig(const std::string &path);
/**
* @brief Used to combine options from cmdline, config and default values
@ -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,13 +88,13 @@ namespace config {
}
template<typename T>
bool GetOption(const std::string& name, T& value)
{
return GetOption (name.c_str (), 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);
bool GetOptionAsAny(const char *name, boost::any &value);
bool GetOptionAsAny(const std::string &name, boost::any &value);
/**
* @brief Set value of given parameter
@ -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;
@ -121,7 +119,7 @@ namespace config {
* @return true if value set to default, false otherwise
*/
bool IsDefault(const char *name);
}
}
}
#endif // CONFIG_H

File diff suppressed because it is too large Load diff

View file

@ -50,53 +50,58 @@
# endif
#endif
namespace i2p
{
namespace crypto
{
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
namespace i2p {
namespace crypto {
bool bn2buf(const BIGNUM *bn, uint8_t *buf, size_t len);
// DSA
DSA * CreateDSA ();
DSA *CreateDSA();
// RSA
const BIGNUM * GetRSAE ();
const BIGNUM *GetRSAE();
// DH
class DHKeys
{
class DHKeys {
public:
DHKeys ();
~DHKeys ();
DHKeys();
void GenerateKeys ();
const uint8_t * GetPublicKey () const { return m_PublicKey; };
void Agree (const uint8_t * pub, uint8_t * shared);
~DHKeys();
void GenerateKeys();
const uint8_t *GetPublicKey() const { return m_PublicKey; };
void Agree(const uint8_t *pub, uint8_t *shared);
private:
DH * m_DH;
DH *m_DH;
uint8_t m_PublicKey[256];
};
// x25519
class X25519Keys
{
class X25519Keys {
public:
X25519Keys ();
X25519Keys (const uint8_t * priv, const uint8_t * pub); // if pub is null, derive from priv
~X25519Keys ();
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);
X25519Keys(const uint8_t *priv, const uint8_t *pub); // if pub is null, derive from priv
~X25519Keys();
bool IsElligatorIneligible () const { return m_IsElligatorIneligible; }
void SetElligatorIneligible () { m_IsElligatorIneligible = true; }
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:
@ -105,40 +110,42 @@ namespace crypto
EVP_PKEY_CTX * m_Ctx;
EVP_PKEY * m_Pkey;
#else
BN_CTX * m_Ctx;
BN_CTX *m_Ctx;
uint8_t m_PrivateKey[32];
#endif
bool m_IsElligatorIneligible = false; // true if definitely ineligible
};
// 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 GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
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 GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
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);
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
void operator^=(const ChipherBlock &other) // XOR
{
if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ?
if (!(((size_t) buf | (size_t) other.buf) & 0x03)) // multiple of 4 ?
{
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,23 +159,25 @@ namespace crypto
{
public:
AESAlignedBuffer ()
{
AESAlignedBuffer() {
m_Buf = m_UnalignedBuffer;
uint8_t rem = ((size_t)m_Buf) & 0x0f;
uint8_t rem = ((size_t) m_Buf) & 0x0f;
if (rem)
m_Buf += (16 - rem);
}
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; };
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:
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
uint8_t * m_Buf;
uint8_t *m_Buf;
};
@ -192,14 +201,15 @@ namespace crypto
#ifdef __AES__
class ECBEncryption: public ECBCryptoAESNI
#else
class ECBEncryption
#endif
{
public:
void SetKey (const AESKey& key);
void SetKey(const AESKey &key);
void Encrypt(const ChipherBlock * in, ChipherBlock * out);
void Encrypt(const ChipherBlock *in, ChipherBlock *out);
private:
AES_KEY m_Key;
@ -208,32 +218,36 @@ 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);
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); };
CBCEncryption() { memset((uint8_t *) m_LastBlock, 0, 16); };
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); };
void SetKey(const AESKey &key) { m_ECBEncryption.SetKey(key); }; // 32 bytes
void SetIV(const uint8_t *iv) { memcpy((uint8_t *) m_LastBlock, iv, 16); }; // 16 bytes
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
void Encrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
ECBEncryption & ECB() { return m_ECBEncryption; }
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; }
private:
@ -242,21 +256,22 @@ namespace crypto
ECBEncryption m_ECBEncryption;
};
class CBCDecryption
{
class CBCDecryption {
public:
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
CBCDecryption() { memset((uint8_t *) m_IV, 0, 16); };
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); };
void SetKey(const AESKey &key) { m_ECBDecryption.SetKey(key); }; // 32 bytes
void SetIV(const uint8_t *iv) { memcpy((uint8_t *) m_IV, iv, 16); }; // 16 bytes
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
void Decrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
ECBDecryption & ECB() { return m_ECBDecryption; }
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; }
private:
@ -268,13 +283,12 @@ namespace crypto
{
public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
{
m_LayerEncryption.SetKey (layerKey);
m_IVEncryption.SetKey (ivKey);
void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
m_LayerEncryption.SetKey(layerKey);
m_IVEncryption.SetKey(ivKey);
}
void Encrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
void Encrypt(const uint8_t *in, uint8_t *out); // 1024 bytes (16 IV + 1008 data)
private:
@ -286,13 +300,12 @@ namespace crypto
{
public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey)
{
m_LayerDecryption.SetKey (layerKey);
m_IVDecryption.SetKey (ivKey);
void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
m_LayerDecryption.SetKey(layerKey);
m_IVDecryption.SetKey(ivKey);
}
void Decrypt (const uint8_t * in, uint8_t * out); // 1024 bytes (16 IV + 1008 data)
void Decrypt(const uint8_t *in, uint8_t *out); // 1024 bytes (16 IV + 1008 data)
private:
@ -301,108 +314,142 @@ 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);
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);
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);
};
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router)
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2)
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (SSU2)
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
void InitNoiseNState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_N (tunnels, router)
void InitNoiseXKState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_XK (NTCP2)
void InitNoiseXKState1(NoiseSymmetricState &state, const uint8_t *pub); // Noise_XK (SSU2)
void InitNoiseIKState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_IK (ratchets)
// init and terminate
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force);
void TerminateCrypto ();
}
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)
{
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;
}
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;
}
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;
}
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
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;
}
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;
}
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{ *pr = sig->r; *ps = sig->s; }
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;
}
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;
}
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 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 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;
}
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;
}
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 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;
}
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
{ return pkey->pkey.rsa; }
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
*pr = sig->r;
*ps = sig->s;
}
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 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;
}
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;
}
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;
}
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;
}
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 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,175 +11,154 @@
#include "Gost.h"
#include "CryptoKey.h"
namespace i2p
{
namespace crypto
{
ElGamalEncryptor::ElGamalEncryptor (const uint8_t * pub)
{
memcpy (m_PublicKey, pub, 256);
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)
{
ElGamalEncrypt (m_PublicKey, data, encrypted);
void ElGamalEncryptor::Encrypt(const uint8_t *data, uint8_t *encrypted) {
ElGamalEncrypt(m_PublicKey, data, encrypted);
}
ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv)
{
memcpy (m_PrivateKey, priv, 256);
ElGamalDecryptor::ElGamalDecryptor(const uint8_t *priv) {
memcpy(m_PrivateKey, priv, 256);
}
bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data)
{
return ElGamalDecrypt (m_PrivateKey, encrypted, data);
bool ElGamalDecryptor::Decrypt(const uint8_t *encrypted, uint8_t *data) {
return ElGamalDecrypt(m_PrivateKey, encrypted, data);
}
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);
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);
}
ECIESP256Encryptor::~ECIESP256Encryptor ()
{
if (m_Curve) EC_GROUP_free (m_Curve);
if (m_PublicKey) EC_POINT_free (m_PublicKey);
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);
ECIESEncrypt(m_Curve, m_PublicKey, data, encrypted);
}
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(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 ()
{
if (m_Curve) EC_GROUP_free (m_Curve);
if (m_PrivateKey) BN_free (m_PrivateKey);
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 ECIESDecrypt(m_Curve, m_PrivateKey, encrypted, data);
return false;
}
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;
GenerateECIESKeyPair (curve, key, p);
bn2buf (key, priv, 32);
RAND_bytes (priv + 32, 224);
BN_free (key);
BIGNUM * x = BN_new (), * y = BN_new ();
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, NULL);
bn2buf (x, pub, 32);
bn2buf (y, pub + 32, 32);
RAND_bytes (pub + 64, 192);
EC_POINT_free (p);
BN_free (x); BN_free (y);
EC_GROUP_free (curve);
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;
GenerateECIESKeyPair(curve, key, p);
bn2buf(key, priv, 32);
RAND_bytes(priv + 32, 224);
BN_free(key);
BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp(curve, p, x, y, NULL);
bn2buf(x, pub, 32);
bn2buf(y, pub + 32, 32);
RAND_bytes(pub + 64, 192);
EC_POINT_free(p);
BN_free(x);
BN_free(y);
EC_GROUP_free(curve);
}
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);
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);
}
ECIESGOSTR3410Encryptor::~ECIESGOSTR3410Encryptor ()
{
if (m_PublicKey) EC_POINT_free (m_PublicKey);
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);
ECIESEncrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PublicKey, data, encrypted);
}
ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv)
{
m_PrivateKey = BN_bin2bn (priv, 32, nullptr);
ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor(const uint8_t *priv) {
m_PrivateKey = BN_bin2bn(priv, 32, nullptr);
}
ECIESGOSTR3410Decryptor::~ECIESGOSTR3410Decryptor ()
{
if (m_PrivateKey) BN_free (m_PrivateKey);
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 ECIESDecrypt(GetGOSTR3410Curve(eGOSTR3410CryptoProA)->GetGroup(), m_PrivateKey, encrypted, data);
return false;
}
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub)
{
auto& curve = GetGOSTR3410Curve (eGOSTR3410CryptoProA);
EC_POINT * p = nullptr;
BIGNUM * key = nullptr;
GenerateECIESKeyPair (curve->GetGroup (), key, p);
bn2buf (key, priv, 32);
RAND_bytes (priv + 32, 224);
BN_free (key);
BIGNUM * x = BN_new (), * y = BN_new ();
EC_POINT_get_affine_coordinates_GFp (curve->GetGroup (), p, x, y, NULL);
bn2buf (x, pub, 32);
bn2buf (y, pub + 32, 32);
RAND_bytes (pub + 64, 192);
EC_POINT_free (p);
BN_free (x); BN_free (y);
void CreateECIESGOSTR3410RandomKeys(uint8_t *priv, uint8_t *pub) {
auto &curve = GetGOSTR3410Curve(eGOSTR3410CryptoProA);
EC_POINT *p = nullptr;
BIGNUM *key = nullptr;
GenerateECIESKeyPair(curve->GetGroup(), key, p);
bn2buf(key, priv, 32);
RAND_bytes(priv + 32, 224);
BN_free(key);
BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp(curve->GetGroup(), p, x, y, NULL);
bn2buf(x, pub, 32);
bn2buf(y, pub + 32, 32);
RAND_bytes(pub + 64, 192);
EC_POINT_free(p);
BN_free(x);
BN_free(y);
}
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub)
{
memcpy (m_PublicKey, pub, 32);
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor(const uint8_t *pub) {
memcpy(m_PublicKey, pub, 32);
}
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub)
{
memcpy (pub, m_PublicKey, 32);
void ECIESX25519AEADRatchetEncryptor::Encrypt(const uint8_t *, uint8_t *pub) {
memcpy(pub, m_PublicKey, 32);
}
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic)
{
m_StaticKeys.SetPrivateKey (priv, calculatePublic);
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor(const uint8_t *priv, bool calculatePublic) {
m_StaticKeys.SetPrivateKey(priv, calculatePublic);
}
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret)
{
return m_StaticKeys.Agree (epub, 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);
memcpy (pub, k.GetPublicKey (), 32);
k.GenerateKeys();
k.GetPrivateKey(priv);
memcpy(pub, k.GetPublicKey(), 32);
}
}
}
}

View file

@ -12,47 +12,48 @@
#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;
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
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
};
// ElGamal
class ElGamalEncryptor: public CryptoKeyEncryptor // for destination
class ElGamalEncryptor : public CryptoKeyEncryptor // for destination
{
public:
ElGamalEncryptor (const uint8_t * pub);
void Encrypt (const uint8_t * data, uint8_t * encrypted) override; // 222 bytes data, 514 bytes encrypted
ElGamalEncryptor(const uint8_t *pub);
void Encrypt(const uint8_t *data, uint8_t *encrypted) override; // 222 bytes data, 514 bytes encrypted
private:
uint8_t m_PublicKey[256];
};
class ElGamalDecryptor: public CryptoKeyDecryptor // for destination
class ElGamalDecryptor : public CryptoKeyDecryptor // for destination
{
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; };
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; };
private:
@ -61,79 +62,86 @@ 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;
ECIESP256Encryptor(const uint8_t *pub);
~ECIESP256Encryptor();
void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
private:
EC_GROUP * m_Curve;
EC_POINT * m_PublicKey;
EC_GROUP *m_Curve;
EC_POINT *m_PublicKey;
};
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; };
ECIESP256Decryptor(const uint8_t *priv);
~ECIESP256Decryptor();
bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
size_t GetPublicKeyLen() const override { return 64; };
private:
EC_GROUP * m_Curve;
BIGNUM * m_PrivateKey;
EC_GROUP *m_Curve;
BIGNUM *m_PrivateKey;
};
void CreateECIESP256RandomKeys (uint8_t * priv, uint8_t * pub);
void CreateECIESP256RandomKeys(uint8_t *priv, uint8_t *pub);
// 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;
ECIESGOSTR3410Encryptor(const uint8_t *pub);
~ECIESGOSTR3410Encryptor();
void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
private:
EC_POINT * m_PublicKey;
EC_POINT *m_PublicKey;
};
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; };
ECIESGOSTR3410Decryptor(const uint8_t *priv);
~ECIESGOSTR3410Decryptor();
bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
size_t GetPublicKeyLen() const override { return 64; };
private:
BIGNUM * m_PrivateKey;
BIGNUM *m_PrivateKey;
};
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub);
void CreateECIESGOSTR3410RandomKeys(uint8_t *priv, uint8_t *pub);
// 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;
ECIESX25519AEADRatchetEncryptor(const uint8_t *pub);
~ECIESX25519AEADRatchetEncryptor() {};
void Encrypt(const uint8_t *, uint8_t *pub) override;
// copies m_PublicKey to pub
private:
@ -141,24 +149,27 @@ 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;
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 (); };
size_t GetPublicKeyLen() const override { return 32; };
const uint8_t *GetPubicKey() const { return m_StaticKeys.GetPublicKey(); };
private:
X25519Keys m_StaticKeys;
};
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
}
void CreateECIESX25519AEADRatchetRandomKeys(uint8_t *priv, uint8_t *pub);
}
}
#endif

View file

@ -14,119 +14,110 @@
#include "Destination.h"
#include "Datagram.h"
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)
{
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) {
if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator);
m_Deflator.reset(new i2p::data::GzipDeflator);
auto identityLen = m_Owner->GetIdentity ()->GetFullLen ();
m_From.resize (identityLen);
m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen);
m_Signature.resize (m_Owner->GetIdentity ()->GetSignatureLen ());
auto identityLen = m_Owner->GetIdentity()->GetFullLen();
m_From.resize(identityLen);
m_Owner->GetIdentity()->ToBuffer(m_From.data(), identityLen);
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);
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);
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
m_Owner->Sign (payload, len, m_Signature.data ());
m_Owner->Sign(hash, 32, m_Signature.data());
} 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}},
fromPort, toPort, false, !session->IsRatchets ()); // datagram
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 ();
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 ();
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
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
verified = identity.Verify(hash, 32, signature);
} else
verified = identity.Verify(buf + headerLen, len - headerLen, signature);
if (verified)
{
if (verified) {
auto h = identity.GetIdentHash();
auto session = ObtainSession(h);
session->Ack();
auto r = FindReceiver(toPort);
if(r)
r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
if (r)
r(identity, fromPort, toPort, buf + headerLen, len - headerLen);
else
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort);
}
else
LogPrint (eLogWarning, "Datagram signature verification failed");
LogPrint(eLogWarning, "DatagramDestination: no receiver for port ", toPort);
} 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);
m_RawReceiver(fromPort, toPort, buf, len);
else
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
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,80 +126,71 @@ 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)
{
size_t uncompressedLen = m_Inflator.Inflate(buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen) {
if (isRaw)
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen);
HandleRawDatagram(fromPort, toPort, uncompressed, uncompressedLen);
else
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
else
LogPrint (eLogWarning, "Datagram: decompression failed");
HandleDatagram(fromPort, toPort, uncompressed, uncompressedLen);
} 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)
{
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) {
size_t size;
auto msg = m_I2NPMsgsPool.AcquireShared ();
uint8_t * buf = msg->GetPayload ();
auto msg = m_I2NPMsgsPool.AcquireShared();
uint8_t *buf = msg->GetPayload();
buf += 4; // reserve for length
if (m_Gzip && m_Deflator)
size = m_Deflator->Deflate (payloads, buf, msg->maxLen - msg->len);
size = m_Deflator->Deflate(payloads, buf, msg->maxLen - msg->len);
else
size = i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len);
size = i2p::data::GzipNoCompression(payloads, buf, msg->maxLen - msg->len);
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
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
msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData, 0, checksum);
}
else
msg->FillI2NPMessageHeader(eI2NPData, 0, checksum);
} else
msg = nullptr;
return msg;
}
void DatagramDestination::CleanUp ()
{
if (m_Sessions.empty ()) return;
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
it->second->Stop();
it = m_Sessions.erase(it); // we are expired
} 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);
if (itr == m_Sessions.end()) {
// not found, create new session
session = std::make_shared<DatagramSession>(m_Owner, identity);
session->Start ();
session->Start();
m_Sessions[identity] = session;
} else {
session = itr->second;
@ -216,47 +198,41 @@ 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)
{
if(item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
for (auto &item: m_Sessions) {
if (item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
}
return nullptr;
}
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) :
const i2p::data::IdentHash &remoteIdent) :
m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent),
m_RequestingLS(false)
{
m_RequestingLS(false) {
}
void DatagramSession::Start ()
{
m_LastUse = i2p::util::GetMillisecondsSinceEpoch ();
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 ())
if (msg || m_SendQueue.empty())
m_SendQueue.push_back(msg);
// flush queue right away if full
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE)
FlushSendQueue();
}
DatagramSession::Info DatagramSession::GetSessionInfo() const
{
if(!m_RoutingSession)
DatagramSession::Info DatagramSession::GetSessionInfo() const {
if (!m_RoutingSession)
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
@ -264,134 +240,115 @@ namespace datagram
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto lease = routingPath->remoteLease;
auto tunnel = routingPath->outboundTunnel;
if(lease)
{
if(tunnel)
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)
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
if (IsRatchets ())
SendMsg (nullptr); // send empty message in case if we have some data to send
if (path)
path->updateTime = i2p::util::GetSecondsSinceEpoch();
if (IsRatchets())
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
for (auto &it: m_PendingRoutingSessions)
if (it->GetOwner() && m_RoutingSession->IsReadyToSend()) // found established session
{
m_RoutingSession = it;
m_PendingRoutingSessions.clear ();
m_PendingRoutingSessions.clear();
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);
if (!m_RoutingSession->GetOwner() || !m_RoutingSession->IsReadyToSend())
m_PendingRoutingSessions.push_back(m_RoutingSession);
}
}
auto path = m_RoutingSession->GetSharedRoutingPath();
if (path && m_RoutingSession->IsRatchets () &&
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)
{
m_RoutingSession->SetSharedRoutingPath (nullptr);
if (path && m_RoutingSession->IsRatchets() &&
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);
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
m_RoutingSession->SetSharedRoutingPath (nullptr);
}
else
{
} else
m_RoutingSession->SetSharedRoutingPath(nullptr);
} else {
// no remote lease set?
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
m_RoutingSession->SetSharedRoutingPath (nullptr);
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);
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,33 +358,31 @@ 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;
if (!ls) return;
// only update lease set if found and newer than previous lease set
uint64_t oldExpire = 0;
if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
if (m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
if (ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
}
void DatagramSession::FlushSendQueue ()
{
if (m_SendQueue.empty ()) return;
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);
}
m_SendQueue.clear();
}
}
}
}

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,15 +40,16 @@ 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 ();
void Start();
void Stop();
/** @brief ack the garlic routing path */
@ -59,24 +57,26 @@ 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 (); }
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) :
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);
if (ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
else IBGW = nullptr;
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
if (obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
else OBEP = nullptr;
}
};
@ -104,50 +104,79 @@ 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 ();
DatagramDestination(std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip);
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);
~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);
// 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 FlushSendQueue (std::shared_ptr<DatagramSession> session);
std::shared_ptr<DatagramSession> GetSession(const i2p::data::IdentHash &ident);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
void SendDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
uint16_t fromPort, uint16_t toPort);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
void SendRawDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
uint16_t fromPort, uint16_t toPort);
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 FlushSendQueue(std::shared_ptr<DatagramSession> session);
void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver () { m_RawReceiver = nullptr; };
void HandleDataMessagePayload(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len,
bool isRaw = false);
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
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 SetRawReceiver(const RawReceiver &receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver() { m_RawReceiver = nullptr; };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash &remote);
// clean up stale sessions
void CleanUp ();
void CleanUp();
private:
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
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);
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 */
Receiver FindReceiver(uint16_t port);
@ -159,7 +188,7 @@ namespace datagram
RawReceiver m_RawReceiver; // default
bool m_Gzip; // gzip compression of data messages
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
std::map<i2p::data::IdentHash, DatagramSession_ptr> m_Sessions;
std::mutex m_ReceiversMutex;
std::map<uint16_t, Receiver> m_ReceiversByPorts;
@ -168,7 +197,7 @@ namespace datagram
std::vector<uint8_t> m_From, m_Signature;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
};
}
}
}
#endif

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;
@ -86,16 +84,15 @@ namespace client
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
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> {
typedef std::function<void(std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
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
{
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
struct LeaseSetRequest {
LeaseSetRequest(boost::asio::io_service &service) : requestTime(0), requestTimeoutTimer(service) {};
std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer;
@ -104,85 +101,128 @@ 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)
{
for (auto& it: requestComplete) it (ls);
requestComplete.clear ();
void Complete(std::shared_ptr<i2p::data::LeaseSet> ls) {
for (auto &it: requestComplete) it(ls);
requestComplete.clear();
}
};
public:
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; };
LeaseSetDestination(boost::asio::io_service &service, bool isPublic,
const std::map<std::string, std::string> *params = nullptr);
virtual void Start ();
virtual void Stop ();
~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; };
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);
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() { return m_Pool; };
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);
void CancelDestinationRequest(const i2p::data::IdentHash &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; }
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 SubmitSessionKey(const uint8_t *key, const uint8_t *tag);
bool IsPublic () const { return m_IsPublic; };
void SetPublic (bool pub) { m_IsPublic = pub; };
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 HandleI2NPMessage(const uint8_t *buf, size_t len);
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
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 HandleDataMessage(const uint8_t *buf, size_t len) = 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 UpdateLeaseSet();
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;
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 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:
boost::asio::io_service& m_Service;
boost::asio::io_service &m_Service;
mutable std::mutex m_RemoteLeaseSetsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
@ -204,83 +244,122 @@ namespace client
public:
// 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; };
bool IsPerClientAuth () const { return m_AuthType > 0; };
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;
};
class ClientDestination: public LeaseSetDestination
{
struct EncryptionKey
{
bool IsPerClientAuth() const { return m_AuthType > 0; };
};
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); };
void GenerateKeys () { i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv, pub); };
void CreateDecryptor () { decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv); };
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); };
};
public:
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
ClientDestination(boost::asio::io_service &service, const i2p::data::PrivateKeys &keys,
bool isPublic, const std::map<std::string, std::string> *params = nullptr);
void Start ();
void Stop ();
~ClientDestination();
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); };
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; };
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> GetStreamingDestination (int port = 0) const;
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination (int port);
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 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; }
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);
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;
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 ();
void CleanupDestination();
// I2CP
void HandleDataMessage (const uint8_t * buf, size_t len);
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
void HandleDataMessage(const uint8_t *buf, size_t len);
void CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels);
private:
std::shared_ptr<ClientDestination> GetSharedFromThis () {
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
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);
void PersistTemporaryKeys(EncryptionKey *keys, bool isSingleKey);
void ReadAuthKey(const std::string &group, const std::map<std::string, std::string> *params);
private:
@ -292,7 +371,7 @@ namespace client
bool m_IsStreamingAnswerPings;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination;
i2p::datagram::DatagramDestination *m_DatagramDestination;
int m_RefCounter; // how many clients(tunnels) use this destination
boost::asio::deadline_timer m_ReadyChecker;
@ -302,22 +381,25 @@ namespace client
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
bool DeleteStream (uint32_t recvStreamID);
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 ();
RunnableClientDestination(const i2p::data::PrivateKeys &keys, bool isPublic,
const std::map<std::string, std::string> *params = nullptr);
void Start ();
void Stop ();
~RunnableClientDestination();
void Start();
void Stop();
};
}
}
}
#endif

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,23 +37,30 @@ 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 () {};
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);
virtual ~RatchetTagSet() {};
int GetTagSetID () const { return m_TagSetID; };
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
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,24 +73,29 @@ namespace garlic
};
class ECIESX25519AEADRatchetSession;
class ReceiveRatchetTagSet: public RatchetTagSet,
public std::enable_shared_from_this<ReceiveRatchetTagSet>
{
class ReceiveRatchetTagSet : public RatchetTagSet,
public std::enable_shared_from_this<ReceiveRatchetTagSet> {
public:
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false):
m_Session (session), m_IsNS (isNS) {};
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; };
bool IsNS() const { return m_IsNS; };
void Expire ();
bool IsExpired (uint64_t ts) const;
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession() { return m_Session; };
virtual bool IsIndexExpired (int index) const;
virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index);
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,23 +105,22 @@ namespace garlic
uint64_t m_ExpirationTimestamp = 0;
};
class SymmetricKeyTagSet: public ReceiveRatchetTagSet
{
class SymmetricKeyTagSet : public ReceiveRatchetTagSet {
public:
SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key);
SymmetricKeyTagSet(GarlicDestination *destination, const uint8_t *key);
bool IsIndexExpired (int index) const { return false; };
bool HandleNextMessage (uint8_t * buf, size_t len, int index);
bool IsIndexExpired(int index) const { return false; };
bool HandleNextMessage(uint8_t *buf, size_t len, int index);
private:
GarlicDestination * m_Destination;
GarlicDestination *m_Destination;
uint8_t m_Key[32];
};
enum ECIESx25519BlockType
{
enum ECIESx25519BlockType {
eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1,
eECIESx25519BlkTermination = 4,
@ -127,12 +136,10 @@ namespace garlic
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession,
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
@ -151,59 +157,92 @@ namespace garlic
public:
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS);
~ECIESX25519AEADRatchetSession ();
ECIESX25519AEADRatchetSession(GarlicDestination *owner, bool attachLeaseSetNS);
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);
~ECIESX25519AEADRatchetSession();
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
bool HandleNextMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
int index = 0);
void Terminate () { m_IsTerminated = true; }
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
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));
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 CheckExpired(uint64_t ts); // true is expired
bool CanBeRestarted(uint64_t ts) const {
return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT;
}
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; };
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);
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);
private:
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const;
bool GenerateEphemeralKeysAndEncode(uint8_t *buf); // buf is 32 bytes
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 HandleNewIncomingSession(const uint8_t *buf, size_t len);
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);
bool HandleNewOutgoingSessionReply(uint8_t *buf, size_t len);
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);
bool
HandleExistingSessionMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
int index);
void GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags);
void NewNextSendRatchet ();
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);
void GenerateMoreReceiveTags(std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags);
void NewNextSendRatchet();
private:
@ -224,30 +263,34 @@ namespace garlic
public:
// for HTTP only
int GetState () const { return (int)m_State; }
i2p::data::IdentHash GetDestination () const
{
return m_Destination ? *m_Destination : i2p::data::IdentHash ();
int GetState() const { return (int) m_State; }
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; };
RouterIncomingRatchetSession(const i2p::crypto::NoiseSymmetricState &initState);
bool HandleNextMessage(const uint8_t *buf, size_t len);
i2p::crypto::NoiseSymmetricState &GetCurrentNoiseState() { return m_CurrentNoiseState; };
private:
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);
}
}
#endif

View file

@ -11,474 +11,461 @@
#include "Crypto.h"
#include "Ed25519.h"
namespace i2p
{
namespace crypto
{
Ed25519::Ed25519 ()
{
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * tmp = BN_new ();
namespace i2p {
namespace crypto {
Ed25519::Ed25519() {
BN_CTX *ctx = BN_CTX_new();
BIGNUM *tmp = BN_new();
q = BN_new ();
q = BN_new();
// 2^255-19
BN_set_bit (q, 255); // 2^255
BN_sub_word (q, 19);
BN_set_bit(q, 255); // 2^255
BN_sub_word(q, 19);
l = BN_new ();
l = BN_new();
// 2^252 + 27742317777372353535851937790883648493
BN_set_bit (l, 252);
two_252_2 = BN_dup (l);
BN_dec2bn (&tmp, "27742317777372353535851937790883648493");
BN_add (l, l, tmp);
BN_sub_word (two_252_2, 2); // 2^252 - 2
BN_set_bit(l, 252);
two_252_2 = BN_dup(l);
BN_dec2bn(&tmp, "27742317777372353535851937790883648493");
BN_add(l, l, tmp);
BN_sub_word(two_252_2, 2); // 2^252 - 2
// -121665*inv(121666)
d = BN_new ();
BN_set_word (tmp, 121666);
BN_mod_inverse (tmp, tmp, q, ctx);
BN_set_word (d, 121665);
BN_set_negative (d, 1);
BN_mod_mul (d, d, tmp, q, ctx);
d = BN_new();
BN_set_word(tmp, 121666);
BN_mod_inverse(tmp, tmp, q, ctx);
BN_set_word(d, 121665);
BN_set_negative(d, 1);
BN_mod_mul(d, d, tmp, q, ctx);
// 2^((q-1)/4)
I = BN_new ();
BN_free (tmp);
tmp = BN_dup (q);
BN_sub_word (tmp, 1);
BN_div_word (tmp, 4);
BN_set_word (I, 2);
BN_mod_exp (I, I, tmp, q, ctx);
BN_free (tmp);
I = BN_new();
BN_free(tmp);
tmp = BN_dup(q);
BN_sub_word(tmp, 1);
BN_div_word(tmp, 4);
BN_set_word(I, 2);
BN_mod_exp(I, I, tmp, q, ctx);
BN_free(tmp);
// 4*inv(5)
BIGNUM * By = BN_new ();
BN_set_word (By, 5);
BN_mod_inverse (By, By, q, ctx);
BN_mul_word (By, 4);
BIGNUM * Bx = RecoverX (By, ctx);
BN_mod (Bx, Bx, q, ctx); // % q
BN_mod (By, By, q, ctx); // % q
BIGNUM *By = BN_new();
BN_set_word(By, 5);
BN_mod_inverse(By, By, q, ctx);
BN_mul_word(By, 4);
BIGNUM *Bx = RecoverX(By, ctx);
BN_mod(Bx, Bx, q, ctx); // % q
BN_mod(By, By, q, ctx); // % q
// precalculate Bi256 table
Bi256Carry = { Bx, By }; // B
for (int i = 0; i < 32; i++)
{
Bi256Carry = {Bx, By}; // B
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
Bi256[i][j] = Sum(Bi256[i][j - 1], Bi256[i][0], ctx); // (256+j+1)^i*B
Bi256Carry = Bi256[i][127];
for (int j = 0; j < 128; j++) // add first point 128 more times
Bi256Carry = Sum (Bi256Carry, Bi256[i][0], ctx);
Bi256Carry = Sum(Bi256Carry, Bi256[i][0], ctx);
}
BN_CTX_free (ctx);
BN_CTX_free(ctx);
}
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)
{
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) {
for (int i = 0; i < 32; i++)
for (int j = 0; j < 128; j++)
Bi256[i][j] = other.Bi256[i][j];
}
Ed25519::~Ed25519 ()
{
BN_free (q);
BN_free (l);
BN_free (d);
BN_free (I);
BN_free (two_252_2);
Ed25519::~Ed25519() {
BN_free(q);
BN_free(l);
BN_free(d);
BN_free(I);
BN_free(two_252_2);
}
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::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
{
return DecodePoint (buf, ctx);
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
{
EncodePoint (Normalize (publicKey, ctx), buf);
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
{
BN_CTX * ctx = BN_CTX_new ();
BIGNUM * h = DecodeBN<64> (digest);
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
// B*S = R + PK*h => R = B*S - PK*h
// we don't decode R, but encode (B*S - PK*h)
auto Bs = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx); // B*S;
BN_mod (h, h, l, ctx); // public key is multiple of B, but B%l = 0
auto PKh = Mul (publicKey, h, ctx); // PK*h
auto Bs = MulB(signature + EDDSA25519_SIGNATURE_LENGTH / 2, ctx); // B*S;
BN_mod(h, h, l, ctx); // public key is multiple of B, but B%l = 0
auto PKh = Mul(publicKey, h, ctx); // PK*h
uint8_t diff[32];
EncodePoint (Normalize (Sum (Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded
bool passed = !memcmp (signature, diff, 32); // R
BN_free (h);
BN_CTX_free (ctx);
EncodePoint(Normalize(Sum(Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded
bool passed = !memcmp(signature, diff, 32); // R
BN_free(h);
BN_CTX_free(ctx);
if (!passed)
LogPrint (eLogError, "25519 signature verification failed");
LogPrint(eLogError, "25519 signature verification failed");
return passed;
}
void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded,
const uint8_t * buf, size_t len, uint8_t * signature) const
{
BN_CTX * bnCtx = BN_CTX_new ();
void Ed25519::Sign(const uint8_t *expandedPrivateKey, const uint8_t *publicKeyEncoded,
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, buf, len); // data
SHA512_Init(&ctx);
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
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
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
BIGNUM * h = DecodeBN<64> (digest);
SHA512_Init(&ctx);
SHA512_Update(&ctx, R, EDDSA25519_SIGNATURE_LENGTH / 2); // R
SHA512_Update(&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update(&ctx, buf, len); // data
SHA512_Final(digest, &ctx);
BIGNUM *h = DecodeBN<64>(digest);
// S = (r + h*a) % l
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (expandedPrivateKey); // left half of expanded key
BN_mod_mul (h, h, a, l, bnCtx); // %l
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_CTX_free (bnCtx);
BIGNUM *a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH>(expandedPrivateKey); // left half of expanded key
BN_mod_mul(h, h, a, l, bnCtx); // %l
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_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
{
BN_CTX * bnCtx = BN_CTX_new ();
void Ed25519::SignRedDSA(const uint8_t *privateKey, const uint8_t *publicKeyEncoded,
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];
RAND_bytes (T, 80);
RAND_bytes(T, 80);
// calculate r = H*(T || publickey || data)
SHA512_CTX ctx;
SHA512_Init (&ctx);
SHA512_Update (&ctx, T, 80);
SHA512_Update (&ctx, publicKeyEncoded, 32);
SHA512_Update (&ctx, buf, len); // data
SHA512_Init(&ctx);
SHA512_Update(&ctx, T, 80);
SHA512_Update(&ctx, publicKeyEncoded, 32);
SHA512_Update(&ctx, buf, len); // data
uint8_t digest[64];
SHA512_Final (digest, &ctx);
BIGNUM * r = DecodeBN<64> (digest);
BN_mod (r, r, l, bnCtx); // % l
EncodeBN (r, digest, 32);
SHA512_Final(digest, &ctx);
BIGNUM *r = DecodeBN<64>(digest);
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
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), 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);
// calculate S
SHA512_Init (&ctx);
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
BIGNUM * h = DecodeBN<64> (digest);
SHA512_Init(&ctx);
SHA512_Update(&ctx, R, EDDSA25519_SIGNATURE_LENGTH / 2); // R
SHA512_Update(&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update(&ctx, buf, len); // data
SHA512_Final(digest, &ctx);
BIGNUM *h = DecodeBN<64>(digest);
// S = (r + h*a) % l
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (privateKey);
BN_mod_mul (h, h, a, l, bnCtx); // %l
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_CTX_free (bnCtx);
BIGNUM *a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH>(privateKey);
BN_mod_mul(h, h, a, l, bnCtx); // %l
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_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)
// t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2)
BIGNUM * x3 = BN_new (), * y3 = BN_new (), * z3 = BN_new (), * t3 = BN_new ();
BIGNUM *x3 = BN_new(), *y3 = BN_new(), *z3 = BN_new(), *t3 = BN_new();
BN_mul (x3, p1.x, p2.x, ctx); // A = x1*x2
BN_mul (y3, p1.y, p2.y, ctx); // B = y1*y2
BN_mul(x3, p1.x, p2.x, ctx); // A = x1*x2
BN_mul(y3, p1.y, p2.y, ctx); // B = y1*y2
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); }
BN_mul (t3, t1, t2, ctx);
BN_mul (t3, t3, d, ctx); // C = d*t1*t2
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);
}
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
BN_mul(z3, p1.z, p2.z, ctx); // D = z1*z2
else
BN_copy (z3, p1.z); // D = z1
}
else
{
BN_copy(z3, p1.z); // D = z1
} else {
if (p2.z)
BN_copy (z3, p2.z); // D = z2
BN_copy(z3, p2.z); // D = z2
else
BN_one (z3); // D = 1
BN_one(z3); // D = 1
}
BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx);
BN_add (E, p1.x, p1.y);
BN_add (F, p2.x, p2.y);
BN_mul (E, E, F, ctx); // (x1 + y1)*(x2 + y2)
BN_sub (E, E, x3);
BN_sub (E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B
BN_sub (F, z3, t3); // F = D - C
BN_add (G, z3, t3); // G = D + C
BN_add (H, y3, x3); // H = B + A
BIGNUM *E = BN_CTX_get(ctx), *F = BN_CTX_get(ctx), *G = BN_CTX_get(ctx), *H = BN_CTX_get(ctx);
BN_add(E, p1.x, p1.y);
BN_add(F, p2.x, p2.y);
BN_mul(E, E, F, ctx); // (x1 + y1)*(x2 + y2)
BN_sub(E, E, x3);
BN_sub(E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B
BN_sub(F, z3, t3); // F = D - C
BN_add(G, z3, t3); // G = D + C
BN_add(H, y3, x3); // H = B + A
BN_mod_mul (x3, E, F, q, ctx); // x3 = E*F
BN_mod_mul (y3, G, H, q, ctx); // y3 = G*H
BN_mod_mul (z3, F, G, q, ctx); // z3 = F*G
BN_mod_mul (t3, E, H, q, ctx); // t3 = E*H
BN_mod_mul(x3, E, F, q, ctx); // x3 = E*F
BN_mod_mul(y3, G, H, q, ctx); // y3 = G*H
BN_mod_mul(z3, F, G, q, ctx); // z3 = F*G
BN_mod_mul(t3, E, H, q, ctx); // t3 = E*H
BN_CTX_end (ctx);
BN_CTX_end(ctx);
return EDDSAPoint {x3, y3, z3, t3};
return EDDSAPoint{x3, y3, z3, t3};
}
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);
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);
BN_sqr (x2, p.x, ctx); // x2 = A = x^2
BN_sqr (y2, p.y, ctx); // y2 = B = y^2
BN_sqr(x2, p.x, ctx); // x2 = A = x^2
BN_sqr(y2, p.y, ctx); // y2 = B = y^2
if (p.t)
BN_sqr (t2, p.t, ctx); // t2 = t^2
else
{
BN_mul (t2, p.x, p.y, ctx); // t = x*y
BN_sqr (t2, t2, ctx); // t2 = t^2
BN_sqr(t2, p.t, ctx); // t2 = t^2
else {
BN_mul(t2, p.x, p.y, ctx); // t = x*y
BN_sqr(t2, t2, ctx); // t2 = t^2
}
BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2
BN_mul(t2, t2, d, ctx); // t2 = C = d*t^2
if (p.z)
BN_sqr (z2, p.z, ctx); // z2 = D = z^2
BN_sqr(z2, p.z, ctx); // z2 = D = z^2
else
BN_one (z2); // z2 = 1
BN_one(z2); // z2 = 1
BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx);
BIGNUM *E = BN_CTX_get(ctx), *F = BN_CTX_get(ctx), *G = BN_CTX_get(ctx), *H = BN_CTX_get(ctx);
// E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy
BN_mul (E, p.x, p.y, ctx);
BN_lshift1 (E, E); // E =2*x*y
BN_sub (F, z2, t2); // F = D - C
BN_add (G, z2, t2); // G = D + C
BN_add (H, y2, x2); // H = B + A
BN_mul(E, p.x, p.y, ctx);
BN_lshift1(E, E); // E =2*x*y
BN_sub(F, z2, t2); // F = D - C
BN_add(G, z2, t2); // G = D + C
BN_add(H, y2, x2); // H = B + A
BN_mod_mul (p.x, E, F, q, ctx); // x2 = E*F
BN_mod_mul (p.y, G, H, q, ctx); // y2 = G*H
if (!p.z) p.z = BN_new ();
BN_mod_mul (p.z, F, G, q, ctx); // z2 = F*G
if (!p.t) p.t = BN_new ();
BN_mod_mul (p.t, E, H, q, ctx); // t2 = E*H
BN_mod_mul(p.x, E, F, q, ctx); // x2 = E*F
BN_mod_mul(p.y, G, H, q, ctx); // y2 = G*H
if (!p.z) p.z = BN_new();
BN_mod_mul(p.z, F, G, q, ctx); // z2 = F*G
if (!p.t) p.t = BN_new();
BN_mod_mul(p.t, E, H, q, ctx); // t2 = E*H
BN_CTX_end (ctx);
BN_CTX_end(ctx);
}
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);
EDDSAPoint res {zero, one};
if (!BN_is_zero (e))
{
int bitCount = BN_num_bits (e);
for (int i = bitCount - 1; i >= 0; i--)
{
Double (res, ctx);
if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx);
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);
EDDSAPoint res{zero, one};
if (!BN_is_zero(e)) {
int bitCount = BN_num_bits(e);
for (int i = bitCount - 1; i >= 0; i--) {
Double(res, ctx);
if (BN_is_bit_set(e, i)) res = Sum(res, p, ctx);
}
}
return res;
}
EDDSAPoint Ed25519::MulB (const uint8_t * e, BN_CTX * ctx) const // B*e, e is 32 bytes Little Endian
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);
EDDSAPoint res {zero, one};
BIGNUM *zero = BN_new(), *one = BN_new();
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
{
res = Sum (res, -Bi256[i][255-x], ctx); // -Bi[256-x]
res = Sum(res, Bi256[i][x - 1], ctx);
else {
res = Sum(res, -Bi256[i][255 - x], ctx); // -Bi[256-x]
carry = true;
}
}
}
if (carry) res = Sum (res, Bi256Carry, ctx);
if (carry) res = Sum(res, Bi256Carry, ctx);
return res;
}
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
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
return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)};
} else
return EDDSAPoint{BN_dup(p.x), BN_dup(p.y)};
}
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
BN_sqr (y2, p.y, ctx); // y^2
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
BN_sqr(y2, p.y, ctx); // y^2
// y^2 - x^2 - 1 - d*x^2*y^2
BN_mul (tmp, d, x2, ctx);
BN_mul (tmp, tmp, y2, ctx);
BN_sub (tmp, y2, tmp);
BN_sub (tmp, tmp, x2);
BN_sub_word (tmp, 1);
BN_mod (tmp, tmp, q, ctx); // % q
bool ret = BN_is_zero (tmp);
BN_CTX_end (ctx);
BN_mul(tmp, d, x2, ctx);
BN_mul(tmp, tmp, y2, ctx);
BN_sub(tmp, y2, tmp);
BN_sub(tmp, tmp, x2);
BN_sub_word(tmp, 1);
BN_mod(tmp, tmp, q, ctx); // % q
bool ret = BN_is_zero(tmp);
BN_CTX_end(ctx);
return ret;
}
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
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
// xx = (y^2 -1)*inv(d*y^2 +1)
BN_mul (xx, d, y2, ctx);
BN_add_word (xx, 1);
BN_mod_inverse (xx, xx, q, ctx);
BN_sub_word (y2, 1);
BN_mul (xx, y2, xx, ctx);
BN_mul(xx, d, y2, ctx);
BN_add_word(xx, 1);
BN_mod_inverse(xx, xx, q, ctx);
BN_sub_word(y2, 1);
BN_mul(xx, y2, xx, ctx);
// x = srqt(xx) = xx^(2^252-2)
BIGNUM * x = BN_new ();
BN_mod_exp (x, xx, two_252_2, q, ctx);
BIGNUM *x = BN_new();
BN_mod_exp(x, xx, two_252_2, q, ctx);
// check (x^2 -xx) % q
BN_sqr (y2, x, ctx);
BN_mod_sub (y2, y2, xx, q, ctx);
if (!BN_is_zero (y2))
BN_mod_mul (x, x, I, q, ctx);
if (BN_is_odd (x))
BN_sub (x, q, x);
BN_CTX_end (ctx);
BN_sqr(y2, x, ctx);
BN_mod_sub(y2, y2, xx, q, ctx);
if (!BN_is_zero(y2))
BN_mod_mul(x, x, I, q, ctx);
if (BN_is_odd(x))
BN_sub(x, q, x);
BN_CTX_end(ctx);
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
for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH / 2; i++) // invert bytes
{
buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i];
buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i];
buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1 - i];
buf1[EDDSA25519_PUBLIC_KEY_LENGTH - 1 - i] = buf[i];
}
bool isHighestBitSet = buf1[0] & 0x80;
if (isHighestBitSet)
buf1[0] &= 0x7f; // clear highest bit
BIGNUM * y = BN_new ();
BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y);
BIGNUM * x = RecoverX (y, ctx);
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
EDDSAPoint p {x, y, z, t};
if (!IsOnCurve (p, ctx))
LogPrint (eLogError, "Decoded point is not on 25519");
BIGNUM *y = BN_new();
BN_bin2bn(buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y);
BIGNUM *x = RecoverX(y, ctx);
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
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
{
EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH);
if (BN_is_bit_set (p.x, 0)) // highest bit
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
for (size_t i = 0; i < len / 2; i++) // invert bytes
{
buf1[i] = buf[len -1 - i];
buf1[len -1 - i] = buf[i];
buf1[i] = buf[len - 1 - i];
buf1[len - 1 - i] = buf[i];
}
BIGNUM * res = BN_new ();
BN_bin2bn (buf1, len, res);
BIGNUM *res = BN_new();
BN_bin2bn(buf1, len, res);
return res;
}
void Ed25519::EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const
{
bn2buf (bn, buf, len);
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
for (size_t i = 0; i < len / 2; i++) // invert bytes
{
uint8_t tmp = buf[i];
buf[i] = buf[len -1 - i];
buf[len -1 - i] = tmp;
buf[i] = buf[len - 1 - i];
buf[len - 1 - i] = tmp;
}
}
#if !OPENSSL_X25519
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);
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);
unsigned int swap = 0;
auto bits = BN_num_bits (k);
while(bits)
{
auto bits = BN_num_bits(k);
while (bits) {
--bits;
auto k_t = BN_is_bit_set(k, bits) ? 1 : 0;
swap ^= k_t;
if (swap)
{
std::swap (x2, x3);
std::swap (z2, z3);
if (swap) {
std::swap(x2, x3);
std::swap(z2, z3);
}
swap = k_t;
BN_mod_sub(tmp0, x3, z3, q, ctx);
@ -500,108 +487,112 @@ namespace crypto
BN_mod_mul(z3, x1, z2, q, ctx);
BN_mod_mul(z2, tmp1, tmp0, q, ctx);
}
if (swap)
{
std::swap (x2, x3);
std::swap (z2, z3);
if (swap) {
std::swap(x2, x3);
std::swap(z2, z3);
}
BN_mod_inverse (z2, z2, q, ctx);
BIGNUM * res = BN_new (); // not from ctx
BN_mod_inverse(z2, z2, q, ctx);
BIGNUM *res = BN_new(); // not from ctx
BN_mod_mul(res, x2, z2, q, ctx);
BN_CTX_end (ctx);
BN_CTX_end(ctx);
return res;
}
void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const
{
BIGNUM * p1 = DecodeBN<32> (p);
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;
BIGNUM * n = DecodeBN<32> (k);
BIGNUM * q1 = ScalarMul (p1, n, ctx);
EncodeBN (q1, buf, 32);
BN_free (p1); BN_free (n); BN_free (q1);
memcpy(k, e, 32);
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);
}
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;
BIGNUM * n = DecodeBN<32> (k);
BIGNUM * q1 = ScalarMul (p1, n, ctx);
EncodeBN (q1, buf, 32);
BN_free (p1); BN_free (n); BN_free (q1);
memcpy(k, e, 32);
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);
}
#endif
void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded)
{
BN_CTX * ctx = BN_CTX_new ();
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
BN_mod (alpha, alpha, l, ctx); // % l
BIGNUM *alpha = DecodeBN<64>(seed); // seed is in Little Endian
BN_mod(alpha, alpha, l, ctx); // % l
uint8_t priv[32];
EncodeBN (alpha, priv, 32); // back to Little Endian
BN_free (alpha);
EncodeBN(alpha, priv, 32); // back to Little Endian
BN_free(alpha);
// A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha)
auto A1 = Sum (DecodePublicKey (pub, ctx), MulB (priv, ctx), ctx); // pub + B*alpha
EncodePublicKey (A1, blinded, ctx);
BN_CTX_free (ctx);
auto A1 = Sum(DecodePublicKey(pub, ctx), MulB(priv, ctx), ctx); // pub + B*alpha
EncodePublicKey(A1, blinded, ctx);
BN_CTX_free(ctx);
}
void Ed25519::BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub)
{
BN_CTX * ctx = BN_CTX_new ();
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
BN_mod (alpha, alpha, l, ctx); // % l
BIGNUM * p = DecodeBN<32> (priv); // priv is in Little Endian
BN_add (alpha, alpha, p); // alpha = alpha + priv
BIGNUM *alpha = DecodeBN<64>(seed); // seed is in Little Endian
BN_mod(alpha, alpha, l, ctx); // % l
BIGNUM *p = DecodeBN<32>(priv); // priv is in Little Endian
BN_add(alpha, alpha, p); // alpha = alpha + priv
// a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L
BN_mod (alpha, alpha, l, ctx); // % l
EncodeBN (alpha, blindedPriv, 32);
BN_mod(alpha, alpha, l, ctx); // % l
EncodeBN(alpha, blindedPriv, 32);
// A' = DERIVE_PUBLIC(a')
auto A1 = MulB (blindedPriv, ctx);
EncodePublicKey (A1, blindedPub, ctx);
BN_free (alpha); BN_free (p);
BN_CTX_free (ctx);
auto A1 = MulB(blindedPriv, ctx);
EncodePublicKey(A1, blindedPub, ctx);
BN_free(alpha);
BN_free(p);
BN_CTX_free(ctx);
}
void Ed25519::ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey)
{
SHA512 (key, EDDSA25519_PRIVATE_KEY_LENGTH, 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);
BN_CTX * ctx = BN_CTX_new ();
BN_mod (p, p, l, ctx); // % l
EncodeBN (p, priv, 32);
BN_CTX_free (ctx);
BN_free (p);
RAND_bytes(seed, 32);
BIGNUM *p = DecodeBN<32>(seed);
BN_CTX *ctx = BN_CTX_new();
BN_mod(p, p, l, ctx); // % l
EncodeBN(p, priv, 32);
BN_CTX_free(ctx);
BN_free(p);
}
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);
g_Ed25519.reset(c);
else
delete c;
}
return g_Ed25519;
}
}
}
}

View file

@ -13,127 +13,168 @@
#include <openssl/bn.h>
#include "Crypto.h"
namespace i2p
{
namespace crypto
{
struct EDDSAPoint
{
BIGNUM * x {nullptr};
BIGNUM * y {nullptr};
BIGNUM * z {nullptr};
BIGNUM * t {nullptr}; // projective coordinates
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); }
EDDSAPoint() {}
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(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);
}
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
{
BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL;
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)); };
return EDDSAPoint {x1, y1, z1, t1};
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 (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));
};
return EDDSAPoint{x1, y1, z1, t1};
}
};
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 ();
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;
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 ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const;
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
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;
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
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
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 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;
EDDSAPoint Sum(const EDDSAPoint &p1, const EDDSAPoint &p2, 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;
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;
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;
BIGNUM *ScalarMul(const BIGNUM *p, const BIGNUM *e, BN_CTX *ctx) const;
#endif
private:
BIGNUM * q, * l, * d, * I;
BIGNUM *q, *l, *d, *I;
// transient values
BIGNUM * two_252_2; // 2^252-2
BIGNUM *two_252_2; // 2^252-2
EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes
// if j > 128 we use 256 - j and carry 1 to next byte
// Bi256[0][0] = B, base point
EDDSAPoint Bi256Carry; // Bi256[32][0]
};
std::unique_ptr<Ed25519>& GetEd25519 ();
std::unique_ptr<Ed25519> &GetEd25519();
}
}
}
#endif

View file

@ -10,49 +10,60 @@
#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 ();
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
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
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 ();
BN_CTX *ctx = BN_CTX_new();
// calculate sqrt(-1)
sqrtn1 = BN_new ();
BN_set_word (sqrtn1, 2);
BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
sqrtn1 = BN_new();
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);
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);
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
uint8_t key1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
@ -61,38 +72,35 @@ 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
BN_sub (xA, p, xA); // p - (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);
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)
{
RAND_bytes (&randByte, 1);
if (random) {
RAND_bytes(&randByte, 1);
highY = randByte & 0x01;
}
BIGNUM * r = BN_CTX_get (ctx);
if (highY)
{
BN_mod_inverse (r, x, p, ctx);
BN_mod_mul (r, r, xA, p, ctx);
BIGNUM *r = BN_CTX_get(ctx);
if (highY) {
BN_mod_inverse(r, x, p, ctx);
BN_mod_mul(r, r, xA, p, ctx);
} else {
BN_mod_inverse(r, xA, p, ctx);
BN_mod_mul(r, r, x, p, ctx);
}
else
{
BN_mod_inverse (r, xA, p, ctx);
BN_mod_mul (r, r, x, p, ctx);
}
BN_mod_mul (r, r, iu, p, ctx);
BN_mod_mul(r, r, iu, p, ctx);
SquareRoot (r, r, ctx);
bn2buf (r, encoded, 32);
SquareRoot(r, r, ctx);
bn2buf(r, encoded, 32);
if (random)
encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte
@ -102,20 +110,18 @@ namespace crypto
encoded[i] = encoded[31 - i];
encoded[31 - i] = tmp;
}
}
else
} else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
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);
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
uint8_t encoded1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
@ -125,71 +131,70 @@ 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
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);
BN_mod_mul (v, v, u, p, ctx);
BN_add_word (v, 1);
BN_mod_inverse (v, v, p, ctx);
BN_mod_mul (v, v, nA, 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);
BN_mod_mul(v, v, nA, p, ctx);
BIGNUM * vpA = BN_CTX_get (ctx);
BN_add (vpA, v, A); // v + A
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);
BN_mod_mul (t, t, vpA, p, ctx);
BN_mod_add (t, 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);
int legendre = Legendre (t, ctx);
BIGNUM * x = BN_CTX_get (ctx);
int legendre = Legendre(t, ctx);
BIGNUM *x = BN_CTX_get(ctx);
if (legendre == 1)
BN_copy (x, v);
else
{
BN_sub (x, p, v);
BN_mod_sub (x, x, A, p, ctx);
BN_copy(x, v);
else {
BN_sub(x, p, v);
BN_mod_sub(x, x, A, p, ctx);
}
bn2buf (x, key, 32);
bn2buf(x, key, 32);
for (size_t i = 0; i < 16; i++) // To Little Endian
{
uint8_t tmp = key[i];
key[i] = key[31 - i];
key[31 - i] = tmp;
}
}
else
} else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return ret;
}
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)
BN_add_word (t, 1);
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)
BN_add_word(t, 1);
if (!BN_cmp (t, p))
BN_mod_mul (r, r, sqrtn1, p, ctx);
if (!BN_cmp(t, p))
BN_mod_mul(r, r, sqrtn1, p, ctx);
if (BN_cmp (r, p12) > 0) // r > (p-1)/2
BN_sub (r, p, r);
if (BN_cmp(r, p12) > 0) // r > (p-1)/2
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);
BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
BIGNUM *r = BN_CTX_get(ctx);
BN_mod_exp(r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
if (BN_is_word(r, 1))
return 1;
else if (BN_is_zero(r))
@ -198,17 +203,16 @@ 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);
g_Elligator.reset(el);
else
delete el;
}
return g_Elligator;
}
}
}
}

View file

@ -13,33 +13,33 @@
#include <memory>
#include <openssl/bn.h>
namespace i2p
{
namespace crypto
{
namespace i2p {
namespace crypto {
class Elligator2
{
class Elligator2 {
public:
Elligator2 ();
~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;
~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
void SquareRoot(const BIGNUM *x, BIGNUM *r, BN_CTX *ctx) const;
int Legendre(const BIGNUM *a, BN_CTX *ctx) const; // a/p
private:
BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu;
BIGNUM *p, *p38, *p12, *p14, *sqrtn1, *A, *nA, *u, *iu;
};
std::unique_ptr<Elligator2>& GetElligator ();
}
std::unique_ptr <Elligator2> &GetElligator();
}
}
#endif

View file

@ -21,7 +21,7 @@
#include "Garlic.h"
namespace i2p {
namespace fs {
namespace fs {
std::string appName = "i2pd";
std::string dataDir = "";
std::string certsDir = "";
@ -31,23 +31,23 @@ namespace fs {
std::string dirSep = "/";
#endif
const std::string & GetAppName () {
const std::string &GetAppName() {
return appName;
}
void SetAppName (const std::string& name) {
void SetAppName(const std::string &name) {
appName = name;
}
const std::string & GetDataDir () {
const std::string &GetDataDir() {
return dataDir;
}
const std::string & GetCertsDir () {
const std::string &GetCertsDir() {
return certsDir;
}
const std::string GetUTF8DataDir () {
const std::string GetUTF8DataDir() {
#ifdef _WIN32
boost::filesystem::wpath path (dataDir);
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8
@ -59,7 +59,7 @@ namespace fs {
#endif
}
void DetectDataDir(const std::string & cmdline_param, bool isService) {
void DetectDataDir(const std::string &cmdline_param, bool isService) {
// with 'datadir' option
if (cmdline_param != "") {
dataDir = cmdline_param;
@ -157,16 +157,13 @@ namespace fs {
#endif
}
void SetCertsDir(const std::string & cmdline_certsdir) {
if (cmdline_certsdir != "")
{
if (cmdline_certsdir[cmdline_certsdir.length()-1] == '/')
certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size()-1); // strip trailing slash
void SetCertsDir(const std::string &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;
@ -184,18 +181,18 @@ namespace fs {
if (!boost::filesystem::exists(tags))
boost::filesystem::create_directory(tags);
else
i2p::garlic::CleanUpTagsFiles ();
i2p::garlic::CleanUpTagsFiles();
return true;
}
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
bool ReadDir(const std::string &path, std::vector<std::string> &files) {
if (!boost::filesystem::exists(path))
return false;
boost::filesystem::directory_iterator it(path);
boost::filesystem::directory_iterator end;
for ( ; it != end; it++) {
for (; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
files.push_back(it->path().string());
@ -204,28 +201,26 @@ namespace fs {
return true;
}
bool Exists(const std::string & path) {
bool Exists(const std::string &path) {
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;
auto t = boost::filesystem::last_write_time (path, ec);
auto t = boost::filesystem::last_write_time(path, ec);
return ec ? 0 : t;
}
bool Remove(const std::string & path) {
bool Remove(const std::string &path) {
if (!boost::filesystem::exists(path))
return false;
return boost::filesystem::remove(path);
}
bool CreateDirectory (const std::string& path)
{
if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (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);
}
@ -234,7 +229,7 @@ namespace fs {
root = path + i2p::fs::dirSep + name;
}
bool HashedStorage::Init(const char * chars, size_t count) {
bool HashedStorage::Init(const char *chars, size_t count) {
if (!boost::filesystem::exists(root)) {
boost::filesystem::create_directories(root);
}
@ -250,7 +245,7 @@ namespace fs {
return true;
}
std::string HashedStorage::Path(const std::string & ident) const {
std::string HashedStorage::Path(const std::string &ident) const {
std::string safe_ident = ident;
std::replace(safe_ident.begin(), safe_ident.end(), '/', '-');
std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-');
@ -263,31 +258,30 @@ namespace fs {
return t.str();
}
void HashedStorage::Remove(const std::string & ident) {
void HashedStorage::Remove(const std::string &ident) {
std::string path = Path(ident);
if (!boost::filesystem::exists(path))
return;
boost::filesystem::remove(path);
}
void HashedStorage::Traverse(std::vector<std::string> & files) {
Iterate([&files] (const std::string & fname) {
void HashedStorage::Traverse(std::vector<std::string> &files) {
Iterate([&files](const std::string &fname) {
files.push_back(fname);
});
}
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;
for ( ; it != end; it++) {
if (!boost::filesystem::is_regular_file( it->status() ))
for (; it != end; it++) {
if (!boost::filesystem::is_regular_file(it->status()))
continue;
const std::string & t = it->path().string();
const std::string &t = it->path().string();
v(t);
}
}
} // fs
} // fs
} // i2p

View file

@ -16,7 +16,7 @@
#include <functional>
namespace i2p {
namespace fs {
namespace fs {
extern std::string dirSep;
/**
@ -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,35 +47,44 @@ namespace fs {
public:
typedef std::function<void(const std::string &)> FilenameVisitor;
HashedStorage(const char *n, const char *p1, const char *p2, const char *s):
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; }
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);
void SetPlace(const std::string &path);
/** path to file with given ident */
std::string Path(const std::string & ident) const;
std::string Path(const std::string &ident) const;
/** remove file by ident */
void Remove(const std::string & 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);
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 ();
const std::string &GetAppName();
/** @brief Set application name, affects autodetection of datadir */
void SetAppName (const std::string& name);
void SetAppName(const std::string &name);
/** @brief Returns datadir path */
const std::string & GetDataDir();
const std::string &GetDataDir();
/** @brief Returns certsdir path */
const std::string & GetCertsDir();
const std::string &GetCertsDir();
/** @brief Returns datadir path in UTF-8 encoding */
const std::string GetUTF8DataDir();
@ -93,7 +101,7 @@ namespace fs {
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
* Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/
*/
void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
void DetectDataDir(const std::string &cmdline_datadir, bool isService = false);
/**
* @brief Set certsdir either from cmdline option or using autodetection
@ -106,7 +114,7 @@ namespace fs {
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates
* Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/i2pd/certificates
*/
void SetCertsDir(const std::string & cmdline_certsdir);
void SetCertsDir(const std::string &cmdline_certsdir);
/**
* @brief Create subdirectories inside datadir
@ -119,33 +127,33 @@ namespace fs {
* @param files Vector to store found files
* @return true on success and false if directory not exists
*/
bool ReadDir(const std::string & path, std::vector<std::string> & files);
bool ReadDir(const std::string &path, std::vector <std::string> &files);
/**
* @brief Remove file with given path
* @param path Absolute path to file
* @return true on success, false if file not exists, throws exception on error
*/
bool Remove(const std::string & path);
bool Remove(const std::string &path);
/**
* @brief Check existence of file
* @param path Absolute path to file
* @return true if file exists, false otherwise
*/
bool Exists(const std::string & path);
bool Exists(const std::string &path);
uint32_t GetLastUpdateTime (const std::string & path); // seconds since epoch
uint32_t GetLastUpdateTime(const std::string &path); // seconds since epoch
bool CreateDirectory (const std::string& path);
bool CreateDirectory(const std::string &path);
template<typename T>
void _ExpandPath(std::stringstream & path, T c) {
void _ExpandPath(std::stringstream &path, T c) {
path << i2p::fs::dirSep << c;
}
template<typename T, typename ... Other>
void _ExpandPath(std::stringstream & path, T c, Other ... other) {
void _ExpandPath(std::stringstream &path, T c, Other ... other) {
_ExpandPath(path, c);
_ExpandPath(path, other ...);
}
@ -168,16 +176,15 @@ 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 ();
s << storage.GetRoot();
_ExpandPath(s, filenames...);
return s.str();
}
} // fs
} // fs
} // i2p
#endif // /* FS_H__ */

View file

@ -15,90 +15,75 @@
#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)
{
SSL_CTX * ctx = SSL_CTX_new (TLS_method ());
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
if (ret)
{
SSL * ssl = SSL_new (ctx);
X509 * cert = SSL_get_certificate (ssl);
if (cert)
{
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) {
SSL *ssl = SSL_new(ctx);
X509 *cert = SSL_get_certificate(ssl);
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)
{
X509_NAME_oneline(X509_get_issuer_name(cert), name, 100);
char *cn = strstr(name, "CN=");
if (cn) {
cn += 3;
char * family = strstr (cn, ".family");
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)
{
auto pkey = X509_get_pubkey(cert);
int keyType = EVP_PKEY_base_id(pkey);
switch (keyType) {
case EVP_PKEY_DSA:
// TODO:
break;
case EVP_PKEY_EC:
{
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
if (ecKey)
{
auto group = EC_KEY_get0_group (ecKey);
if (group)
{
int curve = EC_GROUP_get_curve_name (group);
if (curve == NID_X9_62_prime256v1)
{
case EVP_PKEY_EC: {
EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(pkey);
if (ecKey) {
auto group = EC_KEY_get0_group(ecKey);
if (group) {
int curve = EC_GROUP_get_curve_name(group);
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);
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);
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>();
verifier->SetPublicKey (signingKey);
verifier->SetPublicKey(signingKey);
} else
LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
else
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
EC_KEY_free (ecKey);
EC_KEY_free(ecKey);
}
break;
}
default:
LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
LogPrint(eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
}
EVP_PKEY_free (pkey);
EVP_PKEY_free(pkey);
if (verifier && cn)
m_SigningKeys.emplace (cn, std::make_pair(verifier, m_SigningKeys.size () + 1));
m_SigningKeys.emplace(cn, std::make_pair(verifier, m_SigningKeys.size() + 1));
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Family: Can't open certificate file ", filename);
SSL_CTX_free (ctx);
SSL_free(ssl);
} 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;
@ -109,91 +94,81 @@ namespace data
return;
}
for (const std::string & file : files) {
for (const std::string &file: files) {
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
LogPrint(eLogWarning, "Family: ignoring file ", file);
continue;
}
LoadCertificate (file);
LoadCertificate(file);
numCertificates++;
}
LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded");
LogPrint(eLogInfo, "Family: ", numCertificates, " certificates loaded");
}
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
const char * signature, const char * key) const
{
bool Families::VerifyFamily(const std::string &family, const IdentHash &ident,
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)
{
LogPrint (eLogError, "Family: ", family, " is too long");
size_t len = family.length(), signatureLen = strlen(signature);
if (len + 32 > 100) {
LogPrint(eLogError, "Family: ", family, " is too long");
return false;
}
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
memcpy(buf, family.c_str(), len);
memcpy(buf + len, (const uint8_t *) ident, 32);
len += 32;
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
auto it = m_SigningKeys.find (family);
if (it != m_SigningKeys.end ())
return it->second.first->Verify (buf, len, signatureBuf);
Base64ToByteStream(signature, signatureLen, signatureBuf, 64);
auto it = m_SigningKeys.find(family);
if (it != m_SigningKeys.end())
return it->second.first->Verify(buf, len, signatureBuf);
// TODO: process key
return true;
}
FamilyID Families::GetFamilyID (const std::string& family) const
{
auto it = m_SigningKeys.find (family);
if (it != m_SigningKeys.end ())
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)
{
SSL * ssl = SSL_new (ctx);
EVP_PKEY * pkey = SSL_get_privatekey (ssl);
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
if (ecKey)
{
auto group = EC_KEY_get0_group (ecKey);
if (group)
{
int curve = EC_GROUP_get_curve_name (group);
if (curve == NID_X9_62_prime256v1)
{
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
int ret = SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
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) {
auto group = EC_KEY_get0_group(ecKey);
if (group) {
int curve = EC_GROUP_get_curve_name(group);
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);
size_t len = family.length ();
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
i2p::crypto::bn2buf(EC_KEY_get0_private_key(ecKey), signingPrivateKey, 32);
i2p::crypto::ECDSAP256Signer signer(signingPrivateKey);
size_t len = family.length();
memcpy(buf, family.c_str(), len);
memcpy(buf + len, (const uint8_t *) ident, 32);
len += 32;
signer.Sign (buf, len, signature);
len = Base64EncodingBufferSize (64);
char * b64 = new char[len+1];
len = ByteStreamToBase64 (signature, 64, b64, len);
signer.Sign(buf, len, signature);
len = Base64EncodingBufferSize(64);
char *b64 = new char[len + 1];
len = ByteStreamToBase64(signature, 64, b64, len);
b64[len] = 0;
sig = b64;
delete[] b64;
}
else
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
} else
LogPrint(eLogWarning, "Family: elliptic curve ", curve, " is not supported");
}
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Family: Can't open keys file: ", filename);
SSL_CTX_free (ctx);
SSL_free(ssl);
} else
LogPrint(eLogError, "Family: Can't open keys file: ", filename);
SSL_CTX_free(ctx);
return sig;
}
}
}
}

View file

@ -15,34 +15,36 @@
#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;
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:
void LoadCertificate (const std::string& filename);
void LoadCertificate(const std::string &filename);
private:
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id)
};
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
std::string CreateFamilySignature(const std::string &family, const IdentHash &ident);
// return base64 signature of empty string in case of failure
}
}
}
#endif

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,37 +49,41 @@ 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>
{
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;
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;
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)
{
SetKey (key);
AESDecryption(const uint8_t *key) : m_Key(key) {
SetKey(key);
}
const i2p::crypto::AESKey& GetKey () const { return m_Key; };
const i2p::crypto::AESKey &GetKey() const { return m_Key; };
private:
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,
@ -107,45 +105,62 @@ 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
GarlicRoutingSession(GarlicDestination *owner, bool attachLeaseSet);
void SetLeaseSetUpdated ()
{
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() {
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);
bool IsLeaseSetNonConfirmed() const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
GarlicDestination * GetOwner () const { return m_Owner; }
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
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; }
LeaseSetUpdateStatus GetLeaseSetUpdateStatus() const { return m_LeaseSetUpdateStatus; }
std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg (uint32_t msgID);
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);
private:
GarlicDestination * m_Owner;
GarlicDestination *m_Owner;
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
@ -156,45 +171,53 @@ namespace garlic
public:
// for HTTP only
virtual size_t GetNumOutgoingTags () const { return 0; };
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
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags () { delete[] sessionTags; };
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;
SessionTag * sessionTags;
SessionTag *sessionTags;
uint32_t tagsCreationTime;
};
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);
ElGamalAESSession(const uint8_t *sessionKey, const SessionTag &sessionTag); // one time encryption
~ElGamalAESSession() {};
bool MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
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 CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
size_t CreateAESBlock(uint8_t *buf, std::shared_ptr<const I2NPMessage> msg);
void TagsConfirmed (uint32_t msgID);
UnconfirmedTags * GenerateSessionTags ();
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:
@ -210,73 +233,97 @@ namespace garlic
public:
// for HTTP only
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
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 ();
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);
void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
~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);
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);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
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 ();
void AddSessionKey(const uint8_t *key, const uint8_t *tag); // one tag
void AddECIESx25519Key(const uint8_t *key, uint64_t tag); // one tag
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);
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated ();
uint64_t AddECIESx25519SessionNextTag(ReceiveRatchetTagSetPtr tagset);
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
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
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const = 0;
protected:
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;
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (uint32_t msgID);
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;
void SaveTags ();
void LoadTags ();
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,
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);
void HandleGarlicPayload(uint8_t *buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
private:
@ -285,7 +332,7 @@ namespace garlic
std::mutex m_SessionsMutex;
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
uint8_t *m_PayloadBuffer; // for ECIESX25519AEADRatchet
// incoming
int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
@ -298,15 +345,23 @@ namespace garlic
public:
// 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; }
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; }
};
void CleanUpTagsFiles ();
void CleanUpTagsFiles();
}
}
}
#endif

View file

@ -14,133 +14,122 @@
#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)
{
m_KeyLen = BN_num_bytes (p);
BN_CTX * ctx = BN_CTX_new ();
m_Group = EC_GROUP_new_curve_GFp (p, a, b, ctx);
EC_POINT * P = EC_POINT_new (m_Group);
EC_POINT_set_affine_coordinates_GFp (m_Group, P, x, y, ctx);
EC_GROUP_set_generator (m_Group, P, q, nullptr);
EC_GROUP_set_curve_name (m_Group, NID_id_GostR3410_2001);
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);
EC_POINT *P = EC_POINT_new(m_Group);
EC_POINT_set_affine_coordinates_GFp(m_Group, P, x, y, ctx);
EC_GROUP_set_generator(m_Group, P, q, nullptr);
EC_GROUP_set_curve_name(m_Group, NID_id_GostR3410_2001);
EC_POINT_free(P);
BN_CTX_free (ctx);
BN_CTX_free(ctx);
}
GOSTR3410Curve::~GOSTR3410Curve ()
{
EC_GROUP_free (m_Group);
GOSTR3410Curve::~GOSTR3410Curve() {
EC_GROUP_free(m_Group);
}
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);
BN_CTX_free (ctx);
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);
BN_CTX_free(ctx);
return p;
}
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);
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 * p = EC_POINT_new (m_Group);
EC_POINT_set_affine_coordinates_GFp (m_Group, p, x, y, nullptr);
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)
{
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
BIGNUM * q = BN_CTX_get (ctx);
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);
EC_GROUP_get_order(m_Group, q, ctx);
BIGNUM * k = BN_CTX_get (ctx);
BN_rand_range (k, q); // 0 < k < q
EC_POINT * C = MulP (k); // C = k*P
GetXY (C, r, nullptr); // r = Cx
EC_POINT_free (C);
BN_mod_mul (s, r, priv, q, ctx); // (r*priv)%q
BIGNUM * tmp = BN_CTX_get (ctx);
BN_mod_mul (tmp, k, digest, q, ctx); // (k*digest)%q
BN_mod_add (s, s, tmp, q, ctx); // (r*priv+k*digest)%q
BN_CTX_end (ctx);
BN_CTX_free (ctx);
BIGNUM *k = BN_CTX_get(ctx);
BN_rand_range(k, q); // 0 < k < q
EC_POINT *C = MulP(k); // C = k*P
GetXY(C, r, nullptr); // r = Cx
EC_POINT_free(C);
BN_mod_mul(s, r, priv, q, ctx); // (r*priv)%q
BIGNUM *tmp = BN_CTX_get(ctx);
BN_mod_mul(tmp, k, digest, q, ctx); // (k*digest)%q
BN_mod_add(s, s, tmp, q, ctx); // (r*priv+k*digest)%q
BN_CTX_end(ctx);
BN_CTX_free(ctx);
}
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);
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);
EC_GROUP_get_order(m_Group, q, ctx);
BIGNUM * h = BN_CTX_get (ctx);
BN_mod (h, digest, q, ctx); // h = digest % q
BN_mod_inverse (h, h, q, ctx); // 1/h mod q
BIGNUM * z1 = BN_CTX_get (ctx);
BN_mod_mul (z1, s, h, q, ctx); // z1 = s/h
BIGNUM * z2 = BN_CTX_get (ctx);
BN_sub (z2, q, r); // z2 = -r
BN_mod_mul (z2, z2, h, q, ctx); // z2 = -r/h
EC_POINT * C = EC_POINT_new (m_Group);
EC_POINT_mul (m_Group, C, z1, pub, z2, ctx); // z1*P + z2*pub
BIGNUM * x = BN_CTX_get (ctx);
GetXY (C, x, nullptr); // Cx
BN_mod (x, x, q, ctx); // Cx % q
bool ret = !BN_cmp (x, r); // Cx = r ?
EC_POINT_free (C);
BN_CTX_end (ctx);
BN_CTX_free (ctx);
BIGNUM *h = BN_CTX_get(ctx);
BN_mod(h, digest, q, ctx); // h = digest % q
BN_mod_inverse(h, h, q, ctx); // 1/h mod q
BIGNUM *z1 = BN_CTX_get(ctx);
BN_mod_mul(z1, s, h, q, ctx); // z1 = s/h
BIGNUM *z2 = BN_CTX_get(ctx);
BN_sub(z2, q, r); // z2 = -r
BN_mod_mul(z2, z2, h, q, ctx); // z2 = -r/h
EC_POINT *C = EC_POINT_new(m_Group);
EC_POINT_mul(m_Group, C, z1, pub, z2, ctx); // z1*P + z2*pub
BIGNUM *x = BN_CTX_get(ctx);
GetXY(C, x, nullptr); // Cx
BN_mod(x, x, q, ctx); // Cx % q
bool ret = !BN_cmp(x, r); // Cx = r ?
EC_POINT_free(C);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
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))
{
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);
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)) {
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);
EC_GROUP_get_order(m_Group, q, ctx);
BIGNUM * h = BN_CTX_get (ctx);
BN_mod (h, digest, q, ctx); // h = digest % q
BN_sub (h, q, h); // h = -h
EC_POINT * H = EC_POINT_new (m_Group);
EC_POINT_mul (m_Group, H, nullptr, C, h, ctx); // -h*C
EC_POINT_add (m_Group, C, S, H, ctx); // s*P - h*C
EC_POINT_free (H);
EC_POINT_free (S);
BIGNUM * r1 = BN_CTX_get (ctx);
BN_mod_inverse (r1, r, q, ctx);
Q = EC_POINT_new (m_Group);
EC_POINT_mul (m_Group, Q, nullptr, C, r1, ctx); // (s*P - h*C)/r
BIGNUM *h = BN_CTX_get(ctx);
BN_mod(h, digest, q, ctx); // h = digest % q
BN_sub(h, q, h); // h = -h
EC_POINT *H = EC_POINT_new(m_Group);
EC_POINT_mul(m_Group, H, nullptr, C, h, ctx); // -h*C
EC_POINT_add(m_Group, C, S, H, ctx); // s*P - h*C
EC_POINT_free(H);
EC_POINT_free(S);
BIGNUM *r1 = BN_CTX_get(ctx);
BN_mod_inverse(r1, r, q, ctx);
Q = EC_POINT_new(m_Group);
EC_POINT_mul(m_Group, Q, nullptr, C, r1, ctx); // (s*P - h*C)/r
}
EC_POINT_free (C);
BN_CTX_end (ctx);
BN_CTX_free (ctx);
EC_POINT_free(C);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return Q;
}
static GOSTR3410Curve * CreateGOSTR3410Curve (GOSTR3410ParamSet paramSet)
{
static GOSTR3410Curve *CreateGOSTR3410Curve(GOSTR3410ParamSet paramSet) {
// a, b, p, q, x, y
static const char * params[eGOSTR3410NumParamSets][6] =
static const char *params[eGOSTR3410NumParamSets][6] =
{
{
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
@ -160,26 +149,30 @@ namespace crypto
} // tc26-2012-paramSetA-512
};
BIGNUM * a = nullptr, * b = nullptr, * p = nullptr, * q =nullptr, * x = nullptr, * y = nullptr;
BIGNUM *a = nullptr, *b = nullptr, *p = nullptr, *q = nullptr, *x = nullptr, *y = nullptr;
BN_hex2bn(&a, params[paramSet][0]);
BN_hex2bn(&b, params[paramSet][1]);
BN_hex2bn(&p, params[paramSet][2]);
BN_hex2bn(&q, params[paramSet][3]);
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);
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);
return curve;
}
static std::array<std::unique_ptr<GOSTR3410Curve>, eGOSTR3410NumParamSets> g_GOSTR3410Curves;
std::unique_ptr<GOSTR3410Curve>& GetGOSTR3410Curve (GOSTR3410ParamSet paramSet)
{
if (!g_GOSTR3410Curves[paramSet])
{
auto c = CreateGOSTR3410Curve (paramSet);
static std::array <std::unique_ptr<GOSTR3410Curve>, eGOSTR3410NumParamSets> g_GOSTR3410Curves;
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);
g_GOSTR3410Curves[paramSet].reset(c);
else
delete 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];
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];
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,189 +810,174 @@ 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]];
r ^= T2[buf[b+40]];
r ^= T3[buf[b+32]];
r ^= T4[buf[b+24]];
r ^= T5[buf[b+16]];
r ^= T6[buf[b+8]];
r = T0[buf[b + 56]];
r ^= T1[buf[b + 48]];
r ^= T2[buf[b + 40]];
r ^= T3[buf[b + 32]];
r ^= T4[buf[b + 24]];
r ^= T5[buf[b + 16]];
r ^= T6[buf[b + 8]];
r ^= T7[buf[b]];
res[b] = r;
}
memcpy (buf, res, 64);
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++)
{
res.F ();
k = k^C_[i];
k.F ();
res = k^res;
GOST3411Block res = k ^ m;
for (int i = 0; i < 12; i++) {
res.F();
k = k ^ C_[i];
k.F();
res = k ^ res;
}
return res;
}
};
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);
res = res^h;
res = res^m;
res.F();
res = res.E(m);
res = res ^ h;
res = res ^ m;
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);
memset (N.buf, 0, 64);
memset (s.buf, 0, 64);
memcpy(h.buf, iv, 64);
memset(N.buf, 0, 64);
memset(s.buf, 0, 64);
size_t l = len;
// stage 2
while (l >= 64)
{
memcpy (m.buf, buf + l - 64, 64); // TODO
h= gN (N, h, m);
N.Add (512);
while (l >= 64) {
memcpy(m.buf, buf + l - 64, 64); // TODO
h = gN(N, h, m);
N.Add(512);
s = m + s;
l -= 64;
}
// stage 3
size_t padding = 64 - l;
if (padding)
{
memset (m.buf, 0, padding - 1);
if (padding) {
memset(m.buf, 0, padding - 1);
m.buf[padding - 1] = 1;
}
memcpy (m.buf + padding, buf, l);
memcpy(m.buf + padding, buf, l);
h = gN (N, h, m);
N.Add (l*8);
h = gN(N, h, m);
N.Add(l * 8);
s = m + s;
GOST3411Block N0;
memset (N0.buf, 0, 64);
h = gN (N0, h, N);
h = gN (N0, h, s);
memset(N0.buf, 0, 64);
h = gN(N0, h, N);
h = gN(N0, h, s);
memcpy (digest, h.buf, 64);
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);
memset(iv, 1, 64);
uint8_t h[64];
H (iv, buf, len, h);
memcpy (digest, h, 32); // first half
H(iv, buf, len, h);
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);
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);
memset (ctx->N.buf, 0, 64);
memset (ctx->s.buf, 0, 64);
memset(iv, is512 ? 0 : 1, 64);
memcpy(ctx->h.buf, iv, 64);
memset(ctx->N.buf, 0, 64);
memset(ctx->s.buf, 0, 64);
ctx->len = 0;
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
{
size_t l = 64 - ctx->len;
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->m.buf[ctx->len + i] = buf[l - i - 1]; // invert
ctx->len += l;
len -= l;
buf += l;
ctx->h = gN (ctx->N, ctx->h, ctx->m);
ctx->N.Add (512);
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;
ctx->h = gN (ctx->N, ctx->h, ctx->m);
ctx->N.Add (512);
ctx->m.buf[i] = buf[63 - i]; // invert
len -= 64;
buf += 64;
ctx->h = gN(ctx->N, ctx->h, ctx->m);
ctx->N.Add(512);
ctx->s = ctx->m + ctx->s;
}
if (len > 0) // carry remaining
{
for (size_t i = 0; i < len; i++)
ctx->m.buf[i] = buf[len-i-1]; // invert
ctx->m.buf[i] = buf[len - i - 1]; // invert
}
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)
{
memset (m.buf, 0, padding - 1);
if (padding) {
memset(m.buf, 0, padding - 1);
m.buf[padding - 1] = 1;
}
memcpy (m.buf + padding, ctx->m.buf, ctx->len);
memcpy(m.buf + padding, ctx->m.buf, ctx->len);
ctx->h = gN (ctx->N, ctx->h, m);
ctx->N.Add (ctx->len*8);
ctx->h = gN(ctx->N, ctx->h, m);
ctx->N.Add(ctx->len * 8);
ctx->s = m + ctx->s;
GOST3411Block N0;
memset (N0.buf, 0, 64);
ctx->h = gN (N0, ctx->h, ctx->N);
ctx->h = gN (N0, ctx->h, ctx->s);
memset(N0.buf, 0, 64);
ctx->h = gN(N0, ctx->h, ctx->N);
ctx->h = gN(N0, ctx->h, ctx->s);
size_t sz = ctx->is512 ? 64 : 32;
for (size_t i = 0; i < sz; i++)
digest[i] = ctx->h.buf[sz - i - 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,42 +26,56 @@ namespace crypto
eGOSTR3410NumParamSets
};
class GOSTR3410Curve
{
class GOSTR3410Curve {
public:
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);
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;
~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;
private:
EC_GROUP * m_Group;
EC_GROUP *m_Group;
size_t m_KeyLen; // in bytes
};
std::unique_ptr<GOSTR3410Curve>& GetGOSTR3410Curve (GOSTR3410ParamSet paramSet);
std::unique_ptr <GOSTR3410Curve> &GetGOSTR3410Curve(GOSTR3410ParamSet paramSet);
// 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);
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);
}
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);
}
}
#endif

View file

@ -13,143 +13,122 @@
#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)
{
memset (&m_Inflator, 0, sizeof (m_Inflator));
inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
GzipInflator::GzipInflator() : m_IsDirty(false) {
memset(&m_Inflator, 0, sizeof(m_Inflator));
inflateInit2(&m_Inflator, MAX_WBITS + 16); // gzip
}
GzipInflator::~GzipInflator ()
{
inflateEnd (&m_Inflator);
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)
{
LogPrint (eLogError, "Gzip: Incorrect length");
size_t len = bufle16toh(in + 11);
if (len + 23 < inLen) {
LogPrint(eLogError, "Gzip: Incorrect length");
return 0;
}
if (len > outLen) len = outLen;
memcpy (out, in + 15, len);
memcpy(out, in + 15, len);
return len;
}
else
{
if (m_IsDirty) inflateReset (&m_Inflator);
} else {
if (m_IsDirty) inflateReset(&m_Inflator);
m_IsDirty = true;
m_Inflator.next_in = const_cast<uint8_t *>(in);
m_Inflator.avail_in = inLen;
m_Inflator.next_out = out;
m_Inflator.avail_out = outLen;
int err;
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
if ((err = inflate(&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
return outLen - m_Inflator.avail_out;
// else
LogPrint (eLogError, "Gzip: Inflate error ", err);
LogPrint(eLogError, "Gzip: Inflate error ", err);
return 0;
}
}
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];
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)
{
inflateEnd (&m_Inflator);
ret = inflate(&m_Inflator, Z_NO_FLUSH);
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
os.write((char *) out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
} while (!m_Inflator.avail_out); // more data to read
delete[] out;
}
void GzipInflator::Inflate (std::istream& in, std::ostream& out)
{
uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE];
while (!in.eof ())
{
in.read ((char *) buf, GZIP_CHUNK_SIZE);
Inflate (buf, in.gcount (), out);
void GzipInflator::Inflate(std::istream &in, std::ostream &out) {
uint8_t *buf = new uint8_t[GZIP_CHUNK_SIZE];
while (!in.eof()) {
in.read((char *) buf, GZIP_CHUNK_SIZE);
Inflate(buf, in.gcount(), out);
}
delete[] buf;
}
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
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
}
GzipDeflator::~GzipDeflator ()
{
deflateEnd (&m_Deflator);
GzipDeflator::~GzipDeflator() {
deflateEnd(&m_Deflator);
}
void GzipDeflator::SetCompressionLevel (int level)
{
deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
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)
{
if (m_IsDirty) deflateReset (&m_Deflator);
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);
m_Deflator.avail_in = inLen;
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;
}
// else
LogPrint (eLogError, "Gzip: Deflate error ", err);
LogPrint(eLogError, "Gzip: Deflate error ", err);
return 0;
}
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);
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)
{
auto flush = (it == bufs.back()) ? Z_FINISH : Z_NO_FLUSH;
err = deflate(&m_Deflator, flush);
if (err) {
if (flush && err == Z_STREAM_END) {
out[9] = 0xff; // OS is always unknown
return outLen - m_Deflator.avail_out;
}
@ -158,44 +137,42 @@ namespace data
offset = outLen - m_Deflator.avail_out;
}
// else
LogPrint (eLogError, "Gzip: Deflate error ", err);
LogPrint(eLogError, "Gzip: Deflate error ", err);
return 0;
}
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);
htole16buf (out + 11, inLen);
htole16buf (out + 13, 0xffff - inLen);
memcpy (out + 15, in, inLen);
htole32buf (out + inLen + 15, crc32 (0, in, inLen));
htole32buf (out + inLen + 19, inLen);
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);
htole16buf(out + 11, inLen);
htole16buf(out + 13, 0xffff - inLen);
memcpy(out + 15, in, inLen);
htole32buf(out + inLen + 15, crc32(0, in, inLen));
htole32buf(out + inLen + 19, inLen);
return inLen + 23;
}
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);
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;
memcpy (out + 15 + len1, it.first, it.second);
crc = crc32 (crc, it.first, it.second);
memcpy(out + 15 + len1, it.first, it.second);
crc = crc32(crc, it.first, it.second);
}
if (len > 0xffff) return 0;
htole32buf (out + len + 15, crc);
htole32buf (out + len + 19, len);
htole16buf (out + 11, len);
htole16buf (out + 13, 0xffff - len);
htole32buf(out + len + 15, crc);
htole32buf(out + len + 19, len);
htole16buf(out + 11, len);
htole16buf(out + 13, 0xffff - len);
return len + 23;
}
} // data
} // data
} // i2p

View file

@ -12,21 +12,21 @@
#include <zlib.h>
#include <vector>
namespace i2p
{
namespace data
{
class GzipInflator
{
namespace i2p {
namespace data {
class GzipInflator {
public:
GzipInflator ();
~GzipInflator ();
GzipInflator();
~GzipInflator();
size_t Inflate(const uint8_t *in, size_t inLen, uint8_t *out, size_t outLen);
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);
void Inflate(const uint8_t *in, size_t inLen, std::ostream &os);
void Inflate(std::istream &in, std::ostream &out);
private:
@ -34,16 +34,18 @@ namespace data
bool m_IsDirty;
};
class GzipDeflator
{
class GzipDeflator {
public:
GzipDeflator ();
~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);
~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:
@ -51,9 +53,10 @@ namespace data
bool m_IsDirty;
};
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
} // 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
} // data
} // i2p
#endif /* GZIP_H__ */

View file

@ -14,15 +14,14 @@
#include "Base.h"
#include "HTTP.h"
namespace i2p
{
namespace http
{
const std::vector<std::string> HTTP_METHODS = {
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 = {
const std::vector <std::string> HTTP_VERSIONS = {
"HTTP/1.0", "HTTP/1.1"
};
const std::vector<const char *> weekdays = {
@ -33,15 +32,15 @@ namespace http
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
inline bool is_http_version(const std::string & str) {
inline bool is_http_version(const std::string &str) {
return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
}
inline bool is_http_method(const std::string & str) {
inline bool is_http_method(const std::string &str) {
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
}
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) {
void strsplit(const std::string &line, std::vector <std::string> &tokens, char delim, std::size_t limit = 0) {
std::size_t count = 0;
std::stringstream ss(line);
std::string token;
@ -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();
@ -72,7 +70,7 @@ namespace http
return std::make_pair(line.substr(0, pos), line.substr(pos + len));
}
void gen_rfc7231_date(std::string & out) {
void gen_rfc7231_date(std::string &out) {
std::time_t now = std::time(nullptr);
char buf[128];
std::tm *tm = std::gmtime(&now);
@ -88,10 +86,10 @@ namespace http
return parse(url);
}
bool URL::parse(const std::string& url) {
bool URL::parse(const std::string &url) {
std::size_t pos_p = 0; /* < current parse position */
std::size_t pos_c = 0; /* < work position */
if(url.at(0) != '/' || pos_p > 0) {
if (url.at(0) != '/' || pos_p > 0) {
std::size_t pos_s = 0;
/* schema */
pos_c = url.find("://");
@ -108,7 +106,7 @@ namespace http
user = url.substr(pos_p, delim - pos_p);
delim += 1;
pass = url.substr(delim, pos_c - delim);
} else if(delim) {
} else if (delim) {
user = url.substr(pos_p, pos_c - pos_p);
}
pos_p = pos_c + 1;
@ -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 */
@ -135,7 +132,7 @@ namespace http
? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */
for (char c : port_str) {
for (char c: port_str) {
if (c < '0' || c > '9')
return false;
port *= 10;
@ -181,15 +178,15 @@ namespace http
return true;
}
bool URL::parse_query(std::map<std::string, std::string> & params) {
std::vector<std::string> tokens;
bool URL::parse_query(std::map <std::string, std::string> &params) {
std::vector <std::string> tokens;
strsplit(query, tokens, '&');
params.clear();
for (const auto& it : tokens) {
for (const auto &it: tokens) {
if (!it.length()) // empty
continue;
std::size_t eq = it.find ('=');
std::size_t eq = it.find('=');
if (eq != std::string::npos) {
auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
params.insert(e);
@ -224,12 +221,11 @@ namespace http
return out;
}
bool URL::is_i2p() const
{
return host.rfind(".i2p") == ( host.size() - 4 );
bool URL::is_i2p() const {
return host.rfind(".i2p") == (host.size() - 4);
}
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) {
void HTTPMsg::add_header(const char *name, std::string &value, bool replace) {
add_header(name, value.c_str(), replace);
}
@ -253,8 +249,10 @@ namespace http
return parse(str);
}
int HTTPReq::parse(const std::string& str) {
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
int HTTPReq::parse(const std::string &str) {
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;
@ -265,7 +263,7 @@ namespace http
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == REQ_LINE) {
std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
std::vector <std::string> tokens;
strsplit(line, tokens, ' ');
if (tokens.size() != 3)
return -1;
@ -280,13 +278,11 @@ 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)
headers.push_back (p);
if (p.first.length() > 0)
headers.push_back(p);
else
return -1;
}
@ -297,57 +293,48 @@ 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)
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)
{
headers.push_back (std::make_pair(name, 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)
{
for (auto& it : headers)
if (it.first == name)
{
void HTTPReq::UpdateHeader(const std::string &name, const std::string &value) {
for (auto &it: headers)
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 ();)
{
if (!it->first.compare(0, name.length (), name) && it->first != exempt)
it = headers.erase (it);
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
it++;
}
}
std::string HTTPReq::GetHeader (const std::string& name) const
{
for (auto& it : headers)
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())
@ -386,8 +371,10 @@ namespace http
return parse(str);
}
int HTTPRes::parse(const std::string& str) {
enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
int HTTPRes::parse(const std::string &str) {
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;
@ -397,7 +384,7 @@ namespace http
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == RES_LINE) {
std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens;
std::vector <std::string> tokens;
strsplit(line, tokens, ' ', 3);
if (tokens.size() != 3)
return -1;
@ -413,8 +400,8 @@ namespace http
} else {
std::string line = str.substr(pos, eol - pos);
auto p = parse_header_line(line);
if (p.first.length () > 0)
headers.insert (p);
if (p.first.length() > 0)
headers.insert(p);
else
return -1;
}
@ -438,7 +425,7 @@ namespace http
/* build response */
std::stringstream ss;
ss << version << " " << code << " " << status << CRLF;
for (auto & h : headers) {
for (auto &h: headers) {
ss << h.first << ": " << h.second << CRLF;
}
ss << CRLF;
@ -447,44 +434,77 @@ namespace http
return ss.str();
}
const char * HTTPCodeToStatus(int code) {
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,12 +514,10 @@ 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 ())
{
std::getline (in, hexLen);
while (!in.eof()) {
std::getline(in, hexLen);
errno = 0;
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
if (errno != 0)
@ -508,20 +526,19 @@ namespace http
return true; /* end of stream */
if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */
return false; /* too large chunk */
char * buf = new char[len];
in.read (buf, len);
out.write (buf, len);
char *buf = new char[len];
in.read(buf, len);
out.write(buf, len);
delete[] buf;
std::getline (in, hexLen); // read \r\n after chunk
std::getline(in, hexLen); // read \r\n after chunk
}
return true;
}
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass)
{
if (user.empty () && pass.empty ()) return "";
return "Basic " + i2p::data::ToBase64Standard (user + ":" + 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);
}
} // http
} // http
} // i2p

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 */
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;
@ -36,26 +33,27 @@ namespace http
std::string query;
std::string frag;
URL(): schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {};
URL() : schema(""), user(""), pass(""), host(""), port(0), path(""), query(""), frag("") {};
/**
* @brief Tries to parse url from string
* @return true on success, false on invalid url
*/
bool parse (const char *str, std::size_t len = 0);
bool parse (const std::string& url);
bool parse(const char *str, std::size_t len = 0);
bool parse(const std::string &url);
/**
* @brief Parse query part of url to key/value map
* @note Honestly, this should be implemented with std::multimap
*/
bool parse_query(std::map<std::string, std::string> & params);
bool parse_query(std::map <std::string, std::string> &params);
/**
* @brief Serialize URL structure to url
* @note Returns relative url if schema if empty, absolute url otherwise
*/
std::string to_string ();
std::string to_string();
/**
* @brief return true if the host is inside i2p
@ -63,26 +61,26 @@ namespace http
bool is_i2p() const;
};
struct HTTPMsg
{
std::map<std::string, std::string> headers;
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, 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
{
std::list<std::pair<std::string, std::string> > headers;
struct HTTPReq {
std::list <std::pair<std::string, std::string>> headers;
std::string version;
std::string method;
std::string uri;
HTTPReq (): version("HTTP/1.0"), method("GET"), uri("/") {};
HTTPReq() : version("HTTP/1.0"), method("GET"), uri("/") {};
/**
* @brief Tries to parse HTTP request from string
@ -90,17 +88,23 @@ 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);
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) { RemoveHeader (name, ""); };
std::string GetHeader (const std::string& name) const;
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) { RemoveHeader(name, ""); };
std::string GetHeader(const std::string &name) const;
};
struct HTTPRes : HTTPMsg {
@ -116,7 +120,7 @@ namespace http
*/
std::string body;
HTTPRes (): version("HTTP/1.1"), status("OK"), code(200) {}
HTTPRes() : version("HTTP/1.1"), status("OK"), code(200) {}
/**
* @brief Tries to parse HTTP response from string
@ -124,7 +128,8 @@ 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);
int parse(const std::string &buf);
/**
* @brief Serialize HTTP response to string
@ -135,10 +140,10 @@ namespace http
*/
std::string to_string();
void write(std::ostream & o);
void write(std::ostream &o);
/** @brief Checks that response declared as chunked data */
bool is_chunked() const ;
bool is_chunked() const;
/** @brief Checks that response contains compressed data */
bool is_gzipped(bool includingI2PGzip = true) const;
@ -149,7 +154,7 @@ namespace http
* @param code HTTP code [100, 599]
* @return Immutable string with status
*/
const char * HTTPCodeToStatus(int code);
const char *HTTPCodeToStatus(int code);
/**
* @brief Replaces %-encoded characters in string with their values
@ -157,7 +162,7 @@ namespace http
* @param null If set to true - decode also %00 sequence, otherwise - skip
* @return Decoded string
*/
std::string UrlDecode(const std::string& data, bool null = false);
std::string UrlDecode(const std::string &data, bool null = false);
/**
* @brief Merge HTTP response content with Transfer-Encoding: chunked
@ -165,11 +170,11 @@ namespace http
* @param out Output stream
* @return true on success, false otherwise
*/
bool MergeChunkedResponse (std::istream& in, std::ostream& out);
bool MergeChunkedResponse(std::istream &in, std::ostream &out);
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass);
std::string CreateBasicAuthorizationString(const std::string &user, const std::string &pass);
} // http
} // http
} // i2p
#endif /* HTTP_H__ */

File diff suppressed because it is too large Load diff

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,177 +132,226 @@ 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;
}
}
const size_t I2NP_MAX_MESSAGE_SIZE = 62708;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
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
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60 * 1000; // 1 minute in milliseconds
struct I2NPMessage
{
uint8_t * buf;
struct I2NPMessage {
uint8_t *buf;
size_t len, offset, maxLen;
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
I2NPMessage() : buf(nullptr), len(I2NP_HEADER_SIZE + 2),
offset(2), maxLen(0), from(nullptr) {}; // reserve 2 bytes for NTCP header
// 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); };
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 ()
{
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);
};
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() {
uint8_t hash[32];
SHA256(GetPayload (), GetPayloadLength (), hash);
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
SHA256(GetPayload(), GetPayloadLength(), hash);
GetHeader()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
}
// 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; };
uint8_t *GetPayload() { return GetBuffer() + I2NP_HEADER_SIZE; };
void Align (size_t alignment)
{
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) {
if (len + alignment > maxLen) return;
size_t rem = ((size_t)GetBuffer ()) % alignment;
if (rem)
{
size_t rem = ((size_t) GetBuffer()) % alignment;
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);
memcpy(buf + len, buf1, len1);
len += len1;
return len1;
}
I2NPMessage& operator=(const I2NPMessage& other)
{
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
len = offset + other.GetLength ();
I2NPMessage &operator=(const I2NPMessage &other) {
memcpy(buf + offset, other.buf + other.offset, other.GetLength());
len = offset + other.GetLength();
from = other.from;
return *this;
}
// 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
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 ();
GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID (msgID);
SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL);
SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0);
const uint8_t *ssu = GetSSUHeader();
GetHeader()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID(msgID);
SetExpiration(bufbe32toh(ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET) * 1000LL);
SetSize(len - offset - I2NP_HEADER_SIZE);
SetChks(0);
}
uint32_t ToSSU () // return msgID
uint32_t ToSSU() // return msgID
{
uint8_t header[I2NP_HEADER_SIZE];
memcpy (header, GetHeader (), I2NP_HEADER_SIZE);
uint8_t * ssu = GetSSUHeader ();
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);
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
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 ()
{
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);
SetSize (len - offset - I2NP_HEADER_SIZE);
SetChks (0);
uint8_t *GetNTCP2Header() { return GetPayload() - I2NP_NTCP2_HEADER_SIZE; };
size_t GetNTCP2Length() const { return GetPayloadLength() + I2NP_NTCP2_HEADER_SIZE; };
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);
SetSize(len - offset - I2NP_HEADER_SIZE);
SetChks(0);
}
void ToNTCP2 ()
{
uint8_t * ntcp2 = GetNTCP2Header ();
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 ToNTCP2() {
uint8_t *ntcp2 = GetNTCP2Header();
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;
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> NewI2NPMessage();
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> NewI2NPShortMessage();
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);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills,
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> 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);
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> 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>
CreateDatabaseSearchReply(const i2p::data::IdentHash &ident, std::vector<i2p::data::IdentHash> routers);
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>
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> 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);
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);
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);
bool IsRouterInfoMsg(std::shared_ptr<I2NPMessage> msg);
class I2NPMessagesHandler
{
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 {
public:
~I2NPMessagesHandler ();
void PutNextMessage (std::shared_ptr<I2NPMessage>&& msg);
void Flush ();
~I2NPMessagesHandler();
void PutNextMessage(std::shared_ptr<I2NPMessage> &&msg);
void Flush();
private:
@ -310,8 +359,10 @@ namespace tunnel
};
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500;
void SetMaxNumTransitTunnels (uint16_t maxNumTransitTunnels);
uint16_t GetMaxNumTransitTunnels ();
void SetMaxNumTransitTunnels(uint16_t maxNumTransitTunnels);
uint16_t GetMaxNumTransitTunnels();
}
#endif

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));
}

File diff suppressed because it is too large Load diff

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)
{
return ident.ToBase64 ().substr (0, 4);
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,22 +42,25 @@ 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;
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;
};
Keys CreateRandomKeys ();
Keys CreateRandomKeys();
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes
const size_t DEFAULT_IDENTITY_SIZE = sizeof(Identity); // 387 bytes
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
@ -85,57 +85,83 @@ 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);
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);
IdentityEx();
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; };
IdentityEx(const uint8_t *publicKey, const uint8_t *signingKey,
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1,
CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
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
IdentityEx(const uint8_t *buf, size_t len);
bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); }
void RecalculateIdentHash(uint8_t * buff=nullptr);
IdentityEx(const IdentityEx &other);
static i2p::crypto::Verifier * CreateVerifier (SigningKeyType keyType);
static std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor (CryptoKeyType keyType, const uint8_t * key);
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);
private:
void CreateVerifier () const;
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
void CreateVerifier() const;
void UpdateVerifier(i2p::crypto::Verifier *verifier) const;
private:
Identity m_StandardIdentity;
IdentHash m_IdentHash;
mutable i2p::crypto::Verifier * m_Verifier = nullptr;
mutable i2p::crypto::Verifier *m_Verifier = nullptr;
mutable std::mutex m_VerifierMutex;
size_t m_ExtendedLen;
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
@ -145,46 +171,69 @@ 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;
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;
PrivateKeys(const PrivateKeys &other) { *this = other; };
size_t GetFullLen () const;
size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const;
PrivateKeys(const Keys &keys) { *this = keys; };
size_t FromBase64(const std::string& s);
std::string ToBase64 () const;
PrivateKeys &operator=(const Keys &keys);
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
PrivateKeys &operator=(const PrivateKeys &other);
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 i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv);
~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 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 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; };
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;
void CreateSigner() const;
void CreateSigner(SigningKeyType keyType) const;
size_t GetPrivateKeyLen() const;
private:
@ -198,51 +247,61 @@ 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; };
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);
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 () {};
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
virtual ~RoutingDestination() {};
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
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
};
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 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 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
};
}
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -19,36 +19,30 @@
#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
bool isUpdated; // transient
/* return true if this lease expires within t millisecond + fudge factor */
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const {
auto expire = i2p::util::GetMillisecondsSinceEpoch ();
if(fudge) expire += rand() % fudge;
bool ExpiresWithin(const uint64_t t, const uint64_t fudge = 1000) const {
auto expire = i2p::util::GetMillisecondsSinceEpoch();
if (fudge) expire += rand() % fudge;
if (endDate < expire) return true;
return (endDate - expire) < t;
}
};
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
@ -56,7 +50,7 @@ namespace data
};
};
typedef std::function<bool(const Lease & l)> LeaseInspectFunc;
typedef std::function<bool(const Lease &l)> LeaseInspectFunc;
const size_t MAX_LS_BUFFER_SIZE = 3072;
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
@ -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 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
LeaseSet(const uint8_t *buf, size_t len, bool storeLeases = true);
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;
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); };
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; };
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;
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);
};
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; };
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);
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; };
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
void ReadFromBuffer(bool readIdentity = true, bool verifySignature = true);
virtual uint64_t
ExtractExpirationTimestamp(const uint8_t *buf, size_t len) const; // returns max expiration time
private:
@ -122,8 +150,8 @@ namespace data
std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases;
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_EncryptionKey;
uint8_t * m_Buffer;
uint8_t *m_EncryptionKey;
uint8_t *m_Buffer;
size_t m_BufferLen;
};
@ -131,7 +159,7 @@ namespace data
* validate lease set buffer signature and extract expiration timestamp
* @returns true if the leaseset is well formed and signature is valid
*/
bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires);
bool LeaseSetBufferValidate(const uint8_t *ptr, size_t sz, uint64_t &expires);
const uint8_t NETDB_STORE_TYPE_STANDARD_LEASESET2 = 3;
const uint8_t NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 = 5;
@ -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
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;
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; };
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);
size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len);
size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len);
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);
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);
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
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
private:
@ -185,91 +231,112 @@ 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
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ()) return nullptr;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2;
std::shared_ptr<i2p::crypto::Verifier> transientVerifier (i2p::data::IdentityEx::CreateVerifier (keyType));
const uint8_t *signedData = buf + offset;
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;
std::shared_ptr<i2p::crypto::Verifier> transientVerifier(i2p::data::IdentityEx::CreateVerifier(keyType));
if (!transientVerifier) return nullptr;
auto keyLen = transientVerifier->GetPublicKeyLen ();
auto keyLen = transientVerifier->GetPublicKeyLen();
if (offset + keyLen >= len) return nullptr;
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 ();
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();
return transientVerifier;
}
//------------------------------------------------------------------------------------
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 * buf, size_t len);
virtual ~LocalLeaseSet () { delete[] m_Buffer; };
LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
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; };
LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *buf, size_t len);
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 ()); };
virtual ~LocalLeaseSet() { delete[] m_Buffer; };
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 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());
};
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
private:
uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_Buffer, * m_Leases;
uint8_t *m_Buffer, *m_Leases;
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;
const uint8_t *encryptionPublicKey;
};
typedef std::vector<KeySection> KeySections;
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
const KeySections& encryptionKeys,
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
LocalLeaseSet2(uint8_t storeType, const i2p::data::PrivateKeys &keys,
const KeySections &encryptionKeys,
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; };
virtual ~LocalLeaseSet2() { delete[] m_Buffer; };
uint8_t * GetBuffer () const { return m_Buffer + 1; };
size_t GetBufferLen () const { return m_BufferLen; };
uint8_t *GetBuffer() const { return m_Buffer + 1; };
uint8_t GetStoreType () const { return m_Buffer[0]; };
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:
uint8_t * m_Buffer; // 1 byte store type + actual buffer
uint8_t *m_Buffer; // 1 byte store type + actual buffer
size_t m_BufferLen;
};
@ -280,27 +347,32 @@ 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; };
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:
IdentHash m_StoreHash;
std::shared_ptr<const LocalLeaseSet2> m_InnerLeaseSet;
};
}
}
}
#endif

View file

@ -33,85 +33,71 @@ struct BigEndian;
// Little-Endian template
#pragma pack(push,1)
#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())
{
operator =(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];
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));
bytes[sizeof(T) - 1 - i] = static_cast<unsigned char>(t >> (i << 3));
return t;
}
// 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 ++ ();
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,64 +105,56 @@ struct LittleEndian
return (*this);
}
LittleEndian<T> operator -- (int)
{
LittleEndian<T> operator--(int) {
LittleEndian<T> tmp(*this);
operator -- ();
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))
if (bytes[i] != (T) (-1))
break;
}
return (*this);
}
};
#pragma pack(pop)
// Big-Endian template
#pragma pack(push,1)
#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())
{
operator =(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];
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 ++ ();
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,24 +197,22 @@ struct BigEndian
return (*this);
}
BigEndian<T> operator -- (int)
{
BigEndian<T> operator--(int) {
BigEndian<T> tmp(*this);
operator -- ();
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))
if (bytes[sizeof(T) - 1 - i] != (T) (-1))
break;
}
return (*this);
}
};
#pragma pack(pop)
#endif // LITTLEBIGENDIAN_H

View file

@ -13,12 +13,12 @@
#include <algorithm>
namespace i2p {
namespace log {
namespace log {
static Log logger;
/**
* @brief Maps our loglevel to their symbolic name
*/
static const char * g_LogLevelStr[eNumLogLevels] =
static const char *g_LogLevelStr[eNumLogLevels] =
{
"none", // eLogNone
"error", // eLogError
@ -45,49 +45,57 @@ namespace log {
#endif
#ifndef _WIN32
/**
* @brief Maps our log levels to syslog one
* @return syslog priority LOG_*, as defined in syslog.h
*/
static inline int GetSyslogPrio (enum LogLevel l) {
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():
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_LogStream(nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"),
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));
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();
@ -102,10 +110,9 @@ namespace log {
break;
}
m_IsRunning = false;
m_Queue.WakeUp ();
if (m_Thread)
{
m_Thread->join ();
m_Queue.WakeUp();
if (m_Thread) {
m_Thread->join();
delete m_Thread;
m_Thread = nullptr;
}
@ -116,13 +123,13 @@ namespace log {
// static_cast<int(*)(int)>(std::tolower) // wrong
// [](int c){ return std::tolower(c); } // wrong
// [](char c){ return std::tolower(c); } // wrong
[](unsigned char c){ return std::tolower(c); } // correct
[](unsigned char c) { return std::tolower(c); } // correct
);
return s;
}
void Log::SetLogLevel (const std::string& level_) {
std::string level=str_tolower(level_);
void Log::SetLogLevel(const std::string &level_) {
std::string level = str_tolower(level_);
if (level == "none") { m_MinLevel = eLogNone; }
else if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; }
@ -135,7 +142,7 @@ namespace log {
LogPrint(eLogInfo, "Log: Logging level set to ", level);
}
const char * Log::TimeAsString(std::time_t t) {
const char *Log::TimeAsString(std::time_t t) {
if (t != m_LastTimestamp) {
strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t));
m_LastTimestamp = t;
@ -148,10 +155,9 @@ 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;
std::hash <std::thread::id> hasher;
unsigned short short_tid;
short_tid = (short) (hasher(msg->tid) % 1000);
switch (m_Destination) {
@ -172,40 +178,36 @@ 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)
{
std::shared_ptr<LogMsg> msg;
while ((msg = m_Queue.Get ()))
Process (msg);
Reopen();
while (m_IsRunning) {
std::shared_ptr <LogMsg> msg;
while ((msg = m_Queue.Get()))
Process(msg);
if (m_LogStream) m_LogStream->flush();
if (m_IsRunning)
m_Queue.Wait ();
m_Queue.Wait();
}
}
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 ())
{
auto os = std::make_shared<std::ofstream>(path, flags);
if (os->is_open()) {
m_HasColors = false;
m_Logfile = path;
m_Destination = eLogFile;
@ -215,13 +217,14 @@ namespace log {
LogPrint(eLogError, "Log: Can't open file ", path);
}
void Log::SendTo (std::shared_ptr<std::ostream> os) {
void Log::SendTo(std::shared_ptr <std::ostream> os) {
m_HasColors = false;
m_Destination = eLogStream;
m_LogStream = os;
}
#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() {
@ -236,14 +240,16 @@ namespace log {
SendTo(m_Logfile);
}
Log & Logger() {
Log &Logger() {
return logger;
}
static ThrowFunction g_ThrowFunction;
ThrowFunction GetThrowFunction () { return g_ThrowFunction; }
void SetThrowFunction (ThrowFunction f) { g_ThrowFunction = f; }
} // log
ThrowFunction GetThrowFunction() { return g_ThrowFunction; }
void SetThrowFunction(ThrowFunction f) { g_ThrowFunction = f; }
} // log
} // i2p

View file

@ -21,11 +21,12 @@
#include "Queue.h"
#ifndef _WIN32
#include <syslog.h>
#endif
enum LogLevel
{
enum LogLevel {
eLogNone = 0,
eLogError,
eLogWarning,
@ -44,34 +45,36 @@ enum LogType {
};
namespace i2p {
namespace log {
namespace log {
struct LogMsg; /* forward declaration */
class Log
{
class Log {
private:
enum LogType m_Destination;
enum LogLevel m_MinLevel;
std::shared_ptr<std::ostream> m_LogStream;
std::shared_ptr <std::ostream> m_LogStream;
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;
std::thread * m_Thread;
std::thread *m_Thread;
private:
/** prevent making copies */
Log (const Log &);
const Log& operator=(const Log&);
Log(const Log &);
void Run ();
void Process (std::shared_ptr<LogMsg> msg);
const Log &operator=(const Log &);
void Run();
void Process(std::shared_ptr <LogMsg> msg);
/**
* @brief Makes formatted string from unix timestamp
@ -79,57 +82,62 @@ namespace log {
*
* This function internally caches the result for last provided value
*/
const char * TimeAsString(std::time_t ts);
const char *TimeAsString(std::time_t ts);
public:
Log ();
~Log ();
Log();
LogType GetLogType () { return m_Destination; };
LogLevel GetLogLevel () { return m_MinLevel; };
~Log();
void Start ();
void Stop ();
LogType GetLogType() { return m_Destination; };
LogLevel GetLogLevel() { return m_MinLevel; };
void Start();
void Stop();
/**
* @brief Sets minimal allowed level for log messages
* @param level String with wanted minimal msg level
*/
void SetLogLevel (const std::string& level);
void SetLogLevel(const std::string &level);
/**
* @brief Sets log destination to logfile
* @param path Path to logfile
*/
void SendTo (const std::string &path);
void SendTo(const std::string &path);
/**
* @brief Sets log destination to given output stream
* @param os Output stream
*/
void SendTo (std::shared_ptr<std::ostream> os);
void SendTo(std::shared_ptr <std::ostream> os);
/**
* @brief Sets format for timestamps in log
* @param format String with timestamp format
*/
void SetTimeFormat (std::string format) { m_TimeFormat = format; };
void SetTimeFormat(std::string format) { m_TimeFormat = format; };
#ifndef _WIN32
#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
void SendTo(const char *name, int facility);
#endif
/**
* @brief Format log message and write to output stream/syslog
* @param msg Pointer to processed message
*/
void Append(std::shared_ptr<i2p::log::LogMsg> &);
void Append(std::shared_ptr <i2p::log::LogMsg> &);
/** @brief Reopen log file */
void Reopen();
@ -148,31 +156,41 @@ namespace log {
LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */
LogMsg (LogLevel lvl, std::time_t ts, std::string&& txt): timestamp(ts), text(std::move(txt)), level(lvl) {}
LogMsg(LogLevel lvl, std::time_t ts, std::string &&txt) : timestamp(ts), text(std::move(txt)), level(lvl) {}
};
Log & Logger();
Log &Logger();
typedef std::function<void (const std::string&)> ThrowFunction;
ThrowFunction GetThrowFunction ();
void SetThrowFunction (ThrowFunction f);
} // log
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,24 +200,33 @@ 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
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
return;
void LogPrint(LogLevel level, TArgs &&... args)
// fold message to single string
std::stringstream ss;
noexcept
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.
GetLogLevel()
)
return;
// fold message to single string
std::stringstream ss;
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
(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);
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);
}
/**
@ -207,18 +234,26 @@ 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;
// fold message to single string
std::stringstream ss("");
auto f = i2p::log::GetThrowFunction();
if (!f) return;
// fold message to single string
std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
(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;
@ -37,14 +35,13 @@ namespace transport
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes
const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25 * 60; // 25 minuntes in seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25 * 60; // 25 minuntes
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,39 +74,54 @@ namespace transport
// RouterInfo flags
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
struct NTCP2Establisher: private i2p::crypto::NoiseSymmetricState
{
NTCP2Establisher ();
~NTCP2Establisher ();
struct NTCP2Establisher : private i2p::crypto::NoiseSymmetricState {
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
~NTCP2Establisher();
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; };
const uint8_t *GetPub() const { return m_EphemeralKeys->GetPublicKey(); };
void KDF1Alice ();
void KDF1Bob ();
void KDF2Alice ();
void KDF2Bob ();
void KDF3Alice (); // for SessionConfirmed part 2
void KDF3Bob ();
const uint8_t *GetRemotePub() const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
uint8_t *GetRemotePub() { return m_RemoteEphemeralPublicKey; }; // to set
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 ();
const uint8_t *GetK() const { return m_CK + 32; };
void CreateSessionRequestMessage ();
void CreateSessionCreatedMessage ();
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
const uint8_t *GetCK() const { return m_CK; };
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);
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 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;
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
@ -119,83 +130,120 @@ namespace transport
uint16_t m3p2Len;
uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE],
m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], * m_SessionConfirmedBuffer;
m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], *m_SessionConfirmedBuffer;
size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen;
};
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,
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; };
~NTCP2Session();
bool IsEstablished () const { return m_IsEstablished; };
bool IsTerminated () const { return m_IsTerminated; };
void Terminate();
void ClientLogin (); // Alice
void ServerLogin (); // Bob
void TerminateByTimeout();
void SendLocalRouterInfo (bool update); // after handshake or by update
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
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
void ServerLogin(); // Bob
void SendLocalRouterInfo(bool update); // after handshake or by update
void SendI2NPMessages(const std::vector<std::shared_ptr<I2NPMessage> > &msgs);
private:
void Established ();
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);
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 SendSessionRequest();
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 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 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);
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 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 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 ReceiveLength();
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 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);
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 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:
NTCP2Server& m_Server;
NTCP2Server &m_Server;
boost::asio::ip::tcp::socket m_Socket;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsEstablished, m_IsTerminated;
@ -203,17 +251,16 @@ namespace transport
std::unique_ptr<NTCP2Establisher> m_Establisher;
// data phase
uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32];
const uint8_t * m_SendKey, * m_ReceiveKey;
const uint8_t *m_SendKey, *m_ReceiveKey;
#if OPENSSL_SIPHASH
EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx;
#else
const uint8_t * m_SendSipKey, * m_ReceiveSipKey;
const uint8_t *m_SendSipKey, *m_ReceiveSipKey;
#endif
uint16_t m_NextReceivedLen;
uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer;
uint8_t *m_NextReceivedBuffer, *m_NextSendBuffer;
size_t m_NextReceivedBufferSize;
union
{
union {
uint8_t buf[8];
uint16_t key;
} m_ReceiveIV, m_SendIV;
@ -229,48 +276,61 @@ 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 ();
NTCP2Server();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
~NTCP2Server();
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 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 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 SetLocalAddress (const boost::asio::ip::address& localAddress);
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 HandleAccept(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 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);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleTermination();
void HandleTerminationTimer(const boost::system::error_code &ecode);
private:
@ -289,9 +349,12 @@ namespace transport
public:
// for HTTP/I2PControl
const decltype(m_NTCP2Sessions)& GetNTCP2Sessions () const { return m_NTCP2Sessions; };
const decltype(m_NTCP2Sessions)
&
GetNTCP2Sessions() const { return m_NTCP2Sessions; };
};
}
}
}
#endif

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,96 +57,150 @@ 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 ();
NetDb();
void Start ();
void Stop ();
~NetDb();
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 Start();
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 Stop();
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> AddRouterInfo(const uint8_t *buf, int len);
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> 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::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::shared_ptr<const RouterInfo> GetRandomRouterInFamily (FamilyID fam) const;
void SetUnreachable (const IdentHash& ident, bool unreachable);
bool AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len);
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
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 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> 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::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::shared_ptr<const RouterInfo> GetRandomRouterInFamily(FamilyID fam) const;
void SetUnreachable(const IdentHash &ident, bool unreachable);
void PostI2NPMsg(std::shared_ptr<const I2NPMessage> msg);
/** set hidden mode, aka don't publish our RI to netdb and don't explore */
void SetHidden(bool hide);
void Reseed ();
Families& GetFamilies () { return m_Families; };
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 (); };
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 (); };
void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
void ClearRouterInfos() { m_RouterInfos.clear(); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
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; };
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 Load();
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters = 40, int numFloodfills = 20);
bool LoadRouterInfo(const std::string &path, uint64_t ts);
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);
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);
template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
std::shared_ptr<const RouterInfo> GetRandomRouter(Filter filter) const;
private:
@ -160,15 +212,16 @@ namespace data
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
bool m_IsRunning;
std::thread * m_Thread;
std::thread *m_Thread;
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
GzipInflator m_Inflator;
Reseeder * m_Reseeder;
Reseeder *m_Reseeder;
Families m_Families;
i2p::fs::HashedStorage m_Storage;
friend class NetDbRequests;
NetDbRequests m_Requests;
bool m_PersistProfiles;
@ -187,7 +240,7 @@ namespace data
};
extern NetDb netdb;
}
}
}
#endif

View file

@ -12,157 +12,142 @@
#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,
if (replyTunnel)
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
replyTunnel->GetNextIdentHash(),
replyTunnel->GetNextTunnelID(), m_IsExploratory,
&m_ExcludedPeers);
else
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 ();
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)
{
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill);
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage(const IdentHash &floodfill) {
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
i2p::context.GetRouterInfo().GetIdentHash(), 0, false,
&m_ExcludedPeers);
m_ExcludedPeers.insert(floodfill);
m_CreationTime = i2p::util::GetSecondsSinceEpoch();
return msg;
}
void RequestedDestination::ClearExcludedPeers ()
{
m_ExcludedPeers.clear ();
void RequestedDestination::ClearExcludedPeers() {
m_ExcludedPeers.clear();
}
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
{
if (m_RequestComplete)
{
m_RequestComplete (r);
void RequestedDestination::Success(std::shared_ptr<RouterInfo> r) {
if (m_RequestComplete) {
m_RequestComplete(r);
m_RequestComplete = nullptr;
}
}
void RequestedDestination::Fail ()
{
if (m_RequestComplete)
{
m_RequestComplete (nullptr);
void RequestedDestination::Fail() {
if (m_RequestComplete) {
m_RequestComplete(nullptr);
m_RequestComplete = nullptr;
}
}
void NetDbRequests::Start ()
{
void NetDbRequests::Start() {
}
void NetDbRequests::Stop ()
{
m_RequestedDestinations.clear ();
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);
auto dest = std::make_shared<RequestedDestination>(destination, isExploratory);
dest->SetRequestComplete(requestComplete);
{
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted
if (!m_RequestedDestinations.insert(std::make_pair(destination, dest)).second) // not inserted
return nullptr;
}
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 ())
{
auto it = m_RequestedDestinations.find(ident);
if (it != m_RequestedDestinations.end()) {
request = it->second;
m_RequestedDestinations.erase (it);
m_RequestedDestinations.erase(it);
}
}
if (request)
{
if (request) {
if (r)
request->Success (r);
request->Success(r);
else
request->Fail ();
request->Fail();
}
}
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 ())
auto it = m_RequestedDestinations.find(ident);
if (it != m_RequestedDestinations.end())
return it->second;
return nullptr;
}
void NetDbRequests::ManageRequests ()
{
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
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 ();)
{
auto& dest = it->second;
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
if (ts < dest->GetCreationTime() + 60) // request is worthless after 1 minute
{
if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds
if (ts > dest->GetCreationTime() + 5) // no response for 5 seconds
{
auto count = dest->GetExcludedPeers ().size ();
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 count = dest->GetExcludedPeers().size();
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());
if (nextFloodfill && outbound && inbound)
outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0,
dest->CreateRequestMessage (nextFloodfill, inbound));
else
{
outbound->SendTunnelDataMsg(nextFloodfill->GetIdentHash(), 0,
dest->CreateRequestMessage(nextFloodfill, inbound));
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");
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
{
if (!dest->IsExploratory ())
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
} else {
if (!dest->IsExploratory())
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)
it = m_RequestedDestinations.erase (it);
it = m_RequestedDestinations.erase(it);
else
++it;
}
}
}
}
}

View file

@ -15,34 +15,44 @@
#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;
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); };
RequestedDestination(const IdentHash &destination, bool isExploratory = false) :
m_Destination(destination), m_IsExploratory(isExploratory), m_CreationTime(0) {};
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 (const IdentHash& floodfill);
~RequestedDestination() { if (m_RequestComplete) m_RequestComplete(nullptr); };
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r);
void Fail ();
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(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,24 +63,28 @@ namespace data
RequestComplete m_RequestComplete;
};
class NetDbRequests
{
class NetDbRequests {
public:
void Start ();
void Stop ();
void Start();
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 ();
void Stop();
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:
mutable std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
};
}
}
}
#endif

View file

@ -9,17 +9,14 @@
#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);
}
}
}
}
#endif

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,40 +53,37 @@ namespace crypto
return *this;
}
Block & operator %=(const LongBlock & other)
{
Block &operator%=(const LongBlock &other) {
unsigned long u;
unsigned int i;
u = 0;
for (i = 0; i < 16; i++) {
u += other.data[i];
data[i] = (unsigned char)u & 0xff;
data[i] = (unsigned char) u & 0xff;
u >>= 8;
}
u += other.data[16];
data[16] = (unsigned char)u & 0x03;
data[16] = (unsigned char) u & 0x03;
u >>= 2;
u += (u << 2);
for (i = 0; i < 16; i++) {
u += data[i];
data[i] = (unsigned char)u & 0xff;
data[i] = (unsigned char) u & 0xff;
u >>= 8;
}
data[16] += (unsigned char)u;
data[16] += (unsigned char) u;
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,
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfc
};
Block orig;
@ -103,15 +92,14 @@ namespace crypto
orig = *this;
*this += minusp;
neg = -(data[16] >> 7);
for(i = 0; i < 17; i++)
for (i = 0; i < 17; i++)
data[i] ^= neg & (orig.data[i] ^ data[i]);
return *this;
}
void PutKey(const uint64_t * key_l)
{
const uint8_t * key = (const uint8_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;
data[2] = key[2] & 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,39 +144,34 @@ 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;
if (want > sz) want = sz;
memcpy(m_Buffer + m_Leftover, buf, want);
sz -= want;
buf += want;
m_Leftover += want;
if(m_Leftover < POLY1305_BLOCK_BYTES) return;
if (m_Leftover < POLY1305_BLOCK_BYTES) return;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
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)
{
memcpy(m_Buffer+m_Leftover, buf, 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;
@ -205,11 +183,11 @@ namespace crypto
/* h *= r */
for (i = 0; i < 17; i++) {
u = 0;
for (j = 0; j <= i ; j++) {
u += (unsigned short)m_H.data[j] * m_R.data[i - j];
for (j = 0; j <= i; j++) {
u += (unsigned short) m_H.data[j] * m_R.data[i - j];
}
for (j = i + 1; j < 17; j++) {
unsigned long v = (unsigned short)m_H.data[j] * m_R.data[i + 17 - j];
unsigned long v = (unsigned short) m_H.data[j] * m_R.data[i + 17 - j];
v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
u += v;
}
@ -222,14 +200,12 @@ 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++)
for (; idx < POLY1305_BLOCK_BYTES; idx++)
m_Buffer[idx] = 0;
m_Final = 1;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
@ -253,8 +229,8 @@ namespace crypto
uint8_t m_Final;
};
void Poly1305HMAC(uint64_t * out, const uint64_t * key, const uint8_t * buf, std::size_t sz);
}
void Poly1305HMAC(uint64_t *out, const uint64_t *key, const uint8_t *buf, std::size_t sz);
}
}
#endif

View file

@ -14,150 +14,128 @@
#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)
{
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) {
}
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 ()
{
m_LastUpdateTime = GetTime ();
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);
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
participation.put(PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
participation.put(PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
participation.put(PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
boost::property_tree::ptree usage;
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
usage.put(PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put(PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
// fill property tree
boost::property_tree::ptree pt;
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
pt.put(PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string(m_LastUpdateTime));
pt.put_child(PEER_PROFILE_SECTION_PARTICIPATION, participation);
pt.put_child(PEER_PROFILE_SECTION_USAGE, usage);
// save to file
std::string ident = identHash.ToBase64 ();
std::string ident = identHash.ToBase64();
std::string path = m_ProfilesStorage.Path(ident);
try {
boost::property_tree::write_ini (path, pt);
} catch (std::exception& ex) {
boost::property_tree::write_ini(path, pt);
} catch (std::exception &ex) {
/* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ());
LogPrint(eLogError, "Profiling: ", ex.what());
}
}
void RouterProfile::Load (const IdentHash& identHash)
{
std::string ident = identHash.ToBase64 ();
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
{
boost::property_tree::read_ini (path, pt);
} catch (std::exception& ex)
{
try {
boost::property_tree::read_ini(path, pt);
} catch (std::exception &ex) {
/* boost exception verbose enough */
LogPrint (eLogError, "Profiling: ", ex.what ());
LogPrint(eLogError, "Profiling: ", ex.what());
return;
}
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
{
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 {
// 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);
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);
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
*this = RouterProfile();
}
else
*this = RouterProfile ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ());
catch (std::exception &ex) {
LogPrint(eLogError, "Profiling: Can't read profile ", ident, " :", ex.what());
}
}
void RouterProfile::TunnelBuildResponse (uint8_t ret)
{
UpdateTime ();
void RouterProfile::TunnelBuildResponse(uint8_t ret) {
UpdateTime();
if (ret > 0)
m_NumTunnelsDeclined++;
else
m_NumTunnelsAgreed++;
}
void RouterProfile::TunnelNonReplied ()
{
void RouterProfile::TunnelNonReplied() {
m_NumTunnelsNonReplied++;
UpdateTime ();
UpdateTime();
}
bool RouterProfile::IsLowPartcipationRate () const
{
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
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);
return m_NumTunnelsNonReplied > 10 * (total + 1);
}
bool RouterProfile::IsBad ()
{
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{
bool RouterProfile::IsBad() {
auto isBad = IsAlwaysDeclining() || IsLowPartcipationRate() /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10 * (m_NumTimesTaken + 1)) {
// reset profile
m_NumTunnelsAgreed = 0;
m_NumTunnelsDeclined = 0;
@ -168,27 +146,24 @@ namespace data
return isBad;
}
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
{
auto profile = std::make_shared<RouterProfile> ();
profile->Load (identHash); // if possible
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);
std::vector<std::string> files;
m_ProfilesStorage.Traverse(files);
for (const auto& path: files) {
for (const auto &path: files) {
if (stat(path.c_str(), &st) != 0) {
LogPrint(eLogWarning, "Profiling: Can't stat(): ", path);
continue;
@ -199,5 +174,5 @@ namespace data
}
}
}
}
}
}

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,29 +30,34 @@ 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;
RouterProfile();
void Save (const IdentHash& identHash);
void Load (const IdentHash& identHash);
RouterProfile &operator=(const RouterProfile &) = default;
bool IsBad ();
void Save(const IdentHash &identHash);
void TunnelBuildResponse (uint8_t ret);
void TunnelNonReplied ();
void Load(const IdentHash &identHash);
bool IsBad();
void TunnelBuildResponse(uint8_t ret);
void TunnelNonReplied();
private:
boost::posix_time::ptime GetTime () const;
void UpdateTime ();
boost::posix_time::ptime GetTime() const;
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate () const;
bool IsLowReplyRate () const;
void UpdateTime();
bool IsAlwaysDeclining() const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate() const;
bool IsLowReplyRate() const;
private:
@ -68,10 +71,12 @@ namespace data
uint32_t m_NumTimesRejected;
};
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
void InitProfilesStorage ();
void DeleteObsoleteProfiles ();
}
std::shared_ptr<RouterProfile> GetRouterProfile(const IdentHash &identHash);
void InitProfilesStorage();
void DeleteObsoleteProfiles();
}
}
#endif

View file

@ -17,105 +17,88 @@
#include <functional>
#include <utility>
namespace i2p
{
namespace util
{
namespace i2p {
namespace util {
template<typename Element>
class Queue
{
class Queue {
public:
void Put (Element e)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_Queue.push (std::move(e));
m_NonEmpty.notify_one ();
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 ())
{
std::unique_lock<std::mutex> l(m_QueueMutex);
for (const auto& it: vec)
m_Queue.push (std::move(it));
m_NonEmpty.notify_one ();
template<template<typename, typename...> class Container, typename... R>
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));
m_NonEmpty.notify_one();
}
}
Element GetNext ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait (l);
el = GetNonThreadSafe ();
Element GetNext() {
std::unique_lock <std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe();
if (!el) {
m_NonEmpty.wait(l);
el = GetNonThreadSafe();
}
return el;
}
Element GetNextWithTimeout (int usec)
{
std::unique_lock<std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe ();
if (!el)
{
m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec));
el = GetNonThreadSafe ();
Element GetNextWithTimeout(int usec) {
std::unique_lock <std::mutex> l(m_QueueMutex);
auto el = GetNonThreadSafe();
if (!el) {
m_NonEmpty.wait_for(l, std::chrono::milliseconds(usec));
el = GetNonThreadSafe();
}
return el;
}
void Wait ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
m_NonEmpty.wait (l);
void Wait() {
std::unique_lock <std::mutex> l(m_QueueMutex);
m_NonEmpty.wait(l);
}
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;
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;
}
bool IsEmpty ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.empty ();
bool IsEmpty() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return m_Queue.empty();
}
int GetSize ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return m_Queue.size ();
int GetSize() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return m_Queue.size();
}
void WakeUp () { m_NonEmpty.notify_all (); };
void WakeUp() { m_NonEmpty.notify_all(); };
Element Get ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe ();
Element Get() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return GetNonThreadSafe();
}
Element Peek ()
{
std::unique_lock<std::mutex> l(m_QueueMutex);
return GetNonThreadSafe (true);
Element Peek() {
std::unique_lock <std::mutex> l(m_QueueMutex);
return GetNonThreadSafe(true);
}
private:
Element GetNonThreadSafe (bool peek = false)
{
if (!m_Queue.empty ())
{
auto el = m_Queue.front ();
Element GetNonThreadSafe(bool peek = false) {
if (!m_Queue.empty()) {
auto el = m_Queue.front();
if (!peek)
m_Queue.pop ();
m_Queue.pop();
return el;
}
return nullptr;
@ -123,11 +106,11 @@ namespace util
private:
std::queue<Element> m_Queue;
std::queue <Element> m_Queue;
std::mutex m_QueueMutex;
std::condition_variable m_NonEmpty;
};
}
}
}
#endif

View file

@ -27,52 +27,44 @@
#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://")
{
num = ReseedFromSU3Url (su3FileName); // from https URL
}
else
{
num = ProcessSU3File (su3FileName.c_str ());
if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") {
num = ReseedFromSU3Url(su3FileName); // from https URL
} 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
LogPrint(eLogWarning, "Reseed: Failed to reseed from ", su3FileName);
} else if (zipFileName.length() > 0) // bootstrap from ZIP file
{
int num = ProcessZIPFile (zipFileName.c_str ());
int num = ProcessZIPFile(zipFileName.c_str());
if (num == 0)
LogPrint (eLogWarning, "Reseed: Failed to reseed from ", zipFileName);
}
else // bootstrap from reseed servers
LogPrint(eLogWarning, "Reseed: Failed to reseed from ", zipFileName);
} else // bootstrap from reseed servers
{
int num = ReseedFromServers ();
int num = ReseedFromServers();
if (num == 0)
LogPrint (eLogWarning, "Reseed: Failed to reseed from servers");
LogPrint(eLogWarning, "Reseed: Failed to reseed from servers");
}
}
@ -80,48 +72,48 @@ 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 (!reseedURLs.empty ())
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 ())
{
LogPrint (eLogInfo, "Reseed: Yggdrasil is supported");
std::string yggReseedURLs; i2p::config::GetOption("reseed.yggurls", yggReseedURLs);
if (!yggReseedURLs.empty ())
if (yggdrasil && !i2p::util::net::GetYggdrasilAddress().is_unspecified()) {
LogPrint(eLogInfo, "Reseed: Yggdrasil is supported");
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())
{
LogPrint (eLogWarning, "Reseed: No reseed servers specified");
if (httpsReseedHostList.empty() && yggReseedHostList.empty()) {
LogPrint(eLogWarning, "Reseed: No reseed servers specified");
return 0;
}
int reseedRetries = 0;
while (reseedRetries < 10)
{
auto ind = rand () % (httpsReseedHostList.size () + yggReseedHostList.size ());
bool isHttps = ind < httpsReseedHostList.size ();
while (reseedRetries < 10) {
auto ind = rand() % (httpsReseedHostList.size() + yggReseedHostList.size());
bool isHttps = ind < httpsReseedHostList.size();
std::string reseedUrl = isHttps ? httpsReseedHostList[ind] :
yggReseedHostList[ind - httpsReseedHostList.size ()];
yggReseedHostList[ind - httpsReseedHostList.size()];
reseedUrl += "i2pseeds.su3";
auto num = ReseedFromSU3Url (reseedUrl, isHttps);
auto num = ReseedFromSU3Url(reseedUrl, isHttps);
if (num > 0) return num; // success
reseedRetries++;
}
LogPrint (eLogWarning, "Reseed: Failed to reseed from servers after 10 attempts");
LogPrint(eLogWarning, "Reseed: Failed to reseed from servers after 10 attempts");
return 0;
}
@ -130,298 +122,274 @@ namespace data
* @param url
* @return number of entries added to netDb
*/
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)
{
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) {
std::stringstream s(su3);
return ProcessSU3Stream (s);
}
else
{
LogPrint (eLogWarning, "Reseed: SU3 download failed");
return ProcessSU3Stream(s);
} 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
{
LogPrint (eLogError, "Reseed: Can't open file ", filename);
if (s.is_open())
return ProcessSU3Stream(s);
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 ())
{
s.seekg (0, std::ios::end);
auto len = s.tellg ();
s.seekg (0, std::ios::beg);
return ProcessZIPStream (s, len);
}
else
{
LogPrint (eLogError, "Reseed: Can't open file ", filename);
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 {
LogPrint(eLogError, "Reseed: Can't open file ", filename);
return 0;
}
}
const char SU3_MAGIC_NUMBER[]="I2Psu3";
int Reseeder::ProcessSU3Stream (std::istream& s)
{
const char SU3_MAGIC_NUMBER[] = "I2Psu3";
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))
{
LogPrint (eLogError, "Reseed: Unexpected SU3 magic number");
s.read(magicNumber, 7); // magic number and zero byte 6
if (strcmp(magicNumber, SU3_MAGIC_NUMBER)) {
LogPrint(eLogError, "Reseed: Unexpected SU3 magic number");
return 0;
}
s.seekg (1, std::ios::cur); // su3 file format version
s.seekg(1, std::ios::cur); // su3 file format version
SigningKeyType signatureType;
s.read ((char *)&signatureType, 2); // signature type
signatureType = be16toh (signatureType);
s.read((char *) &signatureType, 2); // signature type
signatureType = be16toh(signatureType);
uint16_t signatureLength;
s.read ((char *)&signatureLength, 2); // signature length
signatureLength = be16toh (signatureLength);
s.seekg (1, std::ios::cur); // unused
s.read((char *) &signatureLength, 2); // signature length
signatureLength = be16toh(signatureLength);
s.seekg(1, std::ios::cur); // unused
uint8_t versionLength;
s.read ((char *)&versionLength, 1); // version length
s.seekg (1, std::ios::cur); // unused
s.read((char *) &versionLength, 1); // version length
s.seekg(1, std::ios::cur); // unused
uint8_t signerIDLength;
s.read ((char *)&signerIDLength, 1); // signer ID length
s.read((char *) &signerIDLength, 1); // signer ID length
uint64_t contentLength;
s.read ((char *)&contentLength, 8); // content length
contentLength = be64toh (contentLength);
s.seekg (1, std::ios::cur); // unused
s.read((char *) &contentLength, 8); // content length
contentLength = be64toh(contentLength);
s.seekg(1, std::ios::cur); // unused
uint8_t fileType;
s.read ((char *)&fileType, 1); // file type
s.read((char *) &fileType, 1); // file type
if (fileType != 0x00) // zip file
{
LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType);
LogPrint(eLogError, "Reseed: Can't handle file type ", (int) fileType);
return 0;
}
s.seekg (1, std::ios::cur); // unused
s.seekg(1, std::ios::cur); // unused
uint8_t contentType;
s.read ((char *)&contentType, 1); // content type
s.read((char *) &contentType, 1); // content type
if (contentType != 0x03) // reseed data
{
LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType);
LogPrint(eLogError, "Reseed: Unexpected content type ", (int) contentType);
return 0;
}
s.seekg (12, std::ios::cur); // unused
s.seekg(12, std::ios::cur); // unused
s.seekg (versionLength, std::ios::cur); // skip version
s.seekg(versionLength, std::ios::cur); // skip version
char signerID[256];
s.read (signerID, signerIDLength); // signerID
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 ())
{
auto it = m_SigningKeys.find(signerID);
if (it != m_SigningKeys.end()) {
// TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
{
size_t pos = s.tellg ();
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];
s.seekg (0, std::ios::beg);
s.read ((char *)tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength);
uint8_t *tbs = new uint8_t[tbsLen];
s.seekg(0, std::ios::beg);
s.read((char *) tbs, tbsLen);
uint8_t *signature = new uint8_t[signatureLength];
s.read((char *) signature, signatureLength);
// RSA-raw
{
// calculate digest
uint8_t digest[64];
SHA512 (tbs, tbsLen, digest);
SHA512(tbs, tbsLen, digest);
// encrypt signature
BN_CTX * bnctx = BN_CTX_new ();
BIGNUM * s = BN_new (), * n = BN_new ();
BN_bin2bn (signature, signatureLength, s);
BN_bin2bn (it->second, 512, n); // RSA 4096 assumed
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
BN_CTX *bnctx = BN_CTX_new();
BIGNUM *s = BN_new(), *n = BN_new();
BN_bin2bn(signature, signatureLength, s);
BN_bin2bn(it->second, 512, n); // RSA 4096 assumed
BN_mod_exp(s, s, i2p::crypto::GetRSAE(), n, bnctx); // s = s^e mod n
uint8_t *enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf(s, enSigBuf, signatureLength);
// digest is right aligned
// we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
if (memcmp(enSigBuf + (signatureLength - 64), digest, 64))
LogPrint(eLogWarning, "Reseed: SU3 signature verification failed");
else
verify = false; // verified
delete[] enSigBuf;
BN_free (s); BN_free (n);
BN_CTX_free (bnctx);
BN_free(s);
BN_free(n);
BN_CTX_free(bnctx);
}
delete[] signature;
delete[] tbs;
s.seekg (pos, std::ios::beg);
}
else
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
}
else
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
s.seekg(pos, std::ios::beg);
} else
LogPrint(eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
} else
LogPrint(eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
}
if (verify) // not verified
{
LogPrint (eLogError, "Reseed: SU3 verification failed");
LogPrint(eLogError, "Reseed: SU3 verification failed");
return 0;
}
// handle content
return ProcessZIPStream (s, contentLength);
return ProcessZIPStream(s, contentLength);
}
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 ())
{
size_t contentPos = s.tellg();
while (!s.eof()) {
uint32_t signature;
s.read ((char *)&signature, 4);
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
s.seekg(2, std::ios::cur); // version
uint16_t bitFlag;
s.read ((char *)&bitFlag, 2);
s.read((char *) &bitFlag, 2);
bitFlag = le16toh (bitFlag);
uint16_t compressionMethod;
s.read ((char *)&compressionMethod, 2);
s.read((char *) &compressionMethod, 2);
compressionMethod = le16toh (compressionMethod);
s.seekg (4, std::ios::cur); // skip fields we don't care about
s.seekg(4, std::ios::cur); // skip fields we don't care about
uint32_t compressedSize, uncompressedSize;
uint32_t crc_32;
s.read ((char *)&crc_32, 4);
s.read((char *) &crc_32, 4);
crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4);
s.read((char *) &compressedSize, 4);
compressedSize = le32toh (compressedSize);
s.read ((char *)&uncompressedSize, 4);
s.read((char *) &uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize);
uint16_t fileNameLength, extraFieldLength;
s.read ((char *)&fileNameLength, 2);
s.read((char *) &fileNameLength, 2);
fileNameLength = le16toh (fileNameLength);
if ( fileNameLength > 255 ) {
if (fileNameLength > 255) {
// too big
LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength);
return numFiles;
}
s.read ((char *)&extraFieldLength, 2);
s.read((char *) &extraFieldLength, 2);
extraFieldLength = le16toh (extraFieldLength);
char localFileName[255];
s.read (localFileName, fileNameLength);
s.read(localFileName, fileNameLength);
localFileName[fileNameLength] = 0;
s.seekg (extraFieldLength, std::ios::cur);
s.seekg(extraFieldLength, std::ios::cur);
// take care about data descriptor if presented
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
{
size_t pos = s.tellg ();
if (!FindZipDataDescriptor (s))
{
LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found");
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) {
size_t pos = s.tellg();
if (!FindZipDataDescriptor(s)) {
LogPrint(eLogError, "Reseed: SU3 archive data descriptor not found");
return numFiles;
}
s.read ((char *)&crc_32, 4);
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
s.read ((char *)&uncompressedSize, 4);
s.read((char *) &compressedSize, 4);
compressedSize = le32toh (compressedSize) +
4; // ??? we must consider signature as part of compressed data
s.read((char *) &uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize);
// now we know compressed and uncompressed size
s.seekg (pos, std::ios::beg); // back to compressed data
s.seekg(pos, std::ios::beg); // back to compressed data
}
LogPrint (eLogDebug, "Reseed: Processing file ", localFileName, " ", compressedSize, " bytes");
if (!compressedSize)
{
LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped");
LogPrint(eLogDebug, "Reseed: Processing file ", localFileName, " ", compressedSize, " bytes");
if (!compressedSize) {
LogPrint(eLogWarning, "Reseed: Unexpected size 0. Skipped");
continue;
}
uint8_t * compressed = new uint8_t[compressedSize];
s.read ((char *)compressed, compressedSize);
uint8_t *compressed = new uint8_t[compressedSize];
s.read((char *) compressed, compressedSize);
if (compressionMethod) // we assume Deflate
{
z_stream inflator;
memset (&inflator, 0, sizeof (inflator));
inflateInit2 (&inflator, -MAX_WBITS); // no zlib header
uint8_t * uncompressed = new uint8_t[uncompressedSize];
memset(&inflator, 0, sizeof(inflator));
inflateInit2(&inflator, -MAX_WBITS); // no zlib header
uint8_t *uncompressed = new uint8_t[uncompressedSize];
inflator.next_in = compressed;
inflator.avail_in = compressedSize;
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)
{
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
if (crc32(0, uncompressed, uncompressedSize) == crc_32) {
i2p::data::netdb.AddRouterInfo(uncompressed, uncompressedSize);
numFiles++;
}
else
LogPrint (eLogError, "Reseed: CRC32 verification failed");
}
else
LogPrint (eLogError, "Reseed: SU3 decompression error ", err);
} else
LogPrint(eLogError, "Reseed: CRC32 verification failed");
} else
LogPrint(eLogError, "Reseed: SU3 decompression error ", err);
delete[] uncompressed;
inflateEnd (&inflator);
}
else // no compression
inflateEnd(&inflator);
} else // no compression
{
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
i2p::data::netdb.AddRouterInfo(compressed, compressedSize);
numFiles++;
}
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
{
s.seekg(12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
} else {
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
LogPrint (eLogWarning, "Reseed: Missing zip central directory header");
LogPrint(eLogWarning, "Reseed: Missing zip central directory header");
break; // no more files
}
size_t end = s.tellg ();
size_t end = s.tellg();
if (end - contentPos >= contentLength)
break; // we are beyond contentLength
}
if (numFiles) // check if routers are not outdated
{
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
auto ts = i2p::util::GetMillisecondsSinceEpoch();
int numOutdated = 0;
i2p::data::netdb.VisitRouterInfos (
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r)
i2p::data::netdb.VisitRouterInfos(
[&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++;
}
});
if (numOutdated > numFiles/2) // more than half
if (numOutdated > numFiles / 2) // more than half
{
LogPrint (eLogError, "Reseed: Mammoth's shit\n"
LogPrint(eLogError, "Reseed: Mammoth's shit\n"
" *_____*\n"
" *_*****_*\n"
" *_(O)_(O)_*\n"
@ -430,74 +398,65 @@ namespace data
" **_________**\n"
" *_________*\n"
" ***___***");
i2p::data::netdb.ClearRouterInfos ();
i2p::data::netdb.ClearRouterInfos();
numFiles = 0;
}
}
return numFiles;
}
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 };
bool Reseeder::FindZipDataDescriptor (std::istream& s)
{
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = {0x50, 0x4B, 0x07, 0x08};
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])
{
s.read((char *) &nextByte, 1);
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) {
nextInd++;
if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE))
if (nextInd >= sizeof(ZIP_DATA_DESCRIPTOR_SIGNATURE))
return true;
}
else
} else
nextInd = 0;
}
return false;
}
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)
{
SSL * ssl = SSL_new (ctx);
X509 * cert = SSL_get_certificate (ssl);
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) {
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)
{
X509_NAME_oneline(X509_get_issuer_name(cert), name, 100);
char *cn = strstr(name, "CN=");
if (cn) {
cn += 3;
char * terminator = strchr (cn, '/');
char *terminator = strchr(cn, '/');
if (terminator) terminator[0] = 0;
}
// extract RSA key (we need n only, e = 65537)
const RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert));
const BIGNUM * n, * e, * d;
const RSA *key = EVP_PKEY_get0_RSA(X509_get_pubkey(cert));
const BIGNUM *n, *e, *d;
RSA_get0_key(key, &n, &e, &d);
PublicKey value;
i2p::crypto::bn2buf (n, value, 512);
i2p::crypto::bn2buf(n, value, 512);
if (cn)
m_SigningKeys[cn] = value;
else
LogPrint (eLogError, "Reseed: Can't find CN field in ", filename);
LogPrint(eLogError, "Reseed: Can't find CN field in ", filename);
}
SSL_free (ssl);
}
else
LogPrint (eLogError, "Reseed: Can't open certificate file ", filename);
SSL_CTX_free (ctx);
SSL_free(ssl);
} 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;
@ -508,25 +467,25 @@ namespace data
return;
}
for (const std::string & file : files) {
for (const std::string &file: files) {
if (file.compare(file.size() - 4, 4, ".crt") != 0) {
LogPrint(eLogWarning, "Reseed: Ignoring file ", file);
continue;
}
LoadCertificate (file);
LoadCertificate(file);
numCertificates++;
}
LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded");
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()) {
if (proxy.size()) {
// parse
if(proxyUrl.parse(proxy)) {
if (proxyUrl.parse(proxy)) {
if (proxyUrl.schema == "http" && !proxyUrl.port) {
proxyUrl.port = 80;
} else if (proxyUrl.schema == "socks" && !proxyUrl.port) {
@ -556,28 +515,24 @@ namespace data
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> s(service, ctx);
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)
{
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) {
s.lowest_layer().connect(*it, ecode);
if(!ecode)
{
auto & sock = s.next_layer();
if(proxyUrl.schema == "http")
{
if (!ecode) {
auto &sock = s.next_layer();
if (proxyUrl.schema == "http") {
i2p::http::HTTPReq proxyReq;
i2p::http::HTTPRes proxyRes;
proxyReq.method = "CONNECT";
proxyReq.version = "HTTP/1.1";
proxyReq.uri = url.host + ":" + std::to_string(url.port);
auto auth = i2p::http::CreateBasicAuthorizationString (proxyUrl.user, proxyUrl.pass);
if (!auth.empty ())
auto auth = i2p::http::CreateBasicAuthorizationString(proxyUrl.user, proxyUrl.pass);
if (!auth.empty())
proxyReq.AddHeader("Proxy-Authorization", auth);
boost::asio::streambuf writebuf, readbuf;
@ -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,33 +589,29 @@ 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 "";
}
buf[4] = (uint8_t) hostsz;
memcpy(buf+5, url.host.c_str(), hostsz);
memcpy(buf + 5, url.host.c_str(), hostsz);
sz += hostsz + 1;
htobe16buf(buf+sz, url.port);
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,70 +619,59 @@ 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)
{
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) {
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 ()))
{
s.lowest_layer().connect (ep, ecode);
if (!ecode)
{
if ((ep.address().is_v4() && i2p::context.SupportsV4()) ||
(ep.address().is_v6() && i2p::context.SupportsV6())) {
s.lowest_layer().connect(ep, ecode);
if (!ecode) {
connected = true;
break;
}
}
it++;
}
if (!connected)
{
if (!connected) {
LogPrint(eLogError, "Reseed: Failed to connect to ", url.host);
return "";
}
}
}
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)
{
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
return ReseedRequest (s, url.to_string());
}
else
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
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) {
LogPrint(eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
return ReseedRequest(s, url.to_string());
} else
LogPrint(eLogError, "Reseed: SSL handshake failed: ", ecode.message());
} 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;
req.AddHeader("User-Agent", "Wget/1.11.4");
req.AddHeader("Connection", "close");
s.write_some (boost::asio::buffer (req.to_string()));
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);
l = s.read_some(boost::asio::buffer(recv_buf, sizeof(recv_buf)), ecode);
if (l) rs.write(recv_buf, l);
} while (!ecode && l);
// process response
std::string data = rs.str();
@ -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 "";
}
@ -780,19 +712,17 @@ namespace data
boost::asio::io_service service;
boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6());
if (url.host.length () < 2) return ""; // assume []
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)
{
LogPrint (eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port);
return ReseedRequest (s, url.to_string());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message ());
if (url.host.length() < 2) return ""; // assume []
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) {
LogPrint(eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port);
return ReseedRequest(s, url.to_string());
} else
LogPrint(eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message());
return "";
}
}
}
}

View file

@ -16,46 +16,52 @@
#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 ();
~Reseeder();
void Bootstrap();
int ReseedFromServers();
int ProcessSU3File(const char *filename);
int ProcessZIPFile(const char *filename);
void LoadCertificates();
private:
int ReseedFromSU3Url (const std::string& url, bool isHttps = true);
void LoadCertificate (const std::string& filename);
int ReseedFromSU3Url(const std::string &url, bool isHttps = true);
int ProcessSU3Stream (std::istream& s);
int ProcessZIPStream (std::istream& s, uint64_t contentLength);
void LoadCertificate(const std::string &filename);
bool FindZipDataDescriptor (std::istream& s);
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);
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);
std::string ReseedRequest(Stream &s, const std::string &uri);
private:
std::map<std::string, PublicKey> m_SigningKeys;
};
}
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -19,12 +19,10 @@
#include "RouterInfo.h"
#include "Garlic.h"
namespace i2p
{
namespace garlic
{
namespace i2p {
namespace garlic {
class RouterIncomingRatchetSession;
}
}
const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys";
@ -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];
@ -71,124 +64,196 @@ namespace garlic
public:
RouterContext ();
void Init ();
RouterContext();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
i2p::data::LocalRouterInfo& GetRouterInfo () { return m_RouterInfo; };
std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo ()
{
return std::shared_ptr<i2p::data::RouterInfo> (&m_RouterInfo,
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() {
return std::shared_ptr<i2p::data::RouterInfo>(&m_RouterInfo,
[](i2p::data::RouterInfo *) {});
}
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
{
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
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 * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
i2p::crypto::X25519Keys& GetNTCP2StaticKeys ();
const uint8_t *GetNTCP2StaticPublicKey() const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
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 ();
const uint8_t *GetNTCP2StaticPrivateKey() const {
return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr;
};
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; };
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; };
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);
const uint8_t *GetNTCP2IV() const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
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; };
i2p::crypto::X25519Keys &GetNTCP2StaticKeys();
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
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;
};
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;
};
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 () {};
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;
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);
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);
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;
void CreateNewRouter();
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
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);
private:

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,9 +95,8 @@ namespace data
};
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer
{
Introducer (): iPort (0), iExp (0) {};
struct Introducer {
Introducer() : iPort(0), iExp(0) {};
boost::asio::ip::address iHost;
int iPort;
IntroKey iKey; // or ih for SSU2
@ -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,167 +119,260 @@ namespace data
bool published = false;
std::unique_ptr<SSUExt> ssu; // not null for SSU
bool IsCompatible (const boost::asio::ip::address& other) const
{
return (IsV4 () && other.is_v4 ()) ||
(IsV6 () && other.is_v6 ());
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 IsNTCP2() const { return transportStyle == eTransportNTCP; };
bool IsIntroducer () const { return caps & eSSUIntroducer; };
bool IsPeerTesting () const { return caps & eSSUTesting; };
bool IsSSU2() const { return transportStyle == eTransportSSU2; };
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 ()); };
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);
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 ();
RouterInfo(const std::string &fullPath);
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
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;
RouterInfo(const RouterInfo &) = default;
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);
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 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 IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; };
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;
RouterInfo &operator=(const RouterInfo &) = default;
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps) { m_Caps = caps; };
RouterInfo(std::shared_ptr<Buffer> &&buf, size_t len);
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
bool IsUnreachable () const { return m_IsUnreachable; };
RouterInfo(const uint8_t *buf, size_t len);
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; };
virtual ~RouterInfo();
bool IsUpdated () const { return m_IsUpdated; };
void SetUpdated (bool updated) { m_IsUpdated = updated; };
bool SaveToFile (const std::string& fullPath);
std::shared_ptr<const IdentityEx> GetRouterIdentity() const { return m_RouterIdentity; };
std::shared_ptr<RouterProfile> GetProfile () const;
void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); };
void SetRouterIdentity(std::shared_ptr<const IdentityEx> identity);
void Update (const uint8_t * buf, size_t len);
void DeleteBuffer () { m_Buffer = nullptr; };
bool IsNewer (const uint8_t * buf, size_t len) const;
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
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);
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 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 IsReachableBy(CompatibleTransports transports) const { return m_ReachableTransports & transports; };
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 */
bool IsFamily (FamilyID famid) const;
bool IsFamily(FamilyID famid) const;
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_RouterIdentity; };
void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
std::shared_ptr<const IdentityEx> GetIdentity() const { return m_RouterIdentity; };
bool IsDestination () const { return false; };
void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
bool IsDestination() const { return false; };
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; };
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;
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;
std::shared_ptr<const Address> GetAddress(Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer() const;
private:
@ -298,7 +381,7 @@ namespace data
std::shared_ptr<Buffer> m_Buffer;
size_t m_BufferLen;
uint64_t m_Timestamp;
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
boost::shared_ptr <Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
bool m_IsUpdated, m_IsUnreachable;
CompatibleTransports m_SupportedTransports, m_ReachableTransports;
uint8_t m_Caps;
@ -306,34 +389,42 @@ 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);
LocalRouterInfo() = default;
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 (); };
void CreateBuffer(const PrivateKeys &privateKeys);
bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
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;
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:
std::map<std::string, std::string> m_Properties;
};
}
}
}
#endif

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,99 +36,145 @@ 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,
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,
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);
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; };
void CreateDirectSession(std::shared_ptr<const i2p::data::RouterInfo> router,
boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
uint16_t GetPort () const { return m_Endpoint.port (); };
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
std::shared_ptr<SSUSession> FindSession(const boost::asio::ip::udp::endpoint &e) const;
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 ();
std::shared_ptr<SSUSession> GetRandomEstablishedV4Session(std::shared_ptr<const SSUSession> excluded);
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);
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 HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions);
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 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);
void CreateSessionThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest = false);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter);
std::shared_ptr<SSUSession> GetRandomV4Session(Filter filter);
template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV6Session (Filter 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);
void ScheduleIntroducersUpdateTimer ();
void ScheduleIntroducersUpdateTimerV6 ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
std::list<std::shared_ptr<SSUSession> >
FindIntroducers(int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash> &excluded);
void SchedulePeerTestsCleanupTimer ();
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
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);
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
};
volatile bool m_IsRunning;
std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6;
std::thread *m_Thread, *m_ReceiversThread, *m_ReceiversThreadV6;
boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6;
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
@ -150,10 +194,16 @@ 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; };
};
}
}
}
#endif

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,94 +24,129 @@ 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 (); };
ReceiveService(const std::string &name) : RunnableService(name) {};
boost::asio::io_service &GetService() { return GetIOService(); };
void Start() { StartIOService(); };
void Stop() { StopIOService(); };
};
public:
SSU2Server ();
~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; };
~SSU2Server() {};
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 Start();
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
void RemoveRelay (uint32_t tag);
std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag);
void Stop();
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);
boost::asio::io_service &GetService() { return GetIOService(); };
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
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);
bool StartPeerTest(std::shared_ptr<const i2p::data::RouterInfo> router, bool v4);
void RescheduleIntroducersUpdateTimer ();
void RescheduleIntroducersUpdateTimerV6 ();
void UpdateOutgoingToken(const boost::asio::ip::udp::endpoint &ep, uint64_t token, uint32_t exp);
i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; };
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; };
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);
boost::asio::ip::udp::socket &OpenSocket(const boost::asio::ip::udp::endpoint &localEndpoint);
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void Receive(boost::asio::ip::udp::socket &socket);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void HandleReceivedFrom(const boost::system::error_code &ecode, size_t bytes_transferred,
Packet *packet, boost::asio::ip::udp::socket &socket);
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;
void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6 ();
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;
void UpdateIntroducers(bool v4);
void ScheduleIntroducersUpdateTimer();
void HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6();
private:
@ -137,9 +170,12 @@ namespace transport
public:
// for HTTP/I2PControl
const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; };
const decltype(m_Sessions)
&
GetSSU2Sessions() const { return m_Sessions; };
};
}
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -20,16 +20,14 @@
#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
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds
const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds
const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52 * 60; // for next token block, in seconds
const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 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,13 +124,12 @@ namespace transport
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70
};
enum SSU2TerminationReason
{
enum SSU2TerminationReason {
eSSU2TerminationReasonNormalClose = 0,
eSSU2TerminationReasonTerminationReceived = 1,
eSSU2TerminationReasonIdleTimeout = 2,
eSSU2TerminationReasonRouterShutdown = 3,
eSSU2TerminationReasonDataPhaseAEADFailure= 4,
eSSU2TerminationReasonDataPhaseAEADFailure = 4,
eSSU2TerminationReasonIncompatibleOptions = 5,
eSSU2TerminationReasonTncompatibleSignatureType = 6,
eSSU2TerminationReasonClockSkew = 7,
@ -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;
@ -172,11 +162,10 @@ namespace transport
uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
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,123 +190,200 @@ namespace transport
} h;
};
struct HandshakePacket
{
struct HandshakePacket {
Header header;
uint8_t headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
uint8_t payload[SSU2_MAX_PACKET_SIZE * 2];
size_t payloadSize = 0;
uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
};
typedef std::function<void ()> OnEstablished;
typedef std::function<void()> OnEstablished;
public:
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
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; };
~SSU2Session();
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; };
void SetRemoteEndpoint(const boost::asio::ip::udp::endpoint &ep) { m_RemoteEndpoint = ep; };
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);
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 Terminate();
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
void Established();
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 SendPathResponse (const uint8_t * data, size_t len);
void ScheduleConnectTimer();
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);
void HandleConnectTimer(const boost::system::error_code &ecode);
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 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 CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (uint8_t * buf, size_t len);
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 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 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 CreatePeerTestBlock(uint8_t *buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock(uint8_t *buf, size_t len);
private:
SSU2Server& m_Server;
SSU2Server &m_Server;
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice
@ -334,8 +398,8 @@ namespace transport
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
std::map<uint32_t, std::pair<std::shared_ptr<SSU2Session>, uint64_t> > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair<std::shared_ptr<SSU2Session>, uint64_t> > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived;
@ -347,13 +411,12 @@ 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);
i2p::crypto::ChaCha20((uint8_t * ) & data, 8, kh, nonce, (uint8_t * ) & data);
return data;
}
}
}
}
#endif

View file

@ -13,129 +13,105 @@
#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)
{
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
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;
msg = newMsg;
}
if (msg->Concat (fragment, fragmentSize) < fragmentSize)
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
if (msg->Concat(fragment, fragmentSize) < fragmentSize)
LogPrint(eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
nextFragmentNum++;
}
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)
{
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) {
}
SSUData::~SSUData ()
{
SSUData::~SSUData() {
}
void SSUData::Start ()
{
void SSUData::Start() {
}
void SSUData::Stop ()
{
m_ResendTimer.cancel ();
m_IncompleteMessages.clear ();
m_SentMessages.clear ();
m_ReceivedMessages.clear ();
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 (m_Session.IsV6 ())
auto ssuAddress = remoteRouter->GetSSUAddress();
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
{
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
LogPrint(eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
} else {
LogPrint(eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
m_PacketSize = m_MaxPacketSize;
}
}
}
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
{
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
void SSUData::UpdatePacketSize(const i2p::data::IdentHash &remoteIdent) {
auto routerInfo = i2p::data::netdb.FindRouter(remoteIdent);
if (routerInfo)
AdjustPacketSize (routerInfo);
AdjustPacketSize(routerInfo);
}
void SSUData::ProcessSentMessageAck (uint32_t msgID)
{
auto it = m_SentMessages.find (msgID);
if (it != m_SentMessages.end ())
{
m_SentMessages.erase (it);
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
void SSUData::ProcessSentMessageAck(uint32_t msgID) {
auto it = m_SentMessages.find(msgID);
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;
uint8_t numAcks = *buf;
buf++;
for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (bufbe32toh (buf+i*4));
buf += numAcks*4;
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;
uint8_t numBitfields = *buf;
buf++;
for (int i = 0; i < numBitfields; i++)
{
uint32_t msgID = bufbe32toh (buf);
for (int i = 0; i < numBitfields; i++) {
uint32_t msgID = bufbe32toh(buf);
buf += 4; // msgID
auto it = m_SentMessages.find (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 ())
{
int numSentFragments = it->second->fragments.size ();
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,195 +120,172 @@ 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++)
{
uint32_t msgID = bufbe32toh (buf); // message ID
for (int i = 0; i < numFragments; i++) {
uint32_t msgID = bufbe32toh(buf); // message ID
buf += 4;
uint8_t frag[4] = {0};
memcpy (frag + 1, buf, 3);
memcpy(frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
uint32_t fragmentInfo = bufbe32toh(frag); // fragment info
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)
{
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU 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 ())
{
auto it = m_IncompleteMessages.find(msgID);
if (it == m_IncompleteMessages.end()) {
// create new message
auto msg = NewI2NPShortMessage ();
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;
it = m_IncompleteMessages.insert(std::make_pair(msgID,
m_Session.GetServer().GetIncompleteMessagesPool().AcquireShared(
std::move(msg)))).first;
}
auto& incompleteMessage = it->second;
auto &incompleteMessage = it->second;
// mark fragment as received
if (fragmentNum < 64)
incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum);
else
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
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 ())
{
incompleteMessage->AttachNextFragment(buf, fragmentSize);
if (!isLast && !incompleteMessage->savedFragments.empty()) {
// try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
{
auto& savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
{
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
for (auto it1 = incompleteMessage->savedFragments.begin();
it1 != incompleteMessage->savedFragments.end();) {
auto &savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) {
incompleteMessage->AttachNextFragment(savedFragment->buf, savedFragment->len);
isLast = savedFragment->isLast;
incompleteMessage->savedFragments.erase (it1++);
}
else
incompleteMessage->savedFragments.erase(it1++);
} else
break;
}
if (isLast)
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete");
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);
if (incompleteMessage->savedFragments.insert (savedFragment).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
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;
m_IncompleteMessages.erase (msgID);
m_IncompleteMessages.erase(msgID);
// process message
SendMsgAck (msgID);
msg->FromSSU (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 ())
{
m_Handler.PutNextMessage (std::move (msg));
}
else
LogPrint (eLogDebug, "SSU: message expired");
}
else
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
}
else
{
SendMsgAck(msgID);
msg->FromSSU(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()) {
m_Handler.PutNextMessage(std::move(msg));
} else
LogPrint(eLogDebug, "SSU: message expired");
} else
LogPrint(eLogWarning, "SSU: Message ", msgID, " already received");
} else {
// we expect DeliveryStatus
if (msg->GetTypeID () == eI2NPDeliveryStatus)
{
LogPrint (eLogDebug, "SSU: session established");
m_Session.Established ();
if (msg->GetTypeID() == eI2NPDeliveryStatus) {
LogPrint(eLogDebug, "SSU: session established");
m_Session.Established();
} else
LogPrint(eLogError, "SSU: unexpected message ", (int) msg->GetTypeID());
}
else
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ());
}
}
else
SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits);
} else
SendFragmentAck(msgID, incompleteMessage->receivedFragmentsBits);
buf += fragmentSize;
}
}
void SSUData::FlushReceivedMessage ()
{
m_Handler.Flush ();
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++;
LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len);
LogPrint(eLogDebug, "SSU: Process data, flags=", (int) flag, ", len=", len);
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
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");
LogPrint(eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
buf += extendedDataSize;
}
// process data
ProcessFragments (buf);
ProcessFragments(buf);
}
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{
uint32_t msgID = msg->ToSSU ();
if (m_SentMessages.find (msgID) != m_SentMessages.end())
{
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
void SSUData::Send(std::shared_ptr<i2p::I2NPMessage> msg) {
uint32_t msgID = msg->ToSSU();
if (m_SentMessages.find(msgID) != m_SentMessages.end()) {
LogPrint(eLogWarning, "SSU: message ", msgID, " already sent");
return;
}
if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
if (m_SentMessages.empty()) // schedule resend at first message only
ScheduleResend();
auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ());
auto& sentMessage = ret.first->second;
if (ret.second)
{
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
auto ret = m_SentMessages.emplace(msgID, m_Session.GetServer().GetSentMessagesPool().AcquireShared());
auto &sentMessage = ret.first->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 len = msg->GetLength ();
uint8_t * msgBuf = msg->GetSSUHeader ();
auto &fragments = sentMessage->fragments;
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)
{
auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared ();
while (len > 0 && fragmentNum <= 127) {
auto fragment = m_Session.GetServer().GetFragmentsPool().AcquireShared();
fragment->fragmentNum = fragmentNum;
uint8_t * payload = fragment->buf + sizeof (SSUHeader);
uint8_t *payload = fragment->buf + sizeof(SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++;
*payload = 1; // always 1 message fragment per message
payload++;
htobe32buf (payload, msgID);
htobe32buf(payload, msgID);
payload += 4;
bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max
size_t size = isLast ? len : payloadSize;
@ -341,176 +294,155 @@ namespace transport
fragmentInfo |= 0x010000;
fragmentInfo |= size;
fragmentInfo = htobe32 (fragmentInfo);
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
fragmentInfo = htobe32(fragmentInfo);
memcpy(payload, (uint8_t * )(&fragmentInfo) + 1, 3);
payload += 3;
memcpy (payload, msgBuf, size);
memcpy(payload, msgBuf, size);
size += payload - fragment->buf;
uint8_t rem = size & 0x0F;
if (rem) // make sure 16 bytes boundary
{
auto padding = 16 - rem;
memset (fragment->buf + size, 0, padding);
memset(fragment->buf + size, 0, padding);
size += padding;
}
fragment->len = size;
fragments.push_back (fragment);
fragments.push_back(fragment);
// 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
{
m_Session.Send (buf, size);
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
try {
m_Session.Send(buf, size);
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
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);
uint8_t *payload = buf + sizeof(SSUHeader);
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACKs
payload++;
htobe32buf (payload, msgID); // msgID
htobe32buf(payload, msgID); // msgID
payload += 4;
*payload = 0; // number of fragments
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
m_Session.Send (buf, 48);
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, 48);
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);
uint8_t *payload = buf + sizeof(SSUHeader);
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACK bitfields
payload++;
// one ack
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
*(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
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send (buf, len);
m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send(buf, len);
}
void SSUData::ScheduleResend()
{
m_ResendTimer.cancel ();
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
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 ();
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& f: it->second->fragments)
if (f)
{
try
{
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
m_Session.Send (buf, f->len); // resend
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 {
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->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");
it = m_SentMessages.erase(it);
}
} else
++it;
}
else
{
LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted");
it = m_SentMessages.erase (it);
}
}
else
++it;
}
if (m_SentMessages.empty ()) return; // nothing to resend
if (m_SentMessages.empty()) return; // nothing to resend
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
ScheduleResend ();
else
{
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated");
m_Session.Close ();
ScheduleResend();
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");
it = m_IncompleteMessages.erase (it);
}
else
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
++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
{
m_ReceivedMessages.clear();
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);
it = m_ReceivedMessages.erase(it);
else
++it;
}
}
}
}
}
}

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,79 +42,88 @@ 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); };
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
uint64_t receivedFragmentsBits;
std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (std::shared_ptr<I2NPMessage>&& m): msg (m), nextFragmentNum (0),
lastFragmentInsertTime (0), receivedFragmentsBits (0) {};
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
IncompleteMessage(std::shared_ptr<I2NPMessage> &&m) : msg(m), nextFragmentNum(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 ();
SSUData(SSUSession &session);
void Start ();
void Stop ();
void CleanUp (uint64_t ts);
~SSUData();
void ProcessMessage (uint8_t * buf, size_t len);
void FlushReceivedMessage ();
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
void Start();
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter);
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
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 SendMsgAck(uint32_t msgID);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
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:
SSUSession& m_Session;
SSUSession &m_Session;
std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages;
std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds
@ -125,7 +132,7 @@ namespace transport
i2p::I2NPMessagesHandler m_Handler;
uint32_t m_LastMessageReceivedTime; // in second
};
}
}
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -17,20 +17,19 @@
#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; };
uint8_t GetPayloadType() const { return flag >> 4; };
bool IsExtendedOptions() const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
};
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
@ -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,87 +69,132 @@ 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,
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,
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; };
void WaitForIntroduction();
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void SendPeerTest (); // Alice
void Close();
SessionState GetState () const { return m_State; };
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void Done();
void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
void Failed();
void FlushData ();
void CleanUp (uint64_t ts);
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 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 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);
boost::asio::io_service &GetService();
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 CreateAESandMacKey(const uint8_t *pubKey);
void Reset ();
size_t GetSSUHeaderSize(const uint8_t *buf) const;
static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size
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 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 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
private:
friend class SSUData; // TODO: change in later
SSUServer& m_Server;
SSUServer &m_Server;
const boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest;
@ -168,10 +210,10 @@ namespace transport
SSUData m_Data;
bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
std::map<uint32_t, std::pair <std::shared_ptr<const i2p::data::RouterInfo>, uint64_t > > m_RelayRequests; // nonce->(Charlie, timestamp)
std::map<uint32_t, std::pair<std::shared_ptr<const i2p::data::RouterInfo>, uint64_t> > m_RelayRequests; // nonce->(Charlie, timestamp)
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
};
}
}
}
#endif

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,63 +36,58 @@ namespace crypto
}
#else
EDDSA25519Verifier::EDDSA25519Verifier ()
{
EDDSA25519Verifier::EDDSA25519Verifier() {
}
EDDSA25519Verifier::~EDDSA25519Verifier ()
{
EDDSA25519Verifier::~EDDSA25519Verifier() {
}
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);
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);
SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
SHA512_Init(&ctx);
SHA512_Update(&ctx, signature, EDDSA25519_SIGNATURE_LENGTH / 2); // R
SHA512_Update(&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update(&ctx, buf, len); // data
SHA512_Final(digest, &ctx);
return GetEd25519 ()->Verify (m_PublicKey, digest, signature);
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);
Ed25519::ExpandPrivateKey(signingPrivateKey, m_ExpandedPrivateKey);
// generate and encode public key
BN_CTX * ctx = BN_CTX_new ();
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
BN_CTX *ctx = BN_CTX_new();
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");
LogPrint(eLogWarning, "Older EdDSA key detected");
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit
publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx);
GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx);
}
BN_CTX_free (ctx);
BN_CTX_free(ctx);
}
EDDSA25519SignerCompat::~EDDSA25519SignerCompat ()
{
EDDSA25519SignerCompat::~EDDSA25519SignerCompat() {
}
void EDDSA25519SignerCompat::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
void EDDSA25519SignerCompat::Sign(const uint8_t *buf, int len, uint8_t *signature) const {
GetEd25519()->Sign(m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
}
#if OPENSSL_EDDSA
@ -136,5 +129,5 @@ namespace crypto
}
}
#endif
}
}
}

View file

@ -19,241 +19,232 @@
#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;
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;
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
{
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH / 2;
class DSAVerifier : public Verifier {
public:
DSAVerifier ()
{
m_PublicKey = CreateDSA ();
DSAVerifier() {
m_PublicKey = CreateDSA();
}
void SetPublicKey (const uint8_t * signingKey)
{
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
void SetPublicKey(const uint8_t *signingKey) {
DSA_set0_key(m_PublicKey, BN_bin2bn(signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
}
~DSAVerifier ()
{
DSA_free (m_PublicKey);
~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);
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 *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 verification
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
int ret = DSA_do_verify(digest, 20, sig, m_PublicKey);
DSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; };
size_t GetPublicKeyLen() const { return DSA_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen() const { return DSA_SIGNATURE_LENGTH; };
private:
DSA * m_PublicKey;
DSA *m_PublicKey;
};
class DSASigner: public Signer
{
class DSASigner : public Signer {
public:
DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
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));
m_PrivateKey = CreateDSA();
DSA_set0_key(m_PrivateKey, BN_bin2bn(signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL),
BN_bin2bn(signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
}
~DSASigner ()
{
DSA_free (m_PrivateKey);
~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);
const BIGNUM * r, * s;
DSA_SIG_get0 (sig, &r, &s);
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
SHA1(buf, len, digest);
DSA_SIG *sig = DSA_do_sign(digest, 20, m_PrivateKey);
const BIGNUM *r, *s;
DSA_SIG_get0(sig, &r, &s);
bn2buf(r, signature, DSA_SIGNATURE_LENGTH / 2);
bn2buf(s, signature + DSA_SIGNATURE_LENGTH / 2, DSA_SIGNATURE_LENGTH / 2);
DSA_SIG_free(sig);
}
private:
DSA * m_PrivateKey;
DSA *m_PrivateKey;
};
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
DSA * dsa = CreateDSA ();
DSA_generate_key (dsa);
const BIGNUM * pub_key, * priv_key;
inline void CreateDSARandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
DSA *dsa = CreateDSA();
DSA_generate_key(dsa);
const BIGNUM *pub_key, *priv_key;
DSA_get0_key(dsa, &pub_key, &priv_key);
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa);
bn2buf(priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf(pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free(dsa);
}
struct SHA256Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
SHA256 (buf, len, 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)
{
SHA384 (buf, len, 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)
{
SHA512 (buf, len, 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 ()
{
m_PublicKey = EC_KEY_new_by_curve_name (curve);
ECDSAVerifier() {
m_PublicKey = EC_KEY_new_by_curve_name(curve);
}
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);
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);
}
~ECDSAVerifier ()
{
EC_KEY_free (m_PublicKey);
~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();
auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
Hash::CalculateHash(buf, len, digest);
ECDSA_SIG *sig = ECDSA_SIG_new();
auto r = BN_bin2bn(signature, GetSignatureLen() / 2, NULL);
auto s = BN_bin2bn(signature + GetSignatureLen() / 2, GetSignatureLen() / 2, NULL);
ECDSA_SIG_set0(sig, r, s);
// ECDSA verification
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
int ret = ECDSA_do_verify(digest, Hash::hashLen, sig, m_PublicKey);
ECDSA_SIG_free(sig);
return ret;
}
size_t GetPublicKeyLen () const { return keyLen; };
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
size_t GetPublicKeyLen() const { return keyLen; };
size_t GetSignatureLen() const { return keyLen; }; // signature length = key length
private:
EC_KEY * m_PublicKey;
EC_KEY *m_PublicKey;
};
template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer
{
class ECDSASigner : public Signer {
public:
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(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 ()
{
EC_KEY_free (m_PrivateKey);
~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);
const BIGNUM * r, * s;
ECDSA_SIG_get0 (sig, &r, &s);
Hash::CalculateHash(buf, len, digest);
ECDSA_SIG *sig = ECDSA_do_sign(digest, Hash::hashLen, m_PrivateKey);
const BIGNUM *r, *s;
ECDSA_SIG_get0(sig, &r, &s);
// signatureLen = keyLen
bn2buf (r, signature, keyLen/2);
bn2buf (s, signature + keyLen/2, keyLen/2);
bn2buf(r, signature, keyLen / 2);
bn2buf(s, signature + keyLen / 2, keyLen / 2);
ECDSA_SIG_free(sig);
}
private:
EC_KEY * m_PrivateKey;
EC_KEY *m_PrivateKey;
};
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);
BIGNUM * x = BN_new(), * y = BN_new();
EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey),
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);
EC_KEY_free (signingKey);
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);
BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(signingKey),
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);
EC_KEY_free(signingKey);
}
// ECDSA_SHA256_P256
@ -261,9 +252,8 @@ 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)
{
CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
inline void CreateECDSAP256RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
CreateECDSARandomKeys(NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA384_P384
@ -271,9 +261,8 @@ 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)
{
CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
inline void CreateECDSAP384RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
CreateECDSARandomKeys(NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
}
// ECDSA_SHA512_P521
@ -281,25 +270,26 @@ 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)
{
CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, 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 ();
EDDSA25519Verifier();
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
void SetPublicKey(const uint8_t *signingKey);
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
~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,16 +301,17 @@ 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 ();
EDDSA25519SignerCompat(const uint8_t *signingPrivateKey, const uint8_t *signingPublicKey = nullptr);
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation
// 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);
@ -364,32 +354,32 @@ namespace crypto
EVP_PKEY_get_raw_private_key (pkey, signingPrivateKey, &len);
EVP_PKEY_free (pkey);
#else
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
RAND_bytes(signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer(signingPrivateKey);
memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH);
#endif
}
// ГОСТ Р 34.11
struct GOSTR3411_256_Hash
{
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest)
{
GOSTR3411_2012_256 (buf, len, 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)
{
GOSTR3411_2012_512 (buf, len, 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,97 +387,102 @@ 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)
{
GOSTR3410Verifier(GOSTR3410ParamSet paramSet) :
m_ParamSet(paramSet), m_PublicKey(nullptr) {
}
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);
}
~GOSTR3410Verifier ()
{
if (m_PublicKey) EC_POINT_free (m_PublicKey);
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);
}
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
~GOSTR3410Verifier() {
if (m_PublicKey) EC_POINT_free(m_PublicKey);
}
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);
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);
return ret;
}
size_t GetPublicKeyLen () const { return keyLen*2; }
size_t GetSignatureLen () const { return keyLen*2; }
size_t GetPublicKeyLen() const { return keyLen * 2; }
size_t GetSignatureLen() const { return keyLen * 2; }
private:
GOSTR3410ParamSet m_ParamSet;
EC_POINT * m_PublicKey;
EC_POINT *m_PublicKey;
};
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_PrivateKey = BN_bin2bn (signingPrivateKey, keyLen, nullptr);
GOSTR3410Signer(GOSTR3410ParamSet paramSet, const uint8_t *signingPrivateKey) :
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
{
~GOSTR3410Signer() { BN_free(m_PrivateKey); }
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);
BIGNUM * r = BN_new (), * s = BN_new ();
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);
Hash::CalculateHash(buf, len, digest);
BIGNUM *d = BN_bin2bn(digest, Hash::hashLen, nullptr);
BIGNUM *r = BN_new(), *s = BN_new();
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);
}
private:
GOSTR3410ParamSet m_ParamSet;
BIGNUM * m_PrivateKey;
BIGNUM *m_PrivateKey;
};
inline void CreateGOSTR3410RandomKeys (GOSTR3410ParamSet paramSet, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
const auto& curve = GetGOSTR3410Curve (paramSet);
auto keyLen = curve->GetKeyLen ();
RAND_bytes (signingPrivateKey, keyLen);
BIGNUM * priv = BN_bin2bn (signingPrivateKey, keyLen, nullptr);
inline void
CreateGOSTR3410RandomKeys(GOSTR3410ParamSet paramSet, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
const auto &curve = GetGOSTR3410Curve(paramSet);
auto keyLen = curve->GetKeyLen();
RAND_bytes(signingPrivateKey, keyLen);
BIGNUM *priv = BN_bin2bn(signingPrivateKey, keyLen, nullptr);
auto pub = curve->MulP (priv);
BN_free (priv);
BIGNUM * x = BN_new (), * y = BN_new ();
curve->GetXY (pub, x, y);
EC_POINT_free (pub);
bn2buf (x, signingPublicKey, keyLen);
bn2buf (y, signingPublicKey + keyLen, keyLen);
BN_free (x); BN_free (y);
auto pub = curve->MulP(priv);
BN_free(priv);
BIGNUM *x = BN_new(), *y = BN_new();
curve->GetXY(pub, x, y);
EC_POINT_free(pub);
bn2buf(x, signingPublicKey, keyLen);
bn2buf(y, signingPublicKey + keyLen, keyLen);
BN_free(x);
BN_free(y);
}
typedef GOSTR3410Verifier<GOSTR3411_256_Hash> GOSTR3410_256_Verifier;
@ -497,26 +492,25 @@ namespace crypto
// RedDSA
typedef EDDSA25519Verifier RedDSA25519Verifier;
class RedDSA25519Signer: public Signer
{
class RedDSA25519Signer : public Signer {
public:
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
{
GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature);
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);
}
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation
~RedDSA25519Signer() {};
void Sign(const uint8_t *buf, int len, uint8_t *signature) const {
GetEd25519()->SignRedDSA(m_PrivateKey, m_PublicKeyEncoded, buf, len, signature);
}
const uint8_t *GetPublicKey() const { return m_PublicKeyEncoded; }; // for keys creation
private:
@ -524,13 +518,12 @@ namespace crypto
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
inline void CreateRedDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey);
RedDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
inline void CreateRedDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
GetEd25519()->CreateRedDSAPrivateKey(signingPrivateKey);
RedDSA25519Signer signer(signingPrivateKey);
memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH);
}
}
}
}
#endif

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);
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;
@ -91,54 +81,52 @@ namespace crypto
const uint64_t k1 = siphash::u8to64le(key + 8);
uint64_t msg;
int i;
const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t));
const uint8_t *end = buf + bufsz - (bufsz % sizeof(uint64_t));
auto left = bufsz & 7;
uint64_t b = ((uint64_t)bufsz) << 56;
uint64_t b = ((uint64_t) bufsz) << 56;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
if(hashsz == 16) v1 ^= 0xee;
if (hashsz == 16) v1 ^= 0xee;
while(buf != end)
{
while (buf != end) {
msg = siphash::u8to64le(buf);
v3 ^= msg;
for(i = 0; i < siphash::crounds; ++i)
for (i = 0; i < siphash::crounds; ++i)
siphash::round(v0, v1, v2, v3);
v0 ^= msg;
buf += 8;
}
while(left)
{
while (left) {
--left;
b |= ((uint64_t)(buf[left])) << (left * 8);
}
v3 ^= b;
for(i = 0; i < siphash::crounds; ++i)
for (i = 0; i < siphash::crounds; ++i)
siphash::round(v0, v1, v2, v3);
v0 ^= b;
if(hashsz == 16)
if (hashsz == 16)
v2 ^= 0xee;
else
v2 ^= 0xff;
for(i = 0; i < siphash::drounds; ++i)
for (i = 0; i < siphash::drounds; ++i)
siphash::round(v0, v1, v2, v3);
b = v0 ^ v1 ^ v2 ^ v3;
siphash::u64to8le(b, h);
if(hashsz == 8) return;
if (hashsz == 8) return;
v1 ^= 0xdd;
@ -148,7 +136,7 @@ namespace crypto
b = v0 ^ v1 ^ v2 ^ v3;
siphash::u64to8le(b, h + 8);
}
}
}
}
#endif

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,82 +61,103 @@ 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; };
Packet() : len(0), offset(0), sendTime(0) {};
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 (); };
uint8_t *GetBuffer() { return buf + offset; };
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; };
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
{
return p1->GetSeqn () < p2->GetSeqn ();
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
{
uint8_t * buf;
typedef std::function<void(const boost::system::error_code &ecode)> SendHandler;
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)
{
SendBuffer(const uint8_t *b, size_t l, SendHandler h) :
len(l), offset(0), handler(h) {
buf = new uint8_t[len];
memcpy (buf, b, len);
memcpy(buf, b, len);
}
SendBuffer (size_t l): // create empty buffer
len(l), offset (0)
{
SendBuffer(size_t l) : // create empty buffer
len(l), offset(0) {
buf = new uint8_t[len];
}
~SendBuffer ()
{
~SendBuffer() {
delete[] buf;
if (handler) handler(boost::system::error_code ());
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; };
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;
};
};
class SendBufferQueue
{
class SendBufferQueue {
public:
SendBufferQueue (): m_Size (0) {};
~SendBufferQueue () { CleanUp (); };
SendBufferQueue() : m_Size(0) {};
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 ();
~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,84 +175,117 @@ 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,
Stream(boost::asio::io_service &service, StreamingDestination &local,
std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); // outgoing
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
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; };
~Stream();
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 ();
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 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 (); };
void Close();
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 Cancel() { m_ReceiveTimer.cancel(); };
void Terminate (bool deleteFromDestination = true);
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);
private:
void CleanUp ();
void CleanUp();
void SendBuffer ();
void SendQuickAck ();
void SendClose ();
bool SendPacket (Packet * packet);
void SendPackets (const std::vector<Packet *>& packets);
void SendUpdatedLeaseSet ();
void SendBuffer();
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 SendQuickAck();
void UpdateCurrentRemoteLease (bool expired = false);
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);
void ScheduleResend();
void HandleResendTimer(const boost::system::error_code &ecode);
void HandleAckSendTimer(const boost::system::error_code &ecode);
private:
boost::asio::io_service& m_Service;
boost::asio::io_service &m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
int32_t m_LastReceivedSequenceNumber;
StreamStatus m_Status;
bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination;
StreamingDestination &m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
@ -257,43 +307,61 @@ 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;
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 ();
StreamingDestination(std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0,
bool gzip = false);
void Start ();
void Stop ();
~StreamingDestination();
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);
void Start();
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 Stop();
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<Stream>
CreateNewOutgoingStream(std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
Packet * NewPacket () { return m_PacketsPool.Acquire(); }
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
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);
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);
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)
{
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
{
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 {
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
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->m_ReceiveTimer.async_wait(
[self, buffer, handler, left](const boost::system::error_code &ec) {
self->HandleReceiveTimer(ec, buffer, handler, left);
});
}
@ -347,33 +415,31 @@ 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)
{
handler(boost::system::error_code(), received);
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);
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
{
handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted), 0);
} else {
// timeout expired
if (remainingTimeout <= 0)
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
else
{
handler(boost::asio::error::make_error_code(boost::asio::error::timed_out), received);
else {
// itermediate interrupt
SendUpdatedLeaseSet (); // send our leaseset if applicable
AsyncReceive (buffer, handler, remainingTimeout);
SendUpdatedLeaseSet(); // send our leaseset if applicable
AsyncReceive(buffer, handler, remainingTimeout);
}
}
}
}
}
}
#endif

View file

@ -15,69 +15,68 @@
#include "Base.h"
namespace i2p {
namespace data {
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); }
Tag() = default;
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; }
Tag(const uint8_t *buf) { memcpy(m_Buf, buf, sz); }
uint8_t * operator()() { return m_Buf; }
const uint8_t * operator()() const { return m_Buf; }
bool operator==(const Tag &other) const { return !memcmp(m_Buf, other.m_Buf, sz); }
operator uint8_t * () { return m_Buf; }
operator const uint8_t * () const { return m_Buf; }
bool operator!=(const Tag &other) const { return !(*this == other); }
const uint8_t * data() const { return m_Buf; }
const uint64_t * GetLL () const { return ll; }
bool operator<(const Tag &other) const { return memcmp(m_Buf, other.m_Buf, sz) < 0; }
bool IsZero () const
{
for (size_t i = 0; i < sz/8; ++i)
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 {
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
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2);
return std::string (str, str + l);
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
{
char str[sz*2];
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, len, str, sz*2);
return std::string (str, str + l);
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)
{
return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
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)
{
return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
size_t FromBase64(const std::string &s) {
return i2p::data::Base64ToByteStream(s.c_str(), s.length(), m_Buf, sz);
}
private:
@ -85,20 +84,18 @@ namespace data {
union // 8 bytes aligned
{
uint8_t m_Buf[sz];
uint64_t ll[sz/8];
uint64_t ll[sz / 8];
};
};
} // 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
{
return s.GetLL ()[0];
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

@ -22,225 +22,184 @@
#include "util.h"
#ifdef _WIN32
#ifndef _WIN64
#define _USE_32BIT_TIME_T
#endif
#ifndef _WIN64
#define _USE_32BIT_TIME_T
#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 ();
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 ();
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 ();
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 ();
std::chrono::system_clock::now().time_since_epoch()).count();
}
static int64_t g_TimeOffset = 0; // in seconds
static void SyncTimeWithNTP (const std::string& address)
{
LogPrint (eLogInfo, "Timestamp: NTP request to ", 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)
{
auto it = boost::asio::ip::udp::resolver(service).resolve(
boost::asio::ip::udp::resolver::query(address, "ntp"), 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 (i2p::context.SupportsV4 ()) found = true;
}
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;
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())) {
if (i2p::context.SupportsMesh()) found = true;
} else if (i2p::context.SupportsV6()) found = true;
}
}
if (found) break;
it++;
}
if (!found)
{
LogPrint (eLogError, "Timestamp: can't find compatible address for ", address);
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)
{
boost::asio::ip::udp::socket socket(service);
socket.open(ep.protocol(), ec);
if (!ec) {
uint8_t buf[48];// 48 bytes NTP request/response
memset (buf, 0, 48);
htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330
memset(buf, 0, 48);
htobe32buf(buf, (3 << 27) | (3 << 24)); // RFC 4330
size_t len = 0;
try
{
socket.send_to (boost::asio::buffer (buf, 48), ep);
try {
socket.send_to(boost::asio::buffer(buf, 48), ep);
int i = 0;
while (!socket.available() && i < 10) // 10 seconds max
{
std::this_thread::sleep_for (std::chrono::seconds(1));
std::this_thread::sleep_for(std::chrono::seconds(1));
i++;
}
if (socket.available ())
len = socket.receive_from (boost::asio::buffer (buf, 48), ep);
if (socket.available())
len = socket.receive_from(boost::asio::buffer(buf, 48), ep);
}
catch (std::exception& e)
{
LogPrint (eLogError, "Timestamp: NTP error: ", e.what ());
catch (std::exception &e) {
LogPrint(eLogError, "Timestamp: NTP error: ", e.what());
}
if (len >= 8)
{
auto ourTs = GetLocalSecondsSinceEpoch ();
uint32_t ts = bufbe32toh (buf + 32);
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
LogPrint (eLogError, "Timestamp: Couldn't open UDP socket");
}
else
LogPrint (eLogError, "Timestamp: Couldn't resolve address ", address);
} else
LogPrint(eLogError, "Timestamp: Couldn't open UDP socket");
} 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);
boost::split (m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on);
std::string ntpservers;
i2p::config::GetOption("nettime.ntpservers", ntpservers);
boost::split(m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on);
}
NTPTimeSync::~NTPTimeSync ()
{
Stop ();
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
LogPrint (eLogWarning, "Timestamp: No NTP server found");
m_Service.post(std::bind(&NTPTimeSync::Sync, this));
m_Thread.reset(new std::thread(std::bind(&NTPTimeSync::Run, this)));
} 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)
{
m_Thread->join ();
m_Thread.reset (nullptr);
m_Timer.cancel();
m_Service.stop();
if (m_Thread) {
m_Thread->join();
m_Thread.reset(nullptr);
}
}
}
void NTPTimeSync::Run ()
{
void NTPTimeSync::Run() {
i2p::util::SetThreadName("Timesync");
while (m_IsRunning)
{
try
{
m_Service.run ();
while (m_IsRunning) {
try {
m_Service.run();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Timestamp: NTP time sync exception: ", ex.what ());
catch (std::exception &ex) {
LogPrint(eLogError, "Timestamp: NTP time sync exception: ", ex.what());
}
}
}
void NTPTimeSync::Sync ()
{
if (m_NTPServersList.size () > 0)
SyncTimeWithNTP (m_NTPServersList[rand () % m_NTPServersList.size ()]);
void NTPTimeSync::Sync() {
if (m_NTPServersList.size() > 0)
SyncTimeWithNTP(m_NTPServersList[rand() % m_NTPServersList.size()]);
else
m_IsRunning = false;
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)
{
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) {
if (ecode != boost::asio::error::operation_aborted)
Sync ();
Sync();
});
}
}
uint64_t GetMillisecondsSinceEpoch ()
{
return GetLocalMillisecondsSinceEpoch () + g_TimeOffset*1000;
uint64_t GetMillisecondsSinceEpoch() {
return GetLocalMillisecondsSinceEpoch() + g_TimeOffset * 1000;
}
uint64_t GetSecondsSinceEpoch ()
{
return GetLocalSecondsSinceEpoch () + g_TimeOffset;
uint64_t GetSecondsSinceEpoch() {
return GetLocalSecondsSinceEpoch() + g_TimeOffset;
}
uint32_t GetMinutesSinceEpoch ()
{
return GetLocalMinutesSinceEpoch () + g_TimeOffset/60;
uint32_t GetMinutesSinceEpoch() {
return GetLocalMinutesSinceEpoch() + g_TimeOffset / 60;
}
uint32_t GetHoursSinceEpoch ()
{
return GetLocalHoursSinceEpoch () + g_TimeOffset/3600;
uint32_t GetHoursSinceEpoch() {
return GetLocalHoursSinceEpoch() + g_TimeOffset / 3600;
}
void GetCurrentDate (char * date)
{
GetDateString (GetSecondsSinceEpoch (), 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)));
auto t = clock::to_time_t(clock::time_point(std::chrono::seconds(timestamp)));
struct tm tm;
#ifdef _WIN32
gmtime_s(&tm, &t);
@ -251,9 +210,8 @@ namespace util
#endif
}
void AdjustTimeOffset (int64_t offset)
{
void AdjustTimeOffset(int64_t offset) {
g_TimeOffset += offset;
}
}
}
}

View file

@ -15,44 +15,48 @@
#include <string>
#include <boost/asio.hpp>
namespace i2p
{
namespace util
{
uint64_t GetMillisecondsSinceEpoch ();
uint64_t GetSecondsSinceEpoch ();
uint32_t GetMinutesSinceEpoch ();
uint32_t GetHoursSinceEpoch ();
namespace i2p {
namespace util {
uint64_t GetMillisecondsSinceEpoch();
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 AdjustTimeOffset (int64_t offset); // in seconds from current
uint64_t GetSecondsSinceEpoch();
class NTPTimeSync
{
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 AdjustTimeOffset(int64_t offset); // in seconds from current
class NTPTimeSync {
public:
NTPTimeSync ();
~NTPTimeSync ();
NTPTimeSync();
void Start ();
void Stop ();
~NTPTimeSync();
void Start();
void Stop();
private:
void Run ();
void Sync ();
void Run();
void Sync();
private:
bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
std::unique_ptr <std::thread> m_Thread;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
int m_SyncInterval;
std::vector<std::string> m_NTPServersList;
std::vector <std::string> m_NTPServersList;
};
}
}
}
#endif

View file

@ -15,104 +15,88 @@
#include "Transports.h"
#include "TransitTunnel.h"
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)
{
m_Encryption.SetKeys (layerKey, ivKey);
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) {
m_Encryption.SetKeys(layerKey, ivKey);
}
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);
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)
{
EncryptTunnelMsg (tunnelMsg, tunnelMsg);
void TransitTunnelParticipant::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
EncryptTunnelMsg(tunnelMsg, tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ());
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData);
m_TunnelDataMsgs.push_back (tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength();
htobe32buf(tunnelMsg->GetPayload(), GetNextTunnelID());
tunnelMsg->FillI2NPMessageHeader(eI2NPTunnelData);
m_TunnelDataMsgs.push_back(tunnelMsg);
}
void TransitTunnelParticipant::FlushTunnelDataMsgs ()
{
if (!m_TunnelDataMsgs.empty ())
{
auto num = m_TunnelDataMsgs.size ();
void TransitTunnelParticipant::FlushTunnelDataMsgs() {
if (!m_TunnelDataMsgs.empty()) {
auto num = m_TunnelDataMsgs.size();
if (num > 1)
LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num);
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs);
m_TunnelDataMsgs.clear ();
LogPrint(eLogDebug, "TransitTunnel: ", GetTunnelID(), "->", GetNextTunnelID(), " ", num);
i2p::transport::transports.SendMessages(GetNextIdentHash(), m_TunnelDataMsgs);
m_TunnelDataMsgs.clear();
}
}
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
{
LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ());
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)
{
LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ());
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;
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.PutTunnelDataMsg (block);
m_Gateway.PutTunnelDataMsg(block);
}
void TransitTunnelGateway::FlushTunnelDataMsgs ()
{
void TransitTunnelGateway::FlushTunnelDataMsgs() {
std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendBuffer ();
m_Gateway.SendBuffer();
}
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg)
{
auto newMsg = CreateEmptyTunnelDataMsg (true);
EncryptTunnelMsg (tunnelMsg, newMsg);
void TransitTunnelEndpoint::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
auto newMsg = CreateEmptyTunnelDataMsg(true);
EncryptTunnelMsg(tunnelMsg, newMsg);
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
LogPrint(eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID());
m_Endpoint.HandleDecryptedTunnelDataMsg(newMsg);
}
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)
{
LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
return std::make_shared<TransitTunnelEndpoint> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
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) {
LogPrint(eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
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 {
LogPrint(eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return std::make_shared<TransitTunnelParticipant>(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
{
LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
return std::make_shared<TransitTunnelParticipant> (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
}
}
}
}

View file

@ -19,43 +19,45 @@
#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,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey);
TransitTunnel(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey);
virtual size_t GetNumTransmittedBytes () const { return 0; };
virtual size_t GetNumTransmittedBytes() const { return 0; };
// 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);
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,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes (0) {};
~TransitTunnelParticipant ();
TransitTunnelParticipant(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes(0) {};
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg);
void FlushTunnelDataMsgs ();
~TransitTunnelParticipant();
size_t GetNumTransmittedBytes() const { return m_NumTransmittedBytes; };
void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
void FlushTunnelDataMsgs();
private:
@ -63,19 +65,20 @@ namespace tunnel
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
};
class TransitTunnelGateway: public TransitTunnel
{
class TransitTunnelGateway : public TransitTunnel {
public:
TransitTunnelGateway (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
TransitTunnelGateway(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
void FlushTunnelDataMsgs ();
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
void SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg);
void FlushTunnelDataMsgs();
size_t GetNumTransmittedBytes() const { return m_Gateway.GetNumSentBytes(); };
private:
@ -83,31 +86,31 @@ namespace tunnel
TunnelGateway m_Gateway;
};
class TransitTunnelEndpoint: public TransitTunnel
{
class TransitTunnelEndpoint : public TransitTunnel {
public:
TransitTunnelEndpoint (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound
TransitTunnelEndpoint(uint32_t receiveTunnelID,
const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint(false) {}; // transit endpoint is always outbound
void Cleanup () { m_Endpoint.Cleanup (); }
void Cleanup() { m_Endpoint.Cleanup(); }
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
size_t GetNumTransmittedBytes() const { return m_Endpoint.GetNumReceivedBytes(); }
private:
TunnelEndpoint m_Endpoint;
};
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey,
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);
}
}
}
#endif

View file

@ -20,48 +20,40 @@
#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)
{
m_Stream << other.m_Stream.rdbuf ();
SignedData() {}
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)
{
m_Stream.write ((char *)buf, len);
void Insert(const uint8_t *buf, size_t len) {
m_Stream.write((char *) buf, len);
}
template<typename T>
void Insert (T t)
{
m_Stream.write ((char *)&t, sizeof (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
{
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
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
{
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
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);
}
private:
@ -69,50 +61,59 @@ 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 ())
{
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()) {
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
m_RemoteIdentity = router->GetRouterIdentity();
m_CreationTime = m_LastActivityTimestamp;
}
virtual ~TransportSession () {};
virtual void Done () = 0;
virtual ~TransportSession() {};
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
virtual void Done() = 0;
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity ()
{
std::string GetIdentHashBase64() const {
return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : "";
}
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; };
size_t GetNumSentBytes() const { return m_NumSentBytes; };
int GetTerminationTimeout () const { return m_TerminationTimeout; };
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
bool IsOutgoing() const { return m_IsOutgoing; };
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;
int GetTerminationTimeout() const { return m_TerminationTimeout; };
void SetTerminationTimeout(int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
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:
@ -124,7 +125,7 @@ namespace transport
uint64_t m_LastActivityTimestamp;
uint32_t m_CreationTime; // seconds since epoch
};
}
}
}
#endif

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