Browse Source

Merge pull request #1456 from PurpleI2P/openssl

recent changes
pull/1777/head
orignal 5 years ago committed by GitHub
parent
commit
d6d9f05443
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ChangeLog
  2. 4
      Makefile.mingw
  3. 3
      Makefile.osx
  4. 13
      Win32/Win32App.cpp
  5. 1
      android/AndroidManifest.xml
  6. 4
      appveyor.yml
  7. 2
      build/CMakeLists.txt
  8. 14
      build/build_mingw.cmd
  9. 34
      contrib/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt
  10. 1
      libi2pd/Config.cpp
  11. 42
      libi2pd/Crypto.cpp
  12. 3
      libi2pd/Crypto.h
  13. 30
      libi2pd/CryptoKey.cpp
  14. 33
      libi2pd/CryptoKey.h
  15. 38
      libi2pd/Destination.cpp
  16. 9
      libi2pd/Destination.h
  17. 132
      libi2pd/ECIESX25519AEADRatchetSession.cpp
  18. 48
      libi2pd/ECIESX25519AEADRatchetSession.h
  19. 2
      libi2pd/Ed25519.cpp
  20. 196
      libi2pd/Elligator.cpp
  21. 39
      libi2pd/Elligator.h
  22. 83
      libi2pd/Garlic.cpp
  23. 9
      libi2pd/Garlic.h
  24. 9
      libi2pd/Identity.cpp
  25. 4
      libi2pd/Identity.h
  26. 5
      libi2pd/LeaseSet.cpp
  27. 2
      libi2pd/LeaseSet.h
  28. 49
      libi2pd/NTCP2.cpp
  29. 3
      libi2pd/NTCP2.h
  30. 15
      libi2pd/RouterContext.cpp
  31. 11
      libi2pd/RouterContext.h
  32. 8
      libi2pd/Transports.cpp
  33. 43
      libi2pd/util.cpp
  34. 2
      libi2pd/version.h
  35. 2
      qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml
  36. 5
      tests/Makefile
  37. 77
      tests/test-elligator.cpp

1
ChangeLog

@ -10,6 +10,7 @@
### Fixed ### Fixed
- Failure to start on Windows XP - Failure to start on Windows XP
- SAM crash if invalid lookup address - SAM crash if invalid lookup address
- Possible crash when UPnP enabled on shutdown
## [2.28.0] - 2019-08-27 ## [2.28.0] - 2019-08-27
### Added ### Added

4
Makefile.mingw

@ -37,6 +37,10 @@ ifeq ($(USE_WIN32_APP), yes)
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif endif
ifeq ($(USE_WINXP_FLAGS), yes)
CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
# don't change following line to ifeq ($(USE_AESNI),yes) !!! # don't change following line to ifeq ($(USE_AESNI),yes) !!!
ifeq ($(USE_AESNI),1) ifeq ($(USE_AESNI),1)
CPU_FLAGS += -maes CPU_FLAGS += -maes

3
Makefile.osx vendored

@ -2,6 +2,9 @@ CXX = clang++
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX
INCFLAGS = -I/usr/local/include INCFLAGS = -I/usr/local/include
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDFLAGS += -Wl,-dead_strip
LDFLAGS += -Wl,-dead_strip_dylibs
LDFLAGS += -Wl,-bind_at_load
ifeq ($(USE_STATIC),yes) ifeq ($(USE_STATIC),yes)
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread

13
Win32/Win32App.cpp

@ -13,10 +13,6 @@
#include "Win32App.h" #include "Win32App.h"
#include <stdio.h> #include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define ID_ABOUT 2000 #define ID_ABOUT 2000
#define ID_EXIT 2001 #define ID_EXIT 2001
#define ID_CONSOLE 2002 #define ID_CONSOLE 2002
@ -40,6 +36,9 @@ namespace win32
{ {
static DWORD GracefulShutdownEndtime = 0; static DWORD GracefulShutdownEndtime = 0;
typedef DWORD (* IPN)();
IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount");
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
{ {
HMENU hPopup = CreatePopupMenu(); HMENU hPopup = CreatePopupMenu();
@ -53,7 +52,7 @@ namespace win32
ID_ACCEPT_TRANSIT, "Accept &transit"); ID_ACCEPT_TRANSIT, "Accept &transit");
else else
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit"); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs"); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
if (!i2p::util::DaemonWin32::Instance ().isGraceful) if (!i2p::util::DaemonWin32::Instance ().isGraceful)
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
else else
@ -161,7 +160,7 @@ namespace win32
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
if (GracefulShutdownEndtime != 0) if (GracefulShutdownEndtime != 0)
{ {
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCount()) / 1000; DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000;
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft); s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
} }
else else
@ -239,7 +238,7 @@ namespace win32
i2p::context.SetAcceptsTunnels (false); i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
GracefulShutdownEndtime = GetTickCount() + 10*60*1000; GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000;
i2p::util::DaemonWin32::Instance ().isGraceful = true; i2p::util::DaemonWin32::Instance ().isGraceful = true;
return 0; return 0;
} }

1
android/AndroidManifest.xml

@ -16,6 +16,7 @@
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar" android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:requestLegacyExternalStorage="true"
> >
<receiver android:name=".NetworkStateChangeReceiver"> <receiver android:name=".NetworkStateChangeReceiver">
<intent-filter> <intent-filter>

4
appveyor.yml

