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

View file

@ -14,22 +14,20 @@
// Armenian localization file // Armenian localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace armenian // language namespace
{ {
namespace armenian // language namespace
{
// language name in lowercase // language name in lowercase
static std::string language = "armenian"; static std::string language = "armenian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // 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; 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 armenian // language namespace
{"", ""}, {"", ""},
}; };
static std::map<std::string, std::vector<std::string>> plurals static std::map <std::string, std::vector<std::string>> plurals
{ {
{"days", {"օր", "օր"}}, {"days", {"օր", "օր"}},
{"hours", {"ժամ", "ժամ"}}, {"hours", {"ժամ", "ժամ"}},
@ -205,11 +203,11 @@ namespace armenian // language namespace
{"", {"", ""}}, {"", {"", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,48 +9,37 @@
#ifndef __I18N_LANGS_H__ #ifndef __I18N_LANGS_H__
#define __I18N_LANGS_H__ #define __I18N_LANGS_H__
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n class Locale {
{
class Locale
{
public: public:
Locale ( Locale(
const std::string& language, const std::string &language,
const std::map<std::string, std::string>& strings, const std::map <std::string, std::string> &strings,
const std::map<std::string, std::vector<std::string>>& plurals, const std::map <std::string, std::vector<std::string>> &plurals,
std::function<int(int)> formula 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 // Get activated language name for webconsole
std::string GetLanguage() const std::string GetLanguage() const {
{
return m_Language; 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); const auto it = m_Strings.find(arg);
if (it == m_Strings.end()) if (it == m_Strings.end()) {
{
return arg; return arg;
} } else {
else
{
return it->second; 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); const auto it = m_Plurals.find(arg2);
if (it == m_Plurals.end()) // not found, fallback to english if (it == m_Plurals.end()) // not found, fallback to english
{ {
return n == 1 ? arg : arg2; return n == 1 ? arg : arg2;
} } else {
else
{
int form = m_Formula(n); int form = m_Formula(n);
return it->second[form]; return it->second[form];
} }
@ -58,48 +47,47 @@ namespace i18n
private: private:
const std::string m_Language; const std::string m_Language;
const std::map<std::string, std::string> m_Strings; 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::vector<std::string>> m_Plurals;
std::function<int(int)> m_Formula; std::function<int(int)> m_Formula;
}; };
struct langData struct langData {
{
std::string LocaleName; // localized name std::string LocaleName; // localized name
std::string ShortCode; // short language code, like "en" 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 // Add localization here with language name as namespace
namespace afrikaans { 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 armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace chinese { 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 english { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace french { 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 german { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace russian { 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 turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale(); }
namespace ukrainian { 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 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 * 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} }, {"afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale}},
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} }, {"armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale}},
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} }, {"chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale}},
{ "english", {"English", "en", i2p::i18n::english::GetLocale} }, {"english", {"English", "en", i2p::i18n::english::GetLocale}},
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} }, {"french", {"Français", "fr", i2p::i18n::french::GetLocale}},
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} }, {"german", {"Deutsch", "de", i2p::i18n::german::GetLocale}},
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} }, {"russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale}},
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} }, {"turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale}},
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} }, {"ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale}},
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} }, {"uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale}},
}; };
} // i18n } // i18n
} // i2p } // i2p
#endif // __I18N_LANGS_H__ #endif // __I18N_LANGS_H__

View file

@ -14,22 +14,21 @@
// Russian localization file // Russian localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace russian // language namespace
{ {
namespace russian // language namespace
{
// language name in lowercase // language name in lowercase
static std::string language = "russian"; static std::string language = "russian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2; return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)
? 1 : 2;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "КиБ"}, {"KiB", "КиБ"},
{"MiB", "МиБ"}, {"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", {"день", "дня", "дней"}}, {"days", {"день", "дня", "дней"}},
{"hours", {"час", "часа", "часов"}}, {"hours", {"час", "часа", "часов"}},
@ -205,11 +204,11 @@ namespace russian // language namespace
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

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

View file

@ -14,22 +14,21 @@
// Ukrainian localization file // Ukrainian localization file
namespace i2p namespace i2p {
{ namespace i18n {
namespace i18n namespace ukrainian // language namespace
{ {
namespace ukrainian // language namespace
{
// language name in lowercase // language name in lowercase
static std::string language = "ukrainian"; static std::string language = "ukrainian";
// See for language plural forms here: // See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) { static int plural(int n) {
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)
? 1 : 2;
} }
static std::map<std::string, std::string> strings static std::map <std::string, std::string> strings
{ {
{"KiB", "КіБ"}, {"KiB", "КіБ"},
{"MiB", "МіБ"}, {"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", {"день", "дня", "днів"}}, {"days", {"день", "дня", "днів"}},
{"hours", {"годину", "години", "годин"}}, {"hours", {"годину", "години", "годин"}},
@ -205,11 +204,11 @@ namespace ukrainian // language namespace
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };
std::shared_ptr<const i2p::i18n::Locale> GetLocale() std::shared_ptr<const i2p::i18n::Locale> GetLocale() {
{ return std::make_shared<i2p::i18n::Locale>(language, strings, plurals,
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); }); [](int n) -> int { return plural(n); });
} }
} // language } // language
} // i18n } // i18n
} // i2p } // i2p

View file

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

View file

@ -11,10 +11,8 @@
#include "Base.h" #include "Base.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
static const char T32[32] = static const char T32[32] =
{ {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
@ -23,8 +21,7 @@ namespace data
'y', 'z', '2', '3', '4', '5', '6', '7', 'y', 'z', '2', '3', '4', '5', '6', '7',
}; };
const char * GetBase32SubstitutionTable () const char *GetBase32SubstitutionTable() {
{
return T32; return T32;
} }
@ -50,8 +47,7 @@ namespace data
'4', '5', '6', '7', '8', '9', '-', '~' '4', '5', '6', '7', '8', '9', '-', '~'
}; };
const char * GetBase64SubstitutionTable () const char *GetBase64SubstitutionTable() {
{
return T64; return T64;
} }
@ -77,15 +73,14 @@ namespace data
* *
*/ */
size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */ size_t ByteStreamToBase64( /* Number of bytes in the encoded buffer */
const uint8_t * InBuffer, /* Input buffer, binary data */ const uint8_t *InBuffer, /* Input buffer, binary data */
size_t InCount, /* Number of bytes in the input buffer */ 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 */ size_t len /* length of output buffer */
) ) {
{ unsigned char *ps;
unsigned char * ps; unsigned char *pd;
unsigned char * pd;
unsigned char acc_1; unsigned char acc_1;
unsigned char acc_2; unsigned char acc_2;
int i; int i;
@ -93,7 +88,7 @@ namespace data
int m; int m;
size_t outCount; size_t outCount;
ps = (unsigned char *)InBuffer; ps = (unsigned char *) InBuffer;
n = InCount / 3; n = InCount / 3;
m = InCount % 3; m = InCount % 3;
if (!m) if (!m)
@ -103,9 +98,8 @@ namespace data
if (outCount > len) return 0; if (outCount > len) return 0;
pd = (unsigned char *)OutBuffer; pd = (unsigned char *) OutBuffer;
for ( i = 0; i < n; i++ ) for (i = 0; i < n; i++) {
{
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x30; acc_2 = (acc_1 << 4) & 0x30;
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; /* base64 digit #1 */
@ -121,8 +115,7 @@ namespace data
acc_2 &= 0x3f; /* base64 digit #4 */ acc_2 &= 0x3f; /* base64 digit #4 */
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
} }
if ( m == 1 ) if (m == 1) {
{
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */ acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; /* base64 digit #1 */
@ -131,9 +124,7 @@ namespace data
*pd++ = P64; *pd++ = P64;
*pd++ = P64; *pd++ = P64;
} } else if (m == 2) {
else if ( m == 2 )
{
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f; acc_2 = (acc_1 << 4) & 0x3f;
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; /* base64 digit #1 */
@ -160,15 +151,14 @@ namespace data
* *
*/ */
size_t Base64ToByteStream ( /* Number of output bytes */ size_t Base64ToByteStream( /* Number of output bytes */
const char * InBuffer, /* BASE64 encoded buffer */ const char *InBuffer, /* BASE64 encoded buffer */
size_t InCount, /* Number of input bytes */ 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 */ size_t len /* length of output buffer */
) ) {
{ unsigned char *ps;
unsigned char * ps; unsigned char *pd;
unsigned char * pd;
unsigned char acc_1; unsigned char acc_1;
unsigned char acc_2; unsigned char acc_2;
int i; int i;
@ -187,18 +177,17 @@ namespace data
else else
return 0; return 0;
ps = (unsigned char *)(InBuffer + InCount - 1); ps = (unsigned char *) (InBuffer + InCount - 1);
while ( *ps-- == P64 ) while (*ps-- == P64)
outCount--; outCount--;
ps = (unsigned char *)InBuffer; ps = (unsigned char *) InBuffer;
if (outCount > len) if (outCount > len)
return 0; return 0;
pd = OutBuffer; pd = OutBuffer;
auto endOfOutBuffer = OutBuffer + outCount; auto endOfOutBuffer = OutBuffer + outCount;
for ( i = 0; i < n; i++ ) for (i = 0; i < n; i++) {
{
acc_1 = iT64[*ps++]; acc_1 = iT64[*ps++];
acc_2 = iT64[*ps++]; acc_2 = iT64[*ps++];
acc_1 <<= 2; acc_1 <<= 2;
@ -222,20 +211,18 @@ namespace data
return outCount; return outCount;
} }
size_t Base64EncodingBufferSize (const size_t input_size) size_t Base64EncodingBufferSize(const size_t input_size) {
{ auto d = div(input_size, 3);
auto d = div (input_size, 3);
if (d.rem) if (d.rem)
d.quot++; d.quot++;
return 4 * d.quot; return 4 * d.quot;
} }
std::string ToBase64Standard (const std::string& in) std::string ToBase64Standard(const std::string &in) {
{ auto len = Base64EncodingBufferSize(in.length());
auto len = Base64EncodingBufferSize (in.length ()); char *str = new char[len + 1];
char * str = new char[len + 1]; auto l = ByteStreamToBase64((const uint8_t *) in.c_str(), in.length(), str, len);
auto l = ByteStreamToBase64 ((const uint8_t *)in.c_str (), in.length (), str, len);
str[l] = 0; str[l] = 0;
// replace '-' by '+' and '~' by '/' // replace '-' by '+' and '~' by '/'
for (size_t i = 0; i < l; i++) for (size_t i = 0; i < l; i++)
@ -258,21 +245,18 @@ namespace data
* *
*/ */
static void iT64Build() static void iT64Build() {
{
int i; int i;
isFirstTime = 0; isFirstTime = 0;
for ( i = 0; i < 256; i++ ) iT64[i] = -1; for (i = 0; i < 256; i++) iT64[i] = -1;
for ( i = 0; i < 64; i++ ) iT64[(int)T64[i]] = i; for (i = 0; i < 64; i++) iT64[(int) T64[i]] = i;
iT64[(int)P64] = 0; 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; int tmp = 0, bits = 0;
size_t ret = 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]; char ch = inBuf[i];
if (ch >= '2' && ch <= '7') // digit if (ch >= '2' && ch <= '7') // digit
ch = (ch - '2') + 26; // 26 means a-z ch = (ch - '2') + 26; // 26 means a-z
@ -283,8 +267,7 @@ namespace data
tmp |= ch; tmp |= ch;
bits += 5; bits += 5;
if (bits >= 8) if (bits >= 8) {
{
if (ret >= outLen) return ret; if (ret >= outLen) return ret;
outBuf[ret] = tmp >> (bits - 8); outBuf[ret] = tmp >> (bits - 8);
bits -= 8; bits -= 8;
@ -295,22 +278,17 @@ namespace data
return ret; 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; size_t ret = 0, pos = 1;
int bits = 8, tmp = inBuf[0]; int bits = 8, tmp = inBuf[0];
while (ret < outLen && (bits > 0 || pos < len)) while (ret < outLen && (bits > 0 || pos < len)) {
{ if (bits < 5) {
if (bits < 5) if (pos < len) {
{
if (pos < len)
{
tmp <<= 8; tmp <<= 8;
tmp |= inBuf[pos] & 0xFF; tmp |= inBuf[pos] & 0xFF;
pos++; pos++;
bits += 8; bits += 8;
} } else // last byte
else // last byte
{ {
tmp <<= (5 - bits); tmp <<= (5 - bits);
bits = 5; bits = 5;
@ -324,5 +302,5 @@ namespace data
} }
return ret; return ret;
} }
} }
} }

View file

@ -14,23 +14,27 @@
#include <iostream> #include <iostream>
namespace i2p { namespace i2p {
namespace data { namespace data {
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); size_t ByteStreamToBase64(const uint8_t *InBuffer, size_t InCount, char *OutBuffer, size_t len);
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
const char * GetBase32SubstitutionTable ();
const char * GetBase64SubstitutionTable ();
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); size_t Base64ToByteStream(const char *InBuffer, size_t InCount, uint8_t *OutBuffer, size_t len);
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
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 * 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); 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 } // i2p
#endif #endif

View file

@ -20,114 +20,110 @@
#include "Signature.h" #include "Signature.h"
#include "Blinding.h" #include "Blinding.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data static EC_POINT *BlindPublicKeyECDSA(const EC_GROUP *group, const EC_POINT *pub, const uint8_t *seed) {
{ BN_CTX *ctx = BN_CTX_new();
static EC_POINT * BlindPublicKeyECDSA (const EC_GROUP * group, const EC_POINT * pub, const uint8_t * seed) BN_CTX_start(ctx);
{ BIGNUM *q = BN_CTX_get(ctx);
BN_CTX * ctx = BN_CTX_new (); EC_GROUP_get_order(group, q, ctx);
BN_CTX_start (ctx);
BIGNUM * q = BN_CTX_get (ctx);
EC_GROUP_get_order (group, q, ctx);
// calculate alpha = seed mod q // calculate alpha = seed mod q
BIGNUM * alpha = BN_CTX_get (ctx); BIGNUM *alpha = BN_CTX_get(ctx);
BN_bin2bn (seed, 64, alpha); // seed is in BigEndian BN_bin2bn(seed, 64, alpha); // seed is in BigEndian
BN_mod (alpha, alpha, q, ctx); // % q BN_mod(alpha, alpha, q, ctx); // % q
// A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha) // A' = BLIND_PUBKEY(A, alpha) = A + DERIVE_PUBLIC(alpha)
auto p = EC_POINT_new (group); auto p = EC_POINT_new(group);
EC_POINT_mul (group, p, alpha, nullptr, nullptr, ctx); // B*alpha EC_POINT_mul(group, p, alpha, nullptr, nullptr, ctx); // B*alpha
EC_POINT_add (group, p, pub, p, ctx); // pub + B*alpha EC_POINT_add(group, p, pub, p, ctx); // pub + B*alpha
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
return p; return p;
} }
static void BlindPrivateKeyECDSA (const EC_GROUP * group, const BIGNUM * priv, const uint8_t * seed, BIGNUM * blindedPriv) static void
{ BlindPrivateKeyECDSA(const EC_GROUP *group, const BIGNUM *priv, const uint8_t *seed, BIGNUM *blindedPriv) {
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
BN_CTX_start (ctx); BN_CTX_start(ctx);
BIGNUM * q = BN_CTX_get (ctx); BIGNUM *q = BN_CTX_get(ctx);
EC_GROUP_get_order (group, q, ctx); EC_GROUP_get_order(group, q, ctx);
// calculate alpha = seed mod q // calculate alpha = seed mod q
BIGNUM * alpha = BN_CTX_get (ctx); BIGNUM *alpha = BN_CTX_get(ctx);
BN_bin2bn (seed, 64, alpha); // seed is in BigEndian BN_bin2bn(seed, 64, alpha); // seed is in BigEndian
BN_mod (alpha, alpha, q, ctx); // % q BN_mod(alpha, alpha, q, ctx); // % q
BN_add (alpha, alpha, priv); // alpha = alpha + priv BN_add(alpha, alpha, priv); // alpha = alpha + priv
// a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod q // a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod q
BN_mod (blindedPriv, alpha, q, ctx); // % q BN_mod(blindedPriv, alpha, q, ctx); // % q
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (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) static void
{ BlindEncodedPublicKeyECDSA(size_t publicKeyLen, const EC_GROUP *group, const uint8_t *pub, const uint8_t *seed,
BIGNUM * x = BN_bin2bn (pub, publicKeyLen/2, NULL); uint8_t *blindedPub) {
BIGNUM * y = BN_bin2bn (pub + publicKeyLen/2, publicKeyLen/2, NULL); BIGNUM *x = BN_bin2bn(pub, publicKeyLen / 2, NULL);
EC_POINT * p = EC_POINT_new (group); BIGNUM *y = BN_bin2bn(pub + publicKeyLen / 2, publicKeyLen / 2, NULL);
EC_POINT_set_affine_coordinates_GFp (group, p, x, y, NULL); EC_POINT *p = EC_POINT_new(group);
EC_POINT * p1 = BlindPublicKeyECDSA (group, p, seed); EC_POINT_set_affine_coordinates_GFp(group, p, x, y, NULL);
EC_POINT_free (p); EC_POINT *p1 = BlindPublicKeyECDSA(group, p, seed);
EC_POINT_get_affine_coordinates_GFp (group, p1, x, y, NULL); EC_POINT_free(p);
EC_POINT_free (p1); EC_POINT_get_affine_coordinates_GFp(group, p1, x, y, NULL);
i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); EC_POINT_free(p1);
i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2); i2p::crypto::bn2buf(x, blindedPub, publicKeyLen / 2);
BN_free (x); BN_free (y); 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) 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 *a = BN_bin2bn(priv, publicKeyLen / 2, NULL);
BIGNUM * a1 = BN_new (); BIGNUM *a1 = BN_new();
BlindPrivateKeyECDSA (group, a, seed, a1); BlindPrivateKeyECDSA(group, a, seed, a1);
BN_free (a); BN_free(a);
i2p::crypto::bn2buf (a1, blindedPriv, publicKeyLen/2); i2p::crypto::bn2buf(a1, blindedPriv, publicKeyLen / 2);
auto p = EC_POINT_new (group); auto p = EC_POINT_new(group);
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
EC_POINT_mul (group, p, a1, nullptr, nullptr, ctx); // B*a1 EC_POINT_mul(group, p, a1, nullptr, nullptr, ctx); // B*a1
BN_CTX_free (ctx); BN_CTX_free(ctx);
BN_free (a1); BN_free(a1);
BIGNUM * x = BN_new(), * y = BN_new(); BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp (group, p, x, y, NULL); EC_POINT_get_affine_coordinates_GFp(group, p, x, y, NULL);
EC_POINT_free (p); EC_POINT_free(p);
i2p::crypto::bn2buf (x, blindedPub, publicKeyLen/2); i2p::crypto::bn2buf(x, blindedPub, publicKeyLen / 2);
i2p::crypto::bn2buf (y, blindedPub + publicKeyLen/2, publicKeyLen/2); i2p::crypto::bn2buf(y, blindedPub + publicKeyLen / 2, publicKeyLen / 2);
BN_free (x); BN_free (y); BN_free(x);
BN_free(y);
} }
template<typename Fn, typename...Args> 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 // blind is BlindEncodedPublicKeyECDSA or BlindEncodedPrivateKeyECDSA
{ {
size_t publicKeyLength = 0; size_t publicKeyLength = 0;
EC_GROUP * group = nullptr; EC_GROUP *group = nullptr;
switch (sigType) switch (sigType) {
{ case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256: {
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
{
publicKeyLength = i2p::crypto::ECDSAP256_KEY_LENGTH; 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; 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; 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; 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; 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; break;
} }
default: default:
LogPrint (eLogError, "Blinding: Signature type ", (int)sigType, " is not ECDSA"); LogPrint(eLogError, "Blinding: Signature type ", (int) sigType, " is not ECDSA");
} }
if (group) if (group) {
{ blind(publicKeyLength, group, key, seed, std::forward<Args>(args)...);
blind (publicKeyLength, group, key, seed, std::forward<Args>(args)...); EC_GROUP_free(group);
EC_GROUP_free (group);
} }
return publicKeyLength; 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_SECRET_FLAG = 0x02; // not used for now
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04; const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth): BlindedPublicKey::BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth) :
m_IsClientAuth (clientAuth) m_IsClientAuth(clientAuth) {
{
if (!identity) return; if (!identity) return;
auto len = identity->GetSigningPublicKeyLen (); auto len = identity->GetSigningPublicKeyLen();
m_PublicKey.resize (len); m_PublicKey.resize(len);
memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len); memcpy(m_PublicKey.data(), identity->GetSigningPublicKeyBuffer(), len);
m_SigType = identity->GetSigningKeyType (); m_SigType = identity->GetSigningKeyType();
if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11 m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11
else else
m_BlindedSigType = m_SigType; m_BlindedSigType = m_SigType;
} }
BlindedPublicKey::BlindedPublicKey (const std::string& b33): BlindedPublicKey::BlindedPublicKey(const std::string &b33) :
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later m_SigType(0) // 0 means invalid, we can't blind DSA, set it later
{ {
uint8_t addr[40]; // TODO: define length from b33 uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40); size_t l = i2p::data::Base32ToByteStream(b33.c_str(), b33.length(), addr, 40);
if (l < 32) if (l < 32) {
{ LogPrint(eLogError, "Blinding: Malformed b33 ", b33);
LogPrint (eLogError, "Blinding: Malformed b33 ", b33);
return; return;
} }
uint32_t checksum = crc32 (0, addr + 3, l - 3); uint32_t checksum = crc32(0, addr + 3, l - 3);
// checksum is Little Endian // 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]; uint8_t flags = addr[0];
size_t offset = 1; size_t offset = 1;
if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures
{ {
m_SigType = bufbe16toh (addr + offset); offset += 2; m_SigType = bufbe16toh(addr + offset);
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2; offset += 2;
} m_BlindedSigType = bufbe16toh(addr + offset);
else // one byte sig offset += 2;
} else // one byte sig
{ {
m_SigType = addr[offset]; offset++; m_SigType = addr[offset];
m_BlindedSigType = addr[offset]; offset++; offset++;
m_BlindedSigType = addr[offset];
offset++;
} }
m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG; m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG;
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType)); std::unique_ptr<i2p::crypto::Verifier> blindedVerifier(i2p::data::IdentityEx::CreateVerifier(m_SigType));
if (blindedVerifier) if (blindedVerifier) {
{ auto len = blindedVerifier->GetPublicKeyLen();
auto len = blindedVerifier->GetPublicKeyLen (); if (offset + len <= l) {
if (offset + len <= l) m_PublicKey.resize(len);
{ memcpy(m_PublicKey.data(), addr + offset, len);
m_PublicKey.resize (len); } else
memcpy (m_PublicKey.data (), addr + offset, len); LogPrint(eLogError, "Blinding: Public key in b33 address is too short for signature type ",
} (int) m_SigType);
else } else
LogPrint (eLogError, "Blinding: Public key in b33 address is too short for signature type ", (int)m_SigType); LogPrint(eLogError, "Blinding: Unknown signature type ", (int) m_SigType, " in b33");
}
else
LogPrint (eLogError, "Blinding: Unknown signature type ", (int)m_SigType, " in b33");
} }
std::string BlindedPublicKey::ToB33 () const std::string BlindedPublicKey::ToB33() const {
{ if (m_PublicKey.size() > 32) return ""; // assume 25519
if (m_PublicKey.size () > 32) return ""; // assume 25519 uint8_t addr[35];
uint8_t addr[35]; char str[60]; // TODO: define actual length char str[60]; // TODO: define actual length
uint8_t flags = 0; uint8_t flags = 0;
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG; if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
addr[0] = flags; // flags addr[0] = flags; // flags
addr[1] = m_SigType; // sig type addr[1] = m_SigType; // sig type
addr[2] = m_BlindedSigType; // blinded sig type addr[2] = m_BlindedSigType; // blinded sig type
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ()); memcpy(addr + 3, m_PublicKey.data(), m_PublicKey.size());
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ()); uint32_t checksum = crc32(0, addr + 3, m_PublicKey.size());
// checksum is Little Endian // checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); addr[0] ^= checksum;
auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60); addr[1] ^= (checksum >> 8);
return std::string (str, str + l); 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 // A = destination's signing public key
// stA = signature type of A, 2 bytes big endian // 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 // 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) // 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]; uint8_t credential[32];
GetCredential (credential); GetCredential(credential);
// subcredential = H("subcredential", credential || blindedPublicKey) // subcredential = H("subcredential", credential || blindedPublicKey)
H ("subcredential", { {credential, 32}, {blinded, len} }, subcredential); H("subcredential", {{credential, 32},
{blinded, len}}, subcredential);
} }
void BlindedPublicKey::GenerateAlpha (const char * date, uint8_t * seed) const void BlindedPublicKey::GenerateAlpha(const char *date, uint8_t *seed) const {
{ uint16_t stA = htobe16(GetSigType()), stA1 = htobe16(GetBlindedSigType());
uint16_t stA = htobe16 (GetSigType ()), stA1 = htobe16 (GetBlindedSigType ());
uint8_t salt[32]; uint8_t salt[32];
//seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64) //seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64)
H ("I2PGenerateAlpha", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt); H("I2PGenerateAlpha", {{GetPublicKey(), GetPublicKeyLen()},
i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed); {(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]; uint8_t seed[64];
GenerateAlpha (date, seed); GenerateAlpha(date, seed);
size_t publicKeyLength = 0; 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_SHA256_P256:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: 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; break;
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
case i2p::data::SIGNING_KEY_TYPE_EDDSA_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; publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break; break;
default: 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; 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]; uint8_t seed[64];
GenerateAlpha (date, seed); GenerateAlpha(date, seed);
size_t publicKeyLength = 0; 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_SHA256_P256:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: 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; break;
case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: 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; publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break; break;
case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: {
{
uint8_t exp[64]; uint8_t exp[64];
i2p::crypto::Ed25519::ExpandPrivateKey (priv, exp); i2p::crypto::Ed25519::ExpandPrivateKey(priv, exp);
i2p::crypto::GetEd25519 ()->BlindPrivateKey (exp, seed, blindedPriv, blindedPub); i2p::crypto::GetEd25519()->BlindPrivateKey(exp, seed, blindedPriv, blindedPub);
publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH;
break; break;
} }
default: 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; 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_CTX ctx;
SHA256_Init (&ctx); SHA256_Init(&ctx);
SHA256_Update (&ctx, p.c_str (), p.length ()); SHA256_Update(&ctx, p.c_str(), p.length());
for (const auto& it: bufs) for (const auto &it: bufs)
SHA256_Update (&ctx, it.first, it.second); SHA256_Update(&ctx, it.first, it.second);
SHA256_Final (hash, &ctx); 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; i2p::data::IdentHash hash;
uint8_t blinded[128]; uint8_t blinded[128];
size_t publicKeyLength = 0; size_t publicKeyLength = 0;
if (date) if (date)
publicKeyLength = GetBlindedKey (date, blinded); publicKeyLength = GetBlindedKey(date, blinded);
else else {
{
char currentDate[9]; char currentDate[9];
i2p::util::GetCurrentDate (currentDate); i2p::util::GetCurrentDate(currentDate);
publicKeyLength = GetBlindedKey (currentDate, blinded); publicKeyLength = GetBlindedKey(currentDate, blinded);
} }
if (publicKeyLength) if (publicKeyLength) {
{ auto stA1 = htobe16(m_BlindedSigType);
auto stA1 = htobe16 (m_BlindedSigType);
SHA256_CTX ctx; SHA256_CTX ctx;
SHA256_Init (&ctx); SHA256_Init(&ctx);
SHA256_Update (&ctx, (const uint8_t *)&stA1, 2); SHA256_Update(&ctx, (const uint8_t *) &stA1, 2);
SHA256_Update (&ctx, blinded, publicKeyLength); SHA256_Update(&ctx, blinded, publicKeyLength);
SHA256_Final ((uint8_t *)hash, &ctx); SHA256_Final((uint8_t *) hash, &ctx);
} } else
else LogPrint(eLogError, "Blinding: Blinded key type ", (int) m_BlindedSigType, " is not supported");
LogPrint (eLogError, "Blinding: Blinded key type ", (int)m_BlindedSigType, " is not supported");
return hash; return hash;
} }
} }
} }

View file

@ -14,34 +14,41 @@
#include <vector> #include <vector>
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
class BlindedPublicKey // for encrypted LS2 class BlindedPublicKey // for encrypted LS2
{ {
public: public:
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false); BlindedPublicKey(std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
std::string ToB33 () const;
const uint8_t * GetPublicKey () const { return m_PublicKey.data (); }; BlindedPublicKey(const std::string &b33); // from b33 without .b32.i2p
size_t GetPublicKeyLen () const { return m_PublicKey.size (); }; std::string ToB33() const;
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 const uint8_t *GetPublicKey() const { return m_PublicKey.data(); };
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 size_t GetPublicKeyLen() const { return m_PublicKey.size(); };
i2p::data::IdentHash GetStoreHash (const char * date = nullptr) const; // date is 8 chars "YYYYMMDD", use current if null
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: private:
void GetCredential (uint8_t * credential) const; // 32 bytes 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 GenerateAlpha(const char *date, uint8_t *seed) const; // 64 bytes, date is 8 chars "YYYYMMDD"
void H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const; void
H(const std::string &p, const std::vector<std::pair<const uint8_t *, size_t> > &bufs, uint8_t *hash) const;
private: private:
@ -49,7 +56,7 @@ namespace data
i2p::data::SigningKeyType m_SigType, m_BlindedSigType; i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
bool m_IsClientAuth = false; bool m_IsClientAuth = false;
}; };
} }
} }
#endif #endif

View file

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

View file

@ -8,32 +8,32 @@
#ifndef BLOOM_FILTER_H_ #ifndef BLOOM_FILTER_H_
#define BLOOM_FILTER_H_ #define BLOOM_FILTER_H_
#include <memory> #include <memory>
#include <cstdint> #include <cstdint>
namespace i2p namespace i2p {
{ namespace util {
namespace util
{
/** @brief interface for bloom filter */ /** @brief interface for bloom filter */
struct IBloomFilter struct IBloomFilter {
{
/** @brief destructor */ /** @brief destructor */
virtual ~IBloomFilter() {}; virtual ~IBloomFilter() {};
/** @brief add entry to bloom filter, return false if filter hit otherwise return true */ /** @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 */ /** @brief optionally decay old entries */
virtual void Decay() = 0; virtual void Decay() = 0;
}; };
typedef std::shared_ptr<IBloomFilter> BloomFilterPtr; typedef std::shared_ptr <IBloomFilter> BloomFilterPtr;
/** @brief create bloom filter */ /** @brief create bloom filter */
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8); BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8);
} }
} }
#endif #endif

View file

@ -7,9 +7,11 @@
*/ */
#include "CPU.h" #include "CPU.h"
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
#include <cpuid.h> #include <cpuid.h>
#endif #endif
#include "Log.h" #include "Log.h"
#ifndef bit_AES #ifndef bit_AES
@ -20,15 +22,12 @@
#endif #endif
namespace i2p namespace i2p {
{ namespace cpu {
namespace cpu
{
bool aesni = false; bool aesni = false;
bool avx = 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__) #if defined(__x86_64__) || defined(__i386__)
int info[4]; int info[4];
__cpuid(0, info[0], info[1], info[2], info[3]); __cpuid(0, info[0], info[1], info[2], info[3]);
@ -54,5 +53,5 @@ namespace cpu
LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled")); LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled")); LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled"));
} }
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -25,7 +25,7 @@
*/ */
namespace i2p { namespace i2p {
namespace config { namespace config {
extern boost::program_options::variables_map m_Options; extern boost::program_options::variables_map m_Options;
/** /**
@ -49,7 +49,7 @@ namespace config {
* *
* Other exceptions will be passed to higher level. * 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 * @brief Load and parse given config file
@ -64,7 +64,7 @@ namespace config {
* *
* Other exceptions will be passed to higher level. * 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 * @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); * Example: uint16_t port; GetOption("sam.port", port);
*/ */
template<typename T> template<typename T>
bool GetOption(const char *name, T& value) bool GetOption(const char *name, T &value) {
{
if (!m_Options.count(name)) if (!m_Options.count(name))
return false; return false;
value = m_Options[name].as<T>(); value = m_Options[name].as<T>();
@ -89,13 +88,13 @@ namespace config {
} }
template<typename T> template<typename T>
bool GetOption(const std::string& name, T& value) bool GetOption(const std::string &name, T &value) {
{ return GetOption(name.c_str(), value);
return GetOption (name.c_str (), value);
} }
bool GetOptionAsAny(const char *name, boost::any& value); bool GetOptionAsAny(const char *name, boost::any &value);
bool GetOptionAsAny(const std::string& name, boost::any& value);
bool GetOptionAsAny(const std::string &name, boost::any &value);
/** /**
* @brief Set value of given parameter * @brief Set value of given parameter
@ -106,8 +105,7 @@ namespace config {
* Example: uint16_t port = 2827; SetOption("bob.port", port); * Example: uint16_t port = 2827; SetOption("bob.port", port);
*/ */
template<typename T> template<typename T>
bool SetOption(const char *name, const T& value) bool SetOption(const char *name, const T &value) {
{
if (!m_Options.count(name)) if (!m_Options.count(name))
return false; return false;
m_Options.at(name).value() = value; m_Options.at(name).value() = value;
@ -121,7 +119,7 @@ namespace config {
* @return true if value set to default, false otherwise * @return true if value set to default, false otherwise
*/ */
bool IsDefault(const char *name); bool IsDefault(const char *name);
} }
} }
#endif // CONFIG_H #endif // CONFIG_H

File diff suppressed because it is too large Load diff

View file

@ -50,53 +50,58 @@
# endif # endif
#endif #endif
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto bool bn2buf(const BIGNUM *bn, uint8_t *buf, size_t len);
{
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
// DSA // DSA
DSA * CreateDSA (); DSA *CreateDSA();
// RSA // RSA
const BIGNUM * GetRSAE (); const BIGNUM *GetRSAE();
// DH // DH
class DHKeys class DHKeys {
{
public: public:
DHKeys (); DHKeys();
~DHKeys ();
void GenerateKeys (); ~DHKeys();
const uint8_t * GetPublicKey () const { return m_PublicKey; };
void Agree (const uint8_t * pub, uint8_t * shared); void GenerateKeys();
const uint8_t *GetPublicKey() const { return m_PublicKey; };
void Agree(const uint8_t *pub, uint8_t *shared);
private: private:
DH * m_DH; DH *m_DH;
uint8_t m_PublicKey[256]; uint8_t m_PublicKey[256];
}; };
// x25519 // x25519
class X25519Keys class X25519Keys {
{
public: public:
X25519Keys (); X25519Keys();
X25519Keys (const uint8_t * priv, const uint8_t * pub); // if pub is null, derive from priv
~X25519Keys ();
void GenerateKeys (); X25519Keys(const uint8_t *priv, const uint8_t *pub); // if pub is null, derive from priv
const uint8_t * GetPublicKey () const { return m_PublicKey; }; ~X25519Keys();
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 GenerateKeys();
void SetElligatorIneligible () { m_IsElligatorIneligible = true; }
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: private:
@ -105,40 +110,42 @@ namespace crypto
EVP_PKEY_CTX * m_Ctx; EVP_PKEY_CTX * m_Ctx;
EVP_PKEY * m_Pkey; EVP_PKEY * m_Pkey;
#else #else
BN_CTX * m_Ctx; BN_CTX *m_Ctx;
uint8_t m_PrivateKey[32]; uint8_t m_PrivateKey[32];
#endif #endif
bool m_IsElligatorIneligible = false; // true if definitely ineligible bool m_IsElligatorIneligible = false; // true if definitely ineligible
}; };
// ElGamal // ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted void ElGamalEncrypt(const uint8_t *key, const uint8_t *data,
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub); 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 // ECIES
void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted void ECIESEncrypt(const EC_GROUP *curve, const EC_POINT *key, const uint8_t *data,
bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data uint8_t *encrypted); // 222 bytes data, 514 bytes encrypted
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); 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 // HMAC
typedef i2p::data::Tag<32> MACKey; 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 // AES
struct ChipherBlock struct ChipherBlock {
{
uint8_t buf[16]; 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++) for (int i = 0; i < 4; i++)
reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i]; reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
} } else {
else
{
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
buf[i] ^= other.buf[i]; buf[i] ^= other.buf[i];
} }
@ -152,23 +159,25 @@ namespace crypto
{ {
public: public:
AESAlignedBuffer () AESAlignedBuffer() {
{
m_Buf = m_UnalignedBuffer; m_Buf = m_UnalignedBuffer;
uint8_t rem = ((size_t)m_Buf) & 0x0f; uint8_t rem = ((size_t) m_Buf) & 0x0f;
if (rem) if (rem)
m_Buf += (16 - rem); m_Buf += (16 - rem);
} }
operator uint8_t * () { return m_Buf; }; operator uint8_t *() { return m_Buf; };
operator const uint8_t * () const { return m_Buf; };
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; }; operator const uint8_t *() const { return m_Buf; };
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
ChipherBlock *GetChipherBlock() { return (ChipherBlock *) m_Buf; };
const ChipherBlock *GetChipherBlock() const { return (const ChipherBlock *) m_Buf; };
private: private:
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment 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__ #ifdef __AES__
class ECBEncryption: public ECBCryptoAESNI class ECBEncryption: public ECBCryptoAESNI
#else #else
class ECBEncryption class ECBEncryption
#endif #endif
{ {
public: 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: private:
AES_KEY m_Key; AES_KEY m_Key;
@ -208,32 +218,36 @@ namespace crypto
#ifdef __AES__ #ifdef __AES__
class ECBDecryption: public ECBCryptoAESNI class ECBDecryption: public ECBCryptoAESNI
#else #else
class ECBDecryption class ECBDecryption
#endif #endif
{ {
public: public:
void SetKey (const AESKey& key); void SetKey(const AESKey &key);
void Decrypt (const ChipherBlock * in, ChipherBlock * out);
void Decrypt(const ChipherBlock *in, ChipherBlock *out);
private: private:
AES_KEY m_Key; AES_KEY m_Key;
}; };
class CBCEncryption class CBCEncryption {
{
public: 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 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 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 GetIV(uint8_t *iv) const { memcpy(iv, (const uint8_t *) m_LastBlock, 16); };
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); void Encrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
void Encrypt (const uint8_t * in, uint8_t * out); // one block
ECBEncryption & ECB() { return m_ECBEncryption; } 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: private:
@ -242,21 +256,22 @@ namespace crypto
ECBEncryption m_ECBEncryption; ECBEncryption m_ECBEncryption;
}; };
class CBCDecryption class CBCDecryption {
{
public: 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 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 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 GetIV(uint8_t *iv) const { memcpy(iv, (const uint8_t *) m_IV, 16); };
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); void Decrypt(int numBlocks, const ChipherBlock *in, ChipherBlock *out);
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
void Decrypt (const uint8_t * in, uint8_t * out); // one block
ECBDecryption & ECB() { return m_ECBDecryption; } 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: private:
@ -268,13 +283,12 @@ namespace crypto
{ {
public: public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey) void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
{ m_LayerEncryption.SetKey(layerKey);
m_LayerEncryption.SetKey (layerKey); m_IVEncryption.SetKey(ivKey);
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: private:
@ -286,13 +300,12 @@ namespace crypto
{ {
public: public:
void SetKeys (const AESKey& layerKey, const AESKey& ivKey) void SetKeys(const AESKey &layerKey, const AESKey &ivKey) {
{ m_LayerDecryption.SetKey(layerKey);
m_LayerDecryption.SetKey (layerKey); m_IVDecryption.SetKey(ivKey);
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: private:
@ -301,108 +314,142 @@ namespace crypto
}; };
// AEAD/ChaCha20/Poly1305 // 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 // 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 // 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 // Noise
struct NoiseSymmetricState struct NoiseSymmetricState {
{
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/; uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
void MixHash (const uint8_t * buf, size_t len); 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 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 InitNoiseNState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_N (tunnels, router)
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2) void InitNoiseXKState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_XK (NTCP2)
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (SSU2) void InitNoiseXKState1(NoiseSymmetricState &state, const uint8_t *pub); // Noise_XK (SSU2)
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets) void InitNoiseIKState(NoiseSymmetricState &state, const uint8_t *pub); // Noise_IK (ratchets)
// init and terminate // init and terminate
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force); void InitCrypto(bool precomputation, bool aesni, bool avx, bool force);
void TerminateCrypto ();
} void TerminateCrypto();
}
} }
// take care about openssl below 1.1.0 // take care about openssl below 1.1.0
#if LEGACY_OPENSSL #if LEGACY_OPENSSL
// define getters and setters introduced in 1.1.0 // define getters and setters introduced in 1.1.0
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
{ if (d->p) BN_free(d->p);
if (d->p) BN_free (d->p); if (d->q) BN_free(d->q);
if (d->q) BN_free (d->q); if (d->g) BN_free(d->g);
if (d->g) BN_free (d->g); d->p = p;
d->p = p; d->q = q; d->g = g; return 1; d->q = q;
} d->g = g;
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) return 1;
{ }
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 ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) {
{ if (d->pub_key) BN_free(d->pub_key);
if (sig->r) BN_free (sig->r); if (d->priv_key) BN_free(d->priv_key);
if (sig->s) BN_free (sig->s); d->pub_key = pub_key;
sig->r = r; sig->s = s; return 1; d->priv_key = priv_key;
} return 1;
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) }
{ *pr = sig->r; *ps = sig->s; }
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key) {
{ *pub_key = d->pub_key;
if (r->n) BN_free (r->n); *priv_key = d->priv_key;
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) inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
{ if (sig->r) BN_free(sig->r);
if (dh->p) BN_free (dh->p); if (sig->s) BN_free(sig->s);
if (dh->q) BN_free (dh->q); sig->r = r;
if (dh->g) BN_free (dh->g); sig->s = s;
dh->p = p; dh->q = q; dh->g = g; return 1; 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) inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {
{ return pkey->pkey.rsa; } *pr = sig->r;
*ps = sig->s;
}
inline EVP_MD_CTX *EVP_MD_CTX_new () inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
{ return EVP_MD_CTX_create(); } if (sig->r) BN_free(sig->r);
inline void EVP_MD_CTX_free (EVP_MD_CTX *ctx) if (sig->s) BN_free(sig->s);
{ EVP_MD_CTX_destroy (ctx); } 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 // ssl
#define TLS_method TLSv1_method #define TLS_method TLSv1_method

View file

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

View file

@ -12,47 +12,48 @@
#include <inttypes.h> #include <inttypes.h>
#include "Crypto.h" #include "Crypto.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto class CryptoKeyEncryptor {
{
class CryptoKeyEncryptor
{
public: public:
virtual ~CryptoKeyEncryptor () {}; virtual ~CryptoKeyEncryptor() {};
virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) = 0;
virtual void Encrypt(const uint8_t *data, uint8_t *encrypted) = 0;
}; };
class CryptoKeyDecryptor class CryptoKeyDecryptor {
{
public: public:
virtual ~CryptoKeyDecryptor () {}; 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 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 // ElGamal
class ElGamalEncryptor: public CryptoKeyEncryptor // for destination class ElGamalEncryptor : public CryptoKeyEncryptor // for destination
{ {
public: public:
ElGamalEncryptor (const uint8_t * pub); ElGamalEncryptor(const uint8_t *pub);
void Encrypt (const uint8_t * data, uint8_t * encrypted) override; // 222 bytes data, 514 bytes encrypted
void Encrypt(const uint8_t *data, uint8_t *encrypted) override; // 222 bytes data, 514 bytes encrypted
private: private:
uint8_t m_PublicKey[256]; uint8_t m_PublicKey[256];
}; };
class ElGamalDecryptor: public CryptoKeyDecryptor // for destination class ElGamalDecryptor : public CryptoKeyDecryptor // for destination
{ {
public: public:
ElGamalDecryptor (const uint8_t * priv); 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; }; bool Decrypt(const uint8_t *encrypted, uint8_t *data) override; // 514 bytes encrypted, 222 bytes data
size_t GetPublicKeyLen() const override { return 256; };
private: private:
@ -61,79 +62,86 @@ namespace crypto
// ECIES P256 // ECIES P256
class ECIESP256Encryptor: public CryptoKeyEncryptor class ECIESP256Encryptor : public CryptoKeyEncryptor {
{
public: public:
ECIESP256Encryptor (const uint8_t * pub); ECIESP256Encryptor(const uint8_t *pub);
~ECIESP256Encryptor ();
void Encrypt (const uint8_t * data, uint8_t * encrypted) override; ~ECIESP256Encryptor();
void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
private: private:
EC_GROUP * m_Curve; EC_GROUP *m_Curve;
EC_POINT * m_PublicKey; EC_POINT *m_PublicKey;
}; };
class ECIESP256Decryptor: public CryptoKeyDecryptor class ECIESP256Decryptor : public CryptoKeyDecryptor {
{
public: public:
ECIESP256Decryptor (const uint8_t * priv); ECIESP256Decryptor(const uint8_t *priv);
~ECIESP256Decryptor ();
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; ~ECIESP256Decryptor();
size_t GetPublicKeyLen () const override { return 64; };
bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
size_t GetPublicKeyLen() const override { return 64; };
private: private:
EC_GROUP * m_Curve; EC_GROUP *m_Curve;
BIGNUM * m_PrivateKey; BIGNUM *m_PrivateKey;
}; };
void CreateECIESP256RandomKeys (uint8_t * priv, uint8_t * pub); void CreateECIESP256RandomKeys(uint8_t *priv, uint8_t *pub);
// ECIES GOST R 34.10 // ECIES GOST R 34.10
class ECIESGOSTR3410Encryptor: public CryptoKeyEncryptor class ECIESGOSTR3410Encryptor : public CryptoKeyEncryptor {
{
public: public:
ECIESGOSTR3410Encryptor (const uint8_t * pub); ECIESGOSTR3410Encryptor(const uint8_t *pub);
~ECIESGOSTR3410Encryptor ();
void Encrypt (const uint8_t * data, uint8_t * encrypted) override; ~ECIESGOSTR3410Encryptor();
void Encrypt(const uint8_t *data, uint8_t *encrypted) override;
private: private:
EC_POINT * m_PublicKey; EC_POINT *m_PublicKey;
}; };
class ECIESGOSTR3410Decryptor: public CryptoKeyDecryptor class ECIESGOSTR3410Decryptor : public CryptoKeyDecryptor {
{
public: public:
ECIESGOSTR3410Decryptor (const uint8_t * priv); ECIESGOSTR3410Decryptor(const uint8_t *priv);
~ECIESGOSTR3410Decryptor ();
bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; ~ECIESGOSTR3410Decryptor();
size_t GetPublicKeyLen () const override { return 64; };
bool Decrypt(const uint8_t *encrypted, uint8_t *data) override;
size_t GetPublicKeyLen() const override { return 64; };
private: 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 // ECIES-X25519-AEAD-Ratchet
class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor class ECIESX25519AEADRatchetEncryptor : public CryptoKeyEncryptor {
{
public: public:
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub); ECIESX25519AEADRatchetEncryptor(const uint8_t *pub);
~ECIESX25519AEADRatchetEncryptor () {};
void Encrypt (const uint8_t *, uint8_t * pub) override; ~ECIESX25519AEADRatchetEncryptor() {};
void Encrypt(const uint8_t *, uint8_t *pub) override;
// copies m_PublicKey to pub // copies m_PublicKey to pub
private: private:
@ -141,24 +149,27 @@ namespace crypto
uint8_t m_PublicKey[32]; uint8_t m_PublicKey[32];
}; };
class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor class ECIESX25519AEADRatchetDecryptor : public CryptoKeyDecryptor {
{
public: public:
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false); ECIESX25519AEADRatchetDecryptor(const uint8_t *priv, bool calculatePublic = false);
~ECIESX25519AEADRatchetDecryptor () {};
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret) override; ~ECIESX25519AEADRatchetDecryptor() {};
bool Decrypt(const uint8_t *epub, uint8_t *sharedSecret) override;
// agree with static and return in sharedSecret (32 bytes) // agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen () const override { return 32; }; size_t GetPublicKeyLen() const override { return 32; };
const uint8_t * GetPubicKey () const { return m_StaticKeys.GetPublicKey (); };
const uint8_t *GetPubicKey() const { return m_StaticKeys.GetPublicKey(); };
private: private:
X25519Keys m_StaticKeys; X25519Keys m_StaticKeys;
}; };
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); void CreateECIESX25519AEADRatchetRandomKeys(uint8_t *priv, uint8_t *pub);
} }
} }
#endif #endif

View file

@ -14,119 +14,110 @@
#include "Destination.h" #include "Destination.h"
#include "Datagram.h" #include "Datagram.h"
namespace i2p namespace i2p {
{ namespace datagram {
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) {
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) if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator); m_Deflator.reset(new i2p::data::GzipDeflator);
auto identityLen = m_Owner->GetIdentity ()->GetFullLen (); auto identityLen = m_Owner->GetIdentity()->GetFullLen();
m_From.resize (identityLen); m_From.resize(identityLen);
m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen); m_Owner->GetIdentity()->ToBuffer(m_From.data(), identityLen);
m_Signature.resize (m_Owner->GetIdentity ()->GetSignatureLen ()); m_Signature.resize(m_Owner->GetIdentity()->GetSignatureLen());
} }
DatagramDestination::~DatagramDestination () DatagramDestination::~DatagramDestination() {
{
m_Sessions.clear(); 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); auto session = ObtainSession(identity);
SendDatagram (session, payload, len, fromPort, toPort); SendDatagram(session, payload, len, fromPort, toPort);
FlushSendQueue (session); 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); auto session = ObtainSession(identity);
SendRawDatagram (session, payload, len, fromPort, toPort); SendRawDatagram(session, payload, len, fromPort, toPort);
FlushSendQueue (session); 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); return ObtainSession(ident);
} }
void DatagramDestination::SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort) void
{ DatagramDestination::SendDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
if (session) uint16_t fromPort, uint16_t toPort) {
{ if (session) {
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) if (m_Owner->GetIdentity()->GetSigningKeyType() == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) {
{
uint8_t hash[32]; uint8_t hash[32];
SHA256(payload, len, hash); SHA256(payload, len, hash);
m_Owner->Sign (hash, 32, m_Signature.data ()); m_Owner->Sign(hash, 32, m_Signature.data());
} } else
else m_Owner->Sign(payload, len, m_Signature.data());
m_Owner->Sign (payload, len, m_Signature.data ());
auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}}, auto msg = CreateDataMessage({{m_From.data(), m_From.size()},
fromPort, toPort, false, !session->IsRatchets ()); // datagram {m_Signature.data(), m_Signature.size()},
{payload, len}},
fromPort, toPort, false, !session->IsRatchets()); // datagram
session->SendMsg(msg); 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) 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) 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; i2p::data::IdentityEx identity;
size_t identityLen = identity.FromBuffer (buf, len); size_t identityLen = identity.FromBuffer(buf, len);
const uint8_t * signature = buf + identityLen; const uint8_t *signature = buf + identityLen;
size_t headerLen = identityLen + identity.GetSignatureLen (); size_t headerLen = identityLen + identity.GetSignatureLen();
bool verified = false; 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]; uint8_t hash[32];
SHA256(buf + headerLen, len - headerLen, hash); SHA256(buf + headerLen, len - headerLen, hash);
verified = identity.Verify (hash, 32, signature); verified = identity.Verify(hash, 32, signature);
} } else
else verified = identity.Verify(buf + headerLen, len - headerLen, signature);
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
if (verified) if (verified) {
{
auto h = identity.GetIdentHash(); auto h = identity.GetIdentHash();
auto session = ObtainSession(h); auto session = ObtainSession(h);
session->Ack(); session->Ack();
auto r = FindReceiver(toPort); auto r = FindReceiver(toPort);
if(r) if (r)
r(identity, fromPort, toPort, buf + headerLen, len -headerLen); r(identity, fromPort, toPort, buf + headerLen, len - headerLen);
else else
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort); LogPrint(eLogWarning, "DatagramDestination: no receiver for port ", toPort);
} } else
else LogPrint(eLogWarning, "Datagram signature verification failed");
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) if (m_RawReceiver)
m_RawReceiver (fromPort, toPort, buf, len); m_RawReceiver(fromPort, toPort, buf, len);
else 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); std::lock_guard<std::mutex> lock(m_ReceiversMutex);
Receiver r = m_Receiver; Receiver r = m_Receiver;
auto itr = m_ReceiversByPorts.find(port); auto itr = m_ReceiversByPorts.find(port);
@ -135,80 +126,71 @@ namespace datagram
return r; 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 // unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE]; uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE); size_t uncompressedLen = m_Inflator.Inflate(buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen) if (uncompressedLen) {
{
if (isRaw) if (isRaw)
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen); HandleRawDatagram(fromPort, toPort, uncompressed, uncompressedLen);
else else
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); HandleDatagram(fromPort, toPort, uncompressed, uncompressedLen);
} } else
else LogPrint(eLogWarning, "Datagram: decompression failed");
LogPrint (eLogWarning, "Datagram: decompression failed");
} }
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage ( std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage(
const std::vector<std::pair<const uint8_t *, size_t> >& payloads, const std::vector<std::pair<const uint8_t *, size_t> > &payloads,
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) {
{
size_t size; size_t size;
auto msg = m_I2NPMsgsPool.AcquireShared (); auto msg = m_I2NPMsgsPool.AcquireShared();
uint8_t * buf = msg->GetPayload (); uint8_t *buf = msg->GetPayload();
buf += 4; // reserve for length buf += 4; // reserve for length
if (m_Gzip && m_Deflator) 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 else
size = i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len); size = i2p::data::GzipNoCompression(payloads, buf, msg->maxLen - msg->len);
if (size) if (size) {
{ htobe32buf(msg->GetPayload(), size); // length
htobe32buf (msg->GetPayload (), size); // length htobe16buf(buf + 4, fromPort); // source port
htobe16buf (buf + 4, fromPort); // source port htobe16buf(buf + 6, toPort); // destination port
htobe16buf (buf + 6, toPort); // destination port buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
msg->len += size + 4; msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData, 0, checksum); msg->FillI2NPMessageHeader(eI2NPData, 0, checksum);
} } else
else
msg = nullptr; msg = nullptr;
return msg; return msg;
} }
void DatagramDestination::CleanUp () void DatagramDestination::CleanUp() {
{ if (m_Sessions.empty()) return;
if (m_Sessions.empty ()) return;
auto now = i2p::util::GetMillisecondsSinceEpoch(); auto now = i2p::util::GetMillisecondsSinceEpoch();
LogPrint(eLogDebug, "DatagramDestination: clean up sessions"); LogPrint(eLogDebug, "DatagramDestination: clean up sessions");
std::unique_lock<std::mutex> lock(m_SessionsMutex); std::unique_lock<std::mutex> lock(m_SessionsMutex);
// for each session ... // 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 // 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()); LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32());
it->second->Stop (); it->second->Stop();
it = m_Sessions.erase (it); // we are expired it = m_Sessions.erase(it); // we are expired
} } else
else
it++; 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::shared_ptr<DatagramSession> session = nullptr;
std::lock_guard<std::mutex> lock(m_SessionsMutex); std::lock_guard<std::mutex> lock(m_SessionsMutex);
auto itr = m_Sessions.find(identity); auto itr = m_Sessions.find(identity);
if (itr == m_Sessions.end()) { if (itr == m_Sessions.end()) {
// not found, create new session // not found, create new session
session = std::make_shared<DatagramSession>(m_Owner, identity); session = std::make_shared<DatagramSession>(m_Owner, identity);
session->Start (); session->Start();
m_Sessions[identity] = session; m_Sessions[identity] = session;
} else { } else {
session = itr->second; session = itr->second;
@ -216,47 +198,41 @@ namespace datagram
return session; 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); std::lock_guard<std::mutex> lock(m_SessionsMutex);
for ( auto & item : m_Sessions) for (auto &item: m_Sessions) {
{ if (item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
if(item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
} }
return nullptr; return nullptr;
} }
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) : const i2p::data::IdentHash &remoteIdent) :
m_LocalDestination(localDestination), m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent), m_RemoteIdent(remoteIdent),
m_RequestingLS(false) m_RequestingLS(false) {
{
} }
void DatagramSession::Start () void DatagramSession::Start() {
{ m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
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 // we used this session
m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
if (msg || m_SendQueue.empty ()) if (msg || m_SendQueue.empty())
m_SendQueue.push_back(msg); m_SendQueue.push_back(msg);
// flush queue right away if full // flush queue right away if full
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE)
FlushSendQueue(); FlushSendQueue();
} }
DatagramSession::Info DatagramSession::GetSessionInfo() const DatagramSession::Info DatagramSession::GetSessionInfo() const {
{ if (!m_RoutingSession)
if(!m_RoutingSession)
return DatagramSession::Info(nullptr, nullptr, m_LastUse); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto routingPath = m_RoutingSession->GetSharedRoutingPath(); auto routingPath = m_RoutingSession->GetSharedRoutingPath();
@ -264,134 +240,115 @@ namespace datagram
return DatagramSession::Info(nullptr, nullptr, m_LastUse); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
auto lease = routingPath->remoteLease; auto lease = routingPath->remoteLease;
auto tunnel = routingPath->outboundTunnel; auto tunnel = routingPath->outboundTunnel;
if(lease) if (lease) {
{ if (tunnel)
if(tunnel)
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse); return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse);
else else
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse); return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse);
} } else if (tunnel)
else if(tunnel)
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse); return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse);
else else
return DatagramSession::Info(nullptr, nullptr, m_LastUse); return DatagramSession::Info(nullptr, nullptr, m_LastUse);
} }
void DatagramSession::Ack() void DatagramSession::Ack() {
{
m_LastUse = i2p::util::GetMillisecondsSinceEpoch(); m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
auto path = GetSharedRoutingPath(); auto path = GetSharedRoutingPath();
if(path) if (path)
path->updateTime = i2p::util::GetSecondsSinceEpoch (); path->updateTime = i2p::util::GetSecondsSinceEpoch();
if (IsRatchets ()) if (IsRatchets())
SendMsg (nullptr); // send empty message in case if we have some data to send SendMsg(nullptr); // send empty message in case if we have some data to send
} }
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath () std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath() {
{ if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired()) {
if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ())
{
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent); m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
if (!m_RemoteLeaseSet) if (!m_RemoteLeaseSet) {
{ if (!m_RequestingLS) {
if(!m_RequestingLS)
{
m_RequestingLS = true; 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; return nullptr;
} }
} }
if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) if (!m_RoutingSession || m_RoutingSession->IsTerminated() || !m_RoutingSession->IsReadyToSend()) {
{
bool found = false; bool found = false;
for (auto& it: m_PendingRoutingSessions) for (auto &it: m_PendingRoutingSessions)
if (it->GetOwner () && m_RoutingSession->IsReadyToSend ()) // found established session if (it->GetOwner() && m_RoutingSession->IsReadyToSend()) // found established session
{ {
m_RoutingSession = it; m_RoutingSession = it;
m_PendingRoutingSessions.clear (); m_PendingRoutingSessions.clear();
found = true; found = true;
break; break;
} }
if (!found) if (!found) {
{
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) if (!m_RoutingSession->GetOwner() || !m_RoutingSession->IsReadyToSend())
m_PendingRoutingSessions.push_back (m_RoutingSession); m_PendingRoutingSessions.push_back(m_RoutingSession);
} }
} }
auto path = m_RoutingSession->GetSharedRoutingPath(); auto path = m_RoutingSession->GetSharedRoutingPath();
if (path && m_RoutingSession->IsRatchets () && if (path && m_RoutingSession->IsRatchets() &&
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT) m_LastUse > m_RoutingSession->GetLastActivityTimestamp() * 1000 + DATAGRAM_SESSION_PATH_TIMEOUT) {
{ m_RoutingSession->SetSharedRoutingPath(nullptr);
m_RoutingSession->SetSharedRoutingPath (nullptr);
path = nullptr; path = nullptr;
} }
if (path) if (path) {
{ if (path->outboundTunnel && !path->outboundTunnel->IsEstablished()) {
if (path->outboundTunnel && !path->outboundTunnel->IsEstablished ())
{
// bad outbound tunnel, switch outbound tunnel // 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) 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 // bad lease, switch to next one
if (m_RemoteLeaseSet) if (m_RemoteLeaseSet) {
{
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding( auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding(
[&](const i2p::data::Lease& l) -> bool [&](const i2p::data::Lease &l) -> bool {
{
return l.tunnelID == path->remoteLease->tunnelID; return l.tunnelID == path->remoteLease->tunnelID;
}); });
auto sz = ls.size(); auto sz = ls.size();
if (sz) if (sz) {
{
auto idx = rand() % sz; auto idx = rand() % sz;
path->remoteLease = ls[idx]; path->remoteLease = ls[idx];
} } else
else m_RoutingSession->SetSharedRoutingPath(nullptr);
m_RoutingSession->SetSharedRoutingPath (nullptr); } else {
}
else
{
// no remote lease set? // no remote lease set?
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32()); LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ",
m_RoutingSession->SetSharedRoutingPath (nullptr); m_RemoteIdent.ToBase32());
m_RoutingSession->SetSharedRoutingPath(nullptr);
} }
} }
} } else {
else
{
// no current path, make one // no current path, make one
path = std::make_shared<i2p::garlic::GarlicRoutingPath>(); path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
if (m_RemoteLeaseSet) if (m_RemoteLeaseSet) {
{
// pick random next good lease // pick random next good lease
auto ls = m_RemoteLeaseSet->GetNonExpiredLeases(); auto ls = m_RemoteLeaseSet->GetNonExpiredLeases();
auto sz = ls.size(); auto sz = ls.size();
if (sz) if (sz) {
{
auto idx = rand() % sz; auto idx = rand() % sz;
path->remoteLease = ls[idx]; path->remoteLease = ls[idx];
} } else
else
return nullptr; 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, 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; if (!path->outboundTunnel) return nullptr;
} } else {
else
{
// no remote lease set currently, bail // no remote lease set currently, bail
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32()); LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
return nullptr; return nullptr;
@ -401,33 +358,31 @@ namespace datagram
return path; 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; m_RequestingLS = false;
if(!ls) return; if (!ls) return;
// only update lease set if found and newer than previous lease set // only update lease set if found and newer than previous lease set
uint64_t oldExpire = 0; uint64_t oldExpire = 0;
if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime(); if (m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls; if (ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
} }
void DatagramSession::FlushSendQueue () void DatagramSession::FlushSendQueue() {
{ if (m_SendQueue.empty()) return;
if (m_SendQueue.empty ()) return;
std::vector<i2p::tunnel::TunnelMessageBlock> send; std::vector<i2p::tunnel::TunnelMessageBlock> send;
auto routingPath = GetSharedRoutingPath(); auto routingPath = GetSharedRoutingPath();
// if we don't have a routing path we will drop all queued messages // if we don't have a routing path we will drop all queued messages
if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease) if (routingPath && routingPath->outboundTunnel && routingPath->remoteLease) {
{ for (const auto &msg: m_SendQueue) {
for (const auto & msg : m_SendQueue)
{
auto m = m_RoutingSession->WrapSingleMessage(msg); auto m = m_RoutingSession->WrapSingleMessage(msg);
if (m) 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); routingPath->outboundTunnel->SendTunnelDataMsg(send);
} }
m_SendQueue.clear(); m_SendQueue.clear();
} }
} }
} }

View file

@ -20,14 +20,11 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Garlic.h" #include "Garlic.h"
namespace i2p namespace i2p {
{ namespace client {
namespace client
{
class ClientDestination; class ClientDestination;
} }
namespace datagram namespace datagram {
{
// milliseconds for max session idle time // milliseconds for max session idle time
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000; 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 // 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 // max 64 messages buffered in send queue for each datagram session
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64; 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: 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 Start();
void Stop ();
void Stop();
/** @brief ack the garlic routing path */ /** @brief ack the garlic routing path */
@ -59,24 +57,26 @@ namespace datagram
/** send an i2np message to remote endpoint for this session */ /** send an i2np message to remote endpoint for this session */
void SendMsg(std::shared_ptr<I2NPMessage> msg); void SendMsg(std::shared_ptr<I2NPMessage> msg);
void FlushSendQueue(); void FlushSendQueue();
/** get the last time in milliseconds for when we used this datagram session */ /** get the last time in milliseconds for when we used this datagram session */
uint64_t LastActivity() const { return m_LastUse; } 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> IBGW;
std::shared_ptr<const i2p::data::IdentHash> OBEP; std::shared_ptr<const i2p::data::IdentHash> OBEP;
const uint64_t activity; const uint64_t activity;
Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {} 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) { 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; 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; else OBEP = nullptr;
} }
}; };
@ -104,50 +104,79 @@ namespace datagram
typedef std::shared_ptr<DatagramSession> DatagramSession_ptr; typedef std::shared_ptr<DatagramSession> DatagramSession_ptr;
const size_t MAX_DATAGRAM_SIZE = 32768; const size_t MAX_DATAGRAM_SIZE = 32768;
class DatagramDestination
{ 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(const i2p::data::IdentityEx &from, uint16_t fromPort, uint16_t toPort,
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver; 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: public:
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip); DatagramDestination(std::shared_ptr<i2p::client::ClientDestination> owner, bool gzip);
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); ~DatagramDestination();
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void
SendDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident, uint16_t fromPort = 0,
uint16_t toPort = 0);
void SendRawDatagramTo(const uint8_t *payload, size_t len, const i2p::data::IdentHash &ident,
uint16_t fromPort = 0, uint16_t toPort = 0);
// TODO: implement calls from other thread from SAM // TODO: implement calls from other thread from SAM
std::shared_ptr<DatagramSession> GetSession(const i2p::data::IdentHash & ident); 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);
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 SendRawDatagram(std::shared_ptr<DatagramSession> session, const uint8_t *payload, size_t len,
void ResetReceiver () { m_Receiver = nullptr; }; 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 FlushSendQueue(std::shared_ptr<DatagramSession> session);
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 HandleDataMessagePayload(uint16_t fromPort, uint16_t toPort, const uint8_t *buf, size_t len,
void ResetRawReceiver () { m_RawReceiver = nullptr; }; 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 // clean up stale sessions
void CleanUp (); void CleanUp();
private: 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); 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 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 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 */ /** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
Receiver FindReceiver(uint16_t port); Receiver FindReceiver(uint16_t port);
@ -159,7 +188,7 @@ namespace datagram
RawReceiver m_RawReceiver; // default RawReceiver m_RawReceiver; // default
bool m_Gzip; // gzip compression of data messages bool m_Gzip; // gzip compression of data messages
std::mutex m_SessionsMutex; 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::mutex m_ReceiversMutex;
std::map<uint16_t, Receiver> m_ReceiversByPorts; std::map<uint16_t, Receiver> m_ReceiversByPorts;
@ -168,7 +197,7 @@ namespace datagram
std::vector<uint8_t> m_From, m_Signature; std::vector<uint8_t> m_From, m_Signature;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool; i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -28,10 +28,8 @@
#include "Datagram.h" #include "Datagram.h"
#include "util.h" #include "util.h"
namespace i2p namespace i2p {
{ namespace client {
namespace client
{
const uint8_t PROTOCOL_TYPE_STREAMING = 6; const uint8_t PROTOCOL_TYPE_STREAMING = 6;
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
const uint8_t PROTOCOL_TYPE_RAW = 18; const uint8_t PROTOCOL_TYPE_RAW = 18;
@ -86,16 +84,15 @@ namespace client
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings"; const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true; 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 // leaseSet = nullptr means not found
struct LeaseSetRequest struct LeaseSetRequest {
{ LeaseSetRequest(boost::asio::io_service &service) : requestTime(0), requestTimeoutTimer(service) {};
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
std::set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
uint64_t requestTime; uint64_t requestTime;
boost::asio::deadline_timer requestTimeoutTimer; boost::asio::deadline_timer requestTimeoutTimer;
@ -104,85 +101,128 @@ namespace client
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel; std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls) void Complete(std::shared_ptr<i2p::data::LeaseSet> ls) {
{ for (auto &it: requestComplete) it(ls);
for (auto& it: requestComplete) it (ls); requestComplete.clear();
requestComplete.clear ();
} }
}; };
public: public:
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr); LeaseSetDestination(boost::asio::io_service &service, bool isPublic,
~LeaseSetDestination (); const std::map<std::string, std::string> *params = nullptr);
const std::string& GetNickname () const { return m_Nickname; };
boost::asio::io_service& GetService () { return m_Service; };
virtual void Start (); ~LeaseSetDestination();
virtual void Stop ();
const std::string &GetNickname() const { return m_Nickname; };
boost::asio::io_service &GetService() { return m_Service; };
virtual void Start();
virtual void Stop();
/** i2cp reconfigure */ /** i2cp reconfigure */
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts); virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; }; 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 IsReady() const {
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); return m_LeaseSet && !m_LeaseSet->IsExpired() && m_Pool->GetOutboundTunnels().size() > 0;
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::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 // implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet (); std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const { return m_Pool; }
// override GarlicDestination // override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); bool SubmitSessionKey(const uint8_t *key, const uint8_t *tag);
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SetLeaseSetUpdated ();
bool IsPublic () const { return m_IsPublic; }; void SubmitECIESx25519Key(const uint8_t *key, uint64_t tag);
void SetPublic (bool pub) { m_IsPublic = pub; };
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: protected:
// implements GarlicDestination // implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len); void HandleI2NPMessage(const uint8_t *buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet); bool HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID);
int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; void SetLeaseSet(std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetAuthType () const { return m_AuthType; };
virtual void CleanupDestination () {}; // additional clean up in derived classes 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 // I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 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;
virtual void
CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels) = 0;
private: private:
void UpdateLeaseSet (); void UpdateLeaseSet();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt ();
void Publish ();
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (uint32_t msgID);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr); std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt();
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 Publish();
void HandleCleanupTimer (const boost::system::error_code& ecode);
void CleanupRemoteLeaseSets (); void HandlePublishConfirmationTimer(const boost::system::error_code &ecode);
i2p::data::CryptoKeyType GetPreferredCryptoType () const;
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: private:
boost::asio::io_service& m_Service; boost::asio::io_service &m_Service;
mutable std::mutex m_RemoteLeaseSetsMutex; 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<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests; std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
@ -204,83 +244,122 @@ namespace client
public: public:
// for HTTP only // for HTTP only
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); }; int GetNumRemoteLeaseSets() const { return m_RemoteLeaseSets.size(); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; }; const decltype(m_RemoteLeaseSets)
bool IsEncryptedLeaseSet () const { return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; }; &
bool IsPerClientAuth () const { return m_AuthType > 0; };
GetLeaseSets() const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet() const {
return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
}; };
class ClientDestination: public LeaseSetDestination bool IsPerClientAuth() const { return m_AuthType > 0; };
{ };
struct EncryptionKey
{ class ClientDestination : public LeaseSetDestination {
struct EncryptionKey {
uint8_t pub[256], priv[256]; uint8_t pub[256], priv[256];
i2p::data::CryptoKeyType keyType; i2p::data::CryptoKeyType keyType;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> decryptor; std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> decryptor;
EncryptionKey (i2p::data::CryptoKeyType t):keyType(t) { memset (pub, 0, 256); memset (priv, 0, 256); }; EncryptionKey(i2p::data::CryptoKeyType t) : keyType(t) {
void GenerateKeys () { i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv, pub); }; memset(pub, 0, 256);
void CreateDecryptor () { decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv); }; memset(priv, 0, 256);
};
void GenerateKeys() { i2p::data::PrivateKeys::GenerateCryptoKeyPair(keyType, priv, pub); };
void CreateDecryptor() { decryptor = i2p::data::PrivateKeys::CreateDecryptor(keyType, priv); };
}; };
public: public:
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, ClientDestination(boost::asio::io_service &service, const i2p::data::PrivateKeys &keys,
bool isPublic, const std::map<std::string, std::string> * params = nullptr); bool isPublic, const std::map<std::string, std::string> *params = nullptr);
~ClientDestination ();
void Start (); ~ClientDestination();
void Stop ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void Start();
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
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 // ref counter
int Acquire () { return ++m_RefCounter; }; int Acquire() { return ++m_RefCounter; };
int Release () { return --m_RefCounter; };
int GetRefCounter () const { return m_RefCounter; }; int Release() { return --m_RefCounter; };
int GetRefCounter() const { return m_RefCounter; };
// streaming // streaming
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port, bool gzip = true); // additional std::shared_ptr<i2p::stream::StreamingDestination>
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const; CreateStreamingDestination(int port, bool gzip = true); // additional
std::shared_ptr<i2p::stream::StreamingDestination> RemoveStreamingDestination (int port); 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 // following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); void
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0); CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash &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 CreateStream(StreamRequestComplete streamRequestComplete,
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to); std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams (); std::shared_ptr<i2p::stream::Stream>
bool IsAcceptingStreams () const; CreateStream(std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
int GetStreamingAckDelay () const { return m_StreamingAckDelay; } void SendPing(const i2p::data::IdentHash &to);
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
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 // datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; i2p::datagram::DatagramDestination *GetDatagramDestination() const { return m_DatagramDestination; };
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
i2p::datagram::DatagramDestination *CreateDatagramDestination(bool gzip = true);
// implements LocalDestination // implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) 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; std::shared_ptr<const i2p::data::IdentityEx> GetIdentity() const { return m_Keys.GetPublic(); };
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const;
bool SupportsEncryptionType(i2p::data::CryptoKeyType keyType) const;
const uint8_t *GetEncryptionPublicKey(i2p::data::CryptoKeyType keyType) const;
protected: protected:
void CleanupDestination (); void CleanupDestination();
// I2CP // I2CP
void HandleDataMessage (const uint8_t * buf, size_t len); void HandleDataMessage(const uint8_t *buf, size_t len);
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
void CreateNewLeaseSet(const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels);
private: private:
std::shared_ptr<ClientDestination> GetSharedFromThis () { std::shared_ptr<ClientDestination> GetSharedFromThis() {
return std::static_pointer_cast<ClientDestination>(shared_from_this ()); 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: private:
@ -292,7 +371,7 @@ namespace client
bool m_IsStreamingAnswerPings; bool m_IsStreamingAnswerPings;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts; 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 int m_RefCounter; // how many clients(tunnels) use this destination
boost::asio::deadline_timer m_ReadyChecker; boost::asio::deadline_timer m_ReadyChecker;
@ -302,22 +381,25 @@ namespace client
public: public:
// for HTTP only // for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const; std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams() const;
bool DeleteStream (uint32_t recvStreamID);
bool DeleteStream(uint32_t recvStreamID);
}; };
class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination class RunnableClientDestination : private i2p::util::RunnableService, public ClientDestination {
{
public: public:
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr); RunnableClientDestination(const i2p::data::PrivateKeys &keys, bool isPublic,
~RunnableClientDestination (); const std::map<std::string, std::string> *params = nullptr);
void Start (); ~RunnableClientDestination();
void Stop ();
void Start();
void Stop();
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -21,10 +21,8 @@
#include "Garlic.h" #include "Garlic.h"
#include "Tag.h" #include "Tag.h"
namespace i2p namespace i2p {
{ namespace garlic {
namespace garlic
{
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after 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_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 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 */ 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 */ // - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */
class RatchetTagSet class RatchetTagSet {
{
public: public:
RatchetTagSet () {}; RatchetTagSet() {};
virtual ~RatchetTagSet () {};
void DHInitialize (const uint8_t * rootKey, const uint8_t * k); virtual ~RatchetTagSet() {};
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 DHInitialize(const uint8_t *rootKey, const uint8_t *k);
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
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: private:
@ -68,24 +73,29 @@ namespace garlic
}; };
class ECIESX25519AEADRatchetSession; 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: public:
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false): ReceiveRatchetTagSet(std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false) :
m_Session (session), m_IsNS (isNS) {}; m_Session(session), m_IsNS(isNS) {};
bool IsNS () const { return m_IsNS; }; bool IsNS() const { return m_IsNS; };
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
int GetTrimBehind () const { return m_TrimBehindIndex; };
void Expire (); std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession() { return m_Session; };
bool IsExpired (uint64_t ts) const;
virtual bool IsIndexExpired (int index) const; void SetTrimBehind(int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
virtual bool HandleNextMessage (uint8_t * buf, size_t len, int 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: private:
@ -95,23 +105,22 @@ namespace garlic
uint64_t m_ExpirationTimestamp = 0; uint64_t m_ExpirationTimestamp = 0;
}; };
class SymmetricKeyTagSet: public ReceiveRatchetTagSet class SymmetricKeyTagSet : public ReceiveRatchetTagSet {
{
public: public:
SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key); SymmetricKeyTagSet(GarlicDestination *destination, const uint8_t *key);
bool IsIndexExpired (int index) const { return false; }; bool IsIndexExpired(int index) const { return false; };
bool HandleNextMessage (uint8_t * buf, size_t len, int index);
bool HandleNextMessage(uint8_t *buf, size_t len, int index);
private: private:
GarlicDestination * m_Destination; GarlicDestination *m_Destination;
uint8_t m_Key[32]; uint8_t m_Key[32];
}; };
enum ECIESx25519BlockType enum ECIESx25519BlockType {
{
eECIESx25519BlkDateTime = 0, eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1, eECIESx25519BlkSessionID = 1,
eECIESx25519BlkTermination = 4, eECIESx25519BlkTermination = 4,
@ -127,12 +136,10 @@ namespace garlic
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02; const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04; const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, class ECIESX25519AEADRatchetSession : public GarlicRoutingSession,
private i2p::crypto::NoiseSymmetricState, private i2p::crypto::NoiseSymmetricState,
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession> public std::enable_shared_from_this<ECIESX25519AEADRatchetSession> {
{ enum SessionState {
enum SessionState
{
eSessionStateNew = 0, eSessionStateNew = 0,
eSessionStateNewSessionReceived, eSessionStateNewSessionReceived,
eSessionStateNewSessionSent, eSessionStateNewSessionSent,
@ -141,8 +148,7 @@ namespace garlic
eSessionStateOneTime eSessionStateOneTime
}; };
struct DHRatchet struct DHRatchet {
{
int keyID = 0; int keyID = 0;
std::shared_ptr<i2p::crypto::X25519Keys> key; std::shared_ptr<i2p::crypto::X25519Keys> key;
uint8_t remote[32]; // last remote public key uint8_t remote[32]; // last remote public key
@ -151,59 +157,92 @@ namespace garlic
public: public:
ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS); ECIESX25519AEADRatchetSession(GarlicDestination *owner, bool attachLeaseSetNS);
~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0); ~ECIESX25519AEADRatchetSession();
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; } bool HandleNextMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } int index = 0);
void Terminate () { m_IsTerminated = true; } std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg);
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
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 CheckExpired(uint64_t ts); // true is expired
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; } bool CanBeRestarted(uint64_t ts) const {
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT;
}
bool IsRatchets () const { return true; }; bool IsInactive(uint64_t ts) const {
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; }; return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted(ts);
bool IsTerminated () const { return m_IsTerminated; } }
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
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: protected:
i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; }; i2p::crypto::NoiseSymmetricState &GetNoiseState() { return *this; };
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
void CreateNonce (uint64_t seqn, uint8_t * nonce); void SetNoiseState(const i2p::crypto::NoiseSymmetricState &state) { GetNoiseState() = state; };
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
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: private:
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes bool GenerateEphemeralKeysAndEncode(uint8_t *buf); // buf is 32 bytes
void InitNewSessionTagset (std::shared_ptr<RatchetTagSet> tagsetNsr) const; void InitNewSessionTagset(std::shared_ptr<RatchetTagSet> tagsetNsr) const;
bool HandleNewIncomingSession (const uint8_t * buf, size_t len); bool HandleNewIncomingSession(const uint8_t *buf, size_t len);
bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len);
bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index);
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true); bool HandleNewOutgoingSessionReply(uint8_t *buf, size_t len);
bool 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); bool
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len); HandleExistingSessionMessage(uint8_t *buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset,
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len); int index);
void GenerateMoreReceiveTags (std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int numTags); void
void NewNextSendRatchet (); 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: private:
@ -224,30 +263,34 @@ namespace garlic
public: public:
// for HTTP only // for HTTP only
int GetState () const { return (int)m_State; } int GetState() const { return (int) m_State; }
i2p::data::IdentHash GetDestination () const
{ i2p::data::IdentHash GetDestination() const {
return m_Destination ? *m_Destination : i2p::data::IdentHash (); return m_Destination ? *m_Destination : i2p::data::IdentHash();
} }
}; };
// single session for all incoming messages // single session for all incoming messages
class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession class RouterIncomingRatchetSession : public ECIESX25519AEADRatchetSession {
{
public: public:
RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); RouterIncomingRatchetSession(const i2p::crypto::NoiseSymmetricState &initState);
bool HandleNextMessage (const uint8_t * buf, size_t len);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; bool HandleNextMessage(const uint8_t *buf, size_t len);
i2p::crypto::NoiseSymmetricState &GetCurrentNoiseState() { return m_CurrentNoiseState; };
private: private:
i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; 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>
std::shared_ptr<I2NPMessage> WrapECIESX25519MessageForRouter (std::shared_ptr<const I2NPMessage> msg, const uint8_t * routerPublicKey); 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 #endif

View file

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

View file

@ -13,127 +13,168 @@
#include <openssl/bn.h> #include <openssl/bn.h>
#include "Crypto.h" #include "Crypto.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto struct EDDSAPoint {
{ BIGNUM *x{nullptr};
struct EDDSAPoint BIGNUM *y{nullptr};
{ BIGNUM *z{nullptr};
BIGNUM * x {nullptr}; BIGNUM *t{nullptr}; // projective coordinates
BIGNUM * y {nullptr};
BIGNUM * z {nullptr};
BIGNUM * t {nullptr}; // projective coordinates
EDDSAPoint () {} 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& operator=(EDDSAPoint&& other) EDDSAPoint(const EDDSAPoint &other) { *this = other; }
{
if (this != &other) EDDSAPoint(EDDSAPoint &&other) { *this = std::move(other); }
{
BN_free (x); x = other.x; other.x = nullptr; EDDSAPoint(BIGNUM *x1, BIGNUM *y1, BIGNUM *z1 = nullptr, BIGNUM *t1 = nullptr)
BN_free (y); y = other.y; other.y = nullptr; : x(x1), y(y1), z(z1), t(t1) {}
BN_free (z); z = other.z; other.z = nullptr;
BN_free (t); t = other.t; other.t = nullptr; ~EDDSAPoint() {
BN_free(x);
BN_free(y);
BN_free(z);
BN_free(t);
}
EDDSAPoint &operator=(EDDSAPoint &&other) {
if (this != &other) {
BN_free(x);
x = other.x;
other.x = nullptr;
BN_free(y);
y = other.y;
other.y = nullptr;
BN_free(z);
z = other.z;
other.z = nullptr;
BN_free(t);
t = other.t;
other.t = nullptr;
} }
return *this; return *this;
} }
EDDSAPoint& operator=(const EDDSAPoint& other) EDDSAPoint &operator=(const EDDSAPoint &other) {
{ if (this != &other) {
if (this != &other) BN_free(x);
{ x = other.x ? BN_dup(other.x) : nullptr;
BN_free (x); x = other.x ? BN_dup (other.x) : nullptr; BN_free(y);
BN_free (y); y = other.y ? BN_dup (other.y) : nullptr; y = other.y ? BN_dup(other.y) : nullptr;
BN_free (z); z = other.z ? BN_dup (other.z) : nullptr; BN_free(z);
BN_free (t); t = other.t ? BN_dup (other.t) : nullptr; z = other.z ? BN_dup(other.z) : nullptr;
BN_free(t);
t = other.t ? BN_dup(other.t) : nullptr;
} }
return *this; return *this;
} }
EDDSAPoint operator-() const EDDSAPoint operator-() const {
{ BIGNUM *x1 = NULL, *y1 = NULL, *z1 = NULL, *t1 = NULL;
BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL; if (x) {
if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); }; x1 = BN_dup(x);
if (y) y1 = BN_dup (y); BN_set_negative(x1, !BN_is_negative(x));
if (z) z1 = BN_dup (z); };
if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); }; if (y) y1 = BN_dup(y);
return EDDSAPoint {x1, y1, z1, t1}; 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_PUBLIC_KEY_LENGTH = 32;
const size_t EDDSA25519_SIGNATURE_LENGTH = 64; const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
class Ed25519
{ class Ed25519 {
public: public:
Ed25519 (); Ed25519();
Ed25519 (const Ed25519& other);
~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 #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 #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 BlindPublicKey(const uint8_t *pub, const uint8_t *seed,
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; uint8_t *blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; 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 bool Verify(const EDDSAPoint &publicKey, const uint8_t *digest, const uint8_t *signature) const;
void CreateRedDSAPrivateKey (uint8_t * priv); // priv is 32 bytes
void
Sign(const uint8_t *expandedPrivateKey, const uint8_t *publicKeyEncoded, const uint8_t *buf, size_t len,
uint8_t *signature) const;
void SignRedDSA(const uint8_t *privateKey, const uint8_t *publicKeyEncoded, const uint8_t *buf, size_t len,
uint8_t *signature) const;
static void
ExpandPrivateKey(const uint8_t *key, uint8_t *expandedKey); // key - 32 bytes, expandedKey - 64 bytes
void CreateRedDSAPrivateKey(uint8_t *priv); // priv is 32 bytes
private: private:
EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const; EDDSAPoint Sum(const EDDSAPoint &p1, const EDDSAPoint &p2, BN_CTX *ctx) const;
void Double (EDDSAPoint& p, BN_CTX * ctx) const;
EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const;
EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const; // B*e, e is 32 bytes Little Endian
EDDSAPoint Normalize (const EDDSAPoint& p, BN_CTX * ctx) const;
bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const; void Double(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; EDDSAPoint Mul(const EDDSAPoint &p, const BIGNUM *e, BN_CTX *ctx) const;
void EncodePoint (const EDDSAPoint& p, uint8_t * buf) 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> template<int len>
BIGNUM * DecodeBN (const uint8_t * buf) const; BIGNUM *DecodeBN(const uint8_t *buf) const;
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const;
void EncodeBN(const BIGNUM *bn, uint8_t *buf, size_t len) const;
#if !OPENSSL_X25519 #if !OPENSSL_X25519
// for 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 #endif
private: private:
BIGNUM * q, * l, * d, * I; BIGNUM *q, *l, *d, *I;
// transient values // 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 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 // if j > 128 we use 256 - j and carry 1 to next byte
// Bi256[0][0] = B, base point // Bi256[0][0] = B, base point
EDDSAPoint Bi256Carry; // Bi256[32][0] EDDSAPoint Bi256Carry; // Bi256[32][0]
}; };
std::unique_ptr<Ed25519>& GetEd25519 (); std::unique_ptr<Ed25519> &GetEd25519();
} }
} }
#endif #endif

View file

@ -10,49 +10,60 @@
#include "Crypto.h" #include "Crypto.h"
#include "Elligator.h" #include "Elligator.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
Elligator2::Elligator2 () Elligator2::Elligator2() {
{
// TODO: share with Ed22519 // TODO: share with Ed22519
p = BN_new (); p = BN_new();
// 2^255-19 // 2^255-19
BN_set_bit (p, 255); // 2^255 BN_set_bit(p, 255); // 2^255
BN_sub_word (p, 19); BN_sub_word(p, 19);
p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8 p38 = BN_dup(p);
p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2 BN_add_word(p38, 3);
p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4 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); A = BN_new();
nA = BN_new (); BN_sub (nA, p, A); 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) // calculate sqrt(-1)
sqrtn1 = BN_new (); sqrtn1 = BN_new();
BN_set_word (sqrtn1, 2); BN_set_word(sqrtn1, 2);
BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4 BN_mod_exp(sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
u = BN_new (); BN_set_word (u, 2); u = BN_new();
iu = BN_new (); BN_mod_inverse (iu, u, p, ctx); 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 () Elligator2::~Elligator2() {
{ BN_free(p);
BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14); BN_free(p38);
BN_free (sqrtn1); BN_free (A); BN_free (nA); BN_free(p12);
BN_free (u); BN_free (iu); 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; bool ret = true;
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
BN_CTX_start (ctx); BN_CTX_start(ctx);
uint8_t key1[32]; uint8_t key1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian for (size_t i = 0; i < 16; i++) // from Little Endian
@ -61,38 +72,35 @@ namespace crypto
key1[31 - i] = key[i]; key1[31 - i] = key[i];
} }
BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x); BIGNUM *x = BN_CTX_get(ctx);
BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A BN_bin2bn(key1, 32, x);
BN_sub (xA, p, xA); // p - (x + A) 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 BIGNUM *uxxA = BN_CTX_get(ctx); // u*x*xA
BN_mod_mul (uxxA, u, x, p, ctx); BN_mod_mul(uxxA, u, x, p, ctx);
BN_mod_mul (uxxA, uxxA, xA, 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 uint8_t randByte = 0; // random highest bits and high y
if (random) if (random) {
{ RAND_bytes(&randByte, 1);
RAND_bytes (&randByte, 1);
highY = randByte & 0x01; highY = randByte & 0x01;
} }
BIGNUM * r = BN_CTX_get (ctx); BIGNUM *r = BN_CTX_get(ctx);
if (highY) if (highY) {
{ BN_mod_inverse(r, x, p, ctx);
BN_mod_inverse (r, x, p, ctx); BN_mod_mul(r, r, xA, 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_mul(r, r, iu, p, ctx);
{
BN_mod_inverse (r, xA, p, ctx);
BN_mod_mul (r, r, x, p, ctx);
}
BN_mod_mul (r, r, iu, p, ctx);
SquareRoot (r, r, ctx); SquareRoot(r, r, ctx);
bn2buf (r, encoded, 32); bn2buf(r, encoded, 32);
if (random) if (random)
encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte
@ -102,20 +110,18 @@ namespace crypto
encoded[i] = encoded[31 - i]; encoded[i] = encoded[31 - i];
encoded[31 - i] = tmp; encoded[31 - i] = tmp;
} }
} } else
else
ret = false; ret = false;
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
return ret; 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; bool ret = true;
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
BN_CTX_start (ctx); BN_CTX_start(ctx);
uint8_t encoded1[32]; uint8_t encoded1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian for (size_t i = 0; i < 16; i++) // from Little Endian
@ -125,71 +131,70 @@ namespace crypto
} }
encoded1[0] &= 0x3F; // drop two highest bits 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) // v = -A/(1+u*r^2)
BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx); BIGNUM *v = BN_CTX_get(ctx);
BN_mod_mul (v, v, u, p, ctx); BN_mod_sqr(v, r, p, ctx);
BN_add_word (v, 1); BN_mod_mul(v, v, u, p, ctx);
BN_mod_inverse (v, v, p, ctx); BN_add_word(v, 1);
BN_mod_mul (v, v, nA, p, ctx); BN_mod_inverse(v, v, p, ctx);
BN_mod_mul(v, v, nA, p, ctx);
BIGNUM * vpA = BN_CTX_get (ctx); BIGNUM *vpA = BN_CTX_get(ctx);
BN_add (vpA, v, A); // v + A BN_add(vpA, v, A); // v + A
// t = v^3+A*v^2+v = v^2*(v+A)+v // t = v^3+A*v^2+v = v^2*(v+A)+v
BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx); BIGNUM *t = BN_CTX_get(ctx);
BN_mod_mul (t, t, vpA, p, ctx); BN_mod_sqr(t, v, p, ctx);
BN_mod_add (t, 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); int legendre = Legendre(t, ctx);
BIGNUM * x = BN_CTX_get (ctx); BIGNUM *x = BN_CTX_get(ctx);
if (legendre == 1) if (legendre == 1)
BN_copy (x, v); BN_copy(x, v);
else else {
{ BN_sub(x, p, v);
BN_sub (x, p, v); BN_mod_sub(x, x, A, p, ctx);
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 for (size_t i = 0; i < 16; i++) // To Little Endian
{ {
uint8_t tmp = key[i]; uint8_t tmp = key[i];
key[i] = key[31 - i]; key[i] = key[31 - i];
key[31 - i] = tmp; key[31 - i] = tmp;
} }
} } else
else
ret = false; ret = false;
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
return ret; return ret;
} }
void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const void Elligator2::SquareRoot(const BIGNUM *x, BIGNUM *r, BN_CTX *ctx) const {
{ BIGNUM *t = BN_CTX_get(ctx);
BIGNUM * t = BN_CTX_get (ctx); BN_mod_exp(t, x, p14, p, ctx); // t = x^((p-1)/4)
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_mod_exp (r, x, p38, p, ctx); // r = x^((p+3)/8) BN_add_word(t, 1);
BN_add_word (t, 1);
if (!BN_cmp (t, p)) if (!BN_cmp(t, p))
BN_mod_mul (r, r, sqrtn1, p, ctx); BN_mod_mul(r, r, sqrtn1, p, ctx);
if (BN_cmp (r, p12) > 0) // r > (p-1)/2 if (BN_cmp(r, p12) > 0) // r > (p-1)/2
BN_sub (r, p, r); 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 // assume a < p, so don't check for a % p = 0, but a = 0 only
if (BN_is_zero(a)) return 0; if (BN_is_zero(a)) return 0;
BIGNUM * r = BN_CTX_get (ctx); BIGNUM *r = BN_CTX_get(ctx);
BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p BN_mod_exp(r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
if (BN_is_word(r, 1)) if (BN_is_word(r, 1))
return 1; return 1;
else if (BN_is_zero(r)) else if (BN_is_zero(r))
@ -198,17 +203,16 @@ namespace crypto
} }
static std::unique_ptr<Elligator2> g_Elligator; static std::unique_ptr<Elligator2> g_Elligator;
std::unique_ptr<Elligator2>& GetElligator ()
{ std::unique_ptr<Elligator2> &GetElligator() {
if (!g_Elligator) if (!g_Elligator) {
{
auto el = new Elligator2(); auto el = new Elligator2();
if (!g_Elligator) // make sure it was not created already if (!g_Elligator) // make sure it was not created already
g_Elligator.reset (el); g_Elligator.reset(el);
else else
delete el; delete el;
} }
return g_Elligator; return g_Elligator;
} }
} }
} }

View file

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

View file

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

View file

@ -16,7 +16,7 @@
#include <functional> #include <functional>
namespace i2p { namespace i2p {
namespace fs { namespace fs {
extern std::string dirSep; extern std::string dirSep;
/** /**
@ -35,8 +35,7 @@ namespace fs {
* std::vector<std::string> files; * std::vector<std::string> files;
* h.Traverse(files); <- finds all files in storage and saves in given vector * h.Traverse(files); <- finds all files in storage and saves in given vector
*/ */
class HashedStorage class HashedStorage {
{
protected: protected:
std::string root; /**< path to storage with it's name included */ std::string root; /**< path to storage with it's name included */
@ -48,35 +47,44 @@ namespace fs {
public: public:
typedef std::function<void(const std::string &)> FilenameVisitor; 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) {}; name(n), prefix1(p1), prefix2(p2), suffix(s) {};
/** create subdirs in storage */ /** create subdirs in storage */
bool Init(const char* chars, size_t cnt); bool Init(const char *chars, size_t cnt);
const std::string & GetRoot() const { return root; }
const std::string & GetName() const { return name; } const std::string &GetRoot() const { return root; }
const std::string &GetName() const { return name; }
/** set directory where to place storage directory */ /** 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 */ /** 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 */ /** 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 */ /** 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 */ /** visit every file in this storage with a visitor */
void Iterate(FilenameVisitor v); void Iterate(FilenameVisitor v);
}; };
/** @brief Returns current application name, default 'i2pd' */ /** @brief Returns current application name, default 'i2pd' */
const std::string & GetAppName (); const std::string &GetAppName();
/** @brief Set application name, affects autodetection of datadir */ /** @brief Set application name, affects autodetection of datadir */
void SetAppName (const std::string& name); void SetAppName(const std::string &name);
/** @brief Returns datadir path */ /** @brief Returns datadir path */
const std::string & GetDataDir(); const std::string &GetDataDir();
/** @brief Returns certsdir path */ /** @brief Returns certsdir path */
const std::string & GetCertsDir(); const std::string &GetCertsDir();
/** @brief Returns datadir path in UTF-8 encoding */ /** @brief Returns datadir path in UTF-8 encoding */
const std::string GetUTF8DataDir(); const std::string GetUTF8DataDir();
@ -93,7 +101,7 @@ namespace fs {
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
* Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/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 * @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 * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates
* Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/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 * @brief Create subdirectories inside datadir
@ -119,33 +127,33 @@ namespace fs {
* @param files Vector to store found files * @param files Vector to store found files
* @return true on success and false if directory not exists * @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 * @brief Remove file with given path
* @param path Absolute path to file * @param path Absolute path to file
* @return true on success, false if file not exists, throws exception on error * @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 * @brief Check existence of file
* @param path Absolute path to file * @param path Absolute path to file
* @return true if file exists, false otherwise * @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> template<typename T>
void _ExpandPath(std::stringstream & path, T c) { void _ExpandPath(std::stringstream &path, T c) {
path << i2p::fs::dirSep << c; path << i2p::fs::dirSep << c;
} }
template<typename T, typename ... Other> 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, c);
_ExpandPath(path, other ...); _ExpandPath(path, other ...);
} }
@ -168,16 +176,15 @@ namespace fs {
} }
template<typename Storage, typename... Filename> template<typename Storage, typename... Filename>
std::string StorageRootPath (const Storage& storage, Filename... filenames) std::string StorageRootPath(const Storage &storage, Filename... filenames) {
{
std::stringstream s(""); std::stringstream s("");
s << storage.GetRoot (); s << storage.GetRoot();
_ExpandPath(s, filenames...); _ExpandPath(s, filenames...);
return s.str(); return s.str();
} }
} // fs } // fs
} // i2p } // i2p
#endif // /* FS_H__ */ #endif // /* FS_H__ */

View file

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

View file

@ -15,34 +15,36 @@
#include "Signature.h" #include "Signature.h"
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
typedef int FamilyID; typedef int FamilyID;
class Families
{ class Families {
public: public:
Families (); Families();
~Families ();
void LoadCertificates (); ~Families();
bool VerifyFamily (const std::string& family, const IdentHash& ident,
const char * signature, const char * key = nullptr) const; void LoadCertificates();
FamilyID GetFamilyID (const std::string& family) const;
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: private:
void LoadCertificate (const std::string& filename); void LoadCertificate(const std::string &filename);
private: private:
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id) 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 // return base64 signature of empty string in case of failure
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -22,26 +22,21 @@
#include "Queue.h" #include "Queue.h"
#include "Identity.h" #include "Identity.h"
namespace i2p namespace i2p {
{ namespace tunnel {
namespace tunnel
{
class OutboundTunnel; class OutboundTunnel;
} }
namespace garlic namespace garlic {
{
enum GarlicDeliveryType enum GarlicDeliveryType {
{
eGarlicDeliveryTypeLocal = 0, eGarlicDeliveryTypeLocal = 0,
eGarlicDeliveryTypeDestination = 1, eGarlicDeliveryTypeDestination = 1,
eGarlicDeliveryTypeRouter = 2, eGarlicDeliveryTypeRouter = 2,
eGarlicDeliveryTypeTunnel = 3 eGarlicDeliveryTypeTunnel = 3
}; };
struct ElGamalBlock struct ElGamalBlock {
{
uint8_t sessionKey[32]; uint8_t sessionKey[32];
uint8_t preIV[32]; uint8_t preIV[32];
uint8_t padding[158]; uint8_t padding[158];
@ -54,37 +49,41 @@ namespace garlic
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds const int ROUTING_PATH_EXPIRATION_TIMEOUT = 30; // 30 seconds
const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used const int ROUTING_PATH_MAX_NUM_TIMES_USED = 100; // how many times might be used
struct SessionTag: public i2p::data::Tag<32> struct SessionTag : public i2p::data::Tag<32> {
{ SessionTag(const uint8_t *buf, uint32_t ts = 0) : Tag<32>(buf), creationTime(ts) {};
SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {};
SessionTag () = default; SessionTag() = default;
SessionTag (const SessionTag& ) = default;
SessionTag& operator= (const SessionTag& ) = default; SessionTag(const SessionTag &) = default;
SessionTag &operator=(const SessionTag &) = default;
#ifndef _WIN32 #ifndef _WIN32
SessionTag (SessionTag&& ) = default;
SessionTag& operator= (SessionTag&& ) = default; SessionTag(SessionTag &&) = default;
SessionTag &operator=(SessionTag &&) = default;
#endif #endif
uint32_t creationTime; // seconds since epoch uint32_t creationTime; // seconds since epoch
}; };
// AESDecryption is associated with session tags and store key // AESDecryption is associated with session tags and store key
class AESDecryption: public i2p::crypto::CBCDecryption class AESDecryption : public i2p::crypto::CBCDecryption {
{
public: public:
AESDecryption (const uint8_t * key): m_Key (key) AESDecryption(const uint8_t *key) : m_Key(key) {
{ SetKey(key);
SetKey (key);
} }
const i2p::crypto::AESKey& GetKey () const { return m_Key; };
const i2p::crypto::AESKey &GetKey() const { return m_Key; };
private: private:
i2p::crypto::AESKey m_Key; i2p::crypto::AESKey m_Key;
}; };
struct GarlicRoutingPath struct GarlicRoutingPath {
{
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel; std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<const i2p::data::Lease> remoteLease; std::shared_ptr<const i2p::data::Lease> remoteLease;
int rtt; // RTT int rtt; // RTT
@ -93,12 +92,11 @@ namespace garlic
}; };
class GarlicDestination; class GarlicDestination;
class GarlicRoutingSession
{ class GarlicRoutingSession {
protected: protected:
enum LeaseSetUpdateStatus enum LeaseSetUpdateStatus {
{
eLeaseSetUpToDate = 0, eLeaseSetUpToDate = 0,
eLeaseSetUpdated, eLeaseSetUpdated,
eLeaseSetSubmitted, eLeaseSetSubmitted,
@ -107,45 +105,62 @@ namespace garlic
public: public:
GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet); GarlicRoutingSession(GarlicDestination *owner, bool attachLeaseSet);
GarlicRoutingSession ();
virtual ~GarlicRoutingSession ();
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0;
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed (uint32_t msgID);
virtual bool IsRatchets () const { return false; };
virtual bool IsReadyToSend () const { return true; };
virtual bool IsTerminated () const { return !GetOwner (); };
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
void SetLeaseSetUpdated () 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; 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 (); bool IsLeaseSetNonConfirmed() const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
GarlicDestination * GetOwner () const { return m_Owner; } bool IsLeaseSetUpdated() const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
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: protected:
LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } LeaseSetUpdateStatus GetLeaseSetUpdateStatus() const { return m_LeaseSetUpdateStatus; }
void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; }
uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; }
void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; }
void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; }
std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg (uint32_t msgID); 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: private:
GarlicDestination * m_Owner; GarlicDestination *m_Owner;
LeaseSetUpdateStatus m_LeaseSetUpdateStatus; LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID; uint32_t m_LeaseSetUpdateMsgID;
@ -156,45 +171,53 @@ namespace garlic
public: public:
// for HTTP only // for HTTP only
virtual size_t GetNumOutgoingTags () const { return 0; }; virtual size_t GetNumOutgoingTags() const { return 0; };
}; };
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>; //using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 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> class ElGamalAESSession : public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession> {
{ struct UnconfirmedTags {
struct UnconfirmedTags UnconfirmedTags(int n) : numTags(n), tagsCreationTime(0) { sessionTags = new SessionTag[numTags]; };
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; ~UnconfirmedTags() { delete[] sessionTags; };
~UnconfirmedTags () { delete[] sessionTags; };
uint32_t msgID; uint32_t msgID;
int numTags; int numTags;
SessionTag * sessionTags; SessionTag *sessionTags;
uint32_t tagsCreationTime; uint32_t tagsCreationTime;
}; };
public: 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); 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); std::shared_ptr<I2NPMessage> WrapSingleMessage(std::shared_ptr<const I2NPMessage> msg);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted bool MessageConfirmed(uint32_t msgID);
bool CleanupExpiredTags(); // returns true if something left
bool CleanupUnconfirmedTags(); // returns true if something has been deleted
private: private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg); 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);
void TagsConfirmed (uint32_t msgID); size_t
UnconfirmedTags * GenerateSessionTags (); 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: private:
@ -210,73 +233,97 @@ namespace garlic
public: public:
// for HTTP only // 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; typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr;
class ECIESX25519AEADRatchetSession; class ECIESX25519AEADRatchetSession;
typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr; typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr;
class ReceiveRatchetTagSet; class ReceiveRatchetTagSet;
typedef std::shared_ptr<ReceiveRatchetTagSet> ReceiveRatchetTagSetPtr; typedef std::shared_ptr<ReceiveRatchetTagSet> ReceiveRatchetTagSetPtr;
struct ECIESX25519AEADRatchetIndexTagset struct ECIESX25519AEADRatchetIndexTagset {
{
int index; int index;
ReceiveRatchetTagSetPtr tagset; ReceiveRatchetTagSetPtr tagset;
}; };
class GarlicDestination: public i2p::data::LocalDestination class GarlicDestination : public i2p::data::LocalDestination {
{
public: public:
GarlicDestination (); GarlicDestination();
~GarlicDestination ();
void CleanUp (); ~GarlicDestination();
void SetNumTags (int numTags) { m_NumTags = numTags; };
int GetNumTags () const { return m_NumTags; }; void CleanUp();
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; }; void SetNumTags(int numTags) { m_NumTags = numTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags (); int GetNumTags() const { return m_NumTags; };
void RemoveDeliveryStatusSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router, 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); std::shared_ptr<I2NPMessage> msg);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void AddSessionKey(const uint8_t *key, const uint8_t *tag); // one tag
void AddECIESx25519Key (const uint8_t * key, uint64_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 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 virtual void SubmitECIESx25519Key(const uint8_t *key, uint64_t tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); void DeliveryStatusSent(GarlicRoutingSessionPtr session, uint32_t msgID);
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
void RemoveECIESx25519Session (const uint8_t * staticKey);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
uint8_t * GetPayloadBuffer ();
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); uint64_t AddECIESx25519SessionNextTag(ReceiveRatchetTagSetPtr tagset);
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
virtual void SetLeaseSetUpdated ();
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO void AddECIESx25519Session(const uint8_t *staticKey, ECIESX25519AEADRatchetSessionPtr session);
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
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: protected:
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag void AddECIESx25519Key(const uint8_t *key, const uint8_t *tag); // one tag
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found 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 void HandleI2NPMessage(const uint8_t *buf, size_t len) = 0; // called from clove only
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0; virtual bool
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg); HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID) = 0;
void HandleDeliveryStatusMessage (uint32_t msgID);
void SaveTags (); void HandleGarlicMessage(std::shared_ptr<I2NPMessage> msg);
void LoadTags ();
void HandleDeliveryStatusMessage(uint32_t msgID);
void SaveTags();
void LoadTags();
private: 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); 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: private:
@ -285,7 +332,7 @@ namespace garlic
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions; std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session 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 // incoming
int m_NumRatchetInboundTags; int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags; std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
@ -298,15 +345,23 @@ namespace garlic
public: public:
// for HTTP only // for HTTP only
size_t GetNumIncomingTags () const { return m_Tags.size (); } 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; }; size_t GetNumIncomingECIESx25519Tags() const { return m_ECIESx25519Tags.size(); }
const decltype(m_ECIESx25519Sessions)& GetECIESx25519Sessions () const { return m_ECIESx25519Sessions; }
const decltype(m_Sessions)
&
GetSessions() const { return m_Sessions; };
const decltype(m_ECIESx25519Sessions)
&
GetECIESx25519Sessions() const { return m_ECIESx25519Sessions; }
}; };
void CleanUpTagsFiles (); void CleanUpTagsFiles();
} }
} }
#endif #endif

View file

@ -14,133 +14,122 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Gost.h" #include "Gost.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
// GOST R 34.10 // GOST R 34.10
GOSTR3410Curve::GOSTR3410Curve (BIGNUM * a, BIGNUM * b, BIGNUM * p, BIGNUM * q, BIGNUM * x, BIGNUM * y) GOSTR3410Curve::GOSTR3410Curve(BIGNUM *a, BIGNUM *b, BIGNUM *p, BIGNUM *q, BIGNUM *x, BIGNUM *y) {
{ m_KeyLen = BN_num_bytes(p);
m_KeyLen = BN_num_bytes (p); BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); m_Group = EC_GROUP_new_curve_GFp(p, a, b, ctx);
m_Group = EC_GROUP_new_curve_GFp (p, a, b, ctx); EC_POINT *P = EC_POINT_new(m_Group);
EC_POINT * P = EC_POINT_new (m_Group); EC_POINT_set_affine_coordinates_GFp(m_Group, P, x, y, ctx);
EC_POINT_set_affine_coordinates_GFp (m_Group, P, x, y, ctx); EC_GROUP_set_generator(m_Group, P, q, nullptr);
EC_GROUP_set_generator (m_Group, P, q, nullptr); EC_GROUP_set_curve_name(m_Group, NID_id_GostR3410_2001);
EC_GROUP_set_curve_name (m_Group, NID_id_GostR3410_2001);
EC_POINT_free(P); EC_POINT_free(P);
BN_CTX_free (ctx); BN_CTX_free(ctx);
} }
GOSTR3410Curve::~GOSTR3410Curve () GOSTR3410Curve::~GOSTR3410Curve() {
{ EC_GROUP_free(m_Group);
EC_GROUP_free (m_Group);
} }
EC_POINT * GOSTR3410Curve::MulP (const BIGNUM * n) const EC_POINT *GOSTR3410Curve::MulP(const BIGNUM *n) const {
{ BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); auto p = EC_POINT_new(m_Group);
auto p = EC_POINT_new (m_Group); EC_POINT_mul(m_Group, p, n, nullptr, nullptr, ctx);
EC_POINT_mul (m_Group, p, n, nullptr, nullptr, ctx); BN_CTX_free(ctx);
BN_CTX_free (ctx);
return p; return p;
} }
bool GOSTR3410Curve::GetXY (const EC_POINT * p, BIGNUM * x, BIGNUM * y) const bool GOSTR3410Curve::GetXY(const EC_POINT *p, BIGNUM *x, BIGNUM *y) const {
{ return EC_POINT_get_affine_coordinates_GFp(m_Group, p, x, y, nullptr);
return EC_POINT_get_affine_coordinates_GFp (m_Group, p, x, y, nullptr);
} }
EC_POINT * GOSTR3410Curve::CreatePoint (const BIGNUM * x, const BIGNUM * y) const EC_POINT *GOSTR3410Curve::CreatePoint(const BIGNUM *x, const BIGNUM *y) const {
{ EC_POINT *p = EC_POINT_new(m_Group);
EC_POINT * p = EC_POINT_new (m_Group); EC_POINT_set_affine_coordinates_GFp(m_Group, p, x, y, nullptr);
EC_POINT_set_affine_coordinates_GFp (m_Group, p, x, y, nullptr);
return p; return p;
} }
void GOSTR3410Curve::Sign (const BIGNUM * priv, const BIGNUM * digest, BIGNUM * r, BIGNUM * s) void GOSTR3410Curve::Sign(const BIGNUM *priv, const BIGNUM *digest, BIGNUM *r, BIGNUM *s) {
{ BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); BN_CTX_start(ctx);
BN_CTX_start (ctx); BIGNUM *q = BN_CTX_get(ctx);
BIGNUM * q = BN_CTX_get (ctx);
EC_GROUP_get_order(m_Group, q, ctx); EC_GROUP_get_order(m_Group, q, ctx);
BIGNUM * k = BN_CTX_get (ctx); BIGNUM *k = BN_CTX_get(ctx);
BN_rand_range (k, q); // 0 < k < q BN_rand_range(k, q); // 0 < k < q
EC_POINT * C = MulP (k); // C = k*P EC_POINT *C = MulP(k); // C = k*P
GetXY (C, r, nullptr); // r = Cx GetXY(C, r, nullptr); // r = Cx
EC_POINT_free (C); EC_POINT_free(C);
BN_mod_mul (s, r, priv, q, ctx); // (r*priv)%q BN_mod_mul(s, r, priv, q, ctx); // (r*priv)%q
BIGNUM * tmp = BN_CTX_get (ctx); BIGNUM *tmp = BN_CTX_get(ctx);
BN_mod_mul (tmp, k, digest, q, ctx); // (k*digest)%q 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_mod_add(s, s, tmp, q, ctx); // (r*priv+k*digest)%q
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
} }
bool GOSTR3410Curve::Verify (const EC_POINT * pub, const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s) bool GOSTR3410Curve::Verify(const EC_POINT *pub, const BIGNUM *digest, const BIGNUM *r, const BIGNUM *s) {
{ BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); BN_CTX_start(ctx);
BN_CTX_start (ctx); BIGNUM *q = BN_CTX_get(ctx);
BIGNUM * q = BN_CTX_get (ctx);
EC_GROUP_get_order(m_Group, q, ctx); EC_GROUP_get_order(m_Group, q, ctx);
BIGNUM * h = BN_CTX_get (ctx); BIGNUM *h = BN_CTX_get(ctx);
BN_mod (h, digest, q, ctx); // h = digest % q BN_mod(h, digest, q, ctx); // h = digest % q
BN_mod_inverse (h, h, q, ctx); // 1/h mod q BN_mod_inverse(h, h, q, ctx); // 1/h mod q
BIGNUM * z1 = BN_CTX_get (ctx); BIGNUM *z1 = BN_CTX_get(ctx);
BN_mod_mul (z1, s, h, q, ctx); // z1 = s/h BN_mod_mul(z1, s, h, q, ctx); // z1 = s/h
BIGNUM * z2 = BN_CTX_get (ctx); BIGNUM *z2 = BN_CTX_get(ctx);
BN_sub (z2, q, r); // z2 = -r BN_sub(z2, q, r); // z2 = -r
BN_mod_mul (z2, z2, h, q, ctx); // z2 = -r/h BN_mod_mul(z2, z2, h, q, ctx); // z2 = -r/h
EC_POINT * C = EC_POINT_new (m_Group); EC_POINT *C = EC_POINT_new(m_Group);
EC_POINT_mul (m_Group, C, z1, pub, z2, ctx); // z1*P + z2*pub EC_POINT_mul(m_Group, C, z1, pub, z2, ctx); // z1*P + z2*pub
BIGNUM * x = BN_CTX_get (ctx); BIGNUM *x = BN_CTX_get(ctx);
GetXY (C, x, nullptr); // Cx GetXY(C, x, nullptr); // Cx
BN_mod (x, x, q, ctx); // Cx % q BN_mod(x, x, q, ctx); // Cx % q
bool ret = !BN_cmp (x, r); // Cx = r ? bool ret = !BN_cmp(x, r); // Cx = r ?
EC_POINT_free (C); EC_POINT_free(C);
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
return ret; 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 // s*P = r*Q + h*C
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
BN_CTX_start (ctx); BN_CTX_start(ctx);
EC_POINT * C = EC_POINT_new (m_Group); // C = k*P = (rx, ry) EC_POINT *C = EC_POINT_new(m_Group); // C = k*P = (rx, ry)
EC_POINT * Q = nullptr; EC_POINT *Q = nullptr;
if (EC_POINT_set_compressed_coordinates_GFp (m_Group, C, r, isNegativeY ? 1 : 0, ctx)) if (EC_POINT_set_compressed_coordinates_GFp(m_Group, C, r, isNegativeY ? 1 : 0, ctx)) {
{ EC_POINT *S = EC_POINT_new(m_Group); // S = s*P
EC_POINT * S = EC_POINT_new (m_Group); // S = s*P EC_POINT_mul(m_Group, S, s, nullptr, nullptr, ctx);
EC_POINT_mul (m_Group, S, s, nullptr, nullptr, ctx); BIGNUM *q = BN_CTX_get(ctx);
BIGNUM * q = BN_CTX_get (ctx);
EC_GROUP_get_order(m_Group, q, ctx); EC_GROUP_get_order(m_Group, q, ctx);
BIGNUM * h = BN_CTX_get (ctx); BIGNUM *h = BN_CTX_get(ctx);
BN_mod (h, digest, q, ctx); // h = digest % q BN_mod(h, digest, q, ctx); // h = digest % q
BN_sub (h, q, h); // h = -h BN_sub(h, q, h); // h = -h
EC_POINT * H = EC_POINT_new (m_Group); EC_POINT *H = EC_POINT_new(m_Group);
EC_POINT_mul (m_Group, H, nullptr, C, h, ctx); // -h*C 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_add(m_Group, C, S, H, ctx); // s*P - h*C
EC_POINT_free (H); EC_POINT_free(H);
EC_POINT_free (S); EC_POINT_free(S);
BIGNUM * r1 = BN_CTX_get (ctx); BIGNUM *r1 = BN_CTX_get(ctx);
BN_mod_inverse (r1, r, q, ctx); BN_mod_inverse(r1, r, q, ctx);
Q = EC_POINT_new (m_Group); Q = EC_POINT_new(m_Group);
EC_POINT_mul (m_Group, Q, nullptr, C, r1, ctx); // (s*P - h*C)/r EC_POINT_mul(m_Group, Q, nullptr, C, r1, ctx); // (s*P - h*C)/r
} }
EC_POINT_free (C); EC_POINT_free(C);
BN_CTX_end (ctx); BN_CTX_end(ctx);
BN_CTX_free (ctx); BN_CTX_free(ctx);
return Q; return Q;
} }
static GOSTR3410Curve * CreateGOSTR3410Curve (GOSTR3410ParamSet paramSet) static GOSTR3410Curve *CreateGOSTR3410Curve(GOSTR3410ParamSet paramSet) {
{
// a, b, p, q, x, y // a, b, p, q, x, y
static const char * params[eGOSTR3410NumParamSets][6] = static const char *params[eGOSTR3410NumParamSets][6] =
{ {
{ {
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
@ -160,26 +149,30 @@ namespace crypto
} // tc26-2012-paramSetA-512 } // 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(&a, params[paramSet][0]);
BN_hex2bn(&b, params[paramSet][1]); BN_hex2bn(&b, params[paramSet][1]);
BN_hex2bn(&p, params[paramSet][2]); BN_hex2bn(&p, params[paramSet][2]);
BN_hex2bn(&q, params[paramSet][3]); BN_hex2bn(&q, params[paramSet][3]);
BN_hex2bn(&x, params[paramSet][4]); BN_hex2bn(&x, params[paramSet][4]);
BN_hex2bn(&y, params[paramSet][5]); BN_hex2bn(&y, params[paramSet][5]);
auto curve = new GOSTR3410Curve (a, b, p, q, x, 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); BN_free(a);
BN_free(b);
BN_free(p);
BN_free(q);
BN_free(x);
BN_free(y);
return curve; return curve;
} }
static std::array<std::unique_ptr<GOSTR3410Curve>, eGOSTR3410NumParamSets> g_GOSTR3410Curves; static std::array <std::unique_ptr<GOSTR3410Curve>, eGOSTR3410NumParamSets> g_GOSTR3410Curves;
std::unique_ptr<GOSTR3410Curve>& GetGOSTR3410Curve (GOSTR3410ParamSet paramSet)
{ std::unique_ptr <GOSTR3410Curve> &GetGOSTR3410Curve(GOSTR3410ParamSet paramSet) {
if (!g_GOSTR3410Curves[paramSet]) if (!g_GOSTR3410Curves[paramSet]) {
{ auto c = CreateGOSTR3410Curve(paramSet);
auto c = CreateGOSTR3410Curve (paramSet);
if (!g_GOSTR3410Curves[paramSet]) // make sure it was not created already if (!g_GOSTR3410Curves[paramSet]) // make sure it was not created already
g_GOSTR3410Curves[paramSet].reset (c); g_GOSTR3410Curves[paramSet].reset(c);
else else
delete c; delete c;
} }
@ -783,28 +776,24 @@ namespace crypto
uint8_t buf[64]; uint8_t buf[64];
uint64_t ll[8]; uint64_t ll[8];
GOST3411Block operator^(const GOST3411Block& other) const GOST3411Block operator^(const GOST3411Block &other) const {
{
GOST3411Block ret; GOST3411Block ret;
for (int i = 0; i < 8; i++) 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; return ret;
} }
GOST3411Block operator^(const uint64_t * other) const GOST3411Block operator^(const uint64_t *other) const {
{
GOST3411Block ret; GOST3411Block ret;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
ret.ll[i] = ll[i]^other[i]; ret.ll[i] = ll[i] ^ other[i];
return ret; return ret;
} }
GOST3411Block operator+(const GOST3411Block& other) const GOST3411Block operator+(const GOST3411Block &other) const {
{
GOST3411Block ret; GOST3411Block ret;
uint8_t carry = 0; 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; uint16_t sum = buf[i] + other.buf[i] + carry;
ret.buf[i] = sum; ret.buf[i] = sum;
carry = sum >> 8; carry = sum >> 8;
@ -812,10 +801,8 @@ namespace crypto
return ret; return ret;
} }
void Add (uint32_t c) void Add(uint32_t c) {
{ for (int i = 63; i >= 0; i--) {
for (int i = 63; i >= 0; i--)
{
if (!c) return; if (!c) return;
c += buf[i]; c += buf[i];
buf[i] = c; buf[i] = c;
@ -823,189 +810,174 @@ namespace crypto
} }
} }
void F () void F() {
{
uint64_t res[8]; uint64_t res[8];
for (int b=0; b<8; b++) for (int b = 0; b < 8; b++) {
{
uint64_t r; uint64_t r;
r = T0[buf[b+56]]; r = T0[buf[b + 56]];
r ^= T1[buf[b+48]]; r ^= T1[buf[b + 48]];
r ^= T2[buf[b+40]]; r ^= T2[buf[b + 40]];
r ^= T3[buf[b+32]]; r ^= T3[buf[b + 32]];
r ^= T4[buf[b+24]]; r ^= T4[buf[b + 24]];
r ^= T5[buf[b+16]]; r ^= T5[buf[b + 16]];
r ^= T6[buf[b+8]]; r ^= T6[buf[b + 8]];
r ^= T7[buf[b]]; r ^= T7[buf[b]];
res[b] = r; 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 k = *this;
GOST3411Block res = k^m; GOST3411Block res = k ^ m;
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++) {
{ res.F();
res.F (); k = k ^ C_[i];
k = k^C_[i]; k.F();
k.F (); res = k ^ res;
res = k^res;
} }
return 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; GOST3411Block res = N ^ h;
res.F (); res.F();
res = res.E (m); res = res.E(m);
res = res^h; res = res ^ h;
res = res^m; res = res ^ m;
return res; 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 // stage 1
GOST3411Block h, N, s, m; GOST3411Block h, N, s, m;
memcpy (h.buf, iv, 64); memcpy(h.buf, iv, 64);
memset (N.buf, 0, 64); memset(N.buf, 0, 64);
memset (s.buf, 0, 64); memset(s.buf, 0, 64);
size_t l = len; size_t l = len;
// stage 2 // stage 2
while (l >= 64) while (l >= 64) {
{ memcpy(m.buf, buf + l - 64, 64); // TODO
memcpy (m.buf, buf + l - 64, 64); // TODO h = gN(N, h, m);
h= gN (N, h, m); N.Add(512);
N.Add (512);
s = m + s; s = m + s;
l -= 64; l -= 64;
} }
// stage 3 // stage 3
size_t padding = 64 - l; size_t padding = 64 - l;
if (padding) if (padding) {
{ memset(m.buf, 0, padding - 1);
memset (m.buf, 0, padding - 1);
m.buf[padding - 1] = 1; m.buf[padding - 1] = 1;
} }
memcpy (m.buf + padding, buf, l); memcpy(m.buf + padding, buf, l);
h = gN (N, h, m); h = gN(N, h, m);
N.Add (l*8); N.Add(l * 8);
s = m + s; s = m + s;
GOST3411Block N0; GOST3411Block N0;
memset (N0.buf, 0, 64); memset(N0.buf, 0, 64);
h = gN (N0, h, N); h = gN(N0, h, N);
h = gN (N0, h, s); 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]; uint8_t iv[64];
memset (iv, 1, 64); memset(iv, 1, 64);
uint8_t h[64]; uint8_t h[64];
H (iv, buf, len, h); H(iv, buf, len, h);
memcpy (digest, h, 32); // first half 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]; uint8_t iv[64];
memset (iv, 0, 64); memset(iv, 0, 64);
H (iv, buf, len, digest); H(iv, buf, len, digest);
} }
// reverse order // reverse order
struct GOSTR3411_2012_CTX struct GOSTR3411_2012_CTX {
{
GOST3411Block h, N, s, m; GOST3411Block h, N, s, m;
size_t len; size_t len;
bool is512; bool is512;
}; };
GOSTR3411_2012_CTX * GOSTR3411_2012_CTX_new () GOSTR3411_2012_CTX *GOSTR3411_2012_CTX_new() {
{
return new GOSTR3411_2012_CTX; 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; 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]; uint8_t iv[64];
memset (iv, is512 ? 0 : 1, 64); memset(iv, is512 ? 0 : 1, 64);
memcpy (ctx->h.buf, iv, 64); memcpy(ctx->h.buf, iv, 64);
memset (ctx->N.buf, 0, 64); memset(ctx->N.buf, 0, 64);
memset (ctx->s.buf, 0, 64); memset(ctx->s.buf, 0, 64);
ctx->len = 0; ctx->len = 0;
ctx->is512 = is512; 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 (!len) return;
if (ctx->len > 0) // something left from buffer if (ctx->len > 0) // something left from buffer
{ {
size_t l = 64 - ctx->len; size_t l = 64 - ctx->len;
if (len < l) l = len; if (len < l) l = len;
for (size_t i = 0; i < l; i++) for (size_t i = 0; i < l; i++)
ctx->m.buf[ctx->len + i] = buf[l-i-1]; // invert ctx->m.buf[ctx->len + i] = buf[l - i - 1]; // invert
ctx->len += l; len -= l; buf += l; ctx->len += l;
len -= l;
buf += l;
ctx->h = gN (ctx->N, ctx->h, ctx->m); ctx->h = gN(ctx->N, ctx->h, ctx->m);
ctx->N.Add (512); ctx->N.Add(512);
ctx->s = ctx->m + ctx->s; ctx->s = ctx->m + ctx->s;
} }
while (len >= 64) while (len >= 64) {
{
for (size_t i = 0; i < 64; i++) for (size_t i = 0; i < 64; i++)
ctx->m.buf[i] = buf[63-i]; // invert ctx->m.buf[i] = buf[63 - i]; // invert
len -= 64; buf += 64; len -= 64;
ctx->h = gN (ctx->N, ctx->h, ctx->m); buf += 64;
ctx->N.Add (512); ctx->h = gN(ctx->N, ctx->h, ctx->m);
ctx->N.Add(512);
ctx->s = ctx->m + ctx->s; ctx->s = ctx->m + ctx->s;
} }
if (len > 0) // carry remaining if (len > 0) // carry remaining
{ {
for (size_t i = 0; i < len; i++) 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; 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; GOST3411Block m;
size_t padding = 64 - ctx->len; size_t padding = 64 - ctx->len;
if (padding) if (padding) {
{ memset(m.buf, 0, padding - 1);
memset (m.buf, 0, padding - 1);
m.buf[padding - 1] = 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->h = gN(ctx->N, ctx->h, m);
ctx->N.Add (ctx->len*8); ctx->N.Add(ctx->len * 8);
ctx->s = m + ctx->s; ctx->s = m + ctx->s;
GOST3411Block N0; GOST3411Block N0;
memset (N0.buf, 0, 64); memset(N0.buf, 0, 64);
ctx->h = gN (N0, ctx->h, ctx->N); ctx->h = gN(N0, ctx->h, ctx->N);
ctx->h = gN (N0, ctx->h, ctx->s); ctx->h = gN(N0, ctx->h, ctx->s);
size_t sz = ctx->is512 ? 64 : 32; size_t sz = ctx->is512 ? 64 : 32;
for (size_t i = 0; i < sz; i++) for (size_t i = 0; i < sz; i++)
digest[i] = ctx->h.buf[sz - i - 1]; digest[i] = ctx->h.buf[sz - i - 1];
} }
} }
} }

View file

@ -12,15 +12,12 @@
#include <memory> #include <memory>
#include <openssl/ec.h> #include <openssl/ec.h>
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
// ГОСТ Р 34.10 // ГОСТ Р 34.10
enum GOSTR3410ParamSet enum GOSTR3410ParamSet {
{
eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1 eGOSTR3410CryptoProA = 0, // 1.2.643.2.2.35.1
// XchA = A, XchB = C // XchA = A, XchB = C
//eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0 //eGOSTR3410CryptoProXchA, // 1.2.643.2.2.36.0
@ -29,42 +26,56 @@ namespace crypto
eGOSTR3410NumParamSets eGOSTR3410NumParamSets
}; };
class GOSTR3410Curve class GOSTR3410Curve {
{
public: public:
GOSTR3410Curve (BIGNUM * a, BIGNUM * b, BIGNUM * p, BIGNUM * q, BIGNUM * x, BIGNUM * y); GOSTR3410Curve(BIGNUM *a, BIGNUM *b, BIGNUM *p, BIGNUM *q, BIGNUM *x, BIGNUM *y);
~GOSTR3410Curve ();
size_t GetKeyLen () const { return m_KeyLen; }; ~GOSTR3410Curve();
const EC_GROUP * GetGroup () const { return m_Group; };
EC_POINT * MulP (const BIGNUM * n) const; size_t GetKeyLen() const { return m_KeyLen; };
bool GetXY (const EC_POINT * p, BIGNUM * x, BIGNUM * y) const;
EC_POINT * CreatePoint (const BIGNUM * x, const BIGNUM * y) const; const EC_GROUP *GetGroup() const { return m_Group; };
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 *MulP(const BIGNUM *n) const;
EC_POINT * RecoverPublicKey (const BIGNUM * digest, const BIGNUM * r, const BIGNUM * s, bool isNegativeY = false) 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: private:
EC_GROUP * m_Group; EC_GROUP *m_Group;
size_t m_KeyLen; // in bytes size_t m_KeyLen; // in bytes
}; };
std::unique_ptr<GOSTR3410Curve>& GetGOSTR3410Curve (GOSTR3410ParamSet paramSet); std::unique_ptr <GOSTR3410Curve> &GetGOSTR3410Curve(GOSTR3410ParamSet paramSet);
// Big Endian // Big Endian
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);
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);
// Little Endian // Little Endian
struct GOSTR3411_2012_CTX; struct GOSTR3411_2012_CTX;
GOSTR3411_2012_CTX * GOSTR3411_2012_CTX_new ();
void GOSTR3411_2012_CTX_Init (GOSTR3411_2012_CTX * ctx, bool is512 = true); GOSTR3411_2012_CTX *GOSTR3411_2012_CTX_new();
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_Init(GOSTR3411_2012_CTX *ctx, bool is512 = true);
void GOSTR3411_2012_CTX_free (GOSTR3411_2012_CTX * ctx);
} 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 #endif

View file

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

View file

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

View file

@ -14,15 +14,14 @@
#include "Base.h" #include "Base.h"
#include "HTTP.h" #include "HTTP.h"
namespace i2p namespace i2p {
{ namespace http {
namespace http const std::vector <std::string> HTTP_METHODS = {
{
const std::vector<std::string> HTTP_METHODS = {
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic 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" "HTTP/1.0", "HTTP/1.1"
}; };
const std::vector<const char *> weekdays = { const std::vector<const char *> weekdays = {
@ -33,15 +32,15 @@ namespace http
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" "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); 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); 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::size_t count = 0;
std::stringstream ss(line); std::stringstream ss(line);
std::string token; 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 pos = 0;
std::size_t len = 1; /*: */ std::size_t len = 1; /*: */
std::size_t max = line.length(); std::size_t max = line.length();
@ -72,7 +70,7 @@ namespace http
return std::make_pair(line.substr(0, pos), line.substr(pos + len)); 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); std::time_t now = std::time(nullptr);
char buf[128]; char buf[128];
std::tm *tm = std::gmtime(&now); std::tm *tm = std::gmtime(&now);
@ -88,10 +86,10 @@ namespace http
return parse(url); 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_p = 0; /* < current parse position */
std::size_t pos_c = 0; /* < work 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; std::size_t pos_s = 0;
/* schema */ /* schema */
pos_c = url.find("://"); pos_c = url.find("://");
@ -108,7 +106,7 @@ namespace http
user = url.substr(pos_p, delim - pos_p); user = url.substr(pos_p, delim - pos_p);
delim += 1; delim += 1;
pass = url.substr(delim, pos_c - delim); pass = url.substr(delim, pos_c - delim);
} else if(delim) { } else if (delim) {
user = url.substr(pos_p, pos_c - pos_p); user = url.substr(pos_p, pos_c - pos_p);
} }
pos_p = pos_c + 1; pos_p = pos_c + 1;
@ -119,8 +117,7 @@ namespace http
auto pos_b = url.find(']', pos_p); auto pos_b = url.find(']', pos_p);
if (pos_b == std::string::npos) return false; if (pos_b == std::string::npos) return false;
pos_c = url.find_first_of(":/", pos_b); pos_c = url.find_first_of(":/", pos_b);
} } else
else
pos_c = url.find_first_of(":/", pos_p); pos_c = url.find_first_of(":/", pos_p);
if (pos_c == std::string::npos) { if (pos_c == std::string::npos) {
/* only hostname, without post and path */ /* only hostname, without post and path */
@ -135,7 +132,7 @@ namespace http
? url.substr(pos_p, std::string::npos) ? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p); : url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */ /* 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') if (c < '0' || c > '9')
return false; return false;
port *= 10; port *= 10;
@ -181,15 +178,15 @@ namespace http
return true; return true;
} }
bool URL::parse_query(std::map<std::string, std::string> & params) { bool URL::parse_query(std::map <std::string, std::string> &params) {
std::vector<std::string> tokens; std::vector <std::string> tokens;
strsplit(query, tokens, '&'); strsplit(query, tokens, '&');
params.clear(); params.clear();
for (const auto& it : tokens) { for (const auto &it: tokens) {
if (!it.length()) // empty if (!it.length()) // empty
continue; continue;
std::size_t eq = it.find ('='); std::size_t eq = it.find('=');
if (eq != std::string::npos) { if (eq != std::string::npos) {
auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1)); auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
params.insert(e); params.insert(e);
@ -224,12 +221,11 @@ namespace http
return out; return out;
} }
bool URL::is_i2p() const bool URL::is_i2p() const {
{ return host.rfind(".i2p") == (host.size() - 4);
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); add_header(name, value.c_str(), replace);
} }
@ -253,8 +249,10 @@ namespace http
return parse(str); return parse(str);
} }
int HTTPReq::parse(const std::string& str) { int HTTPReq::parse(const std::string &str) {
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE; enum {
REQ_LINE, HEADER_LINE
} expect = REQ_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */ std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0; std::size_t eol = 0, pos = 0;
URL url; URL url;
@ -265,7 +263,7 @@ namespace http
while ((eol = str.find(CRLF, pos)) != std::string::npos) { while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == REQ_LINE) { if (expect == REQ_LINE) {
std::string line = str.substr(pos, eol - pos); std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens; std::vector <std::string> tokens;
strsplit(line, tokens, ' '); strsplit(line, tokens, ' ');
if (tokens.size() != 3) if (tokens.size() != 3)
return -1; return -1;
@ -280,13 +278,11 @@ namespace http
uri = tokens[1]; uri = tokens[1];
version = tokens[2]; version = tokens[2];
expect = HEADER_LINE; expect = HEADER_LINE;
} } else {
else
{
std::string line = str.substr(pos, eol - pos); std::string line = str.substr(pos, eol - pos);
auto p = parse_header_line(line); auto p = parse_header_line(line);
if (p.first.length () > 0) if (p.first.length() > 0)
headers.push_back (p); headers.push_back(p);
else else
return -1; return -1;
} }
@ -297,57 +293,48 @@ namespace http
return eoh + strlen(HTTP_EOH); return eoh + strlen(HTTP_EOH);
} }
void HTTPReq::write(std::ostream & o) void HTTPReq::write(std::ostream &o) {
{
o << method << " " << uri << " " << version << CRLF; o << method << " " << uri << " " << version << CRLF;
for (auto & h : headers) for (auto &h: headers)
o << h.first << ": " << h.second << CRLF; o << h.first << ": " << h.second << CRLF;
o << CRLF; o << CRLF;
} }
std::string HTTPReq::to_string() std::string HTTPReq::to_string() {
{
std::stringstream ss; std::stringstream ss;
write(ss); write(ss);
return ss.str(); return ss.str();
} }
void HTTPReq::AddHeader (const std::string& name, const std::string& value) void HTTPReq::AddHeader(const std::string &name, const std::string &value) {
{ headers.push_back(std::make_pair(name, value));
headers.push_back (std::make_pair(name, value));
} }
void HTTPReq::UpdateHeader (const std::string& name, const std::string& value) void HTTPReq::UpdateHeader(const std::string &name, const std::string &value) {
{ for (auto &it: headers)
for (auto& it : headers) if (it.first == name) {
if (it.first == name)
{
it.second = value; it.second = value;
break; break;
} }
} }
void HTTPReq::RemoveHeader (const std::string& name, const std::string& exempt) void HTTPReq::RemoveHeader(const std::string &name, const std::string &exempt) {
{ for (auto it = headers.begin(); it != headers.end();) {
for (auto it = headers.begin (); it != headers.end ();) if (!it->first.compare(0, name.length(), name) && it->first != exempt)
{ it = headers.erase(it);
if (!it->first.compare(0, name.length (), name) && it->first != exempt)
it = headers.erase (it);
else else
it++; it++;
} }
} }
std::string HTTPReq::GetHeader (const std::string& name) const std::string HTTPReq::GetHeader(const std::string &name) const {
{ for (auto &it: headers)
for (auto& it : headers)
if (it.first == name) if (it.first == name)
return it.second; return it.second;
return ""; return "";
} }
bool HTTPRes::is_chunked() const bool HTTPRes::is_chunked() const {
{
auto it = headers.find("Transfer-Encoding"); auto it = headers.find("Transfer-Encoding");
if (it == headers.end()) if (it == headers.end())
return false; return false;
@ -356,8 +343,7 @@ namespace http
return false; return false;
} }
bool HTTPRes::is_gzipped(bool includingI2PGzip) const bool HTTPRes::is_gzipped(bool includingI2PGzip) const {
{
auto it = headers.find("Content-Encoding"); auto it = headers.find("Content-Encoding");
if (it == headers.end()) if (it == headers.end())
return false; /* no header */ return false; /* no header */
@ -368,8 +354,7 @@ namespace http
return false; return false;
} }
long int HTTPMsg::content_length() const long int HTTPMsg::content_length() const {
{
unsigned long int length = 0; unsigned long int length = 0;
auto it = headers.find("Content-Length"); auto it = headers.find("Content-Length");
if (it == headers.end()) if (it == headers.end())
@ -386,8 +371,10 @@ namespace http
return parse(str); return parse(str);
} }
int HTTPRes::parse(const std::string& str) { int HTTPRes::parse(const std::string &str) {
enum { RES_LINE, HEADER_LINE } expect = RES_LINE; enum {
RES_LINE, HEADER_LINE
} expect = RES_LINE;
std::size_t eoh = str.find(HTTP_EOH); /* request head size */ std::size_t eoh = str.find(HTTP_EOH); /* request head size */
std::size_t eol = 0, pos = 0; std::size_t eol = 0, pos = 0;
@ -397,7 +384,7 @@ namespace http
while ((eol = str.find(CRLF, pos)) != std::string::npos) { while ((eol = str.find(CRLF, pos)) != std::string::npos) {
if (expect == RES_LINE) { if (expect == RES_LINE) {
std::string line = str.substr(pos, eol - pos); std::string line = str.substr(pos, eol - pos);
std::vector<std::string> tokens; std::vector <std::string> tokens;
strsplit(line, tokens, ' ', 3); strsplit(line, tokens, ' ', 3);
if (tokens.size() != 3) if (tokens.size() != 3)
return -1; return -1;
@ -413,8 +400,8 @@ namespace http
} else { } else {
std::string line = str.substr(pos, eol - pos); std::string line = str.substr(pos, eol - pos);
auto p = parse_header_line(line); auto p = parse_header_line(line);
if (p.first.length () > 0) if (p.first.length() > 0)
headers.insert (p); headers.insert(p);
else else
return -1; return -1;
} }
@ -438,7 +425,7 @@ namespace http
/* build response */ /* build response */
std::stringstream ss; std::stringstream ss;
ss << version << " " << code << " " << status << CRLF; ss << version << " " << code << " " << status << CRLF;
for (auto & h : headers) { for (auto &h: headers) {
ss << h.first << ": " << h.second << CRLF; ss << h.first << ": " << h.second << CRLF;
} }
ss << CRLF; ss << CRLF;
@ -447,44 +434,77 @@ namespace http
return ss.str(); return ss.str();
} }
const char * HTTPCodeToStatus(int code) { const char *HTTPCodeToStatus(int code) {
const char *ptr; const char *ptr;
switch (code) { switch (code) {
case 105: ptr = "Name Not Resolved"; break; case 105:
ptr = "Name Not Resolved";
break;
/* success */ /* success */
case 200: ptr = "OK"; break; case 200:
case 206: ptr = "Partial Content"; break; ptr = "OK";
break;
case 206:
ptr = "Partial Content";
break;
/* redirect */ /* redirect */
case 301: ptr = "Moved Permanently"; break; case 301:
case 302: ptr = "Found"; break; ptr = "Moved Permanently";
case 304: ptr = "Not Modified"; break; break;
case 307: ptr = "Temporary Redirect"; break; case 302:
ptr = "Found";
break;
case 304:
ptr = "Not Modified";
break;
case 307:
ptr = "Temporary Redirect";
break;
/* client error */ /* client error */
case 400: ptr = "Bad Request"; break; case 400:
case 401: ptr = "Unauthorized"; break; ptr = "Bad Request";
case 403: ptr = "Forbidden"; break; break;
case 404: ptr = "Not Found"; break; case 401:
case 407: ptr = "Proxy Authentication Required"; break; ptr = "Unauthorized";
case 408: ptr = "Request Timeout"; break; 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 */ /* server error */
case 500: ptr = "Internal Server Error"; break; case 500:
case 502: ptr = "Bad Gateway"; break; ptr = "Internal Server Error";
case 503: ptr = "Not Implemented"; break; break;
case 504: ptr = "Gateway Timeout"; break; case 502:
default: ptr = "Unknown Status"; break; ptr = "Bad Gateway";
break;
case 503:
ptr = "Not Implemented";
break;
case 504:
ptr = "Gateway Timeout";
break;
default:
ptr = "Unknown Status";
break;
} }
return ptr; 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); std::string decoded(data);
size_t pos = 0; 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); char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
if (c == '\0' && !allow_null) if (c == '\0' && !allow_null) {
{
pos += 3; pos += 3;
continue; continue;
} }
@ -494,12 +514,10 @@ namespace http
return decoded; return decoded;
} }
bool MergeChunkedResponse (std::istream& in, std::ostream& out) bool MergeChunkedResponse(std::istream &in, std::ostream &out) {
{
std::string hexLen; std::string hexLen;
while (!in.eof ()) while (!in.eof()) {
{ std::getline(in, hexLen);
std::getline (in, hexLen);
errno = 0; errno = 0;
long int len = strtoul(hexLen.c_str(), (char **) NULL, 16); long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
if (errno != 0) if (errno != 0)
@ -508,20 +526,19 @@ namespace http
return true; /* end of stream */ return true; /* end of stream */
if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */ if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */
return false; /* too large chunk */ return false; /* too large chunk */
char * buf = new char[len]; char *buf = new char[len];
in.read (buf, len); in.read(buf, len);
out.write (buf, len); out.write(buf, len);
delete[] buf; delete[] buf;
std::getline (in, hexLen); // read \r\n after chunk std::getline(in, hexLen); // read \r\n after chunk
} }
return true; return true;
} }
std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass) std::string CreateBasicAuthorizationString(const std::string &user, const std::string &pass) {
{ if (user.empty() && pass.empty()) return "";
if (user.empty () && pass.empty ()) return ""; return "Basic " + i2p::data::ToBase64Standard(user + ":" + pass);
return "Basic " + i2p::data::ToBase64Standard (user + ":" + pass);
} }
} // http } // http
} // i2p } // i2p

View file

@ -16,17 +16,14 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace i2p namespace i2p {
{ namespace http {
namespace http
{
const char CRLF[] = "\r\n"; /**< HTTP line terminator */ const char CRLF[] = "\r\n"; /**< HTTP line terminator */
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */ 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_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_VERSIONS; /**< list of valid HTTP versions */
struct URL struct URL {
{
std::string schema; std::string schema;
std::string user; std::string user;
std::string pass; std::string pass;
@ -36,26 +33,27 @@ namespace http
std::string query; std::string query;
std::string frag; 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 * @brief Tries to parse url from string
* @return true on success, false on invalid url * @return true on success, false on invalid url
*/ */
bool parse (const char *str, std::size_t len = 0); bool parse(const char *str, std::size_t len = 0);
bool parse (const std::string& url);
bool parse(const std::string &url);
/** /**
* @brief Parse query part of url to key/value map * @brief Parse query part of url to key/value map
* @note Honestly, this should be implemented with std::multimap * @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 * @brief Serialize URL structure to url
* @note Returns relative url if schema if empty, absolute url otherwise * @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 * @brief return true if the host is inside i2p
@ -63,26 +61,26 @@ namespace http
bool is_i2p() const; bool is_i2p() const;
}; };
struct HTTPMsg struct HTTPMsg {
{ std::map <std::string, std::string> headers;
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 add_header(const char *name, const char *value, bool replace = false);
void del_header(const char *name); void del_header(const char *name);
/** @brief Returns declared message length or -1 if unknown */ /** @brief Returns declared message length or -1 if unknown */
long int content_length() const; long int content_length() const;
}; };
struct HTTPReq struct HTTPReq {
{ std::list <std::pair<std::string, std::string>> headers;
std::list<std::pair<std::string, std::string> > headers;
std::string version; std::string version;
std::string method; std::string method;
std::string uri; 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 * @brief Tries to parse HTTP request from string
@ -90,17 +88,23 @@ namespace http
* @note Positive return value is a size of header * @note Positive return value is a size of header
*/ */
int parse(const char *buf, size_t len); 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 */ /** @brief Serialize HTTP request to string */
std::string to_string(); std::string to_string();
void write(std::ostream & o);
void AddHeader (const std::string& name, const std::string& value); void write(std::ostream &o);
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 AddHeader(const std::string &name, const std::string &value);
void RemoveHeader (const std::string& name) { RemoveHeader (name, ""); };
std::string GetHeader (const std::string& name) const; 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 { struct HTTPRes : HTTPMsg {
@ -116,7 +120,7 @@ namespace http
*/ */
std::string body; 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 * @brief Tries to parse HTTP response from string
@ -124,7 +128,8 @@ namespace http
* @note Positive return value is a size of header * @note Positive return value is a size of header
*/ */
int parse(const char *buf, size_t len); 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 * @brief Serialize HTTP response to string
@ -135,10 +140,10 @@ namespace http
*/ */
std::string to_string(); std::string to_string();
void write(std::ostream & o); void write(std::ostream &o);
/** @brief Checks that response declared as chunked data */ /** @brief Checks that response declared as chunked data */
bool is_chunked() const ; bool is_chunked() const;
/** @brief Checks that response contains compressed data */ /** @brief Checks that response contains compressed data */
bool is_gzipped(bool includingI2PGzip = true) const; bool is_gzipped(bool includingI2PGzip = true) const;
@ -149,7 +154,7 @@ namespace http
* @param code HTTP code [100, 599] * @param code HTTP code [100, 599]
* @return Immutable string with status * @return Immutable string with status
*/ */
const char * HTTPCodeToStatus(int code); const char *HTTPCodeToStatus(int code);
/** /**
* @brief Replaces %-encoded characters in string with their values * @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 * @param null If set to true - decode also %00 sequence, otherwise - skip
* @return Decoded string * @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 * @brief Merge HTTP response content with Transfer-Encoding: chunked
@ -165,11 +170,11 @@ namespace http
* @param out Output stream * @param out Output stream
* @return true on success, false otherwise * @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 } // i2p
#endif /* HTTP_H__ */ #endif /* HTTP_H__ */

File diff suppressed because it is too large Load diff

View file

@ -19,8 +19,7 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "LeaseSet.h" #include "LeaseSet.h"
namespace i2p namespace i2p {
{
// I2NP header // I2NP header
const size_t I2NP_HEADER_TYPEID_OFFSET = 0; const size_t I2NP_HEADER_TYPEID_OFFSET = 0;
const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1; 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_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_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_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_REQUEST_EXPIRATION_OFFSET =
const size_t ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4; 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_PADDING_OFFSET = ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464; 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_OPTIONS_OFFSET = 0;
const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201; const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201;
enum I2NPMessageType enum I2NPMessageType {
{
eI2NPDummyMsg = 0, eI2NPDummyMsg = 0,
eI2NPDatabaseStore = 1, eI2NPDatabaseStore = 1,
eI2NPDatabaseLookup = 2, eI2NPDatabaseLookup = 2,
@ -132,177 +132,226 @@ namespace i2p
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000 const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100 const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
namespace tunnel namespace tunnel {
{
class InboundTunnel; class InboundTunnel;
class TunnelPool; class TunnelPool;
} }
const size_t I2NP_MAX_MESSAGE_SIZE = 62708; const size_t I2NP_MAX_MESSAGE_SIZE = 62708;
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096; 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_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 struct I2NPMessage {
{ uint8_t *buf;
uint8_t * buf;
size_t len, offset, maxLen; size_t len, offset, maxLen;
std::shared_ptr<i2p::tunnel::InboundTunnel> from; std::shared_ptr<i2p::tunnel::InboundTunnel> from;
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), I2NPMessage() : buf(nullptr), len(I2NP_HEADER_SIZE + 2),
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header offset(2), maxLen(0), from(nullptr) {}; // reserve 2 bytes for NTCP header
// header accessors // header accessors
uint8_t * GetHeader () { return GetBuffer (); }; uint8_t *GetHeader() { return GetBuffer(); };
const uint8_t * GetHeader () const { return GetBuffer (); };
void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; }; const uint8_t *GetHeader() const { return GetBuffer(); };
uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; };
void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); }; void SetTypeID(uint8_t typeID) { GetHeader()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); };
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); }; uint8_t GetTypeID() const { return GetHeader()[I2NP_HEADER_TYPEID_OFFSET]; };
uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); };
void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); }; void SetMsgID(uint32_t msgID) { htobe32buf(GetHeader() + I2NP_HEADER_MSGID_OFFSET, msgID); };
uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); };
void UpdateSize () { SetSize (GetPayloadLength ()); }; uint32_t GetMsgID() const { return bufbe32toh(GetHeader() + I2NP_HEADER_MSGID_OFFSET); };
void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; };
void UpdateChks () 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]; uint8_t hash[32];
SHA256(GetPayload (), GetPayloadLength (), hash); SHA256(GetPayload(), GetPayloadLength(), hash);
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0]; GetHeader()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
} }
// payload // payload
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; }; uint8_t *GetPayload() { return GetBuffer() + I2NP_HEADER_SIZE; };
const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; };
uint8_t * GetBuffer () { return buf + offset; };
const uint8_t * GetBuffer () const { return buf + offset; };
size_t GetLength () const { return len - offset; };
size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; };
void Align (size_t alignment) 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; if (len + alignment > maxLen) return;
size_t rem = ((size_t)GetBuffer ()) % alignment; size_t rem = ((size_t) GetBuffer()) % alignment;
if (rem) if (rem) {
{
offset += (alignment - rem); offset += (alignment - rem);
len += (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 // make sure with don't write beyond maxLen
if (len + len1 > maxLen) len1 = maxLen - len; if (len + len1 > maxLen) len1 = maxLen - len;
memcpy (buf + len, buf1, len1); memcpy(buf + len, buf1, len1);
len += len1; len += len1;
return len1; return len1;
} }
I2NPMessage& operator=(const I2NPMessage& other) I2NPMessage &operator=(const I2NPMessage &other) {
{ memcpy(buf + offset, other.buf + other.offset, other.GetLength());
memcpy (buf + offset, other.buf + other.offset, other.GetLength ()); len = offset + other.GetLength();
len = offset + other.GetLength ();
from = other.from; from = other.from;
return *this; return *this;
} }
// for SSU only // for SSU only
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; }; 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
void FromSSU(uint32_t msgID) // we have received SSU message and convert it to regular
{ {
const uint8_t * ssu = GetSSUHeader (); const uint8_t *ssu = GetSSUHeader();
GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid GetHeader()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
SetMsgID (msgID); SetMsgID(msgID);
SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL); SetExpiration(bufbe32toh(ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET) * 1000LL);
SetSize (len - offset - I2NP_HEADER_SIZE); SetSize(len - offset - I2NP_HEADER_SIZE);
SetChks (0); SetChks(0);
} }
uint32_t ToSSU () // return msgID
uint32_t ToSSU() // return msgID
{ {
uint8_t header[I2NP_HEADER_SIZE]; uint8_t header[I2NP_HEADER_SIZE];
memcpy (header, GetHeader (), I2NP_HEADER_SIZE); memcpy(header, GetHeader(), I2NP_HEADER_SIZE);
uint8_t * ssu = GetSSUHeader (); uint8_t *ssu = GetSSUHeader();
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL); htobe32buf(ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET,
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET); bufbe64toh(header + I2NP_HEADER_EXPIRATION_OFFSET) / 1000LL);
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET); len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh(header + I2NP_HEADER_SIZE_OFFSET);
return bufbe32toh(header + I2NP_HEADER_MSGID_OFFSET);
} }
// for NTCP2 only // for NTCP2 only
uint8_t * GetNTCP2Header () { return GetPayload () - I2NP_NTCP2_HEADER_SIZE; }; uint8_t *GetNTCP2Header() { return GetPayload() - I2NP_NTCP2_HEADER_SIZE; };
size_t GetNTCP2Length () const { return GetPayloadLength () + I2NP_NTCP2_HEADER_SIZE; };
void FromNTCP2 () size_t GetNTCP2Length() const { return GetPayloadLength() + I2NP_NTCP2_HEADER_SIZE; };
{
const uint8_t * ntcp2 = GetNTCP2Header (); void FromNTCP2() {
memcpy (GetHeader () + I2NP_HEADER_TYPEID_OFFSET, ntcp2 + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid const uint8_t *ntcp2 = GetNTCP2Header();
SetExpiration (bufbe32toh (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET)*1000LL); memcpy(GetHeader() + I2NP_HEADER_TYPEID_OFFSET, ntcp2 + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid
SetSize (len - offset - I2NP_HEADER_SIZE); SetExpiration(bufbe32toh(ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET) * 1000LL);
SetChks (0); SetSize(len - offset - I2NP_HEADER_SIZE);
SetChks(0);
} }
void ToNTCP2 () void ToNTCP2() {
{ uint8_t *ntcp2 = GetNTCP2Header();
uint8_t * ntcp2 = GetNTCP2Header (); htobe32buf(ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET,
htobe32buf (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET, bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL); bufbe64toh(GetHeader() + I2NP_HEADER_EXPIRATION_OFFSET) / 1000LL);
memcpy (ntcp2 + I2NP_HEADER_TYPEID_OFFSET, GetHeader () + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid 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 FillI2NPMessageHeader(I2NPMessageType msgType, uint32_t replyMsgID = 0, bool checksum = true);
void RenewI2NPMessageHeader ();
bool IsExpired () const; void RenewI2NPMessageHeader();
bool IsExpired() const;
}; };
template<int sz> template<int sz>
struct I2NPMessageBuffer: public I2NPMessage struct I2NPMessageBuffer : public I2NPMessage {
{ I2NPMessageBuffer() {
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; }; buf = m_Buffer;
maxLen = sz;
};
uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding uint8_t m_Buffer[sz + 32]; // 16 alignment + 16 padding
}; };
std::shared_ptr<I2NPMessage> NewI2NPMessage (); std::shared_ptr<I2NPMessage> NewI2NPMessage();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint);
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); std::shared_ptr<I2NPMessage> NewI2NPShortMessage();
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> NewI2NPTunnelMessage(bool endpoint);
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> NewI2NPMessage(size_t len);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, 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, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false); const uint8_t *replyKey, const uint8_t *replyTag,
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers); 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>
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only CreateDatabaseSearchReply(const i2p::data::IdentHash &ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf); std::shared_ptr<I2NPMessage>
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); CreateDatabaseStoreMsg(std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0,
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint); 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> CreateDatabaseStoreMsg(const i2p::data::IdentHash &storeHash,
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); std::shared_ptr<I2NPMessage>
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg); 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); bool IsRouterInfoMsg(std::shared_ptr<I2NPMessage> msg);
void HandleI2NPMessage (uint8_t * msg, size_t len);
void HandleI2NPMessage (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: public:
~I2NPMessagesHandler (); ~I2NPMessagesHandler();
void PutNextMessage (std::shared_ptr<I2NPMessage>&& msg);
void Flush (); void PutNextMessage(std::shared_ptr<I2NPMessage> &&msg);
void Flush();
private: private:
@ -310,8 +359,10 @@ namespace tunnel
}; };
const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 2500; 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 #endif

View file

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

View file

@ -8,6 +8,7 @@
#ifndef I2PENDIAN_H__ #ifndef I2PENDIAN_H__
#define I2PENDIAN_H__ #define I2PENDIAN_H__
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
@ -53,13 +54,19 @@
#else #else
#define NEEDS_LOCAL_ENDIAN #define NEEDS_LOCAL_ENDIAN
#include <cstdint> #include <cstdint>
uint16_t htobe16(uint16_t int16); uint16_t htobe16(uint16_t int16);
uint32_t htobe32(uint32_t int32); uint32_t htobe32(uint32_t int32);
uint64_t htobe64(uint64_t int64); uint64_t htobe64(uint64_t int64);
uint16_t be16toh(uint16_t big16); uint16_t be16toh(uint16_t big16);
uint32_t be32toh(uint32_t big32); uint32_t be32toh(uint32_t big32);
uint64_t be64toh(uint64_t big64); uint64_t be64toh(uint64_t big64);
// assume LittleEndine // assume LittleEndine
@ -72,99 +79,81 @@ uint64_t be64toh(uint64_t big64);
#endif #endif
inline uint16_t buf16toh(const void *buf) inline uint16_t buf16toh(const void *buf) {
{
uint16_t b16; uint16_t b16;
memcpy(&b16, buf, sizeof(uint16_t)); memcpy(&b16, buf, sizeof(uint16_t));
return b16; return b16;
} }
inline uint32_t buf32toh(const void *buf) inline uint32_t buf32toh(const void *buf) {
{
uint32_t b32; uint32_t b32;
memcpy(&b32, buf, sizeof(uint32_t)); memcpy(&b32, buf, sizeof(uint32_t));
return b32; return b32;
} }
inline uint64_t buf64toh(const void *buf) inline uint64_t buf64toh(const void *buf) {
{
uint64_t b64; uint64_t b64;
memcpy(&b64, buf, sizeof(uint64_t)); memcpy(&b64, buf, sizeof(uint64_t));
return b64; return b64;
} }
inline uint16_t bufbe16toh(const void *buf) inline uint16_t bufbe16toh(const void *buf) {
{
return be16toh(buf16toh(buf)); return be16toh(buf16toh(buf));
} }
inline uint32_t bufbe32toh(const void *buf) inline uint32_t bufbe32toh(const void *buf) {
{
return be32toh(buf32toh(buf)); return be32toh(buf32toh(buf));
} }
inline uint64_t bufbe64toh(const void *buf) inline uint64_t bufbe64toh(const void *buf) {
{
return be64toh(buf64toh(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)); 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)); 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)); 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)); htobuf16(buf, htobe16(big16));
} }
inline void htobe32buf(void *buf, uint32_t big32) inline void htobe32buf(void *buf, uint32_t big32) {
{
htobuf32(buf, htobe32(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)); htobuf64(buf, htobe64(big64));
} }
inline void htole16buf(void *buf, uint16_t big16) inline void htole16buf(void *buf, uint16_t big16) {
{
htobuf16(buf, htole16(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)); htobuf32(buf, htole32(big32));
} }
inline void htole64buf(void *buf, uint64_t big64) inline void htole64buf(void *buf, uint64_t big64) {
{
htobuf64(buf, htole64(big64)); htobuf64(buf, htole64(big64));
} }
inline uint16_t bufle16toh(const void *buf) inline uint16_t bufle16toh(const void *buf) {
{
return le16toh(buf16toh(buf)); return le16toh(buf16toh(buf));
} }
inline uint32_t bufle32toh(const void *buf) inline uint32_t bufle32toh(const void *buf) {
{
return le32toh(buf32toh(buf)); return le32toh(buf32toh(buf));
} }
inline uint64_t bufle64toh(const void *buf) inline uint64_t bufle64toh(const void *buf) {
{
return le64toh(buf64toh(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 "Signature.h"
#include "CryptoKey.h" #include "CryptoKey.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
typedef Tag<32> IdentHash; typedef Tag<32> IdentHash;
inline std::string GetIdentHashAbbreviation (const IdentHash& ident)
{ inline std::string GetIdentHashAbbreviation(const IdentHash &ident) {
return ident.ToBase64 ().substr (0, 4); return ident.ToBase64().substr(0, 4);
} }
struct Keys struct Keys {
{
uint8_t privateKey[256]; uint8_t privateKey[256];
uint8_t signingPrivateKey[20]; uint8_t signingPrivateKey[20];
uint8_t publicKey[256]; uint8_t publicKey[256];
@ -45,22 +42,25 @@ namespace data
const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4; const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4;
const uint8_t CERTIFICATE_TYPE_KEY = 5; const uint8_t CERTIFICATE_TYPE_KEY = 5;
struct Identity struct Identity {
{
uint8_t publicKey[256]; uint8_t publicKey[256];
uint8_t signingKey[128]; uint8_t signingKey[128];
uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length uint8_t certificate[3]; // byte 1 - type, bytes 2-3 - length
Identity () = default; Identity() = default;
Identity (const Keys& keys) { *this = keys; };
Identity& operator=(const Keys& keys); Identity(const Keys &keys) { *this = keys; };
size_t FromBuffer (const uint8_t * buf, size_t len);
IdentHash Hash () const; 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_ELGAMAL = 0;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
@ -85,57 +85,83 @@ namespace data
typedef uint16_t CryptoKeyType; typedef uint16_t CryptoKeyType;
const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521 const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521
class IdentityEx class IdentityEx {
{
public: public:
IdentityEx (); 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);
size_t FromBuffer (const uint8_t * buf, size_t len); IdentityEx(const uint8_t *publicKey, const uint8_t *signingKey,
size_t ToBuffer (uint8_t * buf, size_t len) const; SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1,
size_t FromBase64(const std::string& s); CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
std::string ToBase64 () const;
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
const IdentHash& GetIdentHash () const { return m_IdentHash; }; IdentityEx(const uint8_t *buf, size_t len);
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(); } IdentityEx(const IdentityEx &other);
void RecalculateIdentHash(uint8_t * buff=nullptr);
static i2p::crypto::Verifier * CreateVerifier (SigningKeyType keyType); IdentityEx(const Identity &standard);
static std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> CreateEncryptor (CryptoKeyType keyType, const uint8_t * key);
~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: private:
void CreateVerifier () const; void CreateVerifier() const;
void UpdateVerifier (i2p::crypto::Verifier * verifier) const;
void UpdateVerifier(i2p::crypto::Verifier *verifier) const;
private: private:
Identity m_StandardIdentity; Identity m_StandardIdentity;
IdentHash m_IdentHash; IdentHash m_IdentHash;
mutable i2p::crypto::Verifier * m_Verifier = nullptr; mutable i2p::crypto::Verifier *m_Verifier = nullptr;
mutable std::mutex m_VerifierMutex; mutable std::mutex m_VerifierMutex;
size_t m_ExtendedLen; size_t m_ExtendedLen;
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
@ -145,46 +171,69 @@ namespace data
{ {
public: public:
PrivateKeys () = default; PrivateKeys() = default;
PrivateKeys (const PrivateKeys& other) { *this = other; };
PrivateKeys (const Keys& keys) { *this = keys; };
PrivateKeys& operator=(const Keys& keys);
PrivateKeys& operator=(const PrivateKeys& other);
~PrivateKeys () = default;
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; }; PrivateKeys(const PrivateKeys &other) { *this = other; };
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; PrivateKeys(const Keys &keys) { *this = keys; };
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); PrivateKeys &operator=(const Keys &keys);
std::string ToBase64 () const;
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); ~PrivateKeys() = default;
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); std::shared_ptr<const IdentityEx> GetPublic() const { return m_Public; };
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); 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 // offline keys
PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const; PrivateKeys CreateOfflineKeys(SigningKeyType type, uint32_t expires) const;
const std::vector<uint8_t>& GetOfflineSignature () const { return m_OfflineSignature; };
const std::vector<uint8_t> &GetOfflineSignature() const { return m_OfflineSignature; };
private: private:
void CreateSigner () const; void CreateSigner() const;
void CreateSigner (SigningKeyType keyType) const;
size_t GetPrivateKeyLen () const; void CreateSigner(SigningKeyType keyType) const;
size_t GetPrivateKeyLen() const;
private: private:
@ -198,51 +247,61 @@ namespace data
}; };
// kademlia // kademlia
struct XORMetric struct XORMetric {
{ union {
union
{
uint8_t metric[32]; uint8_t metric[32];
uint64_t metric_ll[4]; uint64_t metric_ll[4];
}; };
void SetMin () { memset (metric, 0, 32); }; 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 SetMax() { memset(metric, 0xFF, 32); };
bool operator<(const XORMetric &other) const { return memcmp(metric, other.metric, 32) < 0; };
}; };
IdentHash CreateRoutingKey (const IdentHash& ident); IdentHash CreateRoutingKey(const IdentHash &ident);
XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
XORMetric operator^(const IdentHash &key1, const IdentHash &key2);
// destination for delivery instructions // destination for delivery instructions
class RoutingDestination class RoutingDestination {
{
public: public:
RoutingDestination () {}; RoutingDestination() {};
virtual ~RoutingDestination () {};
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0; virtual ~RoutingDestination() {};
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 std::shared_ptr<const IdentityEx> GetIdentity() const = 0;
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
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: public:
virtual ~LocalDestination() {}; 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 Decrypt(const uint8_t *encrypted, uint8_t *data,
virtual bool SupportsEncryptionType (CryptoKeyType keyType) const { return GetIdentity ()->GetCryptoKeyType () == keyType; }; // override for LeaseSet CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
virtual const uint8_t * GetEncryptionPublicKey (CryptoKeyType keyType) const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet
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 #endif

File diff suppressed because it is too large Load diff

View file

@ -19,36 +19,30 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Blinding.h" #include "Blinding.h"
namespace i2p namespace i2p {
{
namespace tunnel namespace tunnel {
{
class InboundTunnel; class InboundTunnel;
} }
namespace data namespace data {
{
const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds const int LEASE_ENDDATE_THRESHOLD = 51000; // in milliseconds
struct Lease struct Lease {
{
IdentHash tunnelGateway; IdentHash tunnelGateway;
uint32_t tunnelID; uint32_t tunnelID;
uint64_t endDate; // 0 means invalid uint64_t endDate; // 0 means invalid
bool isUpdated; // transient bool isUpdated; // transient
/* return true if this lease expires within t millisecond + fudge factor */ /* return true if this lease expires within t millisecond + fudge factor */
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const { bool ExpiresWithin(const uint64_t t, const uint64_t fudge = 1000) const {
auto expire = i2p::util::GetMillisecondsSinceEpoch (); auto expire = i2p::util::GetMillisecondsSinceEpoch();
if(fudge) expire += rand() % fudge; if (fudge) expire += rand() % fudge;
if (endDate < expire) return true; if (endDate < expire) return true;
return (endDate - expire) < t; return (endDate - expire) < t;
} }
}; };
struct LeaseCmp struct LeaseCmp {
{ bool operator()(std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const {
bool operator() (std::shared_ptr<const Lease> l1, std::shared_ptr<const Lease> l2) const
{
if (l1->tunnelID != l2->tunnelID) if (l1->tunnelID != l2->tunnelID)
return l1->tunnelID < l2->tunnelID; return l1->tunnelID < l2->tunnelID;
else 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 MAX_LS_BUFFER_SIZE = 3072;
const size_t LEASE_SIZE = 44; // 32 + 4 + 8 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 MAX_NUM_LEASES = 16;
const uint8_t NETDB_STORE_TYPE_LEASESET = 1; const uint8_t NETDB_STORE_TYPE_LEASESET = 1;
class LeaseSet: public RoutingDestination
{ class LeaseSet : public RoutingDestination {
public: public:
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true); 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
const uint8_t * GetBuffer () const { return m_Buffer; }; virtual ~LeaseSet() {
size_t GetBufferLen () const { return m_BufferLen; }; delete[] m_EncryptionKey;
bool IsValid () const { return m_IsValid; }; delete[] m_Buffer;
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; virtual void Update(const uint8_t *buf, size_t len, bool verifySignature = true);
bool IsExpired () const;
bool IsEmpty () const { return m_Leases.empty (); }; virtual bool IsNewer(const uint8_t *buf, size_t len) const;
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
bool ExpiresSoon(const uint64_t dlt=1000 * 5, const uint64_t fudge = 0) const ; void PopulateLeases(); // from buffer
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); }; const uint8_t *GetBuffer() const { return m_Buffer; };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only size_t GetBufferLen() const { return m_BufferLen; };
virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
virtual bool IsPublishedEncrypted () const { return false; }; 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 // implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; }; 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; }; void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
bool IsDestination() const { return true; };
protected: protected:
void UpdateLeasesBegin (); void UpdateLeasesBegin();
void UpdateLeasesEnd ();
void UpdateLease (const Lease& lease, uint64_t ts); void UpdateLeasesEnd();
void UpdateLease(const Lease &lease, uint64_t ts);
// called from LeaseSet2 // called from LeaseSet2
LeaseSet (bool storeLeases); LeaseSet(bool storeLeases);
void SetBuffer (const uint8_t * buf, size_t len);
void SetBufferLen (size_t len); void SetBuffer(const uint8_t *buf, size_t len);
void SetIdentity (std::shared_ptr<const IdentityEx> identity) { m_Identity = identity; };
void SetExpirationTime (uint64_t t) { m_ExpirationTime = t; }; void SetBufferLen(size_t len);
void SetIsValid (bool isValid) { m_IsValid = isValid; };
bool IsStoreLeases () const { return m_StoreLeases; }; 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: private:
void ReadFromBuffer (bool readIdentity = true, bool verifySignature = true); void ReadFromBuffer(bool readIdentity = true, bool verifySignature = true);
virtual uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const; // returns max expiration time
virtual uint64_t
ExtractExpirationTimestamp(const uint8_t *buf, size_t len) const; // returns max expiration time
private: private:
@ -122,8 +150,8 @@ namespace data
std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases; std::set<std::shared_ptr<Lease>, LeaseCmp> m_Leases;
uint64_t m_ExpirationTime; // in milliseconds uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity; std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_EncryptionKey; uint8_t *m_EncryptionKey;
uint8_t * m_Buffer; uint8_t *m_Buffer;
size_t m_BufferLen; size_t m_BufferLen;
}; };
@ -131,7 +159,7 @@ namespace data
* validate lease set buffer signature and extract expiration timestamp * validate lease set buffer signature and extract expiration timestamp
* @returns true if the leaseset is well formed and signature is valid * @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_STANDARD_LEASESET2 = 3;
const uint8_t NETDB_STORE_TYPE_ENCRYPTED_LEASESET2 = 5; 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_UNPUBLISHED_LEASESET = 0x0002;
const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004; const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004;
class LeaseSet2: public LeaseSet class LeaseSet2 : public LeaseSet {
{
public: public:
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); LeaseSet2(uint8_t storeType, const uint8_t *buf, size_t len, bool storeLeases = true,
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 CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
uint8_t GetStoreType () const { return m_StoreType; };
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; }; LeaseSet2(const uint8_t *buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
bool IsPublic () const { return m_IsPublic; }; const uint8_t *secret = nullptr,
bool IsPublishedEncrypted () const { return m_IsPublishedEncrypted; }; CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; }; uint8_t GetStoreType() const { return m_StoreType; };
void Update (const uint8_t * buf, size_t len, bool verifySignature);
bool IsNewer (const uint8_t * buf, size_t len) const; 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 // implements RoutingDestination
void Encrypt (const uint8_t * data, uint8_t * encrypted) const; void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
CryptoKeyType GetEncryptionType() const { return m_EncryptionType; };
private: private:
void ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity = true, bool verifySignature = true); 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); void ReadFromBufferEncrypted(const uint8_t *buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len); 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> 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 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 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: private:
@ -185,91 +231,112 @@ namespace data
// also called from Streaming.cpp // also called from Streaming.cpp
template<typename Verifier> 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; if (offset + 6 >= len) return nullptr;
const uint8_t * signedData = buf + offset; const uint8_t *signedData = buf + offset;
uint32_t expiresTimestamp = bufbe32toh (buf + offset); offset += 4; // expires timestamp uint32_t expiresTimestamp = bufbe32toh(buf + offset);
if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch ()) return nullptr; offset += 4; // expires timestamp
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; if (expiresTimestamp < i2p::util::GetSecondsSinceEpoch()) return nullptr;
std::shared_ptr<i2p::crypto::Verifier> transientVerifier (i2p::data::IdentityEx::CreateVerifier (keyType)); uint16_t keyType = bufbe16toh(buf + offset);
offset += 2;
std::shared_ptr<i2p::crypto::Verifier> transientVerifier(i2p::data::IdentityEx::CreateVerifier(keyType));
if (!transientVerifier) return nullptr; if (!transientVerifier) return nullptr;
auto keyLen = transientVerifier->GetPublicKeyLen (); auto keyLen = transientVerifier->GetPublicKeyLen();
if (offset + keyLen >= len) return nullptr; if (offset + keyLen >= len) return nullptr;
transientVerifier->SetPublicKey (buf + offset); offset += keyLen; transientVerifier->SetPublicKey(buf + offset);
if (offset + verifier->GetSignatureLen () >= len) return nullptr; offset += keyLen;
if (!verifier->Verify (signedData, keyLen + 6, buf + offset)) return nullptr; if (offset + verifier->GetSignatureLen() >= len) return nullptr;
offset += verifier->GetSignatureLen (); if (!verifier->Verify(signedData, keyLen + 6, buf + offset)) return nullptr;
offset += verifier->GetSignatureLen();
return transientVerifier; return transientVerifier;
} }
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
class LocalLeaseSet class LocalLeaseSet {
{
public: public:
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * encryptionPublicKey, std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels); LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *encryptionPublicKey,
LocalLeaseSet (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
virtual ~LocalLeaseSet () { delete[] m_Buffer; };
virtual uint8_t * GetBuffer () const { return m_Buffer; }; LocalLeaseSet(std::shared_ptr<const IdentityEx> identity, const uint8_t *buf, size_t len);
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 (); }; virtual ~LocalLeaseSet() { delete[] m_Buffer; };
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 uint8_t *GetBuffer() const { return m_Buffer; };
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 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: private:
uint64_t m_ExpirationTime; // in milliseconds uint64_t m_ExpirationTime; // in milliseconds
std::shared_ptr<const IdentityEx> m_Identity; std::shared_ptr<const IdentityEx> m_Identity;
uint8_t * m_Buffer, * m_Leases; uint8_t *m_Buffer, *m_Leases;
size_t m_BufferLen; size_t m_BufferLen;
}; };
class LocalLeaseSet2: public LocalLeaseSet class LocalLeaseSet2 : public LocalLeaseSet {
{
public: public:
struct KeySection struct KeySection {
{
uint16_t keyType, keyLen; uint16_t keyType, keyLen;
const uint8_t * encryptionPublicKey; const uint8_t *encryptionPublicKey;
}; };
typedef std::vector<KeySection> KeySections; typedef std::vector<KeySection> KeySections;
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, LocalLeaseSet2(uint8_t storeType, const i2p::data::PrivateKeys &keys,
const KeySections& encryptionKeys, const KeySections &encryptionKeys,
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > &tunnels,
bool isPublic, bool isPublishedEncrypted = false); 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; }; uint8_t *GetBuffer() const { return m_Buffer + 1; };
size_t GetBufferLen () const { return m_BufferLen; };
uint8_t GetStoreType () const { return m_Buffer[0]; }; size_t GetBufferLen() const { return m_BufferLen; };
uint8_t GetStoreType() const { return m_Buffer[0]; };
protected: 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: 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; size_t m_BufferLen;
}; };
@ -280,27 +347,32 @@ namespace data
typedef i2p::data::Tag<32> AuthPublicKey; typedef i2p::data::Tag<32> AuthPublicKey;
class LocalEncryptedLeaseSet2: public LocalLeaseSet2 class LocalEncryptedLeaseSet2 : public LocalLeaseSet2 {
{
public: 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; }; const IdentHash &GetStoreHash() const { return m_StoreHash; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet() const { return m_InnerLeaseSet; };
private: 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: private:
IdentHash m_StoreHash; IdentHash m_StoreHash;
std::shared_ptr<const LocalLeaseSet2> m_InnerLeaseSet; std::shared_ptr<const LocalLeaseSet2> m_InnerLeaseSet;
}; };
} }
} }
#endif #endif

View file

@ -33,85 +33,71 @@ struct BigEndian;
// Little-Endian template // Little-Endian template
#pragma pack(push,1) #pragma pack(push, 1)
template<typename T> template<typename T>
struct LittleEndian struct LittleEndian {
{ union {
union
{
unsigned char bytes[sizeof(T)]; unsigned char bytes[sizeof(T)];
T raw_value; T raw_value;
}; };
LittleEndian(T t = T()) LittleEndian(T t = T()) {
{ operator=(t);
operator =(t);
} }
LittleEndian(const LittleEndian<T> & t) LittleEndian(const LittleEndian<T> &t) {
{
raw_value = t.raw_value; raw_value = t.raw_value;
} }
LittleEndian(const BigEndian<T> & t) LittleEndian(const BigEndian<T> &t) {
{
for (unsigned i = 0; i < sizeof(T); i++) 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(); T t = T();
for (unsigned i = 0; i < sizeof(T); i++) for (unsigned i = 0; i < sizeof(T); i++)
t |= T(bytes[i]) << (i << 3); t |= T(bytes[i]) << (i << 3);
return t; return t;
} }
const T operator = (const T t) const T operator=(const T t) {
{
for (unsigned i = 0; i < sizeof(T); i++) 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; return t;
} }
// operators // operators
const T operator += (const T t) const T operator+=(const T t) {
{
return (*this = *this + t); return (*this = *this + t);
} }
const T operator -= (const T t) const T operator-=(const T t) {
{
return (*this = *this - t); return (*this = *this - t);
} }
const T operator *= (const T t) const T operator*=(const T t) {
{
return (*this = *this * t); return (*this = *this * t);
} }
const T operator /= (const T t) const T operator/=(const T t) {
{
return (*this = *this / t); return (*this = *this / t);
} }
const T operator %= (const T t) const T operator%=(const T t) {
{
return (*this = *this % t); return (*this = *this % t);
} }
LittleEndian<T> operator ++ (int) LittleEndian<T> operator++(int) {
{
LittleEndian<T> tmp(*this); LittleEndian<T> tmp(*this);
operator ++ (); operator++();
return tmp; return tmp;
} }
LittleEndian<T> & operator ++ () LittleEndian<T> &operator++() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++)
{
++bytes[i]; ++bytes[i];
if (bytes[i] != 0) if (bytes[i] != 0)
break; break;
@ -119,64 +105,56 @@ struct LittleEndian
return (*this); return (*this);
} }
LittleEndian<T> operator -- (int) LittleEndian<T> operator--(int) {
{
LittleEndian<T> tmp(*this); LittleEndian<T> tmp(*this);
operator -- (); operator--();
return tmp; return tmp;
} }
LittleEndian<T> & operator -- () LittleEndian<T> &operator--() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++)
{
--bytes[i]; --bytes[i];
if (bytes[i] != (T)(-1)) if (bytes[i] != (T) (-1))
break; break;
} }
return (*this); return (*this);
} }
}; };
#pragma pack(pop) #pragma pack(pop)
// Big-Endian template // Big-Endian template
#pragma pack(push,1) #pragma pack(push, 1)
template<typename T> template<typename T>
struct BigEndian struct BigEndian {
{ union {
union
{
unsigned char bytes[sizeof(T)]; unsigned char bytes[sizeof(T)];
T raw_value; T raw_value;
}; };
BigEndian(T t = T()) BigEndian(T t = T()) {
{ operator=(t);
operator =(t);
} }
BigEndian(const BigEndian<T> & t) BigEndian(const BigEndian<T> &t) {
{
raw_value = t.raw_value; raw_value = t.raw_value;
} }
BigEndian(const LittleEndian<T> & t) BigEndian(const LittleEndian<T> &t) {
{
for (unsigned i = 0; i < sizeof(T); i++) 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(); T t = T();
for (unsigned i = 0; i < sizeof(T); i++) for (unsigned i = 0; i < sizeof(T); i++)
t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3); t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3);
return t; return t;
} }
const T operator = (const T t) const T operator=(const T t) {
{
for (unsigned i = 0; i < sizeof(T); i++) for (unsigned i = 0; i < sizeof(T); i++)
bytes[sizeof(T) - 1 - i] = t >> (i << 3); bytes[sizeof(T) - 1 - i] = t >> (i << 3);
return t; return t;
@ -184,42 +162,34 @@ struct BigEndian
// operators // operators
const T operator += (const T t) const T operator+=(const T t) {
{
return (*this = *this + t); return (*this = *this + t);
} }
const T operator -= (const T t) const T operator-=(const T t) {
{
return (*this = *this - t); return (*this = *this - t);
} }
const T operator *= (const T t) const T operator*=(const T t) {
{
return (*this = *this * t); return (*this = *this * t);
} }
const T operator /= (const T t) const T operator/=(const T t) {
{
return (*this = *this / t); return (*this = *this / t);
} }
const T operator %= (const T t) const T operator%=(const T t) {
{
return (*this = *this % t); return (*this = *this % t);
} }
BigEndian<T> operator ++ (int) BigEndian<T> operator++(int) {
{
BigEndian<T> tmp(*this); BigEndian<T> tmp(*this);
operator ++ (); operator++();
return tmp; return tmp;
} }
BigEndian<T> & operator ++ () BigEndian<T> &operator++() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++)
{
++bytes[sizeof(T) - 1 - i]; ++bytes[sizeof(T) - 1 - i];
if (bytes[sizeof(T) - 1 - i] != 0) if (bytes[sizeof(T) - 1 - i] != 0)
break; break;
@ -227,24 +197,22 @@ struct BigEndian
return (*this); return (*this);
} }
BigEndian<T> operator -- (int) BigEndian<T> operator--(int) {
{
BigEndian<T> tmp(*this); BigEndian<T> tmp(*this);
operator -- (); operator--();
return tmp; return tmp;
} }
BigEndian<T> & operator -- () BigEndian<T> &operator--() {
{ for (unsigned i = 0; i < sizeof(T); i++) {
for (unsigned i = 0; i < sizeof(T); i++)
{
--bytes[sizeof(T) - 1 - i]; --bytes[sizeof(T) - 1 - i];
if (bytes[sizeof(T) - 1 - i] != (T)(-1)) if (bytes[sizeof(T) - 1 - i] != (T) (-1))
break; break;
} }
return (*this); return (*this);
} }
}; };
#pragma pack(pop) #pragma pack(pop)
#endif // LITTLEBIGENDIAN_H #endif // LITTLEBIGENDIAN_H

View file

@ -13,12 +13,12 @@
#include <algorithm> #include <algorithm>
namespace i2p { namespace i2p {
namespace log { namespace log {
static Log logger; static Log logger;
/** /**
* @brief Maps our loglevel to their symbolic name * @brief Maps our loglevel to their symbolic name
*/ */
static const char * g_LogLevelStr[eNumLogLevels] = static const char *g_LogLevelStr[eNumLogLevels] =
{ {
"none", // eLogNone "none", // eLogNone
"error", // eLogError "error", // eLogError
@ -45,49 +45,57 @@ namespace log {
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
/** /**
* @brief Maps our log levels to syslog one * @brief Maps our log levels to syslog one
* @return syslog priority LOG_*, as defined in syslog.h * @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; int priority = LOG_DEBUG;
switch (l) { switch (l) {
case eLogNone : priority = LOG_CRIT; break; case eLogNone :
case eLogError : priority = LOG_ERR; break; priority = LOG_CRIT;
case eLogWarning : priority = LOG_WARNING; break; break;
case eLogInfo : priority = LOG_INFO; break; case eLogError :
case eLogDebug : priority = LOG_DEBUG; break; priority = LOG_ERR;
default : priority = LOG_DEBUG; break; 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; return priority;
} }
#endif #endif
Log::Log(): Log::Log() :
m_Destination(eLogStdout), m_MinLevel(eLogInfo), m_Destination(eLogStdout), m_MinLevel(eLogInfo),
m_LogStream (nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"), m_LogStream(nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"),
m_IsRunning (false), m_Thread (nullptr) m_IsRunning(false), m_Thread(nullptr) {
{
} }
Log::~Log () Log::~Log() {
{
delete m_Thread; delete m_Thread;
} }
void Log::Start () void Log::Start() {
{ if (!m_IsRunning) {
if (!m_IsRunning)
{
m_IsRunning = true; 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 () void Log::Stop() {
{ switch (m_Destination) {
switch (m_Destination)
{
#ifndef _WIN32 #ifndef _WIN32
case eLogSyslog : case eLogSyslog :
closelog(); closelog();
@ -102,10 +110,9 @@ namespace log {
break; break;
} }
m_IsRunning = false; m_IsRunning = false;
m_Queue.WakeUp (); m_Queue.WakeUp();
if (m_Thread) if (m_Thread) {
{ m_Thread->join();
m_Thread->join ();
delete m_Thread; delete m_Thread;
m_Thread = nullptr; m_Thread = nullptr;
} }
@ -116,13 +123,13 @@ namespace log {
// static_cast<int(*)(int)>(std::tolower) // wrong // static_cast<int(*)(int)>(std::tolower) // wrong
// [](int c){ return std::tolower(c); } // wrong // [](int c){ return std::tolower(c); } // wrong
// [](char 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; return s;
} }
void Log::SetLogLevel (const std::string& level_) { void Log::SetLogLevel(const std::string &level_) {
std::string level=str_tolower(level_); std::string level = str_tolower(level_);
if (level == "none") { m_MinLevel = eLogNone; } if (level == "none") { m_MinLevel = eLogNone; }
else if (level == "error") { m_MinLevel = eLogError; } else if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; } else if (level == "warn") { m_MinLevel = eLogWarning; }
@ -135,7 +142,7 @@ namespace log {
LogPrint(eLogInfo, "Log: Logging level set to ", level); 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) { if (t != m_LastTimestamp) {
strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t)); strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t));
m_LastTimestamp = t; m_LastTimestamp = t;
@ -148,10 +155,9 @@ namespace log {
* Unfortunately, with current startup process with late fork() this * Unfortunately, with current startup process with late fork() this
* will give us nothing but pain. Maybe later. See in NetDb as example. * 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; if (!msg) return;
std::hash<std::thread::id> hasher; std::hash <std::thread::id> hasher;
unsigned short short_tid; unsigned short short_tid;
short_tid = (short) (hasher(msg->tid) % 1000); short_tid = (short) (hasher(msg->tid) % 1000);
switch (m_Destination) { switch (m_Destination) {
@ -172,40 +178,36 @@ namespace log {
default: default:
std::cout << TimeAsString(msg->timestamp) std::cout << TimeAsString(msg->timestamp)
<< "@" << short_tid << "@" << short_tid
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels] << "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level]
<< LogMsgColors[eNumLogLevels]
<< " - " << msg->text << std::endl; << " - " << msg->text << std::endl;
break; break;
} // switch } // switch
} }
void Log::Run () void Log::Run() {
{
i2p::util::SetThreadName("Logging"); i2p::util::SetThreadName("Logging");
Reopen (); Reopen();
while (m_IsRunning) while (m_IsRunning) {
{ std::shared_ptr <LogMsg> msg;
std::shared_ptr<LogMsg> msg; while ((msg = m_Queue.Get()))
while ((msg = m_Queue.Get ())) Process(msg);
Process (msg);
if (m_LogStream) m_LogStream->flush(); if (m_LogStream) m_LogStream->flush();
if (m_IsRunning) 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); 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 if (m_LogStream) m_LogStream = nullptr; // close previous
auto flags = std::ofstream::out | std::ofstream::app; auto flags = std::ofstream::out | std::ofstream::app;
auto os = std::make_shared<std::ofstream> (path, flags); auto os = std::make_shared<std::ofstream>(path, flags);
if (os->is_open ()) if (os->is_open()) {
{
m_HasColors = false; m_HasColors = false;
m_Logfile = path; m_Logfile = path;
m_Destination = eLogFile; m_Destination = eLogFile;
@ -215,13 +217,14 @@ namespace log {
LogPrint(eLogError, "Log: Can't open file ", path); 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_HasColors = false;
m_Destination = eLogStream; m_Destination = eLogStream;
m_LogStream = os; m_LogStream = os;
} }
#ifndef _WIN32 #ifndef _WIN32
void Log::SendTo(const char *name, int facility) { void Log::SendTo(const char *name, int facility) {
if (m_MinLevel == eLogNone) return; if (m_MinLevel == eLogNone) return;
m_HasColors = false; m_HasColors = false;
@ -229,6 +232,7 @@ namespace log {
m_LogStream = nullptr; m_LogStream = nullptr;
openlog(name, LOG_CONS | LOG_PID, facility); openlog(name, LOG_CONS | LOG_PID, facility);
} }
#endif #endif
void Log::Reopen() { void Log::Reopen() {
@ -236,14 +240,16 @@ namespace log {
SendTo(m_Logfile); SendTo(m_Logfile);
} }
Log & Logger() { Log &Logger() {
return logger; return logger;
} }
static ThrowFunction g_ThrowFunction; 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 } // i2p

View file

@ -21,11 +21,12 @@
#include "Queue.h" #include "Queue.h"
#ifndef _WIN32 #ifndef _WIN32
#include <syslog.h> #include <syslog.h>
#endif #endif
enum LogLevel enum LogLevel {
{
eLogNone = 0, eLogNone = 0,
eLogError, eLogError,
eLogWarning, eLogWarning,
@ -44,34 +45,36 @@ enum LogType {
}; };
namespace i2p { namespace i2p {
namespace log { namespace log {
struct LogMsg; /* forward declaration */ struct LogMsg; /* forward declaration */
class Log class Log {
{
private: private:
enum LogType m_Destination; enum LogType m_Destination;
enum LogLevel m_MinLevel; enum LogLevel m_MinLevel;
std::shared_ptr<std::ostream> m_LogStream; std::shared_ptr <std::ostream> m_LogStream;
std::string m_Logfile; std::string m_Logfile;
std::time_t m_LastTimestamp; std::time_t m_LastTimestamp;
char m_LastDateTime[64]; 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; bool m_HasColors;
std::string m_TimeFormat; std::string m_TimeFormat;
volatile bool m_IsRunning; volatile bool m_IsRunning;
std::thread * m_Thread; std::thread *m_Thread;
private: private:
/** prevent making copies */ /** prevent making copies */
Log (const Log &); Log(const Log &);
const Log& operator=(const Log&);
void Run (); const Log &operator=(const Log &);
void Process (std::shared_ptr<LogMsg> msg);
void Run();
void Process(std::shared_ptr <LogMsg> msg);
/** /**
* @brief Makes formatted string from unix timestamp * @brief Makes formatted string from unix timestamp
@ -79,57 +82,62 @@ namespace log {
* *
* This function internally caches the result for last provided value * 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: public:
Log (); Log();
~Log ();
LogType GetLogType () { return m_Destination; }; ~Log();
LogLevel GetLogLevel () { return m_MinLevel; };
void Start (); LogType GetLogType() { return m_Destination; };
void Stop ();
LogLevel GetLogLevel() { return m_MinLevel; };
void Start();
void Stop();
/** /**
* @brief Sets minimal allowed level for log messages * @brief Sets minimal allowed level for log messages
* @param level String with wanted minimal msg level * @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 * @brief Sets log destination to logfile
* @param path Path 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 * @brief Sets log destination to given output stream
* @param os 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 * @brief Sets format for timestamps in log
* @param format String with timestamp format * @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 * @brief Sets log destination to syslog
* @param name Wanted program name * @param name Wanted program name
* @param facility Wanted log category * @param facility Wanted log category
*/ */
void SendTo (const char *name, int facility); void SendTo(const char *name, int facility);
#endif
#endif
/** /**
* @brief Format log message and write to output stream/syslog * @brief Format log message and write to output stream/syslog
* @param msg Pointer to processed message * @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 */ /** @brief Reopen log file */
void Reopen(); void Reopen();
@ -148,31 +156,41 @@ namespace log {
LogLevel level; /**< message level */ LogLevel level; /**< message level */
std::thread::id tid; /**< id of thread that generated message */ 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; typedef std::function<void(const std::string &)> ThrowFunction;
ThrowFunction GetThrowFunction ();
void SetThrowFunction (ThrowFunction f); ThrowFunction GetThrowFunction();
} // log
void SetThrowFunction(ThrowFunction f);
} // log
} // i2p } // i2p
/** internal usage only -- folding args array to single string */ /** internal usage only -- folding args array to single string */
template<typename TValue> 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 #if (__cplusplus < 201703L) // below C++ 17
/** internal usage only -- folding args array to single string */ /** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs> 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<TValue>(arg)
LogPrint (s, std::forward<TArgs>(args)...); );
LogPrint (s, std::forward<TArgs>(args)
...);
} }
#endif #endif
@ -182,24 +200,33 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
* @param args Array of message parts * @param args Array of message parts
*/ */
template<typename... TArgs> template<typename... TArgs>
void LogPrint (LogLevel level, TArgs&&... args) noexcept void LogPrint(LogLevel level, TArgs &&... args)
{
i2p::log::Log &log = i2p::log::Logger();
if (level > log.GetLogLevel ())
return;
// fold message to single string noexcept
std::stringstream ss; {
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 #if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
#else #else
LogPrint (ss, std::forward<TArgs>(args)...); LogPrint (ss, std::forward<TArgs>(args)
...);
#endif #endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str()); auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
msg->tid = std::this_thread::get_id(); msg->
log.Append(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 * @param args Array of message parts
*/ */
template<typename... TArgs> template<typename... TArgs>
void ThrowFatal (TArgs&&... args) noexcept void ThrowFatal(TArgs &&... args)
noexcept
{ {
auto f = i2p::log::GetThrowFunction (); auto f = i2p::log::GetThrowFunction();
if (!f) return; if (!f) return;
// fold message to single string // fold message to single string
std::stringstream ss(""); std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher #if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...); (LogPrint (ss, std::forward<TArgs>(args)), ...);
#else #else
LogPrint (ss, std::forward<TArgs>(args)...); LogPrint (ss, std::forward<TArgs>(args)
...);
#endif #endif
f (ss.str ()); f (ss
.
str()
);
} }
#endif // LOG_H__ #endif // LOG_H__

File diff suppressed because it is too large Load diff

View file

@ -22,10 +22,8 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "TransportSession.h" #include "TransportSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; 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_TIMEOUT = 120; // 2 minutes
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 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 = 25 * 60; // 25 minuntes in seconds
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25 * 60; // 25 minuntes
const int NTCP2_CLOCK_SKEW = 60; // in seconds const int NTCP2_CLOCK_SKEW = 60; // in seconds
const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up
enum NTCP2BlockType enum NTCP2BlockType {
{
eNTCP2BlkDateTime = 0, eNTCP2BlkDateTime = 0,
eNTCP2BlkOptions, // 1 eNTCP2BlkOptions, // 1
eNTCP2BlkRouterInfo, // 2 eNTCP2BlkRouterInfo, // 2
@ -53,8 +50,7 @@ namespace transport
eNTCP2BlkPadding = 254 eNTCP2BlkPadding = 254
}; };
enum NTCP2TerminationReason enum NTCP2TerminationReason {
{
eNTCP2NormalClose = 0, eNTCP2NormalClose = 0,
eNTCP2TerminationReceived, // 1 eNTCP2TerminationReceived, // 1
eNTCP2IdleTimeout, // 2 eNTCP2IdleTimeout, // 2
@ -78,39 +74,54 @@ namespace transport
// RouterInfo flags // RouterInfo flags
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
struct NTCP2Establisher: private i2p::crypto::NoiseSymmetricState struct NTCP2Establisher : private i2p::crypto::NoiseSymmetricState {
{ NTCP2Establisher();
NTCP2Establisher ();
~NTCP2Establisher ();
const uint8_t * GetPub () const { return m_EphemeralKeys->GetPublicKey (); }; ~NTCP2Establisher();
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
const uint8_t * GetK () const { return m_CK + 32; }; const uint8_t *GetPub() const { return m_EphemeralKeys->GetPublicKey(); };
const uint8_t * GetCK () const { return m_CK; };
const uint8_t * GetH () const { return m_H; };
void KDF1Alice (); const uint8_t *GetRemotePub() const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
void KDF1Bob (); uint8_t *GetRemotePub() { return m_RemoteEphemeralPublicKey; }; // to set
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 const uint8_t *GetK() const { return m_CK + 32; };
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
void CreateEphemeralKey ();
void CreateSessionRequestMessage (); const uint8_t *GetCK() const { return m_CK; };
void CreateSessionCreatedMessage ();
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew); const uint8_t *GetH() const { return m_H; };
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce); void KDF1Alice();
bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
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; std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
@ -119,83 +130,120 @@ namespace transport
uint16_t m3p2Len; uint16_t m3p2Len;
uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], 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; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen;
}; };
class NTCP2Server; 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: 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); 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; }; ~NTCP2Session();
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; }; void Terminate();
bool IsTerminated () const { return m_IsTerminated; };
void ClientLogin (); // Alice void TerminateByTimeout();
void ServerLogin (); // Bob
void SendLocalRouterInfo (bool update); // after handshake or by update void Done();
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
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: private:
void Established (); void Established();
void CreateNonce (uint64_t seqn, uint8_t * nonce); void CreateNonce(uint64_t seqn, uint8_t *nonce);
void CreateNextReceivedBuffer (size_t size);
void KeyDerivationFunctionDataPhase (); void CreateNextReceivedBuffer(size_t size);
void SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey);
void KeyDerivationFunctionDataPhase();
void SetSipKeys(const uint8_t *sendSipKey, const uint8_t *receiveSipKey);
// establish // establish
void SendSessionRequest (); void SendSessionRequest();
void SendSessionCreated ();
void SendSessionConfirmed ();
void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void SendSessionCreated();
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 SendSessionConfirmed();
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 HandleSessionRequestSent(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 HandleSessionRequestReceived(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
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 // data
void ReceiveLength (); void ReceiveLength();
void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void ProcessNextFrame (const uint8_t * frame, size_t len);
void SetNextSentFrameLength (size_t frameLen, uint8_t * lengthBuf); void HandleReceivedLength(const boost::system::error_code &ecode, std::size_t bytes_transferred);
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 Receive();
void EncryptAndSendNextBuffer (size_t payloadLen);
void HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleReceived(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 ProcessNextFrame(const uint8_t *frame, size_t len);
void SendRouterInfo ();
void SendTermination (NTCP2TerminationReason reason); void SetNextSentFrameLength(size_t frameLen, uint8_t *lengthBuf);
void SendTerminationAndTerminate (NTCP2TerminationReason reason);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs); 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: private:
NTCP2Server& m_Server; NTCP2Server &m_Server;
boost::asio::ip::tcp::socket m_Socket; boost::asio::ip::tcp::socket m_Socket;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint; boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsEstablished, m_IsTerminated; bool m_IsEstablished, m_IsTerminated;
@ -203,17 +251,16 @@ namespace transport
std::unique_ptr<NTCP2Establisher> m_Establisher; std::unique_ptr<NTCP2Establisher> m_Establisher;
// data phase // data phase
uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32]; 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 #if OPENSSL_SIPHASH
EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx; EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx;
#else #else
const uint8_t * m_SendSipKey, * m_ReceiveSipKey; const uint8_t *m_SendSipKey, *m_ReceiveSipKey;
#endif #endif
uint16_t m_NextReceivedLen; uint16_t m_NextReceivedLen;
uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; uint8_t *m_NextReceivedBuffer, *m_NextSendBuffer;
size_t m_NextReceivedBufferSize; size_t m_NextReceivedBufferSize;
union union {
{
uint8_t buf[8]; uint8_t buf[8];
uint16_t key; uint16_t key;
} m_ReceiveIV, m_SendIV; } m_ReceiveIV, m_SendIV;
@ -229,48 +276,61 @@ namespace transport
int m_NextPaddingSize; int m_NextPaddingSize;
}; };
class NTCP2Server: private i2p::util::RunnableServiceWithWork class NTCP2Server : private i2p::util::RunnableServiceWithWork {
{
public: public:
enum ProxyType enum ProxyType {
{
eNoProxy, eNoProxy,
eSocksProxy, eSocksProxy,
eHTTPProxy eHTTPProxy
}; };
NTCP2Server (); NTCP2Server();
~NTCP2Server ();
void Start (); ~NTCP2Server();
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false); void Start();
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident); 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); void Connect(std::shared_ptr<NTCP2Session> conn);
bool UsingProxy() const { return m_ProxyType != eNoProxy; }; 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: private:
void HandleAccept (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 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 HandleAcceptV6(std::shared_ptr<NTCP2Session> conn, const boost::system::error_code &error);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer); void HandleConnect(const boost::system::error_code &ecode, std::shared_ptr<NTCP2Session> conn,
std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code &ecode, std::shared_ptr<NTCP2Session> conn,
std::shared_ptr<boost::asio::deadline_timer> timer);
void
AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
// timer // timer
void ScheduleTermination (); void ScheduleTermination();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void HandleTerminationTimer(const boost::system::error_code &ecode);
private: private:
@ -289,9 +349,12 @@ namespace transport
public: public:
// for HTTP/I2PControl // for HTTP/I2PControl
const decltype(m_NTCP2Sessions)& GetNTCP2Sessions () const { return m_NTCP2Sessions; }; const decltype(m_NTCP2Sessions)
&
GetNTCP2Sessions() const { return m_NTCP2Sessions; };
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -32,10 +32,8 @@
#include "version.h" #include "version.h"
#include "util.h" #include "util.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
const int NETDB_MIN_ROUTERS = 90; const int NETDB_MIN_ROUTERS = 90;
const int NETDB_MIN_FLOODFILLS = 5; const int NETDB_MIN_FLOODFILLS = 5;
const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds 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 */ /** 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; typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter;
class NetDb class NetDb {
{
public: public:
NetDb (); NetDb();
~NetDb ();
void Start (); ~NetDb();
void Stop ();
std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len); void Start();
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType);
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true); void Stop();
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<const RouterInfo> AddRouterInfo(const uint8_t *buf, int len);
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; bool AddRouterInfo(const IdentHash &ident, const uint8_t *buf, int len);
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); 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 */ /** set hidden mode, aka don't publish our RI to netdb and don't explore */
void SetHidden(bool hide); void SetHidden(bool hide);
void Reseed (); void Reseed();
Families& GetFamilies () { return m_Families; };
Families &GetFamilies() { return m_Families; };
// for web interface // for web interface
int GetNumRouters () const { return m_RouterInfos.size (); }; int GetNumRouters() const { return m_RouterInfos.size(); };
int GetNumFloodfills () const { return m_Floodfills.size (); };
int GetNumLeaseSets () const { return m_LeaseSets.size (); }; int GetNumFloodfills() const { return m_Floodfills.size(); };
int GetNumLeaseSets() const { return m_LeaseSets.size(); };
/** visit all lease sets we currently store */ /** visit all lease sets we currently store */
void VisitLeaseSets(LeaseSetVisitor v); void VisitLeaseSets(LeaseSetVisitor v);
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */ /** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
void VisitStoredRouterInfos(RouterInfoVisitor v); void VisitStoredRouterInfos(RouterInfoVisitor v);
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */ /** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
void VisitRouterInfos(RouterInfoVisitor v); 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 */ /** 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); size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
void ClearRouterInfos () { m_RouterInfos.clear (); }; 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); };
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: private:
void Load (); void Load();
bool LoadRouterInfo (const std::string& path, uint64_t ts);
void SaveUpdated ();
void Run (); // exploratory thread
void Explore (int numDestinations);
void Publish ();
void Flood (const IdentHash& ident, std::shared_ptr<I2NPMessage> floodMsg);
void ManageLeaseSets ();
void ManageRequests ();
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters = 40, int numFloodfills = 20); bool LoadRouterInfo(const std::string &path, uint64_t ts);
std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len, bool& updated); void SaveUpdated();
std::shared_ptr<const RouterInfo> AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len, bool& updated);
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> template<typename Filter>
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const; std::shared_ptr<const RouterInfo> GetRandomRouter(Filter filter) const;
private: private:
@ -160,15 +212,16 @@ namespace data
std::list<std::shared_ptr<RouterInfo> > m_Floodfills; std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
bool m_IsRunning; bool m_IsRunning;
std::thread * m_Thread; std::thread *m_Thread;
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
GzipInflator m_Inflator; GzipInflator m_Inflator;
Reseeder * m_Reseeder; Reseeder *m_Reseeder;
Families m_Families; Families m_Families;
i2p::fs::HashedStorage m_Storage; i2p::fs::HashedStorage m_Storage;
friend class NetDbRequests; friend class NetDbRequests;
NetDbRequests m_Requests; NetDbRequests m_Requests;
bool m_PersistProfiles; bool m_PersistProfiles;
@ -187,7 +240,7 @@ namespace data
}; };
extern NetDb netdb; extern NetDb netdb;
} }
} }
#endif #endif

View file

@ -12,157 +12,142 @@
#include "NetDb.hpp" #include "NetDb.hpp"
#include "NetDbRequests.h" #include "NetDbRequests.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data std::shared_ptr<I2NPMessage>
{ RequestedDestination::CreateRequestMessage(std::shared_ptr<const RouterInfo> router,
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel) {
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
{
std::shared_ptr<I2NPMessage> msg; std::shared_ptr<I2NPMessage> msg;
if(replyTunnel) if (replyTunnel)
msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, replyTunnel->GetNextIdentHash(),
replyTunnel->GetNextTunnelID(), m_IsExploratory,
&m_ExcludedPeers); &m_ExcludedPeers);
else else
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers); msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0,
if(router) m_IsExploratory, &m_ExcludedPeers);
m_ExcludedPeers.insert (router->GetIdentHash ()); if (router)
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_ExcludedPeers.insert(router->GetIdentHash());
m_CreationTime = i2p::util::GetSecondsSinceEpoch();
return msg; return msg;
} }
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage(const IdentHash &floodfill) {
{ auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination,
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, i2p::context.GetRouterInfo().GetIdentHash(), 0, false,
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); &m_ExcludedPeers);
m_ExcludedPeers.insert (floodfill); m_ExcludedPeers.insert(floodfill);
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch();
return msg; return msg;
} }
void RequestedDestination::ClearExcludedPeers () void RequestedDestination::ClearExcludedPeers() {
{ m_ExcludedPeers.clear();
m_ExcludedPeers.clear ();
} }
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r) void RequestedDestination::Success(std::shared_ptr<RouterInfo> r) {
{ if (m_RequestComplete) {
if (m_RequestComplete) m_RequestComplete(r);
{
m_RequestComplete (r);
m_RequestComplete = nullptr; m_RequestComplete = nullptr;
} }
} }
void RequestedDestination::Fail () void RequestedDestination::Fail() {
{ if (m_RequestComplete) {
if (m_RequestComplete) m_RequestComplete(nullptr);
{
m_RequestComplete (nullptr);
m_RequestComplete = nullptr; m_RequestComplete = nullptr;
} }
} }
void NetDbRequests::Start () void NetDbRequests::Start() {
{
} }
void NetDbRequests::Stop () void NetDbRequests::Stop() {
{ m_RequestedDestinations.clear();
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 // request RouterInfo directly
auto dest = std::make_shared<RequestedDestination> (destination, isExploratory); auto dest = std::make_shared<RequestedDestination>(destination, isExploratory);
dest->SetRequestComplete (requestComplete); dest->SetRequestComplete(requestComplete);
{ {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); 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 nullptr;
} }
return dest; 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::shared_ptr<RequestedDestination> request;
{ {
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident); auto it = m_RequestedDestinations.find(ident);
if (it != m_RequestedDestinations.end ()) if (it != m_RequestedDestinations.end()) {
{
request = it->second; request = it->second;
m_RequestedDestinations.erase (it); m_RequestedDestinations.erase(it);
} }
} }
if (request) if (request) {
{
if (r) if (r)
request->Success (r); request->Success(r);
else 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); std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
auto it = m_RequestedDestinations.find (ident); auto it = m_RequestedDestinations.find(ident);
if (it != m_RequestedDestinations.end ()) if (it != m_RequestedDestinations.end())
return it->second; return it->second;
return nullptr; return nullptr;
} }
void NetDbRequests::ManageRequests () void NetDbRequests::ManageRequests() {
{ uint64_t ts = i2p::util::GetSecondsSinceEpoch();
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) for (auto it = m_RequestedDestinations.begin(); it != m_RequestedDestinations.end();) {
{ auto &dest = it->second;
auto& dest = it->second;
bool done = false; 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 (); auto count = dest->GetExcludedPeers().size();
if (!dest->IsExploratory () && count < 7) if (!dest->IsExploratory() && count < 7) {
{ auto pool = i2p::tunnel::tunnels.GetExploratoryPool();
auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); auto outbound = pool->GetNextOutboundTunnel();
auto outbound = pool->GetNextOutboundTunnel (); auto inbound = pool->GetNextInboundTunnel();
auto inbound = pool->GetNextInboundTunnel (); auto nextFloodfill = netdb.GetClosestFloodfill(dest->GetDestination(),
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); dest->GetExcludedPeers());
if (nextFloodfill && outbound && inbound) if (nextFloodfill && outbound && inbound)
outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0, outbound->SendTunnelDataMsg(nextFloodfill->GetIdentHash(), 0,
dest->CreateRequestMessage (nextFloodfill, inbound)); dest->CreateRequestMessage(nextFloodfill, inbound));
else else {
{
done = true; done = true;
if (!inbound) LogPrint (eLogWarning, "NetDbReq: No inbound tunnels"); if (!inbound) LogPrint(eLogWarning, "NetDbReq: No inbound tunnels");
if (!outbound) LogPrint (eLogWarning, "NetDbReq: No outbound tunnels"); if (!outbound) LogPrint(eLogWarning, "NetDbReq: No outbound tunnels");
if (!nextFloodfill) LogPrint (eLogWarning, "NetDbReq: No more floodfills"); if (!nextFloodfill) LogPrint(eLogWarning, "NetDbReq: No more floodfills");
} }
} } else {
else if (!dest->IsExploratory())
{ LogPrint(eLogWarning, "NetDbReq: ", dest->GetDestination().ToBase64(),
if (!dest->IsExploratory ()) " not found after 7 attempts");
LogPrint (eLogWarning, "NetDbReq: ", dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
done = true; done = true;
} }
} }
} } else // delete obsolete request
else // delete obsolete request
done = true; done = true;
if (done) if (done)
it = m_RequestedDestinations.erase (it); it = m_RequestedDestinations.erase(it);
else else
++it; ++it;
} }
} }
} }
} }

View file

@ -15,34 +15,44 @@
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data class RequestedDestination {
{
class RequestedDestination
{
public: 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): RequestedDestination(const IdentHash &destination, bool isExploratory = false) :
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {}; m_Destination(destination), m_IsExploratory(isExploratory), m_CreationTime(0) {};
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
const IdentHash& GetDestination () const { return m_Destination; }; ~RequestedDestination() { if (m_RequestComplete) m_RequestComplete(nullptr); };
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; }; const IdentHash &GetDestination() const { return m_Destination; };
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
void Success (std::shared_ptr<RouterInfo> r); int GetNumExcludedPeers() const { return m_ExcludedPeers.size(); };
void Fail ();
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: private:
@ -53,24 +63,28 @@ namespace data
RequestComplete m_RequestComplete; RequestComplete m_RequestComplete;
}; };
class NetDbRequests class NetDbRequests {
{
public: public:
void Start (); void Start();
void Stop ();
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); void Stop();
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const; std::shared_ptr<RequestedDestination> CreateRequest(const IdentHash &destination, bool isExploratory,
void ManageRequests (); 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: private:
mutable std::mutex m_RequestedDestinationsMutex; mutable std::mutex m_RequestedDestinationsMutex;
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations; std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
}; };
} }
} }
#endif #endif

View file

@ -9,17 +9,14 @@
#include "Poly1305.h" #include "Poly1305.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto 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)
{
Poly1305 p(key); Poly1305 p(key);
p.Update(buf, sz); p.Update(buf, sz);
p.Finish(out); p.Finish(out);
} }
} }
} }
#endif #endif

View file

@ -8,52 +8,44 @@
#ifndef LIBI2PD_POLY1305_H #ifndef LIBI2PD_POLY1305_H
#define LIBI2PD_POLY1305_H #define LIBI2PD_POLY1305_H
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include "Crypto.h" #include "Crypto.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #if !OPENSSL_AEAD_CHACHA20_POLY1305
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
const std::size_t POLY1305_DIGEST_BYTES = 16; const std::size_t POLY1305_DIGEST_BYTES = 16;
const std::size_t POLY1305_DIGEST_DWORDS = 4; const std::size_t POLY1305_DIGEST_DWORDS = 4;
const std::size_t POLY1305_KEY_BYTES = 32; const std::size_t POLY1305_KEY_BYTES = 32;
const std::size_t POLY1305_KEY_DWORDS = 8; const std::size_t POLY1305_KEY_DWORDS = 8;
const std::size_t POLY1305_BLOCK_BYTES = 16; const std::size_t POLY1305_BLOCK_BYTES = 16;
namespace poly1305 namespace poly1305 {
{ struct LongBlock {
struct LongBlock
{
unsigned long data[17]; unsigned long data[17];
operator unsigned long * ()
{ operator unsigned long *() {
return data; return data;
} }
}; };
struct Block struct Block {
{
unsigned char data[17]; unsigned char data[17];
void Zero() void Zero() {
{
memset(data, 0, sizeof(data)); memset(data, 0, sizeof(data));
} }
operator uint8_t * () operator uint8_t *() {
{
return data; return data;
} }
Block & operator += (const Block & other) Block &operator+=(const Block &other) {
{
unsigned short u; unsigned short u;
unsigned int i; 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]; u += (unsigned short) data[i] + (unsigned short) other.data[i];
data[i] = (unsigned char) u & 0xff; data[i] = (unsigned char) u & 0xff;
u >>= 8; u >>= 8;
@ -61,40 +53,37 @@ namespace crypto
return *this; return *this;
} }
Block & operator %=(const LongBlock & other) Block &operator%=(const LongBlock &other) {
{
unsigned long u; unsigned long u;
unsigned int i; unsigned int i;
u = 0; u = 0;
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
u += other.data[i]; u += other.data[i];
data[i] = (unsigned char)u & 0xff; data[i] = (unsigned char) u & 0xff;
u >>= 8; u >>= 8;
} }
u += other.data[16]; u += other.data[16];
data[16] = (unsigned char)u & 0x03; data[16] = (unsigned char) u & 0x03;
u >>= 2; u >>= 2;
u += (u << 2); u += (u << 2);
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
u += data[i]; u += data[i];
data[i] = (unsigned char)u & 0xff; data[i] = (unsigned char) u & 0xff;
u >>= 8; u >>= 8;
} }
data[16] += (unsigned char)u; data[16] += (unsigned char) u;
return *this; return *this;
} }
Block & operator = (const Block & other) Block &operator=(const Block &other) {
{
memcpy(data, other.data, sizeof(data)); memcpy(data, other.data, sizeof(data));
return *this; return *this;
} }
Block & operator ~ () Block &operator~() {
{
static const Block minusp = { static const Block minusp = {
0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfc 0xfc
}; };
Block orig; Block orig;
@ -103,15 +92,14 @@ namespace crypto
orig = *this; orig = *this;
*this += minusp; *this += minusp;
neg = -(data[16] >> 7); neg = -(data[16] >> 7);
for(i = 0; i < 17; i++) for (i = 0; i < 17; i++)
data[i] ^= neg & (orig.data[i] ^ data[i]); data[i] ^= neg & (orig.data[i] ^ data[i]);
return *this; return *this;
} }
void PutKey(const uint64_t * key_l) void PutKey(const uint64_t *key_l) {
{ const uint8_t *key = (const uint8_t *) key_l;
const uint8_t * key = (const uint8_t*) key_l;
data[0] = key[0] & 0xff; data[0] = key[0] & 0xff;
data[1] = key[1] & 0xff; data[1] = key[1] & 0xff;
data[2] = key[2] & 0xff; data[2] = key[2] & 0xff;
@ -132,28 +120,23 @@ namespace crypto
} }
template<typename Int_t> 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); memcpy(data, d, 16);
data[16] = last; data[16] = last;
} }
}; };
struct Buffer struct Buffer {
{
uint8_t data[POLY1305_BLOCK_BYTES]; uint8_t data[POLY1305_BLOCK_BYTES];
operator uint8_t * () operator uint8_t *() {
{
return data; return data;
} }
}; };
} }
struct Poly1305 struct Poly1305 {
{ Poly1305(const uint64_t *key) {
Poly1305(const uint64_t * key)
{
m_Leftover = 0; m_Leftover = 0;
m_H.Zero(); m_H.Zero();
m_Final = 0; m_Final = 0;
@ -161,39 +144,34 @@ namespace crypto
m_Pad.Put(key + 2); 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 // process leftover
if(m_Leftover) if (m_Leftover) {
{
size_t want = POLY1305_BLOCK_BYTES - 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); memcpy(m_Buffer + m_Leftover, buf, want);
sz -= want; sz -= want;
buf += want; buf += want;
m_Leftover += want; m_Leftover += want;
if(m_Leftover < POLY1305_BLOCK_BYTES) return; if (m_Leftover < POLY1305_BLOCK_BYTES) return;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES); Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
m_Leftover = 0; m_Leftover = 0;
} }
// process blocks // process blocks
if(sz >= POLY1305_BLOCK_BYTES) if (sz >= POLY1305_BLOCK_BYTES) {
{
size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1)); size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1));
Blocks(buf, want); Blocks(buf, want);
buf += want; buf += want;
sz -= want; sz -= want;
} }
// leftover // leftover
if(sz) if (sz) {
{ memcpy(m_Buffer + m_Leftover, buf, sz);
memcpy(m_Buffer+m_Leftover, buf, sz);
m_Leftover += 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; const unsigned char hi = m_Final ^ 1;
while (sz >= POLY1305_BLOCK_BYTES) { while (sz >= POLY1305_BLOCK_BYTES) {
unsigned long u; unsigned long u;
@ -205,11 +183,11 @@ namespace crypto
/* h *= r */ /* h *= r */
for (i = 0; i < 17; i++) { for (i = 0; i < 17; i++) {
u = 0; u = 0;
for (j = 0; j <= i ; j++) { for (j = 0; j <= i; j++) {
u += (unsigned short)m_H.data[j] * m_R.data[i - j]; u += (unsigned short) m_H.data[j] * m_R.data[i - j];
} }
for (j = i + 1; j < 17; 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); */ v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */
u += v; u += v;
} }
@ -222,14 +200,12 @@ namespace crypto
} }
} }
void Finish(uint64_t * out) void Finish(uint64_t *out) {
{
// process leftovers // process leftovers
if(m_Leftover) if (m_Leftover) {
{
size_t idx = m_Leftover; size_t idx = m_Leftover;
m_Buffer[idx++] = 1; m_Buffer[idx++] = 1;
for(; idx < POLY1305_BLOCK_BYTES; idx++) for (; idx < POLY1305_BLOCK_BYTES; idx++)
m_Buffer[idx] = 0; m_Buffer[idx] = 0;
m_Final = 1; m_Final = 1;
Blocks(m_Buffer, POLY1305_BLOCK_BYTES); Blocks(m_Buffer, POLY1305_BLOCK_BYTES);
@ -253,8 +229,8 @@ namespace crypto
uint8_t m_Final; 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 #endif

View file

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

View file

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

View file

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

View file

@ -27,52 +27,44 @@
#include "util.h" #include "util.h"
#include "Config.h" #include "Config.h"
namespace i2p namespace i2p {
{ namespace data {
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) @brief tries to bootstrap into I2P network (from local files and servers, with respect of options)
*/ */
void Reseeder::Bootstrap () void Reseeder::Bootstrap() {
{ std::string su3FileName;
std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName); i2p::config::GetOption("reseed.file", su3FileName);
std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName); std::string zipFileName;
i2p::config::GetOption("reseed.zipfile", zipFileName);
if (su3FileName.length() > 0) // bootstrap from SU3 file or URL if (su3FileName.length() > 0) // bootstrap from SU3 file or URL
{ {
int num; int num;
if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://") {
{ num = ReseedFromSU3Url(su3FileName); // from https URL
num = ReseedFromSU3Url (su3FileName); // from https URL } else {
} num = ProcessSU3File(su3FileName.c_str());
else
{
num = ProcessSU3File (su3FileName.c_str ());
} }
if (num == 0) if (num == 0)
LogPrint (eLogWarning, "Reseed: Failed to reseed from ", su3FileName); LogPrint(eLogWarning, "Reseed: Failed to reseed from ", su3FileName);
} } else if (zipFileName.length() > 0) // bootstrap from ZIP file
else if (zipFileName.length() > 0) // bootstrap from ZIP file
{ {
int num = ProcessZIPFile (zipFileName.c_str ()); int num = ProcessZIPFile(zipFileName.c_str());
if (num == 0) if (num == 0)
LogPrint (eLogWarning, "Reseed: Failed to reseed from ", zipFileName); LogPrint(eLogWarning, "Reseed: Failed to reseed from ", zipFileName);
} } else // bootstrap from reseed servers
else // bootstrap from reseed servers
{ {
int num = ReseedFromServers (); int num = ReseedFromServers();
if (num == 0) 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 * @brief bootstrap from random server, retry 10 times
* @return number of entries added to netDb * @return number of entries added to netDb
*/ */
int Reseeder::ReseedFromServers () int Reseeder::ReseedFromServers() {
{ bool ipv6;
bool ipv6; i2p::config::GetOption("ipv6", ipv6); i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv4;
bool yggdrasil; i2p::config::GetOption("meshnets.yggdrasil", yggdrasil); i2p::config::GetOption("ipv4", ipv4);
bool yggdrasil;
i2p::config::GetOption("meshnets.yggdrasil", yggdrasil);
std::vector<std::string> httpsReseedHostList; std::vector<std::string> httpsReseedHostList;
if (ipv4 || ipv6) if (ipv4 || ipv6) {
{ std::string reseedURLs;
std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs); i2p::config::GetOption("reseed.urls", reseedURLs);
if (!reseedURLs.empty ()) if (!reseedURLs.empty())
boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on);
} }
std::vector<std::string> yggReseedHostList; std::vector<std::string> yggReseedHostList;
if (yggdrasil && !i2p::util::net::GetYggdrasilAddress ().is_unspecified ()) if (yggdrasil && !i2p::util::net::GetYggdrasilAddress().is_unspecified()) {
{ LogPrint(eLogInfo, "Reseed: Yggdrasil is supported");
LogPrint (eLogInfo, "Reseed: Yggdrasil is supported"); std::string yggReseedURLs;
std::string yggReseedURLs; i2p::config::GetOption("reseed.yggurls", yggReseedURLs); i2p::config::GetOption("reseed.yggurls", yggReseedURLs);
if (!yggReseedURLs.empty ()) if (!yggReseedURLs.empty())
boost::split(yggReseedHostList, yggReseedURLs, boost::is_any_of(","), boost::token_compress_on); boost::split(yggReseedHostList, yggReseedURLs, boost::is_any_of(","), boost::token_compress_on);
} }
if (httpsReseedHostList.empty () && yggReseedHostList.empty()) if (httpsReseedHostList.empty() && yggReseedHostList.empty()) {
{ LogPrint(eLogWarning, "Reseed: No reseed servers specified");
LogPrint (eLogWarning, "Reseed: No reseed servers specified");
return 0; return 0;
} }
int reseedRetries = 0; int reseedRetries = 0;
while (reseedRetries < 10) while (reseedRetries < 10) {
{ auto ind = rand() % (httpsReseedHostList.size() + yggReseedHostList.size());
auto ind = rand () % (httpsReseedHostList.size () + yggReseedHostList.size ()); bool isHttps = ind < httpsReseedHostList.size();
bool isHttps = ind < httpsReseedHostList.size ();
std::string reseedUrl = isHttps ? httpsReseedHostList[ind] : std::string reseedUrl = isHttps ? httpsReseedHostList[ind] :
yggReseedHostList[ind - httpsReseedHostList.size ()]; yggReseedHostList[ind - httpsReseedHostList.size()];
reseedUrl += "i2pseeds.su3"; reseedUrl += "i2pseeds.su3";
auto num = ReseedFromSU3Url (reseedUrl, isHttps); auto num = ReseedFromSU3Url(reseedUrl, isHttps);
if (num > 0) return num; // success if (num > 0) return num; // success
reseedRetries++; 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; return 0;
} }
@ -130,298 +122,274 @@ namespace data
* @param url * @param url
* @return number of entries added to netDb * @return number of entries added to netDb
*/ */
int Reseeder::ReseedFromSU3Url (const std::string& url, bool isHttps) int Reseeder::ReseedFromSU3Url(const std::string &url, bool isHttps) {
{ LogPrint(eLogInfo, "Reseed: Downloading SU3 from ", url);
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url); std::string su3 = isHttps ? HttpsRequest(url) : YggdrasilRequest(url);
std::string su3 = isHttps ? HttpsRequest (url) : YggdrasilRequest (url); if (su3.length() > 0) {
if (su3.length () > 0)
{
std::stringstream s(su3); std::stringstream s(su3);
return ProcessSU3Stream (s); return ProcessSU3Stream(s);
} } else {
else LogPrint(eLogWarning, "Reseed: SU3 download failed");
{
LogPrint (eLogWarning, "Reseed: SU3 download failed");
return 0; return 0;
} }
} }
int Reseeder::ProcessSU3File (const char * filename) int Reseeder::ProcessSU3File(const char *filename) {
{
std::ifstream s(filename, std::ifstream::binary); std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ()) if (s.is_open())
return ProcessSU3Stream (s); return ProcessSU3Stream(s);
else else {
{ LogPrint(eLogError, "Reseed: Can't open file ", filename);
LogPrint (eLogError, "Reseed: Can't open file ", filename);
return 0; return 0;
} }
} }
int Reseeder::ProcessZIPFile (const char * filename) int Reseeder::ProcessZIPFile(const char *filename) {
{
std::ifstream s(filename, std::ifstream::binary); std::ifstream s(filename, std::ifstream::binary);
if (s.is_open ()) if (s.is_open()) {
{ s.seekg(0, std::ios::end);
s.seekg (0, std::ios::end); auto len = s.tellg();
auto len = s.tellg (); s.seekg(0, std::ios::beg);
s.seekg (0, std::ios::beg); return ProcessZIPStream(s, len);
return ProcessZIPStream (s, len); } else {
} LogPrint(eLogError, "Reseed: Can't open file ", filename);
else
{
LogPrint (eLogError, "Reseed: Can't open file ", filename);
return 0; return 0;
} }
} }
const char SU3_MAGIC_NUMBER[]="I2Psu3"; const char SU3_MAGIC_NUMBER[] = "I2Psu3";
int Reseeder::ProcessSU3Stream (std::istream& s)
{ int Reseeder::ProcessSU3Stream(std::istream &s) {
char magicNumber[7]; char magicNumber[7];
s.read (magicNumber, 7); // magic number and zero byte 6 s.read(magicNumber, 7); // magic number and zero byte 6
if (strcmp (magicNumber, SU3_MAGIC_NUMBER)) if (strcmp(magicNumber, SU3_MAGIC_NUMBER)) {
{ LogPrint(eLogError, "Reseed: Unexpected SU3 magic number");
LogPrint (eLogError, "Reseed: Unexpected SU3 magic number");
return 0; return 0;
} }
s.seekg (1, std::ios::cur); // su3 file format version s.seekg(1, std::ios::cur); // su3 file format version
SigningKeyType signatureType; SigningKeyType signatureType;
s.read ((char *)&signatureType, 2); // signature type s.read((char *) &signatureType, 2); // signature type
signatureType = be16toh (signatureType); signatureType = be16toh(signatureType);
uint16_t signatureLength; uint16_t signatureLength;
s.read ((char *)&signatureLength, 2); // signature length s.read((char *) &signatureLength, 2); // signature length
signatureLength = be16toh (signatureLength); signatureLength = be16toh(signatureLength);
s.seekg (1, std::ios::cur); // unused s.seekg(1, std::ios::cur); // unused
uint8_t versionLength; uint8_t versionLength;
s.read ((char *)&versionLength, 1); // version length s.read((char *) &versionLength, 1); // version length
s.seekg (1, std::ios::cur); // unused s.seekg(1, std::ios::cur); // unused
uint8_t signerIDLength; uint8_t signerIDLength;
s.read ((char *)&signerIDLength, 1); // signer ID length s.read((char *) &signerIDLength, 1); // signer ID length
uint64_t contentLength; uint64_t contentLength;
s.read ((char *)&contentLength, 8); // content length s.read((char *) &contentLength, 8); // content length
contentLength = be64toh (contentLength); contentLength = be64toh(contentLength);
s.seekg (1, std::ios::cur); // unused s.seekg(1, std::ios::cur); // unused
uint8_t fileType; uint8_t fileType;
s.read ((char *)&fileType, 1); // file type s.read((char *) &fileType, 1); // file type
if (fileType != 0x00) // zip file 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; return 0;
} }
s.seekg (1, std::ios::cur); // unused s.seekg(1, std::ios::cur); // unused
uint8_t contentType; uint8_t contentType;
s.read ((char *)&contentType, 1); // content type s.read((char *) &contentType, 1); // content type
if (contentType != 0x03) // reseed data if (contentType != 0x03) // reseed data
{ {
LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType); LogPrint(eLogError, "Reseed: Unexpected content type ", (int) contentType);
return 0; 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]; char signerID[256];
s.read (signerID, signerIDLength); // signerID s.read(signerID, signerIDLength); // signerID
signerID[signerIDLength] = 0; signerID[signerIDLength] = 0;
bool verify; i2p::config::GetOption("reseed.verify", verify); bool verify;
if (verify) i2p::config::GetOption("reseed.verify", verify);
{ if (verify) {
//try to verify signature //try to verify signature
auto it = m_SigningKeys.find (signerID); auto it = m_SigningKeys.find(signerID);
if (it != m_SigningKeys.end ()) if (it != m_SigningKeys.end()) {
{
// TODO: implement all signature types // TODO: implement all signature types
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) {
{ size_t pos = s.tellg();
size_t pos = s.tellg ();
size_t tbsLen = pos + contentLength; size_t tbsLen = pos + contentLength;
uint8_t * tbs = new uint8_t[tbsLen]; uint8_t *tbs = new uint8_t[tbsLen];
s.seekg (0, std::ios::beg); s.seekg(0, std::ios::beg);
s.read ((char *)tbs, tbsLen); s.read((char *) tbs, tbsLen);
uint8_t * signature = new uint8_t[signatureLength]; uint8_t *signature = new uint8_t[signatureLength];
s.read ((char *)signature, signatureLength); s.read((char *) signature, signatureLength);
// RSA-raw // RSA-raw
{ {
// calculate digest // calculate digest
uint8_t digest[64]; uint8_t digest[64];
SHA512 (tbs, tbsLen, digest); SHA512(tbs, tbsLen, digest);
// encrypt signature // encrypt signature
BN_CTX * bnctx = BN_CTX_new (); BN_CTX *bnctx = BN_CTX_new();
BIGNUM * s = BN_new (), * n = BN_new (); BIGNUM *s = BN_new(), *n = BN_new();
BN_bin2bn (signature, signatureLength, s); BN_bin2bn(signature, signatureLength, s);
BN_bin2bn (it->second, 512, n); // RSA 4096 assumed BN_bin2bn(it->second, 512, n); // RSA 4096 assumed
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n BN_mod_exp(s, s, i2p::crypto::GetRSAE(), n, bnctx); // s = s^e mod n
uint8_t * enSigBuf = new uint8_t[signatureLength]; uint8_t *enSigBuf = new uint8_t[signatureLength];
i2p::crypto::bn2buf (s, enSigBuf, signatureLength); i2p::crypto::bn2buf(s, enSigBuf, signatureLength);
// digest is right aligned // digest is right aligned
// we can't use RSA_verify due wrong padding in SU3 // we can't use RSA_verify due wrong padding in SU3
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64)) if (memcmp(enSigBuf + (signatureLength - 64), digest, 64))
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed"); LogPrint(eLogWarning, "Reseed: SU3 signature verification failed");
else else
verify = false; // verified verify = false; // verified
delete[] enSigBuf; delete[] enSigBuf;
BN_free (s); BN_free (n); BN_free(s);
BN_CTX_free (bnctx); BN_free(n);
BN_CTX_free(bnctx);
} }
delete[] signature; delete[] signature;
delete[] tbs; delete[] tbs;
s.seekg (pos, std::ios::beg); s.seekg(pos, std::ios::beg);
} } else
else LogPrint(eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported"); } else
} LogPrint(eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
else
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
} }
if (verify) // not verified if (verify) // not verified
{ {
LogPrint (eLogError, "Reseed: SU3 verification failed"); LogPrint(eLogError, "Reseed: SU3 verification failed");
return 0; return 0;
} }
// handle content // handle content
return ProcessZIPStream (s, contentLength); return ProcessZIPStream(s, contentLength);
} }
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; 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; int numFiles = 0;
size_t contentPos = s.tellg (); size_t contentPos = s.tellg();
while (!s.eof ()) while (!s.eof()) {
{
uint32_t signature; uint32_t signature;
s.read ((char *)&signature, 4); s.read((char *) &signature, 4);
signature = le32toh (signature); signature = le32toh (signature);
if (signature == ZIP_HEADER_SIGNATURE) if (signature == ZIP_HEADER_SIGNATURE) {
{
// next local file // next local file
s.seekg (2, std::ios::cur); // version s.seekg(2, std::ios::cur); // version
uint16_t bitFlag; uint16_t bitFlag;
s.read ((char *)&bitFlag, 2); s.read((char *) &bitFlag, 2);
bitFlag = le16toh (bitFlag); bitFlag = le16toh (bitFlag);
uint16_t compressionMethod; uint16_t compressionMethod;
s.read ((char *)&compressionMethod, 2); s.read((char *) &compressionMethod, 2);
compressionMethod = le16toh (compressionMethod); 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 compressedSize, uncompressedSize;
uint32_t crc_32; uint32_t crc_32;
s.read ((char *)&crc_32, 4); s.read((char *) &crc_32, 4);
crc_32 = le32toh (crc_32); crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4); s.read((char *) &compressedSize, 4);
compressedSize = le32toh (compressedSize); compressedSize = le32toh (compressedSize);
s.read ((char *)&uncompressedSize, 4); s.read((char *) &uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize); uncompressedSize = le32toh (uncompressedSize);
uint16_t fileNameLength, extraFieldLength; uint16_t fileNameLength, extraFieldLength;
s.read ((char *)&fileNameLength, 2); s.read((char *) &fileNameLength, 2);
fileNameLength = le16toh (fileNameLength); fileNameLength = le16toh (fileNameLength);
if ( fileNameLength > 255 ) { if (fileNameLength > 255) {
// too big // too big
LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength); LogPrint(eLogError, "Reseed: SU3 fileNameLength too large: ", fileNameLength);
return numFiles; return numFiles;
} }
s.read ((char *)&extraFieldLength, 2); s.read((char *) &extraFieldLength, 2);
extraFieldLength = le16toh (extraFieldLength); extraFieldLength = le16toh (extraFieldLength);
char localFileName[255]; char localFileName[255];
s.read (localFileName, fileNameLength); s.read(localFileName, fileNameLength);
localFileName[fileNameLength] = 0; localFileName[fileNameLength] = 0;
s.seekg (extraFieldLength, std::ios::cur); s.seekg(extraFieldLength, std::ios::cur);
// take care about data descriptor if presented // take care about data descriptor if presented
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) {
{ size_t pos = s.tellg();
size_t pos = s.tellg (); if (!FindZipDataDescriptor(s)) {
if (!FindZipDataDescriptor (s)) LogPrint(eLogError, "Reseed: SU3 archive data descriptor not found");
{
LogPrint (eLogError, "Reseed: SU3 archive data descriptor not found");
return numFiles; return numFiles;
} }
s.read ((char *)&crc_32, 4); s.read((char *) &crc_32, 4);
crc_32 = le32toh (crc_32); crc_32 = le32toh (crc_32);
s.read ((char *)&compressedSize, 4); s.read((char *) &compressedSize, 4);
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data compressedSize = le32toh (compressedSize) +
s.read ((char *)&uncompressedSize, 4); 4; // ??? we must consider signature as part of compressed data
s.read((char *) &uncompressedSize, 4);
uncompressedSize = le32toh (uncompressedSize); uncompressedSize = le32toh (uncompressedSize);
// now we know compressed and uncompressed size // 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"); LogPrint(eLogDebug, "Reseed: Processing file ", localFileName, " ", compressedSize, " bytes");
if (!compressedSize) if (!compressedSize) {
{ LogPrint(eLogWarning, "Reseed: Unexpected size 0. Skipped");
LogPrint (eLogWarning, "Reseed: Unexpected size 0. Skipped");
continue; continue;
} }
uint8_t * compressed = new uint8_t[compressedSize]; uint8_t *compressed = new uint8_t[compressedSize];
s.read ((char *)compressed, compressedSize); s.read((char *) compressed, compressedSize);
if (compressionMethod) // we assume Deflate if (compressionMethod) // we assume Deflate
{ {
z_stream inflator; z_stream inflator;
memset (&inflator, 0, sizeof (inflator)); memset(&inflator, 0, sizeof(inflator));
inflateInit2 (&inflator, -MAX_WBITS); // no zlib header inflateInit2(&inflator, -MAX_WBITS); // no zlib header
uint8_t * uncompressed = new uint8_t[uncompressedSize]; uint8_t *uncompressed = new uint8_t[uncompressedSize];
inflator.next_in = compressed; inflator.next_in = compressed;
inflator.avail_in = compressedSize; inflator.avail_in = compressedSize;
inflator.next_out = uncompressed; inflator.next_out = uncompressed;
inflator.avail_out = uncompressedSize; inflator.avail_out = uncompressedSize;
int err; int err;
if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0) if ((err = inflate(&inflator, Z_SYNC_FLUSH)) >= 0) {
{
uncompressedSize -= inflator.avail_out; uncompressedSize -= inflator.avail_out;
if (crc32 (0, uncompressed, uncompressedSize) == crc_32) if (crc32(0, uncompressed, uncompressedSize) == crc_32) {
{ i2p::data::netdb.AddRouterInfo(uncompressed, uncompressedSize);
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
numFiles++; numFiles++;
} } else
else LogPrint(eLogError, "Reseed: CRC32 verification failed");
LogPrint (eLogError, "Reseed: CRC32 verification failed"); } else
} LogPrint(eLogError, "Reseed: SU3 decompression error ", err);
else
LogPrint (eLogError, "Reseed: SU3 decompression error ", err);
delete[] uncompressed; delete[] uncompressed;
inflateEnd (&inflator); inflateEnd(&inflator);
} } else // no compression
else // no compression
{ {
i2p::data::netdb.AddRouterInfo (compressed, compressedSize); i2p::data::netdb.AddRouterInfo(compressed, compressedSize);
numFiles++; numFiles++;
} }
delete[] compressed; delete[] compressed;
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4) s.seekg(12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
} } else {
else
{
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE) 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 break; // no more files
} }
size_t end = s.tellg (); size_t end = s.tellg();
if (end - contentPos >= contentLength) if (end - contentPos >= contentLength)
break; // we are beyond contentLength break; // we are beyond contentLength
} }
if (numFiles) // check if routers are not outdated if (numFiles) // check if routers are not outdated
{ {
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch();
int numOutdated = 0; int numOutdated = 0;
i2p::data::netdb.VisitRouterInfos ( i2p::data::netdb.VisitRouterInfos(
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r) [&numOutdated, ts](std::shared_ptr<const RouterInfo> r) {
if (r && ts > r->GetTimestamp() +
10 * i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT * 1000LL) // 270 hours
{ {
if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours LogPrint(eLogError, "Reseed: Router ", r->GetIdentHash().ToBase64(), " is outdated by ",
{ (ts - r->GetTimestamp()) / 1000LL / 3600LL, " hours");
LogPrint (eLogError, "Reseed: Router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours");
numOutdated++; 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"
" *_*****_*\n" " *_*****_*\n"
" *_(O)_(O)_*\n" " *_(O)_(O)_*\n"
@ -430,74 +398,65 @@ namespace data
" **_________**\n" " **_________**\n"
" *_________*\n" " *_________*\n"
" ***___***"); " ***___***");
i2p::data::netdb.ClearRouterInfos (); i2p::data::netdb.ClearRouterInfos();
numFiles = 0; numFiles = 0;
} }
} }
return numFiles; return numFiles;
} }
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 }; const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = {0x50, 0x4B, 0x07, 0x08};
bool Reseeder::FindZipDataDescriptor (std::istream& s)
{ bool Reseeder::FindZipDataDescriptor(std::istream &s) {
size_t nextInd = 0; size_t nextInd = 0;
while (!s.eof ()) while (!s.eof()) {
{
uint8_t nextByte; uint8_t nextByte;
s.read ((char *)&nextByte, 1); s.read((char *) &nextByte, 1);
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) {
{
nextInd++; nextInd++;
if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE)) if (nextInd >= sizeof(ZIP_DATA_DESCRIPTOR_SIGNATURE))
return true; return true;
} } else
else
nextInd = 0; nextInd = 0;
} }
return false; return false;
} }
void Reseeder::LoadCertificate (const std::string& filename) void Reseeder::LoadCertificate(const std::string &filename) {
{ SSL_CTX *ctx = SSL_CTX_new(TLS_method());
SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); int ret = SSL_CTX_use_certificate_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) {
if (ret) SSL *ssl = SSL_new(ctx);
{ X509 *cert = SSL_get_certificate(ssl);
SSL * ssl = SSL_new (ctx);
X509 * cert = SSL_get_certificate (ssl);
// verify // verify
if (cert) if (cert) {
{
// extract issuer name // extract issuer name
char name[100]; char name[100];
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100); X509_NAME_oneline(X509_get_issuer_name(cert), name, 100);
char * cn = strstr (name, "CN="); char *cn = strstr(name, "CN=");
if (cn) if (cn) {
{
cn += 3; cn += 3;
char * terminator = strchr (cn, '/'); char *terminator = strchr(cn, '/');
if (terminator) terminator[0] = 0; if (terminator) terminator[0] = 0;
} }
// extract RSA key (we need n only, e = 65537) // extract RSA key (we need n only, e = 65537)
const RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert)); const RSA *key = EVP_PKEY_get0_RSA(X509_get_pubkey(cert));
const BIGNUM * n, * e, * d; const BIGNUM *n, *e, *d;
RSA_get0_key(key, &n, &e, &d); RSA_get0_key(key, &n, &e, &d);
PublicKey value; PublicKey value;
i2p::crypto::bn2buf (n, value, 512); i2p::crypto::bn2buf(n, value, 512);
if (cn) if (cn)
m_SigningKeys[cn] = value; m_SigningKeys[cn] = value;
else else
LogPrint (eLogError, "Reseed: Can't find CN field in ", filename); LogPrint(eLogError, "Reseed: Can't find CN field in ", filename);
} }
SSL_free (ssl); SSL_free(ssl);
} } else
else LogPrint(eLogError, "Reseed: Can't open certificate file ", filename);
LogPrint (eLogError, "Reseed: Can't open certificate file ", filename); SSL_CTX_free(ctx);
SSL_CTX_free (ctx);
} }
void Reseeder::LoadCertificates () void Reseeder::LoadCertificates() {
{
std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed"; std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed";
std::vector<std::string> files; std::vector<std::string> files;
@ -508,25 +467,25 @@ namespace data
return; return;
} }
for (const std::string & file : files) { for (const std::string &file: files) {
if (file.compare(file.size() - 4, 4, ".crt") != 0) { if (file.compare(file.size() - 4, 4, ".crt") != 0) {
LogPrint(eLogWarning, "Reseed: Ignoring file ", file); LogPrint(eLogWarning, "Reseed: Ignoring file ", file);
continue; continue;
} }
LoadCertificate (file); LoadCertificate(file);
numCertificates++; 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; 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 // check for proxy url
if(proxy.size()) { if (proxy.size()) {
// parse // parse
if(proxyUrl.parse(proxy)) { if (proxyUrl.parse(proxy)) {
if (proxyUrl.schema == "http" && !proxyUrl.port) { if (proxyUrl.schema == "http" && !proxyUrl.port) {
proxyUrl.port = 80; proxyUrl.port = 80;
} else if (proxyUrl.schema == "socks" && !proxyUrl.port) { } else if (proxyUrl.schema == "socks" && !proxyUrl.port) {
@ -556,28 +515,24 @@ namespace data
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::context::verify_none); 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 // proxy connection
auto it = boost::asio::ip::tcp::resolver(service).resolve ( auto it = boost::asio::ip::tcp::resolver(service).resolve(
boost::asio::ip::tcp::resolver::query (proxyUrl.host, std::to_string(proxyUrl.port)), ecode); boost::asio::ip::tcp::resolver::query(proxyUrl.host, std::to_string(proxyUrl.port)), ecode);
if(!ecode) if (!ecode) {
{
s.lowest_layer().connect(*it, ecode); s.lowest_layer().connect(*it, ecode);
if(!ecode) if (!ecode) {
{ auto &sock = s.next_layer();
auto & sock = s.next_layer(); if (proxyUrl.schema == "http") {
if(proxyUrl.schema == "http")
{
i2p::http::HTTPReq proxyReq; i2p::http::HTTPReq proxyReq;
i2p::http::HTTPRes proxyRes; i2p::http::HTTPRes proxyRes;
proxyReq.method = "CONNECT"; proxyReq.method = "CONNECT";
proxyReq.version = "HTTP/1.1"; proxyReq.version = "HTTP/1.1";
proxyReq.uri = url.host + ":" + std::to_string(url.port); proxyReq.uri = url.host + ":" + std::to_string(url.port);
auto auth = i2p::http::CreateBasicAuthorizationString (proxyUrl.user, proxyUrl.pass); auto auth = i2p::http::CreateBasicAuthorizationString(proxyUrl.user, proxyUrl.pass);
if (!auth.empty ()) if (!auth.empty())
proxyReq.AddHeader("Proxy-Authorization", auth); proxyReq.AddHeader("Proxy-Authorization", auth);
boost::asio::streambuf writebuf, readbuf; boost::asio::streambuf writebuf, readbuf;
@ -585,48 +540,42 @@ namespace data
out << proxyReq.to_string(); out << proxyReq.to_string();
boost::asio::write(sock, writebuf.data(), boost::asio::transfer_all(), ecode); boost::asio::write(sock, writebuf.data(), boost::asio::transfer_all(), ecode);
if (ecode) if (ecode) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT write error: ", ecode.message()); LogPrint(eLogError, "Reseed: HTTP CONNECT write error: ", ecode.message());
return ""; return "";
} }
boost::asio::read_until(sock, readbuf, "\r\n\r\n", ecode); boost::asio::read_until(sock, readbuf, "\r\n\r\n", ecode);
if (ecode) if (ecode) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message()); LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message());
return ""; 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(); sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply"); LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply");
return ""; return "";
} }
if(proxyRes.code != 200) if (proxyRes.code != 200) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: HTTP CONNECT got bad status: ", proxyRes.code); LogPrint(eLogError, "Reseed: HTTP CONNECT got bad status: ", proxyRes.code);
return ""; return "";
} }
} } else {
else
{
// assume socks if not http, is checked before this for other types // assume socks if not http, is checked before this for other types
// TODO: support username/password auth etc // TODO: support username/password auth etc
uint8_t hs_writebuf[3] = {0x05, 0x01, 0x00}; uint8_t hs_writebuf[3] = {0x05, 0x01, 0x00};
uint8_t hs_readbuf[2]; uint8_t hs_readbuf[2];
boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(), ecode); boost::asio::write(sock, boost::asio::buffer(hs_writebuf, 3), boost::asio::transfer_all(),
if(ecode) ecode);
{ if (ecode) {
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake write failed: ", ecode.message()); LogPrint(eLogError, "Reseed: SOCKS handshake write failed: ", ecode.message());
return ""; return "";
} }
boost::asio::read(sock, boost::asio::buffer(hs_readbuf, 2), ecode); boost::asio::read(sock, boost::asio::buffer(hs_readbuf, 2), ecode);
if(ecode) if (ecode) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake read failed: ", ecode.message()); LogPrint(eLogError, "Reseed: SOCKS handshake read failed: ", ecode.message());
return ""; return "";
@ -640,33 +589,29 @@ namespace data
buf[3] = 0x03; buf[3] = 0x03;
sz += 4; sz += 4;
size_t hostsz = url.host.size(); size_t hostsz = url.host.size();
if(1 + 2 + hostsz + sz > sizeof(buf)) if (1 + 2 + hostsz + sz > sizeof(buf)) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed, hostname too big: ", url.host); LogPrint(eLogError, "Reseed: SOCKS handshake failed, hostname too big: ", url.host);
return ""; return "";
} }
buf[4] = (uint8_t) hostsz; buf[4] = (uint8_t) hostsz;
memcpy(buf+5, url.host.c_str(), hostsz); memcpy(buf + 5, url.host.c_str(), hostsz);
sz += hostsz + 1; sz += hostsz + 1;
htobe16buf(buf+sz, url.port); htobe16buf(buf + sz, url.port);
sz += 2; sz += 2;
boost::asio::write(sock, boost::asio::buffer(buf, sz), boost::asio::transfer_all(), ecode); boost::asio::write(sock, boost::asio::buffer(buf, sz), boost::asio::transfer_all(), ecode);
if(ecode) if (ecode) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed writing: ", ecode.message()); LogPrint(eLogError, "Reseed: SOCKS handshake failed writing: ", ecode.message());
return ""; return "";
} }
boost::asio::read(sock, boost::asio::buffer(buf, 10), ecode); boost::asio::read(sock, boost::asio::buffer(buf, 10), ecode);
if(ecode) if (ecode) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake failed reading: ", ecode.message()); LogPrint(eLogError, "Reseed: SOCKS handshake failed reading: ", ecode.message());
return ""; return "";
} }
if(buf[1] != 0x00) if (buf[1] != 0x00) {
{
sock.close(); sock.close();
LogPrint(eLogError, "Reseed: SOCKS handshake bad reply code: ", std::to_string(buf[1])); LogPrint(eLogError, "Reseed: SOCKS handshake bad reply code: ", std::to_string(buf[1]));
return ""; return "";
@ -674,70 +619,59 @@ namespace data
} }
} }
} }
} } else {
else
{
// direct connection // direct connection
auto it = boost::asio::ip::tcp::resolver(service).resolve ( auto it = boost::asio::ip::tcp::resolver(service).resolve(
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode); boost::asio::ip::tcp::resolver::query(url.host, std::to_string(url.port)), ecode);
if (!ecode) if (!ecode) {
{
bool connected = false; bool connected = false;
boost::asio::ip::tcp::resolver::iterator end; boost::asio::ip::tcp::resolver::iterator end;
while (it != end) while (it != end) {
{
boost::asio::ip::tcp::endpoint ep = *it; boost::asio::ip::tcp::endpoint ep = *it;
if ((ep.address ().is_v4 () && i2p::context.SupportsV4 ()) || if ((ep.address().is_v4() && i2p::context.SupportsV4()) ||
(ep.address ().is_v6 () && i2p::context.SupportsV6 ())) (ep.address().is_v6() && i2p::context.SupportsV6())) {
{ s.lowest_layer().connect(ep, ecode);
s.lowest_layer().connect (ep, ecode); if (!ecode) {
if (!ecode)
{
connected = true; connected = true;
break; break;
} }
} }
it++; it++;
} }
if (!connected) if (!connected) {
{
LogPrint(eLogError, "Reseed: Failed to connect to ", url.host); LogPrint(eLogError, "Reseed: Failed to connect to ", url.host);
return ""; return "";
} }
} }
} }
if (!ecode) if (!ecode) {
{ SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str());
SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str ()); s.handshake(boost::asio::ssl::stream_base::client, ecode);
s.handshake (boost::asio::ssl::stream_base::client, ecode); if (!ecode) {
if (!ecode) LogPrint(eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
{ return ReseedRequest(s, url.to_string());
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port); } else
return ReseedRequest (s, url.to_string()); LogPrint(eLogError, "Reseed: SSL handshake failed: ", ecode.message());
} } else
else LogPrint(eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message());
LogPrint (eLogError, "Reseed: SSL handshake failed: ", ecode.message ());
}
else
LogPrint (eLogError, "Reseed: Couldn't connect to ", url.host, ": ", ecode.message ());
return ""; return "";
} }
template<typename Stream> 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; boost::system::error_code ecode;
i2p::http::HTTPReq req; i2p::http::HTTPReq req;
req.uri = uri; req.uri = uri;
req.AddHeader("User-Agent", "Wget/1.11.4"); req.AddHeader("User-Agent", "Wget/1.11.4");
req.AddHeader("Connection", "close"); req.AddHeader("Connection", "close");
s.write_some (boost::asio::buffer (req.to_string())); s.write_some(boost::asio::buffer(req.to_string()));
// read response // read response
std::stringstream rs; std::stringstream rs;
char recv_buf[1024]; size_t l = 0; char recv_buf[1024];
size_t l = 0;
do { do {
l = s.read_some (boost::asio::buffer (recv_buf, sizeof(recv_buf)), ecode); l = s.read_some(boost::asio::buffer(recv_buf, sizeof(recv_buf)), ecode);
if (l) rs.write (recv_buf, l); if (l) rs.write(recv_buf, l);
} while (!ecode && l); } while (!ecode && l);
// process response // process response
std::string data = rs.str(); std::string data = rs.str();
@ -765,11 +699,9 @@ namespace data
return data; return data;
} }
std::string Reseeder::YggdrasilRequest (const std::string& address) std::string Reseeder::YggdrasilRequest(const std::string &address) {
{
i2p::http::URL url; i2p::http::URL url;
if (!url.parse(address)) if (!url.parse(address)) {
{
LogPrint(eLogError, "Reseed: Failed to parse url: ", address); LogPrint(eLogError, "Reseed: Failed to parse url: ", address);
return ""; return "";
} }
@ -780,19 +712,17 @@ namespace data
boost::asio::io_service service; boost::asio::io_service service;
boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6()); boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6());
if (url.host.length () < 2) return ""; // assume [] if (url.host.length() < 2) return ""; // assume []
auto host = url.host.substr (1, url.host.length () - 2); auto host = url.host.substr(1, url.host.length() - 2);
LogPrint (eLogDebug, "Reseed: Connecting to Yggdrasil ", url.host, ":", url.port); 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); s.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v6::from_string(host), url.port), ecode);
if (!ecode) if (!ecode) {
{ LogPrint(eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port);
LogPrint (eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port); return ReseedRequest(s, url.to_string());
return ReseedRequest (s, url.to_string()); } else
} LogPrint(eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message());
else
LogPrint (eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message ());
return ""; return "";
} }
} }
} }

View file

@ -16,46 +16,52 @@
#include "Identity.h" #include "Identity.h"
#include "Crypto.h" #include "Crypto.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
class Reseeder class Reseeder {
{
typedef Tag<512> PublicKey; typedef Tag<512> PublicKey;
public: public:
Reseeder(); 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: private:
int ReseedFromSU3Url (const std::string& url, bool isHttps = true); int ReseedFromSU3Url(const std::string &url, bool isHttps = true);
void LoadCertificate (const std::string& filename);
int ProcessSU3Stream (std::istream& s); void LoadCertificate(const std::string &filename);
int ProcessZIPStream (std::istream& s, uint64_t contentLength);
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> template<typename Stream>
std::string ReseedRequest (Stream& s, const std::string& uri); std::string ReseedRequest(Stream &s, const std::string &uri);
private: private:
std::map<std::string, PublicKey> m_SigningKeys; std::map<std::string, PublicKey> m_SigningKeys;
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -19,12 +19,10 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "Garlic.h" #include "Garlic.h"
namespace i2p namespace i2p {
{ namespace garlic {
namespace garlic
{
class RouterIncomingRatchetSession; class RouterIncomingRatchetSession;
} }
const char ROUTER_INFO[] = "router.info"; const char ROUTER_INFO[] = "router.info";
const char ROUTER_KEYS[] = "router.keys"; const char ROUTER_KEYS[] = "router.keys";
@ -32,8 +30,7 @@ namespace garlic
const char SSU2_KEYS[] = "ssu2.keys"; const char SSU2_KEYS[] = "ssu2.keys";
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
enum RouterStatus enum RouterStatus {
{
eRouterStatusOK = 0, eRouterStatusOK = 0,
eRouterStatusTesting = 1, eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2, eRouterStatusFirewalled = 2,
@ -43,27 +40,23 @@ namespace garlic
eRouterStatusMesh = 6 eRouterStatusMesh = 6
}; };
enum RouterError enum RouterError {
{
eRouterErrorNone = 0, eRouterErrorNone = 0,
eRouterErrorClockSkew = 1, eRouterErrorClockSkew = 1,
eRouterErrorOffline = 2, eRouterErrorOffline = 2,
eRouterErrorSymmetricNAT = 3 eRouterErrorSymmetricNAT = 3
}; };
class RouterContext: public i2p::garlic::GarlicDestination class RouterContext : public i2p::garlic::GarlicDestination {
{
private: private:
struct NTCP2PrivateKeys struct NTCP2PrivateKeys {
{
uint8_t staticPublicKey[32]; uint8_t staticPublicKey[32];
uint8_t staticPrivateKey[32]; uint8_t staticPrivateKey[32];
uint8_t iv[16]; uint8_t iv[16];
}; };
struct SSU2PrivateKeys struct SSU2PrivateKeys {
{
uint8_t staticPublicKey[32]; uint8_t staticPublicKey[32];
uint8_t staticPrivateKey[32]; uint8_t staticPrivateKey[32];
uint8_t intro[32]; uint8_t intro[32];
@ -71,124 +64,196 @@ namespace garlic
public: public:
RouterContext (); RouterContext();
void Init ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void Init();
i2p::data::LocalRouterInfo& GetRouterInfo () { return m_RouterInfo; };
std::shared_ptr<i2p::data::RouterInfo> GetSharedRouterInfo () const i2p::data::PrivateKeys &GetPrivateKeys() const { return m_Keys; };
{
return std::shared_ptr<i2p::data::RouterInfo> (&m_RouterInfo, 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 *) {}); [](i2p::data::RouterInfo *) {});
} }
std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination ()
{ std::shared_ptr<i2p::garlic::GarlicDestination> GetSharedDestination() {
return std::shared_ptr<i2p::garlic::GarlicDestination> (this, return std::shared_ptr<i2p::garlic::GarlicDestination>(this,
[](i2p::garlic::GarlicDestination *) {}); [](i2p::garlic::GarlicDestination *) {});
} }
const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; 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 * GetSSU2StaticPublicKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; }; const uint8_t *GetNTCP2StaticPrivateKey() const {
const uint8_t * GetSSU2StaticPrivateKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; }; return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr;
const uint8_t * GetSSU2IntroKey () const { return m_SSU2Keys ? m_SSU2Keys->intro : nullptr; }; };
i2p::crypto::X25519Keys& GetSSU2StaticKeys ();
uint32_t GetUptime () const; // in seconds const uint8_t *GetNTCP2IV() const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
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 i2p::crypto::X25519Keys &GetNTCP2StaticKeys();
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 const uint8_t *GetSSU2StaticPublicKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; };
void UpdateStats ();
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing const uint8_t *GetSSU2StaticPrivateKey() const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; };
void CleanupDestination (); // garlic destination
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 // implements LocalDestination
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); }; 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); }; bool Decrypt(const uint8_t *encrypted, uint8_t *data, i2p::data::CryptoKeyType preferredCrypto) const;
void SetLeaseSetUpdated () {};
void Sign(const uint8_t *buf, int len, uint8_t *signature) const { m_Keys.Sign(buf, len, signature); };
void SetLeaseSetUpdated() {};
// implements GarlicDestination // implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; }; std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet() { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool() const;
// override GarlicDestination // override GarlicDestination
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage(std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage(std::shared_ptr<I2NPMessage> msg);
protected: protected:
// implements GarlicDestination // implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len); void HandleI2NPMessage(const uint8_t *buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
bool HandleCloveI2NPMessage(I2NPMessageType typeID, const uint8_t *payload, size_t len, uint32_t msgID);
private: private:
void CreateNewRouter (); void CreateNewRouter();
void NewRouterInfo ();
void UpdateRouterInfo ();
void NewNTCP2Keys ();
void NewSSU2Keys ();
bool IsSSU2Only () const; // SSU2 and no SSU
bool Load ();
void SaveKeys ();
uint16_t SelectRandomPort () const;
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize); 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: private:

File diff suppressed because it is too large Load diff

View file

@ -21,10 +21,8 @@
#include "Profiling.h" #include "Profiling.h"
#include "Family.h" #include "Family.h"
namespace i2p namespace i2p {
{ namespace data {
namespace data
{
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
const char ROUTER_INFO_PROPERTY_NETID[] = "netId"; const char ROUTER_INFO_PROPERTY_NETID[] = "netId";
@ -58,12 +56,10 @@ namespace data
const uint8_t COST_SSU2_NON_PUBLISHED = 15; 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 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: public:
enum SupportedTransports enum SupportedTransports {
{
eNTCP2V4 = 0x01, eNTCP2V4 = 0x01,
eNTCP2V6 = 0x02, eNTCP2V6 = 0x02,
eSSUV4 = 0x04, eSSUV4 = 0x04,
@ -75,8 +71,7 @@ namespace data
}; };
typedef uint8_t CompatibleTransports; typedef uint8_t CompatibleTransports;
enum Caps enum Caps {
{
eFloodfill = 0x01, eFloodfill = 0x01,
eHighBandwidth = 0x02, eHighBandwidth = 0x02,
eExtraBandwidth = 0x04, eExtraBandwidth = 0x04,
@ -85,16 +80,14 @@ namespace data
eUnreachable = 0x20 eUnreachable = 0x20
}; };
enum AddressCaps enum AddressCaps {
{
eV4 = 0x01, eV4 = 0x01,
eV6 = 0x02, eV6 = 0x02,
eSSUTesting = 0x04, eSSUTesting = 0x04,
eSSUIntroducer = 0x08 eSSUIntroducer = 0x08
}; };
enum TransportStyle enum TransportStyle {
{
eTransportUnknown = 0, eTransportUnknown = 0,
eTransportNTCP, eTransportNTCP,
eTransportSSU, eTransportSSU,
@ -102,9 +95,8 @@ namespace data
}; };
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
struct Introducer struct Introducer {
{ Introducer() : iPort(0), iExp(0) {};
Introducer (): iPort (0), iExp (0) {};
boost::asio::ip::address iHost; boost::asio::ip::address iHost;
int iPort; int iPort;
IntroKey iKey; // or ih for SSU2 IntroKey iKey; // or ih for SSU2
@ -112,14 +104,12 @@ namespace data
uint32_t iExp; uint32_t iExp;
}; };
struct SSUExt struct SSUExt {
{
int mtu; int mtu;
std::vector<Introducer> introducers; std::vector<Introducer> introducers;
}; };
struct Address struct Address {
{
TransportStyle transportStyle; TransportStyle transportStyle;
boost::asio::ip::address host; boost::asio::ip::address host;
Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU 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; bool published = false;
std::unique_ptr<SSUExt> ssu; // not null for SSU std::unique_ptr<SSUExt> ssu; // not null for SSU
bool IsCompatible (const boost::asio::ip::address& other) const bool IsCompatible(const boost::asio::ip::address &other) const {
{ return (IsV4() && other.is_v4()) ||
return (IsV4 () && other.is_v4 ()) || (IsV6() && other.is_v6());
(IsV6 () && other.is_v6 ());
} }
bool operator==(const Address& other) const bool operator==(const Address &other) const {
{
return transportStyle == other.transportStyle && return transportStyle == other.transportStyle &&
host == other.host && port == other.port; host == other.host && port == other.port;
} }
bool operator!=(const Address& other) const bool operator!=(const Address &other) const {
{
return !(*this == other); return !(*this == other);
} }
bool IsNTCP2 () const { return transportStyle == eTransportNTCP; }; bool IsNTCP2() const { return transportStyle == eTransportNTCP; };
bool IsSSU2 () const { return transportStyle == eTransportSSU2; };
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); };
bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
bool IsIntroducer () const { return caps & eSSUIntroducer; }; bool IsSSU2() const { return transportStyle == eTransportSSU2; };
bool IsPeerTesting () const { return caps & eSSUTesting; };
bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); }; bool IsPublishedNTCP2() const { return IsNTCP2() && published; };
bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); };
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: public:
Buffer () = default; Buffer() = default;
Buffer (const uint8_t * buf, size_t len);
Buffer(const uint8_t *buf, size_t len);
}; };
typedef std::vector<std::shared_ptr<Address> > Addresses; typedef std::vector<std::shared_ptr<Address> > Addresses;
RouterInfo (const std::string& fullPath); RouterInfo(const std::string &fullPath);
RouterInfo (const RouterInfo& ) = default;
RouterInfo& operator=(const RouterInfo& ) = default;
RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len);
RouterInfo (const uint8_t * buf, size_t len);
virtual ~RouterInfo ();
std::shared_ptr<const IdentityEx> GetRouterIdentity () const { return m_RouterIdentity; }; RouterInfo(const RouterInfo &) = default;
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;
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); RouterInfo &operator=(const RouterInfo &) = default;
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; }; RouterInfo(std::shared_ptr<Buffer> &&buf, size_t len);
void SetCaps (uint8_t caps) { m_Caps = caps; };
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; RouterInfo(const uint8_t *buf, size_t len);
bool IsUnreachable () const { return m_IsUnreachable; };
const uint8_t * GetBuffer () const { return m_Buffer->data (); }; virtual ~RouterInfo();
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
size_t GetBufferLen () const { return m_BufferLen; };
bool IsUpdated () const { return m_IsUpdated; }; std::shared_ptr<const IdentityEx> GetRouterIdentity() const { return m_RouterIdentity; };
void SetUpdated (bool updated) { m_IsUpdated = updated; };
bool SaveToFile (const std::string& fullPath);
std::shared_ptr<RouterProfile> GetProfile () const; void SetRouterIdentity(std::shared_ptr<const IdentityEx> identity);
void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); };
void Update (const uint8_t * buf, size_t len); std::string GetIdentHashBase64() const { return GetIdentHash().ToBase64(); };
void DeleteBuffer () { m_Buffer = nullptr; };
bool IsNewer (const uint8_t * buf, size_t len) const; 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 */ /** 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 // implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_RouterIdentity; }; std::shared_ptr<const IdentityEx> GetIdentity() const { return m_RouterIdentity; };
void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
bool IsDestination () const { return false; }; void Encrypt(const uint8_t *data, uint8_t *encrypted) const;
bool IsDestination() const { return false; };
protected: protected:
RouterInfo (); RouterInfo();
uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; };
void UpdateBuffer (const uint8_t * buf, size_t len); uint8_t *GetBufferPointer(size_t offset = 0) { return m_Buffer->data() + offset; };
void SetBufferLen (size_t len) { m_BufferLen = len; };
void RefreshTimestamp (); void UpdateBuffer(const uint8_t *buf, size_t len);
const Addresses& GetAddresses () const { return *m_Addresses; };
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; }; void SetBufferLen(size_t len) { m_BufferLen = len; };
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
void RefreshTimestamp();
const Addresses &GetAddresses() const { return *m_Addresses; };
CompatibleTransports GetReachableTransports() const { return m_ReachableTransports; };
void SetReachableTransports(CompatibleTransports transports) { m_ReachableTransports = transports; };
private: private:
bool LoadFile (const std::string& fullPath); bool LoadFile(const std::string &fullPath);
void ReadFromFile (const std::string& fullPath);
void ReadFromStream (std::istream& s); void ReadFromFile(const std::string &fullPath);
void ReadFromBuffer (bool verifySignature);
size_t ReadString (char* str, size_t len, std::istream& s) const; void ReadFromStream(std::istream &s);
void ExtractCaps (const char * value);
uint8_t ExtractAddressCaps (const char * value) const; 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> template<typename Filter>
std::shared_ptr<const Address> GetAddress (Filter filter) const; std::shared_ptr<const Address> GetAddress(Filter filter) const;
virtual std::shared_ptr<Buffer> NewBuffer () const;
virtual std::shared_ptr<Buffer> NewBuffer() const;
private: private:
@ -298,7 +381,7 @@ namespace data
std::shared_ptr<Buffer> m_Buffer; std::shared_ptr<Buffer> m_Buffer;
size_t m_BufferLen; size_t m_BufferLen;
uint64_t m_Timestamp; 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; bool m_IsUpdated, m_IsUnreachable;
CompatibleTransports m_SupportedTransports, m_ReachableTransports; CompatibleTransports m_SupportedTransports, m_ReachableTransports;
uint8_t m_Caps; uint8_t m_Caps;
@ -306,34 +389,42 @@ namespace data
mutable std::shared_ptr<RouterProfile> m_Profile; mutable std::shared_ptr<RouterProfile> m_Profile;
}; };
class LocalRouterInfo: public RouterInfo class LocalRouterInfo : public RouterInfo {
{
public: public:
LocalRouterInfo () = default; LocalRouterInfo() = default;
void CreateBuffer (const PrivateKeys& privateKeys);
void UpdateCaps (uint8_t caps);
void SetProperty (const std::string& key, const std::string& value) override; void CreateBuffer(const PrivateKeys &privateKeys);
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); void UpdateCaps(uint8_t caps);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
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: private:
void WriteToStream (std::ostream& s) const; void WriteToStream(std::ostream &s) const;
void UpdateCapsProperty ();
void WriteString (const std::string& str, std::ostream& s) const; void UpdateCapsProperty();
std::shared_ptr<Buffer> NewBuffer () const override;
void WriteString(const std::string &str, std::ostream &s) const;
std::shared_ptr<Buffer> NewBuffer() const override;
private: private:
std::map<std::string, std::string> m_Properties; std::map<std::string, std::string> m_Properties;
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -25,10 +25,8 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "SSUSession.h" #include "SSUSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour 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_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU_SOCKET_SEND_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 i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size
boost::asio::ip::udp::endpoint from; boost::asio::ip::udp::endpoint from;
size_t len; size_t len;
}; };
class SSUServer class SSUServer {
{
public: public:
SSUServer (int port); SSUServer(int port);
~SSUServer ();
void Start (); ~SSUServer();
void Stop ();
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false); void Start();
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
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); 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; }; void CreateDirectSession(std::shared_ptr<const i2p::data::RouterInfo> router,
i2p::util::MemoryPool<Fragment>& GetFragmentsPool () { return m_FragmentsPool; }; boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
i2p::util::MemoryPool<IncompleteMessage>& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; };
i2p::util::MemoryPool<SentMessage>& GetSentMessagesPool () { return m_SentMessagesPool; };
uint16_t GetPort () const { return m_Endpoint.port (); }; std::shared_ptr<SSUSession> FindSession(const boost::asio::ip::udp::endpoint &e) const;
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); std::shared_ptr<SSUSession> GetRandomEstablishedV4Session(std::shared_ptr<const SSUSession> excluded);
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); std::shared_ptr<SSUSession> GetRandomEstablishedV6Session(std::shared_ptr<const SSUSession> excluded);
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce); void DeleteSession(std::shared_ptr<SSUSession> session);
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
void RemovePeerTest (uint32_t nonce); 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: private:
void OpenSocket (); void OpenSocket();
void OpenSocketV6 ();
void Run (); void OpenSocketV6();
void RunReceivers ();
void RunReceiversV6 (); void Run();
void Receive ();
void ReceiveV6 (); void RunReceivers();
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 RunReceiversV6();
void HandleReceivedPackets (std::vector<SSUPacket *> packets,
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> >* sessions); 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> template<typename Filter>
std::shared_ptr<SSUSession> GetRandomV4Session (Filter filter); std::shared_ptr<SSUSession> GetRandomV4Session(Filter filter);
template<typename 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); std::list<std::shared_ptr<SSUSession> >
void ScheduleIntroducersUpdateTimer (); FindIntroducers(int maxNumIntroducers, bool v4, std::set<i2p::data::IdentHash> &excluded);
void ScheduleIntroducersUpdateTimerV6 ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void SchedulePeerTestsCleanupTimer (); void ScheduleIntroducersUpdateTimer();
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
void ScheduleIntroducersUpdateTimerV6();
void HandleIntroducersUpdateTimer(const boost::system::error_code &ecode, bool v4);
void SchedulePeerTestsCleanupTimer();
void HandlePeerTestsCleanupTimer(const boost::system::error_code &ecode);
// timer // timer
void ScheduleTermination (); void ScheduleTermination();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleTerminationV6 (); void HandleTerminationTimer(const boost::system::error_code &ecode);
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
void ScheduleTerminationV6();
void HandleTerminationTimerV6(const boost::system::error_code &ecode);
private: private:
struct PeerTest struct PeerTest {
{
uint64_t creationTime; uint64_t creationTime;
PeerTestParticipant role; PeerTestParticipant role;
std::shared_ptr<SSUSession> session; // for Bob to Alice std::shared_ptr<SSUSession> session; // for Bob to Alice
}; };
volatile bool m_IsRunning; 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 m_Service, m_ReceiversService, m_ReceiversServiceV6;
boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6; boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6;
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6; boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
@ -150,10 +194,16 @@ namespace transport
public: public:
// for HTTP only // for HTTP only
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; const decltype(m_Sessions)
const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; }; &
GetSessions() const { return m_Sessions; };
const decltype(m_SessionsV6)
&
GetSessionsV6() const { return m_SessionsV6; };
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -13,10 +13,8 @@
#include "util.h" #include "util.h"
#include "SSU2Session.h" #include "SSU2Session.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K 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_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
class SSU2Server: private i2p::util::RunnableServiceWithWork class SSU2Server : private i2p::util::RunnableServiceWithWork {
{ struct Packet {
struct Packet
{
uint8_t buf[SSU2_MAX_PACKET_SIZE]; uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len; size_t len;
boost::asio::ip::udp::endpoint from; boost::asio::ip::udp::endpoint from;
}; };
class ReceiveService: public i2p::util::RunnableService class ReceiveService : public i2p::util::RunnableService {
{
public: public:
ReceiveService (const std::string& name): RunnableService (name) {}; ReceiveService(const std::string &name) : RunnableService(name) {};
boost::asio::io_service& GetService () { return GetIOService (); };
void Start () { StartIOService (); }; boost::asio::io_service &GetService() { return GetIOService(); };
void Stop () { StopIOService (); };
void Start() { StartIOService(); };
void Stop() { StopIOService(); };
}; };
public: public:
SSU2Server (); SSU2Server();
~SSU2Server () {};
void Start (); ~SSU2Server() {};
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const;
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void AddSession (std::shared_ptr<SSU2Session> session); void Start();
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 Stop();
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, boost::asio::io_service &GetService() { return GetIOService(); };
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, 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); 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); bool StartPeerTest(std::shared_ptr<const i2p::data::RouterInfo> router, bool v4);
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 UpdateOutgoingToken(const boost::asio::ip::udp::endpoint &ep, uint64_t token, uint32_t exp);
void RescheduleIntroducersUpdateTimerV6 ();
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: private:
boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint); boost::asio::ip::udp::socket &OpenSocket(const boost::asio::ip::udp::endpoint &localEndpoint);
void Receive (boost::asio::ip::udp::socket& socket);
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
Packet * packet, boost::asio::ip::udp::socket& socket);
void HandleReceivedPacket (Packet * packet);
void HandleReceivedPackets (std::vector<Packet *> packets);
void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
void ScheduleTermination (); void Receive(boost::asio::ip::udp::socket &socket);
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleResend (); void HandleReceivedFrom(const boost::system::error_code &ecode, size_t bytes_transferred,
void HandleResendTimer (const boost::system::error_code& ecode); Packet *packet, boost::asio::ip::udp::socket &socket);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session); void HandleReceivedPacket(Packet *packet);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const; void HandleReceivedPackets(std::vector<Packet *> packets);
void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer (); void ProcessNextPacket(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6 (); 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: private:
@ -137,9 +170,12 @@ namespace transport
public: public:
// for HTTP/I2PControl // for HTTP/I2PControl
const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; }; const decltype(m_Sessions)
&
GetSSU2Sessions() const { return m_Sessions; };
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -20,16 +20,14 @@
#include "RouterContext.h" #include "RouterContext.h"
#include "TransportSession.h" #include "TransportSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU2_CLOCK_SKEW = 60; // in seconds const int SSU2_CLOCK_SKEW = 60; // in seconds
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust 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_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_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 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 int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64; const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
enum SSU2MessageType enum SSU2MessageType {
{
eSSU2SessionRequest = 0, eSSU2SessionRequest = 0,
eSSU2SessionCreated = 1, eSSU2SessionCreated = 1,
eSSU2SessionConfirmed = 2, eSSU2SessionConfirmed = 2,
@ -60,8 +57,7 @@ namespace transport
eSSU2HolePunch = 11 eSSU2HolePunch = 11
}; };
enum SSU2BlockType enum SSU2BlockType {
{
eSSU2BlkDateTime = 0, eSSU2BlkDateTime = 0,
eSSU2BlkOptions, // 1 eSSU2BlkOptions, // 1
eSSU2BlkRouterInfo, // 2 eSSU2BlkRouterInfo, // 2
@ -86,8 +82,7 @@ namespace transport
eSSU2BlkPadding = 254 eSSU2BlkPadding = 254
}; };
enum SSU2SessionState enum SSU2SessionState {
{
eSSU2SessionStateUnknown, eSSU2SessionStateUnknown,
eSSU2SessionStateTokenReceived, eSSU2SessionStateTokenReceived,
eSSU2SessionStateSessionRequestSent, eSSU2SessionStateSessionRequestSent,
@ -105,8 +100,7 @@ namespace transport
eSSU2SessionStateTokenRequestReceived eSSU2SessionStateTokenRequestReceived
}; };
enum SSU2PeerTestCode enum SSU2PeerTestCode {
{
eSSU2PeerTestCodeAccept = 0, eSSU2PeerTestCodeAccept = 0,
eSSU2PeerTestCodeBobReasonUnspecified = 1, eSSU2PeerTestCodeBobReasonUnspecified = 1,
eSSU2PeerTestCodeBobNoCharlieAvailable = 2, eSSU2PeerTestCodeBobNoCharlieAvailable = 2,
@ -122,8 +116,7 @@ namespace transport
eSSU2PeerTestCodeUnspecified = 128 eSSU2PeerTestCodeUnspecified = 128
}; };
enum SSU2RelayResponseCode enum SSU2RelayResponseCode {
{
eSSU2RelayResponseCodeAccept = 0, eSSU2RelayResponseCodeAccept = 0,
eSSU2RelayResponseCodeBobRelayTagNotFound = 5, eSSU2RelayResponseCodeBobRelayTagNotFound = 5,
eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65, eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65,
@ -131,13 +124,12 @@ namespace transport
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70 eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70
}; };
enum SSU2TerminationReason enum SSU2TerminationReason {
{
eSSU2TerminationReasonNormalClose = 0, eSSU2TerminationReasonNormalClose = 0,
eSSU2TerminationReasonTerminationReceived = 1, eSSU2TerminationReasonTerminationReceived = 1,
eSSU2TerminationReasonIdleTimeout = 2, eSSU2TerminationReasonIdleTimeout = 2,
eSSU2TerminationReasonRouterShutdown = 3, eSSU2TerminationReasonRouterShutdown = 3,
eSSU2TerminationReasonDataPhaseAEADFailure= 4, eSSU2TerminationReasonDataPhaseAEADFailure = 4,
eSSU2TerminationReasonIncompatibleOptions = 5, eSSU2TerminationReasonIncompatibleOptions = 5,
eSSU2TerminationReasonTncompatibleSignatureType = 6, eSSU2TerminationReasonTncompatibleSignatureType = 6,
eSSU2TerminationReasonClockSkew = 7, eSSU2TerminationReasonClockSkew = 7,
@ -158,10 +150,8 @@ namespace transport
eSSU2TerminationReasonReplacedByNewSession = 22 eSSU2TerminationReasonReplacedByNewSession = 22
}; };
struct SSU2IncompleteMessage struct SSU2IncompleteMessage {
{ struct Fragment {
struct Fragment
{
uint8_t buf[SSU2_MAX_PACKET_SIZE]; uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len; size_t len;
bool isLast; bool isLast;
@ -172,11 +162,10 @@ namespace transport
uint32_t lastFragmentInsertTime; // in seconds uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments; 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]; uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = 0; size_t payloadSize = 0;
uint64_t sendTime; // in milliseconds uint64_t sendTime; // in milliseconds
@ -188,14 +177,12 @@ namespace transport
const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02; const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
class SSU2Server; class SSU2Server;
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
{ class SSU2Session : public TransportSession, public std::enable_shared_from_this<SSU2Session> {
union Header union Header {
{
uint64_t ll[2]; uint64_t ll[2];
uint8_t buf[16]; uint8_t buf[16];
struct struct {
{
uint64_t connID; uint64_t connID;
uint32_t packetNum; uint32_t packetNum;
uint8_t type; uint8_t type;
@ -203,123 +190,200 @@ namespace transport
} h; } h;
}; };
struct HandshakePacket struct HandshakePacket {
{
Header header; Header header;
uint8_t headerX[48]; // part1 for SessionConfirmed 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; size_t payloadSize = 0;
uint64_t sendTime = 0; // in milliseconds uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed bool isSecondFragment = false; // for SessionConfirmed
}; };
typedef std::function<void ()> OnEstablished; typedef std::function<void()> OnEstablished;
public: 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); std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
~SSU2Session ();
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; ~SSU2Session();
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 (); void SetRemoteEndpoint(const boost::asio::ip::udp::endpoint &ep) { m_RemoteEndpoint = ep; };
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); const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() const { return m_RemoteEndpoint; };
bool ProcessSessionCreated (uint8_t * buf, size_t len);
bool ProcessSessionConfirmed (uint8_t * buf, size_t len); i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports() const { return m_RemoteTransports; };
bool ProcessRetry (uint8_t * buf, size_t len);
bool ProcessHolePunch (uint8_t * buf, size_t len); std::shared_ptr<const i2p::data::RouterInfo::Address> GetAddress() const { return m_Address; };
bool ProcessPeerTest (uint8_t * buf, size_t len);
void ProcessData (uint8_t * buf, size_t len); 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: private:
void Terminate (); void Terminate();
void Established ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
bool SendQueue (); // returns true if ack block was sent
bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket ();
void ConnectAfterIntroduction ();
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len); void Established();
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
void SendSessionRequest (uint64_t token = 0); void ScheduleConnectTimer();
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 HandleConnectTimer(const boost::system::error_code &ecode);
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); void PostI2NPMessages(std::vector<std::shared_ptr<I2NPMessage> > msgs);
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); bool SendQueue(); // returns true if ack block was sent
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); bool SendFragmentedMessage(std::shared_ptr<I2NPMessage> msg);
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); void ResendHandshakePacket();
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); void ConnectAfterIntroduction();
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); void ProcessSessionRequest(Header &header, uint8_t *buf, size_t len);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (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: private:
SSU2Server& m_Server; SSU2Server &m_Server;
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys; std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState; std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice 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::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<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP 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_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_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived; bool m_IsDataReceived;
@ -347,13 +411,12 @@ namespace transport
size_t m_MaxPayloadSize; 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; 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; return data;
} }
} }
} }
#endif #endif

View file

@ -13,129 +13,105 @@
#include "SSU.h" #include "SSU.h"
#include "SSUData.h" #include "SSUData.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport void IncompleteMessage::AttachNextFragment(const uint8_t *fragment, size_t fragmentSize) {
{ if (msg->len + fragmentSize > msg->maxLen) {
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) LogPrint(eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
{ auto newMsg = NewI2NPMessage();
if (msg->len + fragmentSize > msg->maxLen)
{
LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *msg; *newMsg = *msg;
msg = newMsg; msg = newMsg;
} }
if (msg->Concat (fragment, fragmentSize) < fragmentSize) if (msg->Concat(fragment, fragmentSize) < fragmentSize)
LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen); LogPrint(eLogError, "SSU: I2NP buffer overflow ", msg->maxLen);
nextFragmentNum++; nextFragmentNum++;
} }
SSUData::SSUData (SSUSession& session): SSUData::SSUData(SSUSession &session) :
m_Session (session), m_ResendTimer (session.GetService ()), m_Session(session), m_ResendTimer(session.GetService()),
m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE), m_MaxPacketSize(session.IsV6() ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE),
m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0) m_PacketSize(m_MaxPacketSize), m_LastMessageReceivedTime(0) {
{
} }
SSUData::~SSUData () SSUData::~SSUData() {
{
} }
void SSUData::Start () void SSUData::Start() {
{
} }
void SSUData::Stop () void SSUData::Stop() {
{ m_ResendTimer.cancel();
m_ResendTimer.cancel (); m_IncompleteMessages.clear();
m_IncompleteMessages.clear (); m_SentMessages.clear();
m_SentMessages.clear (); m_ReceivedMessages.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; if (!remoteRouter) return;
auto ssuAddress = remoteRouter->GetSSUAddress (); auto ssuAddress = remoteRouter->GetSSUAddress();
if (ssuAddress && ssuAddress->ssu->mtu) if (ssuAddress && ssuAddress->ssu->mtu) {
{ if (m_Session.IsV6())
if (m_Session.IsV6 ())
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
else else
m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; 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 // make sure packet size multiple of 16
m_PacketSize >>= 4; m_PacketSize >>= 4;
m_PacketSize <<= 4; m_PacketSize <<= 4;
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize); LogPrint(eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
} } else {
else LogPrint(eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
{
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
m_PacketSize = m_MaxPacketSize; m_PacketSize = m_MaxPacketSize;
} }
} }
} }
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent) void SSUData::UpdatePacketSize(const i2p::data::IdentHash &remoteIdent) {
{ auto routerInfo = i2p::data::netdb.FindRouter(remoteIdent);
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
if (routerInfo) if (routerInfo)
AdjustPacketSize (routerInfo); AdjustPacketSize(routerInfo);
} }
void SSUData::ProcessSentMessageAck (uint32_t msgID) void SSUData::ProcessSentMessageAck(uint32_t msgID) {
{ auto it = m_SentMessages.find(msgID);
auto it = m_SentMessages.find (msgID); if (it != m_SentMessages.end()) {
if (it != m_SentMessages.end ()) m_SentMessages.erase(it);
{ if (m_SentMessages.empty())
m_SentMessages.erase (it); m_ResendTimer.cancel();
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
} }
} }
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag) void SSUData::ProcessAcks(uint8_t *&buf, uint8_t flag) {
{ if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) {
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{
// explicit ACKs // explicit ACKs
uint8_t numAcks =*buf; uint8_t numAcks = *buf;
buf++; buf++;
for (int i = 0; i < numAcks; i++) for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (bufbe32toh (buf+i*4)); ProcessSentMessageAck(bufbe32toh(buf + i * 4));
buf += numAcks*4; buf += numAcks * 4;
} }
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) {
{
// explicit ACK bitfields // explicit ACK bitfields
uint8_t numBitfields =*buf; uint8_t numBitfields = *buf;
buf++; buf++;
for (int i = 0; i < numBitfields; i++) for (int i = 0; i < numBitfields; i++) {
{ uint32_t msgID = bufbe32toh(buf);
uint32_t msgID = bufbe32toh (buf);
buf += 4; // msgID buf += 4; // msgID
auto it = m_SentMessages.find (msgID); auto it = m_SentMessages.find(msgID);
// process individual Ack bitfields // process individual Ack bitfields
bool isNonLast = false; bool isNonLast = false;
int fragment = 0; int fragment = 0;
do do {
{
uint8_t bitfield = *buf; uint8_t bitfield = *buf;
isNonLast = bitfield & 0x80; isNonLast = bitfield & 0x80;
bitfield &= 0x7F; // clear MSB bitfield &= 0x7F; // clear MSB
if (bitfield && it != m_SentMessages.end ()) if (bitfield && it != m_SentMessages.end()) {
{ int numSentFragments = it->second->fragments.size();
int numSentFragments = it->second->fragments.size ();
// process bits // process bits
uint8_t mask = 0x01; uint8_t mask = 0x01;
for (int j = 0; j < 7; j++) for (int j = 0; j < 7; j++) {
{ if (bitfield & mask) {
if (bitfield & mask)
{
if (fragment < numSentFragments) if (fragment < numSentFragments)
it->second->fragments[fragment] = nullptr; it->second->fragments[fragment] = nullptr;
} }
@ -144,195 +120,172 @@ namespace transport
} }
} }
buf++; buf++;
} } while (isNonLast);
while (isNonLast);
} }
} }
} }
void SSUData::ProcessFragments (uint8_t * buf) void SSUData::ProcessFragments(uint8_t *buf) {
{
uint8_t numFragments = *buf; // number of fragments uint8_t numFragments = *buf; // number of fragments
buf++; buf++;
for (int i = 0; i < numFragments; i++) for (int i = 0; i < numFragments; i++) {
{ uint32_t msgID = bufbe32toh(buf); // message ID
uint32_t msgID = bufbe32toh (buf); // message ID
buf += 4; buf += 4;
uint8_t frag[4] = {0}; uint8_t frag[4] = {0};
memcpy (frag + 1, buf, 3); memcpy(frag + 1, buf, 3);
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 uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16 bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) {
{ LogPrint(eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size");
return; return;
} }
// find message with msgID // find message with msgID
auto it = m_IncompleteMessages.find (msgID); auto it = m_IncompleteMessages.find(msgID);
if (it == m_IncompleteMessages.end ()) if (it == m_IncompleteMessages.end()) {
{
// create new message // create new message
auto msg = NewI2NPShortMessage (); auto msg = NewI2NPShortMessage();
msg->len -= I2NP_SHORT_HEADER_SIZE; msg->len -= I2NP_SHORT_HEADER_SIZE;
it = m_IncompleteMessages.insert (std::make_pair (msgID, it = m_IncompleteMessages.insert(std::make_pair(msgID,
m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first; m_Session.GetServer().GetIncompleteMessagesPool().AcquireShared(
std::move(msg)))).first;
} }
auto& incompleteMessage = it->second; auto &incompleteMessage = it->second;
// mark fragment as received // mark fragment as received
if (fragmentNum < 64) if (fragmentNum < 64)
incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum); incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum);
else else
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); LogPrint(eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
// handle current fragment // handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum) if (fragmentNum == incompleteMessage->nextFragmentNum) {
{
// expected fragment // expected fragment
incompleteMessage->AttachNextFragment (buf, fragmentSize); incompleteMessage->AttachNextFragment(buf, fragmentSize);
if (!isLast && !incompleteMessage->savedFragments.empty ()) if (!isLast && !incompleteMessage->savedFragments.empty()) {
{
// try saved fragments // try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();) for (auto it1 = incompleteMessage->savedFragments.begin();
{ it1 != incompleteMessage->savedFragments.end();) {
auto& savedFragment = *it1; auto &savedFragment = *it1;
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) {
{ incompleteMessage->AttachNextFragment(savedFragment->buf, savedFragment->len);
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
isLast = savedFragment->isLast; isLast = savedFragment->isLast;
incompleteMessage->savedFragments.erase (it1++); incompleteMessage->savedFragments.erase(it1++);
} } else
else
break; break;
} }
if (isLast) if (isLast)
LogPrint (eLogDebug, "SSU: Message ", msgID, " complete"); LogPrint(eLogDebug, "SSU: Message ", msgID, " complete");
} }
} } else {
else
{
if (fragmentNum < incompleteMessage->nextFragmentNum) if (fragmentNum < incompleteMessage->nextFragmentNum)
// duplicate fragment // duplicate fragment
LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored"); LogPrint(eLogWarning, "SSU: Duplicate fragment ", (int) fragmentNum, " of message ", msgID,
else ", ignored");
{ else {
// missing fragment // missing fragment
LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); LogPrint(eLogWarning, "SSU: Missing fragments from ", (int) incompleteMessage->nextFragmentNum,
auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast); " to ", fragmentNum - 1, " of message ", msgID);
if (incompleteMessage->savedFragments.insert (savedFragment).second) auto savedFragment = m_Session.GetServer().GetFragmentsPool().AcquireShared(fragmentNum, buf,
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); fragmentSize,
isLast);
if (incompleteMessage->savedFragments.insert(savedFragment).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch();
else 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; isLast = false;
} }
if (isLast) if (isLast) {
{
// delete incomplete message // delete incomplete message
auto msg = incompleteMessage->msg; auto msg = incompleteMessage->msg;
incompleteMessage->msg = nullptr; incompleteMessage->msg = nullptr;
m_IncompleteMessages.erase (msgID); m_IncompleteMessages.erase(msgID);
// process message // process message
SendMsgAck (msgID); SendMsgAck(msgID);
msg->FromSSU (msgID); msg->FromSSU(msgID);
if (m_Session.GetState () == eSessionStateEstablished) if (m_Session.GetState() == eSessionStateEstablished) {
{ if (!m_ReceivedMessages.count(msgID)) {
if (!m_ReceivedMessages.count (msgID)) m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch();
{ m_ReceivedMessages.emplace(msgID, m_LastMessageReceivedTime);
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); if (!msg->IsExpired()) {
m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime); m_Handler.PutNextMessage(std::move(msg));
if (!msg->IsExpired ()) } else
{ LogPrint(eLogDebug, "SSU: message expired");
m_Handler.PutNextMessage (std::move (msg)); } else
} LogPrint(eLogWarning, "SSU: Message ", msgID, " already received");
else } else {
LogPrint (eLogDebug, "SSU: message expired");
}
else
LogPrint (eLogWarning, "SSU: Message ", msgID, " already received");
}
else
{
// we expect DeliveryStatus // we expect DeliveryStatus
if (msg->GetTypeID () == eI2NPDeliveryStatus) if (msg->GetTypeID() == eI2NPDeliveryStatus) {
{ LogPrint(eLogDebug, "SSU: session established");
LogPrint (eLogDebug, "SSU: session established"); m_Session.Established();
m_Session.Established (); } else
LogPrint(eLogError, "SSU: unexpected message ", (int) msg->GetTypeID());
} }
else } else
LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ()); SendFragmentAck(msgID, incompleteMessage->receivedFragmentsBits);
}
}
else
SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits);
buf += fragmentSize; buf += fragmentSize;
} }
} }
void SSUData::FlushReceivedMessage () void SSUData::FlushReceivedMessage() {
{ m_Handler.Flush();
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 * start = buf;
uint8_t flag = *buf; uint8_t flag = *buf;
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 // process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag); ProcessAcks(buf, flag);
// extended data if presented // extended data if presented
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) {
{
uint8_t extendedDataSize = *buf; uint8_t extendedDataSize = *buf;
buf++; // size buf++; // size
LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present"); LogPrint(eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present");
buf += extendedDataSize; buf += extendedDataSize;
} }
// process data // process data
ProcessFragments (buf); ProcessFragments(buf);
} }
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg) void SSUData::Send(std::shared_ptr<i2p::I2NPMessage> msg) {
{ uint32_t msgID = msg->ToSSU();
uint32_t msgID = msg->ToSSU (); if (m_SentMessages.find(msgID) != m_SentMessages.end()) {
if (m_SentMessages.find (msgID) != m_SentMessages.end()) LogPrint(eLogWarning, "SSU: message ", msgID, " already sent");
{
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
return; return;
} }
if (m_SentMessages.empty ()) // schedule resend at first message only if (m_SentMessages.empty()) // schedule resend at first message only
ScheduleResend (); ScheduleResend();
auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ()); auto ret = m_SentMessages.emplace(msgID, m_Session.GetServer().GetSentMessagesPool().AcquireShared());
auto& sentMessage = ret.first->second; auto &sentMessage = ret.first->second;
if (ret.second) if (ret.second) {
{ sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch() + RESEND_INTERVAL;
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0; sentMessage->numResends = 0;
} }
auto& fragments = sentMessage->fragments; auto &fragments = sentMessage->fragments;
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) size_t payloadSize =
size_t len = msg->GetLength (); m_PacketSize - sizeof(SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
uint8_t * msgBuf = msg->GetSSUHeader (); size_t len = msg->GetLength();
uint8_t *msgBuf = msg->GetSSUHeader();
uint32_t fragmentNum = 0; uint32_t fragmentNum = 0;
while (len > 0 && fragmentNum <= 127) while (len > 0 && fragmentNum <= 127) {
{ auto fragment = m_Session.GetServer().GetFragmentsPool().AcquireShared();
auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared ();
fragment->fragmentNum = fragmentNum; 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 = DATA_FLAG_WANT_REPLY; // for compatibility
payload++; payload++;
*payload = 1; // always 1 message fragment per message *payload = 1; // always 1 message fragment per message
payload++; payload++;
htobe32buf (payload, msgID); htobe32buf(payload, msgID);
payload += 4; payload += 4;
bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max
size_t size = isLast ? len : payloadSize; size_t size = isLast ? len : payloadSize;
@ -341,176 +294,155 @@ namespace transport
fragmentInfo |= 0x010000; fragmentInfo |= 0x010000;
fragmentInfo |= size; fragmentInfo |= size;
fragmentInfo = htobe32 (fragmentInfo); fragmentInfo = htobe32(fragmentInfo);
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); memcpy(payload, (uint8_t * )(&fragmentInfo) + 1, 3);
payload += 3; payload += 3;
memcpy (payload, msgBuf, size); memcpy(payload, msgBuf, size);
size += payload - fragment->buf; size += payload - fragment->buf;
uint8_t rem = size & 0x0F; uint8_t rem = size & 0x0F;
if (rem) // make sure 16 bytes boundary if (rem) // make sure 16 bytes boundary
{ {
auto padding = 16 - rem; auto padding = 16 - rem;
memset (fragment->buf + size, 0, padding); memset(fragment->buf + size, 0, padding);
size += padding; size += padding;
} }
fragment->len = size; fragment->len = size;
fragments.push_back (fragment); fragments.push_back(fragment);
// encrypt message with session key // encrypt message with session key
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf); m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
try try {
{ m_Session.Send(buf, size);
m_Session.Send (buf, size);
} }
catch (boost::system::system_error& ec) catch (boost::system::system_error &ec) {
{ LogPrint(eLogWarning, "SSU: Can't send data fragment ", ec.what());
LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ());
} }
if (!isLast) if (!isLast) {
{
len -= payloadSize; len -= payloadSize;
msgBuf += payloadSize; msgBuf += payloadSize;
} } else
else
len = 0; len = 0;
fragmentNum++; 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 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 = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
payload++; payload++;
*payload = 1; // number of ACKs *payload = 1; // number of ACKs
payload++; payload++;
htobe32buf (payload, msgID); // msgID htobe32buf(payload, msgID); // msgID
payload += 4; payload += 4;
*payload = 0; // number of fragments *payload = 0; // number of fragments
// encrypt message with session key // encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, 48);
m_Session.Send (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; if (!bits) return;
uint8_t buf[64 + 18] = {0}; 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 = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
payload++; payload++;
*payload = 1; // number of ACK bitfields *payload = 1; // number of ACK bitfields
payload++; payload++;
// one ack // one ack
*(uint32_t *)(payload) = htobe32 (msgID); // msgID *(uint32_t * )(payload) = htobe32(msgID); // msgID
payload += 4; payload += 4;
size_t len = 0; size_t len = 0;
while (bits) while (bits) {
{
*payload = (bits & 0x7F); // next 7 bits *payload = (bits & 0x7F); // next 7 bits
bits >>= 7; bits >>= 7;
if (bits) *payload &= 0x80; // 0x80 means non-last if (bits) *payload &= 0x80; // 0x80 means non-last
payload++; len++; payload++;
len++;
} }
*payload = 0; // number of fragments *payload = 0; // number of fragments
len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4 len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4
// encrypt message with session key // encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send (buf, len); m_Session.Send(buf, len);
} }
void SSUData::ScheduleResend() void SSUData::ScheduleResend() {
{ m_ResendTimer.cancel();
m_ResendTimer.cancel (); m_ResendTimer.expires_from_now(boost::posix_time::seconds(RESEND_INTERVAL));
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
auto s = m_Session.shared_from_this(); auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) m_ResendTimer.async_wait(
{ s->m_Data.HandleResendTimer (ecode); }); [s](const boost::system::error_code &ecode) { s->m_Data.HandleResendTimer(ecode); });
} }
void SSUData::HandleResendTimer (const boost::system::error_code& ecode) void SSUData::HandleResendTimer(const boost::system::error_code &ecode) {
{ if (ecode != boost::asio::error::operation_aborted) {
if (ecode != boost::asio::error::operation_aborted)
{
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch();
int numResent = 0; int numResent = 0;
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) for (auto it = m_SentMessages.begin(); it != m_SentMessages.end();) {
{ if (ts >= it->second->nextResendTime) {
if (ts >= it->second->nextResendTime) if (it->second->numResends < MAX_NUM_RESENDS) {
{ for (auto &f: it->second->fragments)
if (it->second->numResends < MAX_NUM_RESENDS) if (f) {
{ try {
for (auto& f: it->second->fragments) m_Session.FillHeaderAndEncrypt(PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
if (f) m_Session.Send(buf, f->len); // resend
{
try
{
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
m_Session.Send (buf, f->len); // resend
numResent++; numResent++;
} }
catch (boost::system::system_error& ec) catch (boost::system::system_error &ec) {
{ LogPrint(eLogWarning, "SSU: Can't resend message ", it->first,
LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ()); " data fragment: ", ec.what());
} }
} }
it->second->numResends++; 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; ++it;
} }
else if (m_SentMessages.empty()) return; // nothing to resend
{
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 (numResent < MAX_OUTGOING_WINDOW_SIZE) if (numResent < MAX_OUTGOING_WINDOW_SIZE)
ScheduleResend (); ScheduleResend();
else else {
{ LogPrint(eLogError, "SSU: resend window exceeds max size. Session terminated");
LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated"); m_Session.Close();
m_Session.Close ();
} }
} }
} }
void SSUData::CleanUp (uint64_t ts) void SSUData::CleanUp(uint64_t ts) {
{ for (auto it = m_IncompleteMessages.begin(); it != m_IncompleteMessages.end();) {
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 ",
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted");
{ it = m_IncompleteMessages.erase(it);
LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); } else
it = m_IncompleteMessages.erase (it);
}
else
++it; ++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 // decay
m_ReceivedMessages.clear (); m_ReceivedMessages.clear();
else else {
{
// delete old received messages // 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) if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT)
it = m_ReceivedMessages.erase (it); it = m_ReceivedMessages.erase(it);
else else
++it; ++it;
} }
} }
} }
} }
} }

View file

@ -21,10 +21,8 @@
#include "RouterInfo.h" #include "RouterInfo.h"
#include "TransportSession.h" #include "TransportSession.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const size_t SSU_MTU_V4 = 1484; const size_t SSU_MTU_V4 = 1484;
const size_t SSU_MTU_V6 = 1488; 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 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_ACK_BITFIELDS_INCLUDED = 0x40;
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
struct Fragment struct Fragment {
{
int fragmentNum; int fragmentNum;
size_t len; size_t len;
bool isLast; bool isLast;
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
Fragment () = default; Fragment() = default;
Fragment (int n, const uint8_t * b, int l, bool last):
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); }; Fragment(int n, const uint8_t *b, int l, bool last) :
fragmentNum(n), len(l), isLast(last) { memcpy(buf, b, len); };
}; };
struct FragmentCmp struct FragmentCmp {
{ bool operator()(const std::shared_ptr<Fragment> &f1, const std::shared_ptr<Fragment> &f2) const {
bool operator() (const std::shared_ptr<Fragment>& f1, const std::shared_ptr<Fragment>& f2) const
{
return f1->fragmentNum < f2->fragmentNum; return f1->fragmentNum < f2->fragmentNum;
}; };
}; };
struct IncompleteMessage struct IncompleteMessage {
{
std::shared_ptr<I2NPMessage> msg; std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum; int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds uint32_t lastFragmentInsertTime; // in seconds
uint64_t receivedFragmentsBits; uint64_t receivedFragmentsBits;
std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments; std::set<std::shared_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (std::shared_ptr<I2NPMessage>&& m): msg (m), nextFragmentNum (0), IncompleteMessage(std::shared_ptr<I2NPMessage> &&m) : msg(m), nextFragmentNum(0),
lastFragmentInsertTime (0), receivedFragmentsBits (0) {}; lastFragmentInsertTime(0),
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); receivedFragmentsBits(0) {};
void AttachNextFragment(const uint8_t *fragment, size_t fragmentSize);
}; };
struct SentMessage struct SentMessage {
{
std::vector<std::shared_ptr<Fragment> > fragments; std::vector<std::shared_ptr<Fragment> > fragments;
uint32_t nextResendTime; // in seconds uint32_t nextResendTime; // in seconds
int numResends; int numResends;
}; };
class SSUSession; class SSUSession;
class SSUData
{ class SSUData {
public: public:
SSUData (SSUSession& session); SSUData(SSUSession &session);
~SSUData ();
void Start (); ~SSUData();
void Stop ();
void CleanUp (uint64_t ts);
void ProcessMessage (uint8_t * buf, size_t len); void Start();
void FlushReceivedMessage ();
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
void AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter); void Stop();
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
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: private:
void SendMsgAck (uint32_t msgID); void SendMsgAck(uint32_t msgID);
void SendFragmentAck (uint32_t msgID, uint64_t bits);
void ProcessAcks (uint8_t *& buf, uint8_t flag);
void ProcessFragments (uint8_t * buf);
void ProcessSentMessageAck (uint32_t msgID);
void ScheduleResend (); void SendFragmentAck(uint32_t msgID, uint64_t bits);
void HandleResendTimer (const boost::system::error_code& ecode);
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: private:
SSUSession& m_Session; SSUSession &m_Session;
std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages; std::map<uint32_t, std::shared_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages; std::map<uint32_t, std::shared_ptr<SentMessage> > m_SentMessages;
std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds std::unordered_map<uint32_t, uint64_t> m_ReceivedMessages; // msgID -> timestamp in seconds
@ -125,7 +132,7 @@ namespace transport
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
uint32_t m_LastMessageReceivedTime; // in second uint32_t m_LastMessageReceivedTime; // in second
}; };
} }
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -17,20 +17,19 @@
#include "TransportSession.h" #include "TransportSession.h"
#include "SSUData.h" #include "SSUData.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04; const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04;
struct SSUHeader
{ struct SSUHeader {
uint8_t mac[16]; uint8_t mac[16];
uint8_t iv[16]; uint8_t iv[16];
uint8_t flag; uint8_t flag;
uint8_t time[4]; uint8_t time[4];
uint8_t GetPayloadType () const { return flag >> 4; }; uint8_t GetPayloadType() const { return flag >> 4; };
bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
bool IsExtendedOptions() const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; };
}; };
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
@ -53,8 +52,7 @@ namespace transport
// extended options // extended options
const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001; const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001;
enum SessionState enum SessionState {
{
eSessionStateUnknown, eSessionStateUnknown,
eSessionStateIntroduced, eSessionStateIntroduced,
eSessionStateEstablished, eSessionStateEstablished,
@ -62,8 +60,7 @@ namespace transport
eSessionStateFailed eSessionStateFailed
}; };
enum PeerTestParticipant enum PeerTestParticipant {
{
ePeerTestParticipantUnknown = 0, ePeerTestParticipantUnknown = 0,
ePeerTestParticipantAlice1, ePeerTestParticipantAlice1,
ePeerTestParticipantAlice2, ePeerTestParticipantAlice2,
@ -72,87 +69,132 @@ namespace transport
}; };
class SSUServer; 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: 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); 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 ProcessNextMessage(uint8_t *buf, size_t len, const boost::asio::ip::udp::endpoint &senderEndpoint);
void WaitForConnect ();
void Introduce (const i2p::data::RouterInfo::Introducer& introducer, ~SSUSession();
void Connect();
void WaitForConnect();
void Introduce(const i2p::data::RouterInfo::Introducer &introducer,
std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie std::shared_ptr<const i2p::data::RouterInfo> to); // Alice to Charlie
void WaitForIntroduction (); void WaitForIntroduction();
void Close ();
void Done ();
void Failed ();
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
SSUServer& GetServer () { return m_Server; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; void Close();
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void SendPeerTest (); // Alice
SessionState GetState () const { return m_State; }; void Done();
size_t GetNumSentBytes () const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void SendKeepAlive (); void Failed();
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
void FlushData (); const boost::asio::ip::udp::endpoint &GetRemoteEndpoint() { return m_RemoteEndpoint; };
void CleanUp (uint64_t ts);
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: private:
boost::asio::io_service& GetService (); 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);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey, void CreateAESandMacKey(const uint8_t *pubKey);
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 (); 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: private:
friend class SSUData; // TODO: change in later friend class SSUData; // TODO: change in later
SSUServer& m_Server; SSUServer &m_Server;
const boost::asio::ip::udp::endpoint m_RemoteEndpoint; const boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_ConnectTimer; boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest; bool m_IsPeerTest;
@ -168,10 +210,10 @@ namespace transport
SSUData m_Data; SSUData m_Data;
bool m_IsDataReceived; bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only 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 std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
}; };
} }
} }
#endif #endif

View file

@ -10,10 +10,8 @@
#include "Log.h" #include "Log.h"
#include "Signature.h" #include "Signature.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto
{
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EDDSA25519Verifier::EDDSA25519Verifier () EDDSA25519Verifier::EDDSA25519Verifier ()
{ {
@ -38,63 +36,58 @@ namespace crypto
} }
#else #else
EDDSA25519Verifier::EDDSA25519Verifier ()
{ EDDSA25519Verifier::EDDSA25519Verifier() {
} }
EDDSA25519Verifier::~EDDSA25519Verifier () EDDSA25519Verifier::~EDDSA25519Verifier() {
{
} }
void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) void EDDSA25519Verifier::SetPublicKey(const uint8_t *signingKey) {
{ memcpy(m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH);
memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH); BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); m_PublicKey = GetEd25519()->DecodePublicKey(m_PublicKeyEncoded, ctx);
m_PublicKey = GetEd25519 ()->DecodePublicKey (m_PublicKeyEncoded, ctx); BN_CTX_free(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]; uint8_t digest[64];
SHA512_CTX ctx; SHA512_CTX ctx;
SHA512_Init (&ctx); SHA512_Init(&ctx);
SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R SHA512_Update(&ctx, signature, EDDSA25519_SIGNATURE_LENGTH / 2); // R
SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key SHA512_Update(&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data SHA512_Update(&ctx, buf, len); // data
SHA512_Final (digest, &ctx); SHA512_Final(digest, &ctx);
return GetEd25519 ()->Verify (m_PublicKey, digest, signature); return GetEd25519()->Verify(m_PublicKey, digest, signature);
} }
#endif #endif
EDDSA25519SignerCompat::EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) EDDSA25519SignerCompat::EDDSA25519SignerCompat(const uint8_t *signingPrivateKey,
{ const uint8_t *signingPublicKey) {
// expand key // expand key
Ed25519::ExpandPrivateKey (signingPrivateKey, m_ExpandedPrivateKey); Ed25519::ExpandPrivateKey(signingPrivateKey, m_ExpandedPrivateKey);
// generate and encode public key // generate and encode public key
BN_CTX * ctx = BN_CTX_new (); BN_CTX *ctx = BN_CTX_new();
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); auto publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, 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 // 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 m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit
publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); publicKey = GetEd25519()->GeneratePublicKey(m_ExpandedPrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, 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 void EDDSA25519SignerCompat::Sign(const uint8_t *buf, int len, uint8_t *signature) const {
{ GetEd25519()->Sign(m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature);
} }
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
@ -136,5 +129,5 @@ namespace crypto
} }
} }
#endif #endif
} }
} }

View file

@ -19,241 +19,232 @@
#include "Ed25519.h" #include "Ed25519.h"
#include "Gost.h" #include "Gost.h"
namespace i2p namespace i2p {
{ namespace crypto {
namespace crypto class Verifier {
{
class Verifier
{
public: public:
virtual ~Verifier () {}; 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 bool Verify(const uint8_t *buf, size_t len, const uint8_t *signature) const = 0;
virtual size_t GetSignatureLen () const = 0;
virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; }; virtual size_t GetPublicKeyLen() const = 0;
virtual void SetPublicKey (const uint8_t * signingKey) = 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: public:
virtual ~Signer () {}; virtual ~Signer() {};
virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0;
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_PUBLIC_KEY_LENGTH = 128;
const size_t DSA_SIGNATURE_LENGTH = 40; const size_t DSA_SIGNATURE_LENGTH = 40;
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2; const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH / 2;
class DSAVerifier: public Verifier
{ class DSAVerifier : public Verifier {
public: public:
DSAVerifier () DSAVerifier() {
{ m_PublicKey = CreateDSA();
m_PublicKey = CreateDSA ();
} }
void SetPublicKey (const uint8_t * signingKey) void SetPublicKey(const uint8_t *signingKey) {
{ DSA_set0_key(m_PublicKey, BN_bin2bn(signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
} }
~DSAVerifier () ~DSAVerifier() {
{ DSA_free(m_PublicKey);
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 // calculate SHA1 digest
uint8_t digest[20]; uint8_t digest[20];
SHA1 (buf, len, digest); SHA1(buf, len, digest);
// signature // signature
DSA_SIG * sig = DSA_SIG_new(); DSA_SIG *sig = DSA_SIG_new();
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL)); DSA_SIG_set0(sig, BN_bin2bn(signature, DSA_SIGNATURE_LENGTH / 2, NULL),
BN_bin2bn(signature + DSA_SIGNATURE_LENGTH / 2, DSA_SIGNATURE_LENGTH / 2, NULL));
// DSA verification // 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); DSA_SIG_free(sig);
return ret; return ret;
} }
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; size_t GetPublicKeyLen() const { return DSA_PUBLIC_KEY_LENGTH; };
size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; };
size_t GetSignatureLen() const { return DSA_SIGNATURE_LENGTH; };
private: private:
DSA * m_PublicKey; DSA *m_PublicKey;
}; };
class DSASigner: public Signer class DSASigner : public Signer {
{
public: 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 // openssl 1.1 always requires DSA public key even for signing
{ {
m_PrivateKey = CreateDSA (); m_PrivateKey = CreateDSA();
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL)); DSA_set0_key(m_PrivateKey, BN_bin2bn(signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL),
BN_bin2bn(signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
} }
~DSASigner () ~DSASigner() {
{ DSA_free(m_PrivateKey);
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]; uint8_t digest[20];
SHA1 (buf, len, digest); SHA1(buf, len, digest);
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey); DSA_SIG *sig = DSA_do_sign(digest, 20, m_PrivateKey);
const BIGNUM * r, * s; const BIGNUM *r, *s;
DSA_SIG_get0 (sig, &r, &s); DSA_SIG_get0(sig, &r, &s);
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2); bn2buf(r, signature, DSA_SIGNATURE_LENGTH / 2);
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2); bn2buf(s, signature + DSA_SIGNATURE_LENGTH / 2, DSA_SIGNATURE_LENGTH / 2);
DSA_SIG_free(sig); DSA_SIG_free(sig);
} }
private: private:
DSA * m_PrivateKey; DSA *m_PrivateKey;
}; };
inline void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateDSARandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ DSA *dsa = CreateDSA();
DSA * dsa = CreateDSA (); DSA_generate_key(dsa);
DSA_generate_key (dsa); const BIGNUM *pub_key, *priv_key;
const BIGNUM * pub_key, * priv_key;
DSA_get0_key(dsa, &pub_key, &priv_key); DSA_get0_key(dsa, &pub_key, &priv_key);
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); bn2buf(priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH); bn2buf(pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
DSA_free (dsa); DSA_free(dsa);
} }
struct SHA256Hash struct SHA256Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) SHA256(buf, len, digest);
{
SHA256 (buf, len, digest);
} }
enum { hashLen = 32 }; enum {
hashLen = 32
};
}; };
struct SHA384Hash struct SHA384Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) SHA384(buf, len, digest);
{
SHA384 (buf, len, digest);
} }
enum { hashLen = 48 }; enum {
hashLen = 48
};
}; };
struct SHA512Hash struct SHA512Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) SHA512(buf, len, digest);
{
SHA512 (buf, len, digest);
} }
enum { hashLen = 64 }; enum {
hashLen = 64
};
}; };
// EcDSA // EcDSA
template<typename Hash, int curve, size_t keyLen> template<typename Hash, int curve, size_t keyLen>
class ECDSAVerifier: public Verifier class ECDSAVerifier : public Verifier {
{
public: public:
ECDSAVerifier () ECDSAVerifier() {
{ m_PublicKey = EC_KEY_new_by_curve_name(curve);
m_PublicKey = EC_KEY_new_by_curve_name (curve);
} }
void SetPublicKey (const uint8_t * signingKey) void SetPublicKey(const uint8_t *signingKey) {
{ BIGNUM *x = BN_bin2bn(signingKey, keyLen / 2, NULL);
BIGNUM * x = BN_bin2bn (signingKey, keyLen/2, NULL); BIGNUM *y = BN_bin2bn(signingKey + keyLen / 2, keyLen / 2, NULL);
BIGNUM * y = BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL); EC_KEY_set_public_key_affine_coordinates(m_PublicKey, x, y);
EC_KEY_set_public_key_affine_coordinates (m_PublicKey, x, y); BN_free(x);
BN_free (x); BN_free (y); BN_free(y);
} }
~ECDSAVerifier () ~ECDSAVerifier() {
{ EC_KEY_free(m_PublicKey);
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]; uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest); Hash::CalculateHash(buf, len, digest);
ECDSA_SIG * sig = ECDSA_SIG_new(); ECDSA_SIG *sig = ECDSA_SIG_new();
auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL); auto r = BN_bin2bn(signature, GetSignatureLen() / 2, NULL);
auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL); auto s = BN_bin2bn(signature + GetSignatureLen() / 2, GetSignatureLen() / 2, NULL);
ECDSA_SIG_set0(sig, r, s); ECDSA_SIG_set0(sig, r, s);
// ECDSA verification // 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); ECDSA_SIG_free(sig);
return ret; return ret;
} }
size_t GetPublicKeyLen () const { return keyLen; }; size_t GetPublicKeyLen() const { return keyLen; };
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
size_t GetSignatureLen() const { return keyLen; }; // signature length = key length
private: private:
EC_KEY * m_PublicKey; EC_KEY *m_PublicKey;
}; };
template<typename Hash, int curve, size_t keyLen> template<typename Hash, int curve, size_t keyLen>
class ECDSASigner: public Signer class ECDSASigner : public Signer {
{
public: public:
ECDSASigner (const uint8_t * signingPrivateKey) ECDSASigner(const uint8_t *signingPrivateKey) {
{ m_PrivateKey = EC_KEY_new_by_curve_name(curve);
m_PrivateKey = EC_KEY_new_by_curve_name (curve); EC_KEY_set_private_key(m_PrivateKey, BN_bin2bn(signingPrivateKey, keyLen / 2, NULL));
EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL));
} }
~ECDSASigner () ~ECDSASigner() {
{ EC_KEY_free(m_PrivateKey);
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]; uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest); Hash::CalculateHash(buf, len, digest);
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey); ECDSA_SIG *sig = ECDSA_do_sign(digest, Hash::hashLen, m_PrivateKey);
const BIGNUM * r, * s; const BIGNUM *r, *s;
ECDSA_SIG_get0 (sig, &r, &s); ECDSA_SIG_get0(sig, &r, &s);
// signatureLen = keyLen // signatureLen = keyLen
bn2buf (r, signature, keyLen/2); bn2buf(r, signature, keyLen / 2);
bn2buf (s, signature + keyLen/2, keyLen/2); bn2buf(s, signature + keyLen / 2, keyLen / 2);
ECDSA_SIG_free(sig); ECDSA_SIG_free(sig);
} }
private: private:
EC_KEY * m_PrivateKey; EC_KEY *m_PrivateKey;
}; };
inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void
{ CreateECDSARandomKeys(int curve, size_t keyLen, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve); EC_KEY *signingKey = EC_KEY_new_by_curve_name(curve);
EC_KEY_generate_key (signingKey); EC_KEY_generate_key(signingKey);
bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2); bn2buf(EC_KEY_get0_private_key(signingKey), signingPrivateKey, keyLen / 2);
BIGNUM * x = BN_new(), * y = BN_new(); BIGNUM *x = BN_new(), *y = BN_new();
EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey), EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(signingKey),
EC_KEY_get0_public_key (signingKey), x, y, NULL); EC_KEY_get0_public_key(signingKey), x, y, NULL);
bn2buf (x, signingPublicKey, keyLen/2); bn2buf(x, signingPublicKey, keyLen / 2);
bn2buf (y, signingPublicKey + keyLen/2, keyLen/2); bn2buf(y, signingPublicKey + keyLen / 2, keyLen / 2);
BN_free (x); BN_free (y); BN_free(x);
EC_KEY_free (signingKey); BN_free(y);
EC_KEY_free(signingKey);
} }
// ECDSA_SHA256_P256 // ECDSA_SHA256_P256
@ -261,9 +252,8 @@ namespace crypto
typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier; typedef ECDSAVerifier<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Verifier;
typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer; typedef ECDSASigner<SHA256Hash, NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH> ECDSAP256Signer;
inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateECDSAP256RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ CreateECDSARandomKeys(NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
} }
// ECDSA_SHA384_P384 // ECDSA_SHA384_P384
@ -271,9 +261,8 @@ namespace crypto
typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier; typedef ECDSAVerifier<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Verifier;
typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer; typedef ECDSASigner<SHA384Hash, NID_secp384r1, ECDSAP384_KEY_LENGTH> ECDSAP384Signer;
inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateECDSAP384RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ CreateECDSARandomKeys(NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
} }
// ECDSA_SHA512_P521 // ECDSA_SHA512_P521
@ -281,25 +270,26 @@ namespace crypto
typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier; typedef ECDSAVerifier<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Verifier;
typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer; typedef ECDSASigner<SHA512Hash, NID_secp521r1, ECDSAP521_KEY_LENGTH> ECDSAP521Signer;
inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateECDSAP521RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ CreateECDSARandomKeys(NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
} }
// EdDSA // EdDSA
class EDDSA25519Verifier: public Verifier class EDDSA25519Verifier : public Verifier {
{
public: public:
EDDSA25519Verifier (); EDDSA25519Verifier();
void SetPublicKey (const uint8_t * signingKey);
~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; }; ~EDDSA25519Verifier();
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
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: private:
@ -311,16 +301,17 @@ namespace crypto
#endif #endif
}; };
class EDDSA25519SignerCompat: public Signer class EDDSA25519SignerCompat : public Signer {
{
public: public:
EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); EDDSA25519SignerCompat(const uint8_t *signingPrivateKey, const uint8_t *signingPublicKey = nullptr);
// we pass signingPublicKey to check if it matches private key
~EDDSA25519SignerCompat ();
void Sign (const uint8_t * buf, int len, uint8_t * signature) const; // we pass signingPublicKey to check if it matches private key
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation ~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: private:
@ -350,8 +341,7 @@ namespace crypto
#endif #endif
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateEDDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EVP_PKEY *pkey = NULL; EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, 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_get_raw_private_key (pkey, signingPrivateKey, &len);
EVP_PKEY_free (pkey); EVP_PKEY_free (pkey);
#else #else
RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); RAND_bytes(signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
EDDSA25519Signer signer (signingPrivateKey); EDDSA25519Signer signer(signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH);
#endif #endif
} }
// ГОСТ Р 34.11 // ГОСТ Р 34.11
struct GOSTR3411_256_Hash struct GOSTR3411_256_Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) GOSTR3411_2012_256(buf, len, digest);
{
GOSTR3411_2012_256 (buf, len, digest);
} }
enum { hashLen = 32 }; enum {
hashLen = 32
};
}; };
struct GOSTR3411_512_Hash struct GOSTR3411_512_Hash {
{ static void CalculateHash(const uint8_t *buf, size_t len, uint8_t *digest) {
static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) GOSTR3411_2012_512(buf, len, digest);
{
GOSTR3411_2012_512 (buf, len, digest);
} }
enum { hashLen = 64 }; enum {
hashLen = 64
};
}; };
// ГОСТ Р 34.10 // ГОСТ Р 34.10
@ -397,97 +387,102 @@ namespace crypto
const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128; const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128;
template<typename Hash> template<typename Hash>
class GOSTR3410Verifier: public Verifier class GOSTR3410Verifier : public Verifier {
{
public: public:
enum { keyLen = Hash::hashLen }; enum {
keyLen = Hash::hashLen
};
GOSTR3410Verifier (GOSTR3410ParamSet paramSet): GOSTR3410Verifier(GOSTR3410ParamSet paramSet) :
m_ParamSet (paramSet), m_PublicKey (nullptr) m_ParamSet(paramSet), m_PublicKey(nullptr) {
{
} }
void SetPublicKey (const uint8_t * signingKey) void SetPublicKey(const uint8_t *signingKey) {
{ BIGNUM *x = BN_bin2bn(signingKey, GetPublicKeyLen() / 2, NULL);
BIGNUM * x = BN_bin2bn (signingKey, GetPublicKeyLen ()/2, NULL); BIGNUM *y = BN_bin2bn(signingKey + GetPublicKeyLen() / 2, GetPublicKeyLen() / 2, NULL);
BIGNUM * y = BN_bin2bn (signingKey + GetPublicKeyLen ()/2, GetPublicKeyLen ()/2, NULL); m_PublicKey = GetGOSTR3410Curve(m_ParamSet)->CreatePoint(x, y);
m_PublicKey = GetGOSTR3410Curve (m_ParamSet)->CreatePoint (x, y); BN_free(x);
BN_free (x); BN_free (y); BN_free(y);
}
~GOSTR3410Verifier ()
{
if (m_PublicKey) EC_POINT_free (m_PublicKey);
} }
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]; uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest); Hash::CalculateHash(buf, len, digest);
BIGNUM * d = BN_bin2bn (digest, Hash::hashLen, nullptr); BIGNUM *d = BN_bin2bn(digest, Hash::hashLen, nullptr);
BIGNUM * r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL); BIGNUM *r = BN_bin2bn(signature, GetSignatureLen() / 2, NULL);
BIGNUM * s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL); BIGNUM *s = BN_bin2bn(signature + GetSignatureLen() / 2, GetSignatureLen() / 2, NULL);
bool ret = GetGOSTR3410Curve (m_ParamSet)->Verify (m_PublicKey, d, r, s); bool ret = GetGOSTR3410Curve(m_ParamSet)->Verify(m_PublicKey, d, r, s);
BN_free (d); BN_free (r); BN_free (s); BN_free(d);
BN_free(r);
BN_free(s);
return ret; return ret;
} }
size_t GetPublicKeyLen () const { return keyLen*2; } size_t GetPublicKeyLen() const { return keyLen * 2; }
size_t GetSignatureLen () const { return keyLen*2; }
size_t GetSignatureLen() const { return keyLen * 2; }
private: private:
GOSTR3410ParamSet m_ParamSet; GOSTR3410ParamSet m_ParamSet;
EC_POINT * m_PublicKey; EC_POINT *m_PublicKey;
}; };
template<typename Hash> template<typename Hash>
class GOSTR3410Signer: public Signer class GOSTR3410Signer : public Signer {
{
public: public:
enum { keyLen = Hash::hashLen }; enum {
keyLen = Hash::hashLen
};
GOSTR3410Signer (GOSTR3410ParamSet paramSet, const uint8_t * signingPrivateKey): GOSTR3410Signer(GOSTR3410ParamSet paramSet, const uint8_t *signingPrivateKey) :
m_ParamSet (paramSet) m_ParamSet(paramSet) {
{ m_PrivateKey = BN_bin2bn(signingPrivateKey, keyLen, nullptr);
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]; uint8_t digest[Hash::hashLen];
Hash::CalculateHash (buf, len, digest); Hash::CalculateHash(buf, len, digest);
BIGNUM * d = BN_bin2bn (digest, Hash::hashLen, nullptr); BIGNUM *d = BN_bin2bn(digest, Hash::hashLen, nullptr);
BIGNUM * r = BN_new (), * s = BN_new (); BIGNUM *r = BN_new(), *s = BN_new();
GetGOSTR3410Curve (m_ParamSet)->Sign (m_PrivateKey, d, r, s); GetGOSTR3410Curve(m_ParamSet)->Sign(m_PrivateKey, d, r, s);
bn2buf (r, signature, keyLen); bn2buf(r, signature, keyLen);
bn2buf (s, signature + keyLen, keyLen); bn2buf(s, signature + keyLen, keyLen);
BN_free (d); BN_free (r); BN_free (s); BN_free(d);
BN_free(r);
BN_free(s);
} }
private: private:
GOSTR3410ParamSet m_ParamSet; GOSTR3410ParamSet m_ParamSet;
BIGNUM * m_PrivateKey; BIGNUM *m_PrivateKey;
}; };
inline void CreateGOSTR3410RandomKeys (GOSTR3410ParamSet paramSet, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void
{ CreateGOSTR3410RandomKeys(GOSTR3410ParamSet paramSet, uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
const auto& curve = GetGOSTR3410Curve (paramSet); const auto &curve = GetGOSTR3410Curve(paramSet);
auto keyLen = curve->GetKeyLen (); auto keyLen = curve->GetKeyLen();
RAND_bytes (signingPrivateKey, keyLen); RAND_bytes(signingPrivateKey, keyLen);
BIGNUM * priv = BN_bin2bn (signingPrivateKey, keyLen, nullptr); BIGNUM *priv = BN_bin2bn(signingPrivateKey, keyLen, nullptr);
auto pub = curve->MulP (priv); auto pub = curve->MulP(priv);
BN_free (priv); BN_free(priv);
BIGNUM * x = BN_new (), * y = BN_new (); BIGNUM *x = BN_new(), *y = BN_new();
curve->GetXY (pub, x, y); curve->GetXY(pub, x, y);
EC_POINT_free (pub); EC_POINT_free(pub);
bn2buf (x, signingPublicKey, keyLen); bn2buf(x, signingPublicKey, keyLen);
bn2buf (y, signingPublicKey + keyLen, keyLen); bn2buf(y, signingPublicKey + keyLen, keyLen);
BN_free (x); BN_free (y); BN_free(x);
BN_free(y);
} }
typedef GOSTR3410Verifier<GOSTR3411_256_Hash> GOSTR3410_256_Verifier; typedef GOSTR3410Verifier<GOSTR3411_256_Hash> GOSTR3410_256_Verifier;
@ -497,26 +492,25 @@ namespace crypto
// RedDSA // RedDSA
typedef EDDSA25519Verifier RedDSA25519Verifier; typedef EDDSA25519Verifier RedDSA25519Verifier;
class RedDSA25519Signer: public Signer
{ class RedDSA25519Signer : public Signer {
public: public:
RedDSA25519Signer (const uint8_t * signingPrivateKey) RedDSA25519Signer(const uint8_t *signingPrivateKey) {
{ memcpy(m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
memcpy (m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); BN_CTX *ctx = BN_CTX_new();
BN_CTX * ctx = BN_CTX_new (); auto publicKey = GetEd25519()->GeneratePublicKey(m_PrivateKey, ctx);
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_PrivateKey, ctx); GetEd25519()->EncodePublicKey(publicKey, m_PublicKeyEncoded, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); BN_CTX_free(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);
} }
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: private:
@ -524,13 +518,12 @@ namespace crypto
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
}; };
inline void CreateRedDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateRedDSA25519RandomKeys(uint8_t *signingPrivateKey, uint8_t *signingPublicKey) {
{ GetEd25519()->CreateRedDSAPrivateKey(signingPrivateKey);
GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey); RedDSA25519Signer signer(signingPrivateKey);
RedDSA25519Signer signer (signingPrivateKey); memcpy(signingPublicKey, signer.GetPublicKey(), EDDSA25519_PUBLIC_KEY_LENGTH);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); }
} }
}
} }
#endif #endif

View file

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

File diff suppressed because it is too large Load diff

View file

@ -27,14 +27,11 @@
#include "Tunnel.h" #include "Tunnel.h"
#include "util.h" // MemoryPool #include "util.h" // MemoryPool
namespace i2p namespace i2p {
{ namespace client {
namespace client
{
class ClientDestination; class ClientDestination;
} }
namespace stream namespace stream {
{
const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001;
const uint16_t PACKET_FLAG_CLOSE = 0x0002; const uint16_t PACKET_FLAG_CLOSE = 0x0002;
const uint16_t PACKET_FLAG_RESET = 0x0004; const uint16_t PACKET_FLAG_RESET = 0x0004;
@ -64,82 +61,103 @@ namespace stream
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
struct Packet struct Packet {
{
size_t len, offset; size_t len, offset;
uint8_t buf[MAX_PACKET_SIZE]; uint8_t buf[MAX_PACKET_SIZE];
uint64_t sendTime; uint64_t sendTime;
Packet (): len (0), offset (0), sendTime (0) {}; Packet() : len(0), offset(0), sendTime(0) {};
uint8_t * GetBuffer () { return buf + offset; };
size_t GetLength () const { return len - offset; };
uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; uint8_t *GetBuffer() { return buf + offset; };
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; }; size_t GetLength() const { return len - offset; };
bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; };
bool IsEcho () const { return GetFlags () & PACKET_FLAG_ECHO; }; 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 struct PacketCmp {
{ bool operator()(const Packet *p1, const Packet *p2) const {
bool operator() (const Packet * p1, const Packet * p2) const return p1->GetSeqn() < p2->GetSeqn();
{
return p1->GetSeqn () < p2->GetSeqn ();
}; };
}; };
typedef std::function<void (const boost::system::error_code& ecode)> SendHandler; typedef std::function<void(const boost::system::error_code &ecode)> SendHandler;
struct SendBuffer
{ struct SendBuffer {
uint8_t * buf; uint8_t *buf;
size_t len, offset; size_t len, offset;
SendHandler handler; SendHandler handler;
SendBuffer (const uint8_t * b, size_t l, SendHandler h): SendBuffer(const uint8_t *b, size_t l, SendHandler h) :
len(l), offset (0), handler(h) len(l), offset(0), handler(h) {
{
buf = new uint8_t[len]; 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]; buf = new uint8_t[len];
} }
~SendBuffer ()
{ ~SendBuffer() {
delete[] buf; 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; }; size_t GetRemainingSize() const { return len - offset; };
void Cancel () { if (handler) handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); handler = nullptr; };
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: public:
SendBufferQueue (): m_Size (0) {}; SendBufferQueue() : m_Size(0) {};
~SendBufferQueue () { CleanUp (); };
void Add (const uint8_t * buf, size_t len, SendHandler handler); ~SendBufferQueue() { CleanUp(); };
void Add (std::shared_ptr<SendBuffer> buf);
size_t Get (uint8_t * buf, size_t len); void Add(const uint8_t *buf, size_t len, SendHandler handler);
size_t GetSize () const { return m_Size; };
bool IsEmpty () const { return m_Buffers.empty (); }; void Add(std::shared_ptr<SendBuffer> buf);
void CleanUp ();
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: private:
@ -147,8 +165,7 @@ namespace stream
size_t m_Size; size_t m_Size;
}; };
enum StreamStatus enum StreamStatus {
{
eStreamStatusNew = 0, eStreamStatusNew = 0,
eStreamStatusOpen, eStreamStatusOpen,
eStreamStatusReset, eStreamStatusReset,
@ -158,84 +175,117 @@ namespace stream
}; };
class StreamingDestination; class StreamingDestination;
class Stream: public std::enable_shared_from_this<Stream>
{ class Stream : public std::enable_shared_from_this<Stream> {
public: 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 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 (); ~Stream();
uint32_t GetSendStreamID () const { return m_SendStreamID; };
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () const { return m_RemoteIdentity; };
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
bool IsEstablished () const { return m_SendStreamID; };
StreamStatus GetStatus () const { return m_Status; };
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void HandleNextPacket (Packet * packet); uint32_t GetSendStreamID() const { return m_SendStreamID; };
void HandlePing (Packet * packet);
size_t Send (const uint8_t * buf, size_t len); uint32_t GetRecvStreamID() const { return m_RecvStreamID; };
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
void SendPing (); 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> template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); void AsyncReceive(const Buffer &buffer, ReceiveHandler handler, int timeout = 0);
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
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())); }; void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); };
/** only call close from destination thread, use Stream::AsyncClose for other threads */ /** only call close from destination thread, use Stream::AsyncClose for other threads */
void Close (); void Close();
void Cancel () { m_ReceiveTimer.cancel (); };
size_t GetNumSentBytes () const { return m_NumSentBytes; }; void Cancel() { m_ReceiveTimer.cancel(); };
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); 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: private:
void CleanUp (); void CleanUp();
void SendBuffer (); void SendBuffer();
void SendQuickAck ();
void SendClose ();
bool SendPacket (Packet * packet);
void SendPackets (const std::vector<Packet *>& packets);
void SendUpdatedLeaseSet ();
void SavePacket (Packet * packet); void SendQuickAck();
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); 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> 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 ScheduleResend();
void HandleResendTimer (const boost::system::error_code& ecode);
void HandleAckSendTimer (const boost::system::error_code& ecode); void HandleResendTimer(const boost::system::error_code &ecode);
void HandleAckSendTimer(const boost::system::error_code &ecode);
private: private:
boost::asio::io_service& m_Service; boost::asio::io_service &m_Service;
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
int32_t m_LastReceivedSequenceNumber; int32_t m_LastReceivedSequenceNumber;
StreamStatus m_Status; StreamStatus m_Status;
bool m_IsAckSendScheduled; bool m_IsAckSendScheduled;
StreamingDestination& m_LocalDestination; StreamingDestination &m_LocalDestination;
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity; 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::crypto::Verifier> m_TransientVerifier; // in case of offline key
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet; std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
@ -257,43 +307,61 @@ namespace stream
size_t m_MTU; size_t m_MTU;
}; };
class StreamingDestination: public std::enable_shared_from_this<StreamingDestination> class StreamingDestination : public std::enable_shared_from_this<StreamingDestination> {
{
public: 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(std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort = 0,
~StreamingDestination (); bool gzip = false);
void Start (); ~StreamingDestination();
void Stop ();
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); void Start();
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 Stop();
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<Stream>
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true); CreateNewOutgoingStream(std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
Packet * NewPacket () { return m_PacketsPool.Acquire(); } void SendPing(std::shared_ptr<const i2p::data::LeaseSet> remote);
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
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: private:
void HandleNextPacket (Packet * packet); void HandleNextPacket(Packet *packet);
std::shared_ptr<Stream> CreateNewIncomingStream (uint32_t receiveStreamID);
void HandlePendingIncomingTimer (const boost::system::error_code& ecode); std::shared_ptr<Stream> CreateNewIncomingStream(uint32_t receiveStreamID);
void HandlePendingIncomingTimer(const boost::system::error_code &ecode);
private: private:
@ -318,28 +386,28 @@ namespace stream
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator; std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
// for HTTP only // 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> 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(); auto s = shared_from_this();
m_Service.post ([s, buffer, handler, timeout](void) m_Service.post([s, buffer, handler, timeout](void) {
{ if (!s->m_ReceiveQueue.empty() || s->m_Status == eStreamStatusReset)
if (!s->m_ReceiveQueue.empty () || s->m_Status == eStreamStatusReset) s->HandleReceiveTimer(boost::asio::error::make_error_code(boost::asio::error::operation_aborted),
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0); buffer, handler, 0);
else else {
{
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout; 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; int left = timeout - t;
auto self = s->shared_from_this(); auto self = s->shared_from_this();
self->m_ReceiveTimer.async_wait ( self->m_ReceiveTimer.async_wait(
[self, buffer, handler, left](const boost::system::error_code & ec) [self, buffer, handler, left](const boost::system::error_code &ec) {
{
self->HandleReceiveTimer(ec, buffer, handler, left); self->HandleReceiveTimer(ec, buffer, handler, left);
}); });
} }
@ -347,33 +415,31 @@ namespace stream
} }
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout) void
{ Stream::HandleReceiveTimer(const boost::system::error_code &ecode, const Buffer &buffer, ReceiveHandler handler,
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer)); int remainingTimeout) {
size_t received = ConcatenatePackets(boost::asio::buffer_cast<uint8_t *>(buffer),
boost::asio::buffer_size(buffer));
if (received > 0) if (received > 0)
handler (boost::system::error_code (), received); handler(boost::system::error_code(), received);
else if (ecode == boost::asio::error::operation_aborted) else if (ecode == boost::asio::error::operation_aborted) {
{
// timeout not expired // timeout not expired
if (m_Status == eStreamStatusReset) 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 else
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0); handler(boost::asio::error::make_error_code(boost::asio::error::operation_aborted), 0);
} } else {
else
{
// timeout expired // timeout expired
if (remainingTimeout <= 0) if (remainingTimeout <= 0)
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); handler(boost::asio::error::make_error_code(boost::asio::error::timed_out), received);
else else {
{
// itermediate interrupt // itermediate interrupt
SendUpdatedLeaseSet (); // send our leaseset if applicable SendUpdatedLeaseSet(); // send our leaseset if applicable
AsyncReceive (buffer, handler, remainingTimeout); AsyncReceive(buffer, handler, remainingTimeout);
}
} }
} }
} }
}
} }
#endif #endif

View file

@ -15,69 +15,68 @@
#include "Base.h" #include "Base.h"
namespace i2p { namespace i2p {
namespace data { namespace data {
template<size_t sz> template<size_t sz>
class Tag class Tag {
{ BOOST_STATIC_ASSERT_MSG(sz
BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes"); % 8 == 0, "Tag size must be multiple of 8 bytes");
public: public:
Tag () = default; Tag() = default;
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }
bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); } Tag(const uint8_t *buf) { memcpy(m_Buf, buf, sz); }
bool operator!= (const Tag& other) const { return !(*this == other); }
bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }
uint8_t * operator()() { return m_Buf; } bool operator==(const Tag &other) const { return !memcmp(m_Buf, other.m_Buf, sz); }
const uint8_t * operator()() const { return m_Buf; }
operator uint8_t * () { return m_Buf; } bool operator!=(const Tag &other) const { return !(*this == other); }
operator const uint8_t * () const { return m_Buf; }
const uint8_t * data() const { return m_Buf; } bool operator<(const Tag &other) const { return memcmp(m_Buf, other.m_Buf, sz) < 0; }
const uint64_t * GetLL () const { return ll; }
bool IsZero () const uint8_t *operator()() { return m_Buf; }
{
for (size_t i = 0; i < sz/8; ++i) 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; if (ll[i]) return false;
return true; return true;
} }
void Fill(uint8_t c) void Fill(uint8_t c) {
{
memset(m_Buf, c, sz); memset(m_Buf, c, sz);
} }
void Randomize() void Randomize() {
{
RAND_bytes(m_Buf, sz); RAND_bytes(m_Buf, sz);
} }
std::string ToBase64 (size_t len = sz) const std::string ToBase64(size_t len = sz) const {
{ char str[sz * 2];
char str[sz*2]; size_t l = i2p::data::ByteStreamToBase64(m_Buf, len, str, sz * 2);
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2); return std::string(str, str + l);
return std::string (str, str + l);
} }
std::string ToBase32 (size_t len = sz) const std::string ToBase32(size_t len = sz) const {
{ char str[sz * 2];
char str[sz*2]; size_t l = i2p::data::ByteStreamToBase32(m_Buf, len, str, sz * 2);
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, len, str, sz*2); return std::string(str, str + l);
return std::string (str, str + l);
} }
size_t FromBase32 (const std::string& s) size_t FromBase32(const std::string &s) {
{ return i2p::data::Base32ToByteStream(s.c_str(), s.length(), m_Buf, sz);
return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
} }
size_t FromBase64 (const std::string& s) size_t FromBase64(const std::string &s) {
{ return i2p::data::Base64ToByteStream(s.c_str(), s.length(), m_Buf, sz);
return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
} }
private: private:
@ -85,20 +84,18 @@ namespace data {
union // 8 bytes aligned union // 8 bytes aligned
{ {
uint8_t m_Buf[sz]; uint8_t m_Buf[sz];
uint64_t ll[sz/8]; uint64_t ll[sz / 8];
}; };
}; };
} // data } // data
} // i2p } // i2p
namespace std namespace std {
{
// hash for std::unordered_map // hash for std::unordered_map
template<size_t sz> struct hash<i2p::data::Tag<sz> > template<size_t sz>
{ struct hash<i2p::data::Tag<sz> > {
size_t operator()(const i2p::data::Tag<sz>& s) const size_t operator()(const i2p::data::Tag<sz> &s) const {
{ return s.GetLL()[0];
return s.GetLL ()[0];
} }
}; };
} }

View file

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

View file

@ -15,44 +15,48 @@
#include <string> #include <string>
#include <boost/asio.hpp> #include <boost/asio.hpp>
namespace i2p namespace i2p {
{ namespace util {
namespace util uint64_t GetMillisecondsSinceEpoch();
{
uint64_t GetMillisecondsSinceEpoch ();
uint64_t GetSecondsSinceEpoch ();
uint32_t GetMinutesSinceEpoch ();
uint32_t GetHoursSinceEpoch ();
void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes uint64_t GetSecondsSinceEpoch();
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 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: public:
NTPTimeSync (); NTPTimeSync();
~NTPTimeSync ();
void Start (); ~NTPTimeSync();
void Stop ();
void Start();
void Stop();
private: private:
void Run (); void Run();
void Sync ();
void Sync();
private: private:
bool m_IsRunning; 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::io_service m_Service;
boost::asio::deadline_timer m_Timer; boost::asio::deadline_timer m_Timer;
int m_SyncInterval; int m_SyncInterval;
std::vector<std::string> m_NTPServersList; std::vector <std::string> m_NTPServersList;
}; };
} }
} }
#endif #endif

View file

@ -15,104 +15,88 @@
#include "Transports.h" #include "Transports.h"
#include "TransitTunnel.h" #include "TransitTunnel.h"
namespace i2p namespace i2p {
{ namespace tunnel {
namespace tunnel TransitTunnel::TransitTunnel(uint32_t receiveTunnelID,
{ const uint8_t *nextIdent, uint32_t nextTunnelID,
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, const uint8_t *layerKey, const uint8_t *ivKey) :
const uint8_t * nextIdent, uint32_t nextTunnelID, TunnelBase(receiveTunnelID, nextTunnelID, nextIdent) {
const uint8_t * layerKey,const uint8_t * ivKey): m_Encryption.SetKeys(layerKey, ivKey);
TunnelBase (receiveTunnelID, nextTunnelID, nextIdent)
{
m_Encryption.SetKeys (layerKey, ivKey);
} }
void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) void TransitTunnel::EncryptTunnelMsg(std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) {
{ m_Encryption.Encrypt(in->GetPayload() + 4, out->GetPayload() + 4);
m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); i2p::transport::transports.UpdateTotalTransitTransmittedBytes(TUNNEL_DATA_MSG_SIZE);
i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE);
} }
TransitTunnelParticipant::~TransitTunnelParticipant () TransitTunnelParticipant::~TransitTunnelParticipant() {
{
} }
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) void TransitTunnelParticipant::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
{ EncryptTunnelMsg(tunnelMsg, tunnelMsg);
EncryptTunnelMsg (tunnelMsg, tunnelMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength (); m_NumTransmittedBytes += tunnelMsg->GetLength();
htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ()); htobe32buf(tunnelMsg->GetPayload(), GetNextTunnelID());
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); tunnelMsg->FillI2NPMessageHeader(eI2NPTunnelData);
m_TunnelDataMsgs.push_back (tunnelMsg); m_TunnelDataMsgs.push_back(tunnelMsg);
} }
void TransitTunnelParticipant::FlushTunnelDataMsgs () void TransitTunnelParticipant::FlushTunnelDataMsgs() {
{ if (!m_TunnelDataMsgs.empty()) {
if (!m_TunnelDataMsgs.empty ()) auto num = m_TunnelDataMsgs.size();
{
auto num = m_TunnelDataMsgs.size ();
if (num > 1) if (num > 1)
LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num); LogPrint(eLogDebug, "TransitTunnel: ", GetTunnelID(), "->", GetNextTunnelID(), " ", num);
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs); i2p::transport::transports.SendMessages(GetNextIdentHash(), m_TunnelDataMsgs);
m_TunnelDataMsgs.clear (); m_TunnelDataMsgs.clear();
} }
} }
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) void TransitTunnel::SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg) {
{ LogPrint(eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID());
LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ());
} }
void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) void TransitTunnel::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
{ LogPrint(eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID());
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; TunnelMessageBlock block;
block.deliveryType = eDeliveryTypeLocal; block.deliveryType = eDeliveryTypeLocal;
block.data = msg; block.data = msg;
std::unique_lock<std::mutex> l(m_SendMutex); 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); std::unique_lock<std::mutex> l(m_SendMutex);
m_Gateway.SendBuffer (); m_Gateway.SendBuffer();
} }
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) void TransitTunnelEndpoint::HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg) {
{ auto newMsg = CreateEmptyTunnelDataMsg(true);
auto newMsg = CreateEmptyTunnelDataMsg (true); EncryptTunnelMsg(tunnelMsg, newMsg);
EncryptTunnelMsg (tunnelMsg, newMsg);
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); LogPrint(eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID());
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); m_Endpoint.HandleDecryptedTunnelDataMsg(newMsg);
} }
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, std::shared_ptr<TransitTunnel> CreateTransitTunnel(uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey, const uint8_t *layerKey, const uint8_t *ivKey,
bool isGateway, bool isEndpoint) bool isGateway, bool isEndpoint) {
{ if (isEndpoint) {
if (isEndpoint) LogPrint(eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created");
{ return std::make_shared<TransitTunnelEndpoint>(receiveTunnelID, nextIdent, nextTunnelID, layerKey,
LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created"); ivKey);
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 "TunnelGateway.h"
#include "TunnelBase.h" #include "TunnelBase.h"
namespace i2p namespace i2p {
{ namespace tunnel {
namespace tunnel class TransitTunnel : public TunnelBase {
{
class TransitTunnel: public TunnelBase
{
public: public:
TransitTunnel (uint32_t receiveTunnelID, TransitTunnel(uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey); const uint8_t *layerKey, const uint8_t *ivKey);
virtual size_t GetNumTransmittedBytes () const { return 0; }; virtual size_t GetNumTransmittedBytes() const { return 0; };
// implements TunnelBase // implements TunnelBase
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg); 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 HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
void EncryptTunnelMsg(std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
private: private:
i2p::crypto::TunnelEncryption m_Encryption; i2p::crypto::TunnelEncryption m_Encryption;
}; };
class TransitTunnelParticipant: public TransitTunnel class TransitTunnelParticipant : public TransitTunnel {
{
public: public:
TransitTunnelParticipant (uint32_t receiveTunnelID, TransitTunnelParticipant(uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_NumTransmittedBytes (0) {}; layerKey, ivKey), m_NumTransmittedBytes(0) {};
~TransitTunnelParticipant ();
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; ~TransitTunnelParticipant();
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg);
void FlushTunnelDataMsgs (); size_t GetNumTransmittedBytes() const { return m_NumTransmittedBytes; };
void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
void FlushTunnelDataMsgs();
private: private:
@ -63,19 +65,20 @@ namespace tunnel
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs; std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
}; };
class TransitTunnelGateway: public TransitTunnel class TransitTunnelGateway : public TransitTunnel {
{
public: public:
TransitTunnelGateway (uint32_t receiveTunnelID, TransitTunnelGateway(uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID,
layerKey, ivKey), m_Gateway(this) {}; layerKey, ivKey), m_Gateway(this) {};
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg); void SendTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> msg);
void FlushTunnelDataMsgs ();
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); }; void FlushTunnelDataMsgs();
size_t GetNumTransmittedBytes() const { return m_Gateway.GetNumSentBytes(); };
private: private:
@ -83,31 +86,31 @@ namespace tunnel
TunnelGateway m_Gateway; TunnelGateway m_Gateway;
}; };
class TransitTunnelEndpoint: public TransitTunnel class TransitTunnelEndpoint : public TransitTunnel {
{
public: public:
TransitTunnelEndpoint (uint32_t receiveTunnelID, TransitTunnelEndpoint(uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey): const uint8_t *layerKey, const uint8_t *ivKey) :
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), TransitTunnel(receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound 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); void HandleTunnelDataMsg(std::shared_ptr<i2p::I2NPMessage> &&tunnelMsg);
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
size_t GetNumTransmittedBytes() const { return m_Endpoint.GetNumReceivedBytes(); }
private: private:
TunnelEndpoint m_Endpoint; TunnelEndpoint m_Endpoint;
}; };
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, std::shared_ptr<TransitTunnel> CreateTransitTunnel(uint32_t receiveTunnelID,
const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t *nextIdent, uint32_t nextTunnelID,
const uint8_t * layerKey,const uint8_t * ivKey, const uint8_t *layerKey, const uint8_t *ivKey,
bool isGateway, bool isEndpoint); bool isGateway, bool isEndpoint);
} }
} }
#endif #endif

View file

@ -20,48 +20,40 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Timestamp.h" #include "Timestamp.h"
namespace i2p namespace i2p {
{ namespace transport {
namespace transport
{
const size_t IPV4_HEADER_SIZE = 20; const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40; const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8; const size_t UDP_HEADER_SIZE = 8;
class SignedData class SignedData {
{
public: public:
SignedData () {} SignedData() {}
SignedData (const SignedData& other)
{ SignedData(const SignedData &other) {
m_Stream << other.m_Stream.rdbuf (); m_Stream << other.m_Stream.rdbuf();
} }
void Reset () void Reset() {
{
m_Stream.str(""); m_Stream.str("");
} }
void Insert (const uint8_t * buf, size_t len) void Insert(const uint8_t *buf, size_t len) {
{ m_Stream.write((char *) buf, len);
m_Stream.write ((char *)buf, len);
} }
template<typename T> template<typename T>
void Insert (T t) void Insert(T t) {
{ m_Stream.write((char *) &t, sizeof(T));
m_Stream.write ((char *)&t, sizeof (T));
} }
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const bool Verify(std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t *signature) const {
{ return ident->Verify((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature);
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
} }
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const void Sign(const i2p::data::PrivateKeys &keys, uint8_t *signature) const {
{ keys.Sign((const uint8_t *) m_Stream.str().c_str(), m_Stream.str().size(), signature);
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
} }
private: private:
@ -69,50 +61,59 @@ namespace transport
std::stringstream m_Stream; std::stringstream m_Stream;
}; };
class TransportSession class TransportSession {
{
public: public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout): TransportSession(std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout) :
m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), m_NumSentBytes(0), m_NumReceivedBytes(0), m_IsOutgoing(router),
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()) m_TerminationTimeout(terminationTimeout),
{ m_LastActivityTimestamp(i2p::util::GetSecondsSinceEpoch()) {
if (router) if (router)
m_RemoteIdentity = router->GetRouterIdentity (); m_RemoteIdentity = router->GetRouterIdentity();
m_CreationTime = m_LastActivityTimestamp; m_CreationTime = m_LastActivityTimestamp;
} }
virtual ~TransportSession () {}; virtual ~TransportSession() {};
virtual void Done () = 0;
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); std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
return m_RemoteIdentity; 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); std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
m_RemoteIdentity = ident; m_RemoteIdentity = ident;
} }
size_t GetNumSentBytes () const { return m_NumSentBytes; }; size_t GetNumSentBytes() const { return m_NumSentBytes; };
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
bool IsOutgoing () const { return m_IsOutgoing; };
int GetTerminationTimeout () const { return m_TerminationTimeout; }; size_t GetNumReceivedBytes() const { return m_NumReceivedBytes; };
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; }; bool IsOutgoing() const { return m_IsOutgoing; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
virtual uint32_t GetRelayTag () const { return 0; }; int GetTerminationTimeout() const { return m_TerminationTimeout; };
virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0; 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: protected:
@ -124,7 +125,7 @@ namespace transport
uint64_t m_LastActivityTimestamp; uint64_t m_LastActivityTimestamp;
uint32_t m_CreationTime; // seconds since epoch uint32_t m_CreationTime; // seconds since epoch
}; };
} }
} }
#endif #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