@ -18,9 +18,9 @@ environment:
install: install:
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc" - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force" - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force" - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
- if "%MSYSTEM%" == "MINGW64" ( - if "%MSYSTEM%" == "MINGW64" (
c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc" c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc"

2
build/CMakeLists.txt

@ -82,6 +82,8 @@ set (LIBI2PD_SRC
"${LIBI2PD_SRC_DIR}/Ed25519.cpp" "${LIBI2PD_SRC_DIR}/Ed25519.cpp"
"${LIBI2PD_SRC_DIR}/NTCP2.cpp" "${LIBI2PD_SRC_DIR}/NTCP2.cpp"
"${LIBI2PD_SRC_DIR}/Blinding.cpp" "${LIBI2PD_SRC_DIR}/Blinding.cpp"
"${LIBI2PD_SRC_DIR}/Elligator.cpp"
"${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp"
) )
if (WITH_WEBSOCKETS) if (WITH_WEBSOCKETS)

14
build/build_mingw.cmd

@ -54,6 +54,14 @@ set bitness=64
call :BUILDING call :BUILDING
echo. echo.
REM building for WinXP
set "WD=C:\msys64-xp\usr\bin\"
set MSYSTEM=MINGW32
set bitness=32
set "xSH=%WD%bash -lc"
call :BUILDING_XP
echo.
del README.txt >> nul del README.txt >> nul
echo Build complete... echo Build complete...
@ -71,5 +79,11 @@ echo Build AESNI...
%xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_aesni_%tag%.log 2>&1 %xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_aesni_%tag%.log 2>&1
echo Build without extensions... echo Build without extensions...
%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build/build_win%bitness%_%tag%.log 2>&1 %xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build/build_win%bitness%_%tag%.log 2>&1
goto EOF
:BUILDING_XP
%xSH% "make clean" >> nul
echo Building i2pd %tag% for winxp...
%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build/build_winxp_%tag%.log 2>&1
:EOF :EOF

34
contrib/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt

@ -1,34 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIF3DCCA8SgAwIBAgIQPxUlcrbHX/xdyJ09E36rJzANBgkqhkiG9w0BAQsFADB3
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEgMB4GA1UEAwwX
cmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwHhcNMTgxMjA3MTYzNDIxWhcNMjgxMjA3
MTYzNDIxWjB3MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhY
MR4wHAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEg
MB4GA1UEAwwXcmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQC912NDk6x85uqB4gyQQcded0RVrbWehWOanDRv7kC3
92jeziPbeMtqrLsfU1MdDtQiGijpNkQ/IIitPw+6vJAIh82gyOUZvsn2XOyb/Fz0
Fu8OrDghwl39yK8kwtqCFw3VAgafgKxz2oRge9mxFBECi50vYEPIBwNhr4yc/opu
wWUmzmRyX4gD7vKmRU6ZTwX4LXnwdl+5VbW3updcZKsDuTnKvC9FGhDRR9kIk2G9
43sLN263nCYPykP7DaB1cUdi1vDEMw5dot+eu16qTIbuypEvYNvbB/9FyCQllm1h
vBbSku3IYpcnRPmoeyhoR/MmCySRbK5R4SrSsVD1YBpwxgn0Q4+fzEgFzT9P4oez
HkDGKVP2HdgmXx9j36fEqqvjqzRleWDwEWwIZVRLCFO+hhhT3JAjnNGJTWv1SQGB
8tz9nyYTJuhvyHE/CO5owFeCdeOGMq2KPge9w34T+mvewTEEhGU8yRAt8Xp8s5Y9
RCUGvuQ79+edRtj7FJg7yVB8pAQ+VB9msNQvzrTnPYC9Wo7chJhBiraMiIabzIhC
f34Gg9lkX1N0dVND5rnZWwzBM6JhNG1iZZCRHVPnXdZRixUlqmFpCP/eekshksj/
6UP/WeGA6X4HyEsC6QEf7eMhcHYjyyTzYagKrwCHg77fmIjF8rmpP2LqWSQW8bDD
uQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0OBBkEF3Jlc2VlZGkycG5l
dGluQG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQCWpXs6iuTy/w2R7q7Ua6vl
JYZwbQ+czk5ydzkBgcNkMMMNRT7sZR9xYvV+ftiL4bFQP/3ZJyo7cYz2Q6+M3oAm
YDcZWBkLUVihSlMxhWwmeFTKV2EL+bzwY1V/cy7wgukKnFIes75dLP/v25jgjdlw
Xe6R+fQM0EoHeVzzrWk/qYp6oEwtQXfZnUu/Bf45hRnnHBzzh1wCql41vbEs3Niq
+SVwY1wLT0yC1L8HqjCLX1/L5PAXxbvEGzwnXSkLKK4bPxdmVDZvS9uzXrWmTbNi
HpKIFnOif16zSgyeaOM7HETIJuVzgooUMtt+Vsr1VGdtm6K7I9J5C+rX/ckU8oaX
UjmzhWXudN0VTslogsKUCV6xG2CskeE3wnuT8HYXz9NMw6c/kIGH4hY7LcfU8Teu
QjSy2RRvy6InmZNV5sY9lzzO6romEycSoUlpCa3Ltb/5KKoYZFTsXr8suqJk89lC
e+TVMHqOZdLK/usqQDcafLypHpw9SH2Tg4jrzV/zLqacbjx6bZD5IrpY0Gf7BXg/
pikwyA9c490G6ZcWrSEP8bzh6LL2rA2AwxaeJJNVyLHCSLrn/7DezM5J/qhd90Qg
kcZGJrUOCSWl6mDvUZn5XiQ955XwOnZQ+wsM85B3CVX22x5bp0SYWHCQBPnthPwP
Q5DD3jExbpwG5n35HEcHYw==
-----END CERTIFICATE-----

1
libi2pd/Config.cpp

@ -192,7 +192,6 @@ namespace config {
"https://netdb.i2p2.no/," "https://netdb.i2p2.no/,"
// "https://us.reseed.i2p2.no:444/," // mamoth's shit // "https://us.reseed.i2p2.no:444/," // mamoth's shit
// "https://uk.reseed.i2p2.no:444/," // mamoth's shit // "https://uk.reseed.i2p2.no:444/," // mamoth's shit
"https://reseed.i2p.net.in/,"
"https://download.xxlspeed.com/," "https://download.xxlspeed.com/,"
"https://reseed-fr.i2pd.xyz/," "https://reseed-fr.i2pd.xyz/,"
"https://reseed.memcpy.io/," "https://reseed.memcpy.io/,"

42
libi2pd/Crypto.cpp

@ -367,6 +367,18 @@ namespace crypto
#endif #endif
} }
void X25519Keys::SetPrivateKey (const uint8_t * priv)
{
#if OPENSSL_X25519
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey);
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
#else
memcpy (m_PrivateKey, priv, 32);
#endif
}
// ElGamal // ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding)
{ {
@ -1259,18 +1271,29 @@ namespace crypto
#endif #endif
} }
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out) void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
uint8_t * out, size_t outLen)
{ {
#if OPENSSL_HKDF #if OPENSSL_HKDF
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, NULL); EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr);
EVP_PKEY_derive_init (pctx); EVP_PKEY_derive_init (pctx);
EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256()); EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256());
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32); if (key && keyLen)
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen); {
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
}
else
{
// zerolen
EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
uint8_t tempKey[32]; unsigned int len;
HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len);
EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len);
}
if (info.length () > 0) if (info.length () > 0)
EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ()); EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ());
size_t outlen = 64; EVP_PKEY_derive (pctx, out, &outLen);
EVP_PKEY_derive (pctx, out, &outlen);
EVP_PKEY_CTX_free (pctx); EVP_PKEY_CTX_free (pctx);
#else #else
uint8_t prk[32]; unsigned int len; uint8_t prk[32]; unsigned int len;
@ -1278,8 +1301,11 @@ namespace crypto
auto l = info.length (); auto l = info.length ();
memcpy (out, info.c_str (), l); out[l] = 0x01; memcpy (out, info.c_str (), l); out[l] = 0x01;
HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len); HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len);
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02; if (outLen > 32) // 64
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len); {
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
}
#endif #endif
} }

3
libi2pd/Crypto.h

@ -80,6 +80,7 @@ namespace crypto
void GenerateKeys (); void GenerateKeys ();
const uint8_t * GetPublicKey () const { return m_PublicKey; }; const uint8_t * GetPublicKey () const { return m_PublicKey; };
void GetPrivateKey (uint8_t * priv) const; void GetPrivateKey (uint8_t * priv) const;
void SetPrivateKey (const uint8_t * priv); // wihout calculating public
void Agree (const uint8_t * pub, uint8_t * shared); void Agree (const uint8_t * pub, uint8_t * shared);
private: private:
@ -296,7 +297,7 @@ namespace crypto
// HKDF // HKDF
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out); // salt - 32, out - 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
// init and terminate // init and terminate
void InitCrypto (bool precomputation); void InitCrypto (bool precomputation);

30
libi2pd/CryptoKey.cpp

@ -146,6 +146,36 @@ namespace crypto
BN_free (x); BN_free (y); BN_free (x); BN_free (y);
} }
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub)
{
memcpy (m_PublicKey, pub, 32);
}
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t * epriv, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding)
{
X25519Keys ep;
ep.SetPrivateKey (epriv);
ep.Agree (m_PublicKey, sharedSecret);
}
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv)
{
m_StaticKeys.SetPrivateKey (priv);
}
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding)
{
m_StaticKeys.Agree (epub, sharedSecret);
return true;
}
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub)
{
X25519Keys k;
k.GenerateKeys ();
k.GetPrivateKey (priv);
memcpy (pub, k.GetPublicKey (), 32);
}
} }
} }

33
libi2pd/CryptoKey.h

@ -116,6 +116,39 @@ namespace crypto
}; };
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub); void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub);
// ECIES-X25519-AEAD-Ratchet
class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor
{
public:
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub);
~ECIESX25519AEADRatchetEncryptor () {};
void Encrypt (const uint8_t * epriv, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding);
// agree with ephemeral priv and return in sharedSecret (32 bytes)
private:
uint8_t m_PublicKey[32];
};
class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor
{
public:
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv);
~ECIESX25519AEADRatchetDecryptor () {};
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding);
// agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen () const { return 32; };
private:
X25519Keys m_StaticKeys;
};
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
} }
} }

38
libi2pd/Destination.cpp

@ -353,30 +353,38 @@ namespace client
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg)); uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
}
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
{
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
} }
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
{ {
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID) switch (typeID)
{ {
case eI2NPData: case eI2NPData:
HandleDataMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); HandleDataMessage (payload, len);
break; break;
case eI2NPDeliveryStatus: case eI2NPDeliveryStatus:
// we assume tunnel tests non-encrypted // we assume tunnel tests non-encrypted
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
break; break;
case eI2NPDatabaseStore: case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); HandleDatabaseStoreMessage (payload, len);
break; break;
case eI2NPDatabaseSearchReply: case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); HandleDatabaseSearchReplyMessage (payload, len);
break; break;
default: default:
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
return false;
} }
return true;
} }
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
@ -511,9 +519,8 @@ namespace client
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
} }
void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{ {
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken) if (msgID == m_PublishReplyToken)
{ {
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32()); LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
@ -525,7 +532,7 @@ namespace client
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
} }
else else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg); i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
} }
void LeaseSetDestination::SetLeaseSetUpdated () void LeaseSetDestination::SetLeaseSetUpdated ()
@ -873,10 +880,13 @@ namespace client
m_EncryptionKeyType = std::stoi(it->second); m_EncryptionKeyType = std::stoi(it->second);
} }
if (isPublic && m_EncryptionKeyType == GetIdentity ()->GetCryptoKeyType ()) // TODO: presist key type memset (m_EncryptionPrivateKey, 0, 256);
memset (m_EncryptionPublicKey, 0, 256);
if (isPublic)
PersistTemporaryKeys (); PersistTemporaryKeys ();
else else
i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey); i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey); m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey);
if (isPublic) if (isPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
@ -1165,8 +1175,8 @@ namespace client
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p"); LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
memset (m_EncryptionPrivateKey, 0, 256); memset (m_EncryptionPrivateKey, 0, 256);
memset (m_EncryptionPublicKey, 0, 256); memset (m_EncryptionPublicKey, 0, 256);
i2p::data::PrivateKeys::GenerateCryptoKeyPair (GetIdentity ()->GetCryptoKeyType (), m_EncryptionPrivateKey, m_EncryptionPublicKey); i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
// TODO:: persist crypto key type
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
if (f1) { if (f1) {
f1.write ((char *)m_EncryptionPublicKey, 256); f1.write ((char *)m_EncryptionPublicKey, 256);

9
libi2pd/Destination.h

@ -121,7 +121,6 @@ namespace client
// 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; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination // override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
@ -131,6 +130,10 @@ namespace client
protected: protected:
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet); void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; }; int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
@ -152,7 +155,7 @@ namespace client
void HandlePublishDelayTimer (const boost::system::error_code& ecode); void HandlePublishDelayTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void HandleDeliveryStatusMessage (uint32_t msgID);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr); 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); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
@ -237,6 +240,8 @@ namespace client
// implements LocalDestination // implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
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 (); };
i2p::data::CryptoKeyType GetEncryptionType () const { return m_EncryptionKeyType; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
protected: protected:

132
libi2pd/ECIESX25519AEADRatchetSession.cpp

@ -0,0 +1,132 @@
#include <string.h>
#include <openssl/sha.h>
#include "Log.h"
#include "Crypto.h"
#include "Elligator.h"
#include "Tag.h"
#include "I2PEndian.h"
#include "ECIESX25519AEADRatchetSession.h"
namespace i2p
{
namespace garlic
{
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession ()
{
// TODO : use precalculated hashes
static const char protocolName[41] = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"; // 40 bytes
SHA256 ((const uint8_t *)protocolName, 40, m_H);
memcpy (m_CK, m_H, 32);
SHA256 (m_H, 32, m_H);
}
ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession ()
{
}
void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (m_H, &ctx);
}
bool ECIESX25519AEADRatchetSession::NewIncomingSession (const i2p::data::LocalDestination& dest,
const uint8_t * buf, size_t len, CloveHandler handleClove)
{
// we are Bob
// KDF1
MixHash (dest.GetEncryptionPublicKey (), 32); // h = SHA256(h || bpk)
uint8_t aepk[32]; // Alice's ephemeral key
if (!i2p::crypto::GetElligator ()->Decode (buf, aepk))
{
LogPrint (eLogError, "Garlic: Can't decode elligator");
return false;
}
buf += 32; len -= 32;
MixHash (aepk, 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32], keyData[64];
dest.Decrypt (aepk, sharedSecret, nullptr); // x25519(bsk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", keyData); // keydata = HKDF(chainKey, sharedSecret, "", 64)
memcpy (m_CK, keyData, 32); // chainKey = keydata[0:31]
// decrypt flags/static
uint8_t nonce[12], fs[32];
memset (nonce, 0, 12); // n = 0
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, keyData + 32, nonce, fs, 32, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
return false;
}
MixHash (buf, 48); // h = SHA256(h || ciphertext)
buf += 48; len -= 48; // 32 data + 16 poly
// decrypt payload
std::vector<uint8_t> payload (len - 16);
// KDF2 for payload
bool isStatic = !i2p::data::Tag<32> (fs).IsZero ();
if (isStatic)
{
// static key, fs is apk
dest.Decrypt (fs, sharedSecret, nullptr); // x25519(bsk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", keyData); // keydata = HKDF(chainKey, sharedSecret, "", 64)
memcpy (m_CK, keyData, 32); // chainKey = keydata[0:31]
}
else // all zeros flags
htole64buf (nonce + 4, 1); // n = 1
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keyData + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
return false;
}
if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext)
HandlePayload (payload.data (), len - 16, handleClove);
return true;
}
void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, CloveHandler& handleClove)
{
size_t offset = 0;
while (offset < len)
{
uint8_t blk = buf[offset];
offset++;
auto size = bufbe16toh (buf + offset);
offset += 2;
LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size);
if (size > len)
{
LogPrint (eLogError, "Garlic: Unexpected block length ", size);
break;
}
switch (blk)
{
case eECIESx25519BlkGalicClove:
handleClove (buf + offset, size);
break;
case eECIESx25519BlkDateTime:
LogPrint (eLogDebug, "Garlic: datetime");
break;
case eECIESx25519BlkOptions:
LogPrint (eLogDebug, "Garlic: options");
break;
case eECIESx25519BlkPadding:
LogPrint (eLogDebug, "Garlic: padding");
break;
default:
LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk);
}
offset += size;
}
}
}
}

48
libi2pd/ECIESX25519AEADRatchetSession.h

@ -0,0 +1,48 @@
#ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__
#define ECIES_X25519_AEAD_RATCHET_SESSION_H__
#include <inttypes.h>
#include <functional>
#include "Identity.h"
namespace i2p
{
namespace garlic
{
enum ECIESx25519BlockType
{
eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1,
eECIESx25519BlkTermination = 4,
eECIESx25519BlkOptions = 5,
eECIESx25519BlkNextSessionKey = 7,
eECIESx25519BlkGalicClove = 11,
eECIESx25519BlkPadding = 254
};
class ECIESX25519AEADRatchetSession
{
public:
typedef std::function<void (const uint8_t * buf, size_t len)> CloveHandler;
ECIESX25519AEADRatchetSession ();
~ECIESX25519AEADRatchetSession ();
bool NewIncomingSession (const i2p::data::LocalDestination& dest, const uint8_t * buf, size_t len,
CloveHandler handleClove);
private:
void MixHash (const uint8_t * buf, size_t len);
void HandlePayload (const uint8_t * buf, size_t len, CloveHandler& handleClove);
private:
uint8_t m_H[32], m_CK[32];
};
}
}
#endif

2
libi2pd/Ed25519.cpp

@ -31,7 +31,7 @@ namespace crypto
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_mul (d, d, tmp, ctx); BN_mod_mul (d, d, tmp, q, ctx);
// 2^((q-1)/4) // 2^((q-1)/4)
I = BN_new (); I = BN_new ();

196
libi2pd/Elligator.cpp

@ -0,0 +1,196 @@
#include "Crypto.h"
#include "Elligator.h"
namespace i2p
{
namespace crypto
{
Elligator2::Elligator2 ()
{
// TODO: share with Ed22519
p = BN_new ();
// 2^255-19
BN_set_bit (p, 255); // 2^255
BN_sub_word (p, 19);
p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8
p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2
p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4
A = BN_new (); BN_set_word (A, 486662);
nA = BN_new (); BN_sub (nA, p, A);
BN_CTX * ctx = BN_CTX_new ();
// calculate sqrt(-1)
sqrtn1 = BN_new ();
BN_set_word (sqrtn1, 2);
BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
u = BN_new (); BN_set_word (u, 2);
iu = BN_new (); BN_mod_inverse (iu, u, p, ctx);
BN_CTX_free (ctx);
}
Elligator2::~Elligator2 ()
{
BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14);
BN_free (sqrtn1); BN_free (A); BN_free (nA);
BN_free (u); BN_free (iu);
}
bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY) const
{
bool ret = true;
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
uint8_t key1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
{
key1[i] = key[31 - i];
key1[31 - i] = key[i];
}
BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x);
BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A
BN_sub (xA, p, xA); // p - (x + A)
BIGNUM * uxxA = BN_CTX_get (ctx); // u*x*xA
BN_mod_mul (uxxA, u, x, p, ctx);
BN_mod_mul (uxxA, uxxA, xA, p, ctx);
if (Legendre (uxxA, ctx) != -1)
{
BIGNUM * r = BN_CTX_get (ctx);
if (highY)
{
BN_mod_inverse (r, x, p, ctx);
BN_mod_mul (r, r, xA, p, ctx);
}
else
{
BN_mod_inverse (r, xA, p, ctx);
BN_mod_mul (r, r, x, p, ctx);
}
BN_mod_mul (r, r, iu, p, ctx);
SquareRoot (r, r, ctx);
bn2buf (r, encoded, 32);
for (size_t i = 0; i < 16; i++) // To Little Endian
{
uint8_t tmp = encoded[i];
encoded[i] = encoded[31 - i];
encoded[31 - i] = tmp;
}
}
else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
return ret;
}
bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const
{
bool ret = true;
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
uint8_t encoded1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
{
encoded1[i] = encoded[31 - i];
encoded1[31 - i] = encoded[i];
}
BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r);
if (BN_cmp (r, p12) <= 0) // r < (p-1)/2
{
// v = -A/(1+u*r^2)
BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx);
BN_mod_mul (v, v, u, p, ctx);
BN_add_word (v, 1);
BN_mod_inverse (v, v, p, ctx);
BN_mod_mul (v, v, nA, p, ctx);
BIGNUM * vpA = BN_CTX_get (ctx);
BN_add (vpA, v, A); // v + A
// t = v^3+A*v^2+v = v^2*(v+A)+v
BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx);
BN_mod_mul (t, t, vpA, p, ctx);
BN_mod_add (t, t, v, p, ctx);
int legendre = Legendre (t, ctx);
BIGNUM * x = BN_CTX_get (ctx);
if (legendre == 1)
BN_copy (x, v);
else
{
BN_sub (x, p, v);
BN_mod_sub (x, x, A, p, ctx);
}
bn2buf (x, key, 32);
for (size_t i = 0; i < 16; i++) // To Little Endian
{
uint8_t tmp = key[i];
key[i] = key[31 - i];
key[31 - i] = tmp;
}
}
else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
return ret;
}
void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const
{
BIGNUM * t = BN_CTX_get (ctx);
BN_mod_exp (t, x, p14, p, ctx); // t = x^((p-1)/4)
BN_mod_exp (r, x, p38, p, ctx); // r = x^((p+3)/8)
BN_add_word (t, 1);
if (!BN_cmp (t, p))
BN_mod_mul (r, r, sqrtn1, p, ctx);
if (BN_cmp (r, p12) > 0) // r > (p-1)/2
BN_sub (r, p, r);
}
int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const
{
// assume a < p, so don't check for a % p = 0, but a = 0 only
if (BN_is_zero(a)) return 0;
BIGNUM * r = BN_CTX_get (ctx);
BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
if (BN_is_word(r, 1))
return 1;
else if (BN_is_zero(r))
return 0;
return -1;
}
static std::unique_ptr<Elligator2> g_Elligator;
std::unique_ptr<Elligator2>& GetElligator ()
{
if (!g_Elligator)
{
auto el = new Elligator2();
if (!g_Elligator) // make sure it was not created already
g_Elligator.reset (el);
else
delete el;
}
return g_Elligator;
}
}
}

39
libi2pd/Elligator.h

@ -0,0 +1,39 @@
#ifndef ELLIGATOR_H__
#define ELLIGATOR_H__
#include <inttypes.h>
#include <memory>
#include <openssl/bn.h>
namespace i2p
{
namespace crypto
{
class Elligator2
{
public:
Elligator2 ();
~Elligator2 ();
bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false) const;
bool Decode (const uint8_t * encoded, uint8_t * key) const;
private:
void SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const;
int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p
private:
BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu;
};
std::unique_ptr<Elligator2>& GetElligator ();
}
}
#endif

83
libi2pd/Garlic.cpp

@ -11,6 +11,7 @@
#include "Timestamp.h" #include "Timestamp.h"
#include "Log.h" #include "Log.h"
#include "FS.h" #include "FS.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "Garlic.h" #include "Garlic.h"
namespace i2p namespace i2p
@ -434,6 +435,7 @@ namespace garlic
} }
buf += 4; // length buf += 4; // length
auto it = m_Tags.find (SessionTag(buf)); auto it = m_Tags.find (SessionTag(buf));
// AES tag might be used even if encryption type is not ElGamal/AES
if (it != m_Tags.end ()) if (it != m_Tags.end ())
{ {
// tag found. Use AES // tag found. Use AES
@ -452,7 +454,13 @@ namespace garlic
} }
else else
{ {
// tag not found. Use ElGamal // tag not found. Handle depending on encryption type
if (GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)
{
HandleECIESx25519 (buf, length);
return;
}
// otherwise assume ElGamal/AES
ElGamalBlock elGamal; ElGamalBlock elGamal;
if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx)) if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx))
{ {
@ -543,7 +551,7 @@ namespace garlic
LogPrint (eLogError, "Garlic: message is too short"); LogPrint (eLogError, "Garlic: message is too short");
break; break;
} }
HandleI2NPMessage (buf, len - offset, from); HandleI2NPMessage (buf, len - offset);
break; break;
case eGarlicDeliveryTypeDestination: case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination"); LogPrint (eLogDebug, "Garlic: type destination");
@ -554,7 +562,7 @@ namespace garlic
LogPrint (eLogError, "Garlic: message is too short"); LogPrint (eLogError, "Garlic: message is too short");
break; break;
} }
HandleI2NPMessage (buf, len - offset, from); HandleI2NPMessage (buf, len - offset);
break; break;
case eGarlicDeliveryTypeTunnel: case eGarlicDeliveryTypeTunnel:
{ {
@ -714,9 +722,8 @@ namespace garlic
m_DeliveryStatusSessions[msgID] = session; m_DeliveryStatusSessions[msgID] = session;
} }
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{ {
uint32_t msgID = bufbe32toh (msg->GetPayload ());
GarlicRoutingSessionPtr session; GarlicRoutingSessionPtr session;
{ {
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex); std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
@ -748,7 +755,8 @@ namespace garlic
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
HandleDeliveryStatusMessage (msg); uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
HandleDeliveryStatusMessage (msgID);
} }
void GarlicDestination::SaveTags () void GarlicDestination::SaveTags ()
@ -821,5 +829,68 @@ namespace garlic
if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT) if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT)
i2p::fs::Remove (it); i2p::fs::Remove (it);
} }
void GarlicDestination::HandleECIESx25519 (const uint8_t * buf, size_t len)
{
ECIESX25519AEADRatchetSession session;
session.NewIncomingSession (*this, buf, len, std::bind (&GarlicDestination::HandleECIESx25519GarlicClove,
this, std::placeholders::_1, std::placeholders::_2));
}
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len)
{
const uint8_t * buf1 = buf;
uint8_t flag = buf[0]; buf++; // flag
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
switch (deliveryType)
{
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination");
buf += 32; // TODO: check destination
// no break here
case eGarlicDeliveryTypeLocal:
{
LogPrint (eLogDebug, "Garlic: type local");
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration
ptrdiff_t offset = buf - buf1;
if (offset <= (int)len)
HandleCloveI2NPMessage (typeID, buf, len - offset);
else
LogPrint (eLogError, "Garlic: clove is too long");
break;
}
case eGarlicDeliveryTypeTunnel:
{
LogPrint (eLogDebug, "Garlic: type tunnel");
// gwHash and gwTunnel sequence is reverted
const uint8_t * gwHash = buf;
buf += 32;
ptrdiff_t offset = buf - buf1;
if (offset + 13 > (int)len)
{
LogPrint (eLogError, "Garlic: message is too short");
break;
}
uint32_t gwTunnel = bufbe32toh (buf); buf += 4;
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration
offset += 13;
if (GetTunnelPool ())
{
auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel)
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset));
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
}
else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
break;
}
default:
LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType);
}
}
} }
} }

9
libi2pd/Garlic.h

@ -190,12 +190,13 @@ namespace garlic
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0; virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
protected: protected:
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) = 0;
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg); void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void HandleDeliveryStatusMessage (uint32_t msgID);
void SaveTags (); void SaveTags ();
void LoadTags (); void LoadTags ();
@ -206,6 +207,10 @@ namespace garlic
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);
// ECIES-X25519-AEAD-Ratchet
void HandleECIESx25519 (const uint8_t * buf, size_t len);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
private: private:
BN_CTX * m_Ctx; // incoming BN_CTX * m_Ctx; // incoming

9
libi2pd/Identity.cpp

@ -417,6 +417,9 @@ namespace data
case CRYPTO_KEY_TYPE_ELGAMAL: case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key); return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key); return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key);
@ -674,6 +677,9 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key); return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break;
default: default:
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType); LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
}; };
@ -750,6 +756,9 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub); i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
break; break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
break;
default: default:
LogPrint (eLogError, "Identity: Crypto key type ", (int)type, " is not supported"); LogPrint (eLogError, "Identity: Crypto key type ", (int)type, " is not supported");
} }

4
libi2pd/Identity.h

@ -55,6 +55,7 @@ namespace data
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;
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET = 4;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
@ -215,6 +216,7 @@ namespace data
virtual bool IsDestination () const = 0; // for garlic virtual bool IsDestination () const = 0; // for garlic
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
}; };
class LocalDestination class LocalDestination
@ -226,6 +228,8 @@ namespace data
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0; virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override for LeaseSet
virtual const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet
}; };
} }
} }

5
libi2pd/LeaseSet.cpp

@ -355,7 +355,6 @@ namespace data
offset += propertiesLen; // skip for now. TODO: implement properties offset += propertiesLen; // skip for now. TODO: implement properties
if (offset + 1 >= len) return 0; if (offset + 1 >= len) return 0;
// key sections // key sections
uint16_t currentKeyType = 0;
int numKeySections = buf[offset]; offset++; int numKeySections = buf[offset]; offset++;
for (int i = 0; i < numKeySections; i++) for (int i = 0; i < numKeySections; i++)
{ {
@ -368,10 +367,10 @@ namespace data
// we pick first valid key, higher key type has higher priority 4-1-0 // we pick first valid key, higher key type has higher priority 4-1-0
// if two keys with of the same type, pick first // if two keys with of the same type, pick first
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset); auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
if (encryptor && (!m_Encryptor || keyType > currentKeyType)) if (encryptor && (!m_Encryptor || keyType > m_EncryptionType))
{ {
m_Encryptor = encryptor; // TODO: atomic m_Encryptor = encryptor; // TODO: atomic
currentKeyType = keyType; m_EncryptionType = keyType;
} }
} }
offset += encryptionKeyLen; offset += encryptionKeyLen;

2
libi2pd/LeaseSet.h

@ -147,6 +147,7 @@ namespace data
// implements RoutingDestination // implements RoutingDestination
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
private: private:
@ -167,6 +168,7 @@ namespace data
uint32_t m_PublishedTimestamp = 0; uint32_t m_PublishedTimestamp = 0;
bool m_IsPublic = true, m_IsPublishedEncrypted = false; bool m_IsPublic = true, m_IsPublishedEncrypted = false;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier; std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
CryptoKeyType m_EncryptionType = CRYPTO_KEY_TYPE_ELGAMAL;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2 std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
}; };

49
libi2pd/NTCP2.cpp

@ -441,23 +441,17 @@ namespace transport
void NTCP2Session::KeyDerivationFunctionDataPhase () void NTCP2Session::KeyDerivationFunctionDataPhase ()
{ {
uint8_t tempKey[32]; unsigned int len; uint8_t k[64];
HMAC(EVP_sha256(), m_Establisher->GetCK (), 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(ck, zerolen) i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "", k); // k_ab, k_ba = HKDF(ck, zerolen)
static uint8_t one[1] = { 1 }; memcpy (m_Kab, k, 32); memcpy (m_Kba, k + 32, 32);
HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Kab, &len); // k_ab = HMAC-SHA256(temp_key, byte(0x01)). uint8_t master[32];
m_Kab[32] = 2; i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "ask", master, 32); // ask_master = HKDF(ck, zerolen, info="ask")
HMAC(EVP_sha256(), tempKey, 32, m_Kab, 33, m_Kba, &len); // k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02))
static uint8_t ask[4] = { 'a', 's', 'k', 1 }, master[32];
HMAC(EVP_sha256(), tempKey, 32, ask, 4, master, &len); // ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01))
uint8_t h[39]; uint8_t h[39];
memcpy (h, m_Establisher->GetH (), 32); memcpy (h, m_Establisher->GetH (), 32);
memcpy (h + 32, "siphash", 7); memcpy (h + 32, "siphash", 7);
HMAC(EVP_sha256(), master, 32, h, 39, tempKey, &len); // temp_key = HMAC-SHA256(ask_master, h || "siphash") i2p::crypto::HKDF (master, h, 39, "", master, 32); // sip_master = HKDF(ask_master, h || "siphash")
HMAC(EVP_sha256(), tempKey, 32, one, 1, master, &len); // sip_master = HMAC-SHA256(temp_key, byte(0x01)) i2p::crypto::HKDF (master, nullptr, 0, "", k); // sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen)
HMAC(EVP_sha256(), master, 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(sip_master, zerolen) memcpy (m_Sipkeysab, k, 32); memcpy (m_Sipkeysba, k + 32, 32);
HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Sipkeysab, &len); // sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)).
m_Sipkeysab[32] = 2;
HMAC(EVP_sha256(), tempKey, 32, m_Sipkeysab, 33, m_Sipkeysba, &len); // sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02))
} }
@ -1035,7 +1029,9 @@ namespace transport
if (ecode) if (ecode)
{ {
LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ());
Terminate ();
} }
else else
{ {
@ -1260,7 +1256,10 @@ namespace transport
bool NTCP2Server::AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming) bool NTCP2Server::AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming)
{ {
if (!session || !session->GetRemoteIdentity ()) return false; if (!session) return false;
if (incoming)
m_PendingIncomingSessions.remove (session);
if (!session->GetRemoteIdentity ()) return false;
auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_NTCP2Sessions.find (ident); auto it = m_NTCP2Sessions.find (ident);
if (it != m_NTCP2Sessions.end ()) if (it != m_NTCP2Sessions.end ())
@ -1270,8 +1269,6 @@ namespace transport
return false; return false;
} }
m_NTCP2Sessions.insert (std::make_pair (ident, session)); m_NTCP2Sessions.insert (std::make_pair (ident, session));
if (incoming)
m_PendingIncomingSessions.remove (session);
return true; return true;
} }
@ -1342,15 +1339,21 @@ namespace transport
{ {
conn->ServerLogin (); conn->ServerLogin ();
m_PendingIncomingSessions.push_back (conn); m_PendingIncomingSessions.push_back (conn);
conn = nullptr;
} }
} }
else else
LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ());
} }
else
LogPrint (eLogError, "NTCP2: Accept error ", error.message ());
if (error != boost::asio::error::operation_aborted) if (error != boost::asio::error::operation_aborted)
{ {
conn = std::make_shared<NTCP2Session> (*this); if (!conn) // connection is used, create new one
conn = std::make_shared<NTCP2Session> (*this);
else // reuse failed
conn->Close ();
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this,
conn, std::placeholders::_1)); conn, std::placeholders::_1));
} }
@ -1406,13 +1409,13 @@ namespace transport
// pending // pending
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
{ {
if ((*it)->IsEstablished () || (*it)->IsTerminated ()) if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts))
it = m_PendingIncomingSessions.erase (it); // established or terminated
else if ((*it)->IsTerminationTimeoutExpired (ts))
{ {
(*it)->Terminate (); (*it)->Terminate ();
it = m_PendingIncomingSessions.erase (it); // expired it = m_PendingIncomingSessions.erase (it); // etsablished of expired
} }
else if ((*it)->IsTerminated ())
it = m_PendingIncomingSessions.erase (it); // already terminated
else else
it++; it++;
} }

3
libi2pd/NTCP2.h

@ -133,6 +133,7 @@ namespace transport
void Terminate (); void Terminate ();
void TerminateByTimeout (); void TerminateByTimeout ();
void Done (); void Done ();
void Close () { m_Socket.close (); }; // for accept
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
@ -194,7 +195,7 @@ namespace transport
std::unique_ptr<NTCP2Establisher> m_Establisher; std::unique_ptr<NTCP2Establisher> m_Establisher;
// data phase // data phase
uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], 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_PKEY * m_SendSipKey, * m_ReceiveSipKey; EVP_PKEY * m_SendSipKey, * m_ReceiveSipKey;

15
libi2pd/RouterContext.cpp

@ -27,12 +27,8 @@ namespace i2p
void RouterContext::Init () void RouterContext::Init ()
{ {
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
#ifdef WIN32
// for compatibility with WinXP
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
#else
m_StartupTime = std::chrono::steady_clock::now(); m_StartupTime = std::chrono::steady_clock::now();
#endif
if (!Load ()) if (!Load ())
CreateNewRouter (); CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr); m_Decryptor = m_Keys.CreateDecryptor (nullptr);
@ -696,9 +692,9 @@ namespace i2p
return i2p::tunnel::tunnels.GetExploratoryPool (); return i2p::tunnel::tunnels.GetExploratoryPool ();
} }
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len)
{ {
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
} }
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
@ -721,12 +717,7 @@ namespace i2p
uint32_t RouterContext::GetUptime () const uint32_t RouterContext::GetUptime () const
{ {
#ifdef WIN32
// for compatibility with WinXP
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
#else
return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count (); return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count ();
#endif
} }
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const

11
libi2pd/RouterContext.h

@ -115,12 +115,17 @@ namespace i2p
// 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;
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// 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:
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented
private: private:
void CreateNewRouter (); void CreateNewRouter ();
@ -137,11 +142,7 @@ namespace i2p
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor; std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor;
uint64_t m_LastUpdateTime; // in seconds uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill; bool m_AcceptsTunnels, m_IsFloodfill;
#ifdef WIN32
uint64_t m_StartupTime = 0; // in seconds since epoch
#else
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime; std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
#endif
uint64_t m_BandwidthLimit; // allowed bandwidth uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio; int m_ShareRatio;
RouterStatus m_Status; RouterStatus m_Status;

8
libi2pd/Transports.cpp

@ -465,6 +465,7 @@ namespace transport
} }
} }
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available"); LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
peer.Done (); peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident); m_Peers.erase (ident);
@ -647,11 +648,16 @@ namespace transport
auto it = m_Peers.find (ident); auto it = m_Peers.find (ident);
if (it != m_Peers.end ()) if (it != m_Peers.end ())
{ {
auto before = it->second.sessions.size ();
it->second.sessions.remove (session); it->second.sessions.remove (session);
if (it->second.sessions.empty ()) // TODO: why? if (it->second.sessions.empty ())
{ {
if (it->second.delayedMessages.size () > 0) if (it->second.delayedMessages.size () > 0)
{
if (before > 0) // we had an active session before
it->second.numAttempts = 0; // start over
ConnectToPeer (ident, it->second); ConnectToPeer (ident, it->second);
}
else else
{ {
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);

43
libi2pd/util.cpp

@ -22,7 +22,7 @@
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
// inet_pton exists Windows since Vista, but XP haven't that function! // inet_pton exists Windows since Vista, but XP doesn't have that function!
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found // This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
int inet_pton_xp(int af, const char *src, void *dst) int inet_pton_xp(int af, const char *src, void *dst)
{ {
@ -62,16 +62,21 @@ namespace net
#ifdef WIN32 #ifdef WIN32
bool IsWindowsXPorLater() bool IsWindowsXPorLater()
{ {
OSVERSIONINFO osvi; static bool isRequested = false;
static bool isXP = false;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); if (!isRequested)
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); {
GetVersionEx(&osvi); // request
OSVERSIONINFO osvi;
if (osvi.dwMajorVersion <= 5)
return true; ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
else osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
return false; GetVersionEx(&osvi);
isXP = osvi.dwMajorVersion <= 5;
isRequested = true;
}
return isXP;
} }
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback) int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
@ -202,21 +207,20 @@ namespace net
std::string localAddressUniversal = localAddress.to_string(); std::string localAddressUniversal = localAddress.to_string();
#endif #endif
if (IsWindowsXPorLater()) typedef int (* IPN)(int af, const char *src, void *dst);
{ IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton");
#define inet_pton inet_pton_xp if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found
}
if(localAddress.is_v4()) if(localAddress.is_v4())
{ {
sockaddr_in inputAddress; sockaddr_in inputAddress;
inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
return GetMTUWindowsIpv4(inputAddress, fallback); return GetMTUWindowsIpv4(inputAddress, fallback);
} }
else if(localAddress.is_v6()) else if(localAddress.is_v6())
{ {
sockaddr_in6 inputAddress; sockaddr_in6 inputAddress;
inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
return GetMTUWindowsIpv6(inputAddress, fallback); return GetMTUWindowsIpv6(inputAddress, fallback);
} else { } else {
LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported"); LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported");
@ -312,15 +316,14 @@ namespace net
if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af)
{ {
// match // match
char * addr = new char[INET6_ADDRSTRLEN]; char addr[INET6_ADDRSTRLEN];
bzero(addr, INET6_ADDRSTRLEN); memset (addr, 0, INET6_ADDRSTRLEN);
if(af == AF_INET) if(af == AF_INET)
inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN);
else else
inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN);
freeifaddrs(addrs); freeifaddrs(addrs);
std::string cur_ifaddr(addr); std::string cur_ifaddr(addr);
delete[] addr;
return boost::asio::ip::address::from_string(cur_ifaddr); return boost::asio::ip::address::from_string(cur_ifaddr);
} }
cur = cur->ifa_next; cur = cur->ifa_next;

2
libi2pd/version.h

@ -21,7 +21,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 43 #define I2P_VERSION_MICRO 44
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

2
qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml

@ -35,7 +35,7 @@
<translation type="qt" /> <translation type="qt" />
<releases> <releases>
<release version="2.29.0" date="2019-10-21" /> <release version="2.29.0" date="2019-10-21" />
<release version="2.28.0" date="2019-08-27" /> <release version="2.28.0" date="2019-08-27" />
<release version="2.27.0" date="2019-07-03" /> <release version="2.27.0" date="2019-07-03" />
<release version="2.26.0" date="2019-06-07" /> <release version="2.26.0" date="2019-06-07" />

5
tests/Makefile

@ -1,6 +1,6 @@
CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -I../libi2pd/ -pthread -Wl,--unresolved-symbols=ignore-in-object-files CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -I../libi2pd/ -pthread -Wl,--unresolved-symbols=ignore-in-object-files
TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding test-elligator
all: $(TESTS) run all: $(TESTS) run
@ -25,6 +25,9 @@ test-aeadchacha20poly1305: ../libi2pd/Crypto.cpp ../libi2pd/ChaCha20.cpp ../libi
test-blinding: ../libi2pd/Crypto.cpp ../libi2pd/Blinding.cpp ../libi2pd/Ed25519.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Log.cpp ../libi2pd/util.cpp ../libi2pd/Identity.cpp ../libi2pd/Signature.cpp ../libi2pd/Timestamp.cpp test-blinding.cpp test-blinding: ../libi2pd/Crypto.cpp ../libi2pd/Blinding.cpp ../libi2pd/Ed25519.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Log.cpp ../libi2pd/util.cpp ../libi2pd/Identity.cpp ../libi2pd/Signature.cpp ../libi2pd/Timestamp.cpp test-blinding.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system
test-elligator: ../libi2pd/Elligator.cpp ../libi2pd/Crypto.cpp test-elligator.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system
run: $(TESTS) run: $(TESTS)
@for TEST in $(TESTS); do ./$$TEST ; done @for TEST in $(TESTS); do ./$$TEST ; done

77
tests/test-elligator.cpp

@ -0,0 +1,77 @@
#include <cassert>
#include <inttypes.h>
#include <string.h>
#include "Elligator.h"
const uint8_t key[32] =
{
0x33, 0x95, 0x19, 0x64, 0x00, 0x3c, 0x94, 0x08, 0x78, 0x06, 0x3c, 0xcf, 0xd0, 0x34, 0x8a, 0xf4,
0x21, 0x50, 0xca, 0x16, 0xd2, 0x64, 0x6f, 0x2c, 0x58, 0x56, 0xe8, 0x33, 0x83, 0x77, 0xd8, 0x80
};
const uint8_t encoded_key[32] =
{
0x28, 0x20, 0xb6, 0xb2, 0x41, 0xe0, 0xf6, 0x8a, 0x6c, 0x4a, 0x7f, 0xee, 0x3d, 0x97, 0x82, 0x28,
0xef, 0x3a, 0xe4, 0x55, 0x33, 0xcd, 0x41, 0x0a, 0xa9, 0x1a, 0x41, 0x53, 0x31, 0xd8, 0x61, 0x2d
};
const uint8_t encoded_key_high_y[32] =
{
0x3c, 0xfb, 0x87, 0xc4, 0x6c, 0x0b, 0x45, 0x75, 0xca, 0x81, 0x75, 0xe0, 0xed, 0x1c, 0x0a, 0xe9,
0xda, 0xe7, 0x9d, 0xb7, 0x8d, 0xf8, 0x69, 0x97, 0xc4, 0x84, 0x7b, 0x9f, 0x20, 0xb2, 0x77, 0x18
};
const uint8_t encoded1[32] =
{
0xe7, 0x35, 0x07, 0xd3, 0x8b, 0xae, 0x63, 0x99, 0x2b, 0x3f, 0x57, 0xaa, 0xc4, 0x8c, 0x0a, 0xbc,
0x14, 0x50, 0x95, 0x89, 0x28, 0x84, 0x57, 0x99, 0x5a, 0x2b, 0x4c, 0xa3, 0x49, 0x0a, 0xa2, 0x07
};
const uint8_t key1[32] =
{
0x1e, 0x8a, 0xff, 0xfe, 0xd6, 0xbf, 0x53, 0xfe, 0x27, 0x1a, 0xd5, 0x72, 0x47, 0x32, 0x62, 0xde,
0xd8, 0xfa, 0xec, 0x68, 0xe5, 0xe6, 0x7e, 0xf4, 0x5e, 0xbb, 0x82, 0xee, 0xba, 0x52, 0x60, 0x4f
};
const uint8_t encoded2[32] =
{
0x95, 0xa1, 0x60, 0x19, 0x04, 0x1d, 0xbe, 0xfe, 0xd9, 0x83, 0x20, 0x48, 0xed, 0xe1, 0x19, 0x28,
0xd9, 0x03, 0x65, 0xf2, 0x4a, 0x38, 0xaa, 0x7a, 0xef, 0x1b, 0x97, 0xe2, 0x39, 0x54, 0x10, 0x1b
};
const uint8_t key2[32] =
{
0x79, 0x4f, 0x05, 0xba, 0x3e, 0x3a, 0x72, 0x95, 0x80, 0x22, 0x46, 0x8c, 0x88, 0x98, 0x1e, 0x0b,
0xe5, 0x78, 0x2b, 0xe1, 0xe1, 0x14, 0x5c, 0xe2, 0xc3, 0xc6, 0xfd, 0xe1, 0x6d, 0xed, 0x53, 0x63
};
const uint8_t encoded3[32] =
{
0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f
};
const uint8_t key3[32] =
{
0x9c, 0xdb, 0x52, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55
};
int main ()
{
uint8_t buf[32];
i2p::crypto::Elligator2 el;
// encoding tests
el.Encode (key, buf);
assert(memcmp (buf, encoded_key, 32) == 0);
el.Encode (key, buf, true); // with highY
assert(memcmp (buf, encoded_key_high_y, 32) == 0);
// decoding tests
el.Decode (encoded1, buf);
assert(memcmp (buf, key1, 32) == 0);
el.Decode (encoded2, buf);
assert(memcmp (buf, key2, 32) == 0);
el.Decode (encoded3, buf);
assert(memcmp (buf, key3, 32) == 0);
}
Loading…
Cancel
Save