diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/.travis.yml b/.travis.yml index c55f1885..af47f458 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ cache: os: - linux #- osx -dist: trusty +dist: xenial sudo: required compiler: - g++ diff --git a/ChangeLog b/ChangeLog index 07110fe2..293764a6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,22 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.30.0] - 2020-02-25 +### Added +- Single threaded SAM +- Experimental support of ECIES-X25519-AEAD-Ratchet crypto type +### Changed +- Minimal MTU size is 1280 for ipv6 +- Use unordered_map instead map for destination's sessions and tags list +- Use std::shuffle instead std::random_shuffle +- SAM is single threaded by default +- Reseeds list +### Fixed +- Correct termination of streaming destination +- Extra ',' in RouterInfo response in I2PControl +- SAM crash on session termination +- Storage for Android 10 + ## [2.29.0] - 2019-10-21 ### Added - Client auth flag for b33 address diff --git a/Makefile.linux b/Makefile.linux index d1ccf143..9db660c2 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -15,13 +15,14 @@ ifeq ($(shell expr match $(CXX) 'clang'),5) NEEDED_CXXFLAGS += -std=c++11 else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10 NEEDED_CXXFLAGS += -std=c++11 -else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7 +else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # gcc 4.7 - 4.9 NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6 - NEEDED_CXXFLAGS += -std=c++0x -else ifeq ($(shell expr match ${CXXVER} "[5-9]"),1) # gcc >= 5 +else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6 NEEDED_CXXFLAGS += -std=c++11 LDLIBS = -latomic +else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc >= 7 + NEEDED_CXXFLAGS += -std=c++17 + LDLIBS = -latomic else # not supported $(error Compiler too old) endif diff --git a/Makefile.osx b/Makefile.osx index d673d3ef..c6af4fc2 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -2,6 +2,9 @@ CXX = clang++ CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX INCFLAGS = -I/usr/local/include 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) 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 diff --git a/README.md b/README.md index b5fb8f7e..25e6c3e5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)](https://github.com/PurpleI2P/i2pd/releases/latest) [![Snapcraft release](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE) +[![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions) i2pd ==== @@ -63,9 +64,10 @@ Build instructions: **Supported systems:** * GNU/Linux - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) + * CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) + * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. * Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd) * Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) -* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) * Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/) * Snap - [![Snap Status](https://build.snapcraft.io/badge/PurpleI2P/i2pd-snap.svg)](https://build.snapcraft.io/user/PurpleI2P/i2pd-snap) * FreeBSD diff --git a/Win32/installer.iss b/Win32/installer.iss index 736c7038..d4f6f247 100644 --- a/Win32/installer.iss +++ b/Win32/installer.iss @@ -1,5 +1,5 @@ #define I2Pd_AppName "i2pd" -#define I2Pd_ver "2.29.0" +#define I2Pd_ver "2.30.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 71526701..a95e3773 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -17,6 +17,7 @@ android:label="@string/app_name" android:theme="@android:style/Theme.Holo.Light.DarkActionBar" android:requestLegacyExternalStorage="true" + android:usesCleartextTraffic="true" > diff --git a/android/README.md b/android/README.md new file mode 100644 index 00000000..e0850b15 --- /dev/null +++ b/android/README.md @@ -0,0 +1,19 @@ +# how to compile? +## Install the gradle + NDK or use android-studio +[https://gradle.org/install/](https://gradle.org/install/) + +## Install the depencies +``` +git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0 +git clone https://github.com/PurpleI2P/android-ifaddrs.git +git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git +git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git +``` +## Set libs in jni/Application.mk on 24 line: +``` +# change to your own +I2PD_LIBS_PATH = /home/user/i2pd/android/ +``` + +## compile apk file +gradle clean assembleRelease diff --git a/android/build.gradle b/android/build.gradle index 291d90ea..6fcb4521 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -30,8 +30,9 @@ android { applicationId "org.purplei2p.i2pd" targetSdkVersion 29 minSdkVersion 14 - versionCode 2290 - versionName "2.29.0" + versionCode 2300 + versionName "2.30.0" + setProperty("archivesBaseName", archivesBaseName + "-" + versionName) ndk { abiFilters 'armeabi-v7a' abiFilters 'x86' @@ -56,9 +57,10 @@ android { splits { abi { // change that to true if you need splitted apk - enable false + enable true reset() - include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + //include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + include "armeabi-v7a", "x86" universalApk true } } @@ -72,7 +74,7 @@ android { } buildTypes { release { - minifyEnabled true + minifyEnabled false signingConfig signingConfigs.orignal proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' } @@ -87,3 +89,16 @@ android { targetCompatibility = '1.8' } } + +ext.abiCodes = ['armeabi-v7a':1, 'x86':2, 'arm64-v8a':3, 'x86_64':4] +import com.android.build.OutputFile + +android.applicationVariants.all { variant -> + variant.outputs.each { output -> + def baseAbiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI)) + + if (baseAbiVersionCode != null) { + output.versionCodeOverride = baseAbiVersionCode + variant.versionCode + } + } +} diff --git a/android/jni/Android.mk b/android/jni/Android.mk index d44fcc5a..2376509e 100755 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -25,29 +25,29 @@ include $(BUILD_SHARED_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_system -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_system.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_date_time -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_filesystem -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_program_options -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) diff --git a/android/jni/Application.mk b/android/jni/Application.mk index 6d51ac1a..0c291661 100755 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -9,15 +9,15 @@ NDK_TOOLCHAIN_VERSION := clang #APP_STL := c++_shared APP_STL := c++_static -# Enable c++11 extensions in source code -APP_CPPFLAGS += -std=c++11 -fexceptions -frtti +# Enable c++17 extensions in source code +APP_CPPFLAGS += -std=c++17 -fexceptions -frtti APP_CPPFLAGS += -DANDROID -D__ANDROID__ -DUSE_UPNP ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) APP_CPPFLAGS += -DANDROID_ARM7A endif -# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0 # git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/android-ifaddrs.git diff --git a/android/res/layout/webview.xml b/android/res/layout/webview.xml new file mode 100644 index 00000000..d13be23e --- /dev/null +++ b/android/res/layout/webview.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/android/res/menu/options_main.xml b/android/res/menu/options_main.xml index 089c2700..b0f23bd2 100644 --- a/android/res/menu/options_main.xml +++ b/android/res/menu/options_main.xml @@ -12,6 +12,10 @@ android:id="@+id/action_graceful_stop" android:orderInCategory="98" android:title="@string/action_graceful_stop" /> + Battery Optimizations Your Android OS version does not support showing the dialog for battery optimizations for applications. Planned shutdown canceled + Start webview diff --git a/android/src/org/purplei2p/i2pd/I2PDActivity.java b/android/src/org/purplei2p/i2pd/I2PDActivity.java index 89881b42..258bafe2 100755 --- a/android/src/org/purplei2p/i2pd/I2PDActivity.java +++ b/android/src/org/purplei2p/i2pd/I2PDActivity.java @@ -39,15 +39,23 @@ import android.view.MenuItem; import android.widget.TextView; import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; // For future package update checking +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + + import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS; public class I2PDActivity extends Activity { + private WebView webView; + private static final String TAG = "i2pdActvt"; private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1; public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; @@ -56,6 +64,7 @@ public class I2PDActivity extends Activity { private TextView textView; private boolean assetsCopied; private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/"; + //private ConfigParser parser = new ConfigParser(i2pdpath); // TODO: private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); @@ -262,6 +271,16 @@ public class I2PDActivity extends Activity { case R.id.action_battery_otimizations: onActionBatteryOptimizations(); return true; + case R.id.action_start_webview: + setContentView(R.layout.webview); + this.webView = (WebView) findViewById(R.id.webview1); + this.webView.setWebViewClient(new WebViewClient()); + + WebSettings webSettings = this.webView.getSettings(); + webSettings.setBuiltInZoomControls(true); + webSettings.setJavaScriptEnabled(false); + this.webView.loadUrl("http://127.0.0.1:7070"); // TODO: instead 7070 I2Pd....HttpPort + break; } return super.onOptionsItemSelected(item); diff --git a/android_binary_only/jni/Android.mk b/android_binary_only/jni/Android.mk index 7e73b23d..a59e32e5 100755 --- a/android_binary_only/jni/Android.mk +++ b/android_binary_only/jni/Android.mk @@ -26,29 +26,29 @@ include $(BUILD_EXECUTABLE) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_system -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_system.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_date_time -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_filesystem -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_program_options -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) diff --git a/android_binary_only/jni/Application.mk b/android_binary_only/jni/Application.mk index 655bf430..bb4afac3 100755 --- a/android_binary_only/jni/Application.mk +++ b/android_binary_only/jni/Application.mk @@ -9,8 +9,8 @@ APP_PLATFORM := android-14 NDK_TOOLCHAIN_VERSION := clang APP_STL := c++_static -# Enable c++11 extensions in source code -APP_CPPFLAGS += -std=c++11 -fvisibility=default -fPIE +# Enable c++17 extensions in source code +APP_CPPFLAGS += -std=c++17 -fvisibility=default -fPIE APP_CPPFLAGS += -DANDROID_BINARY -DANDROID -D__ANDROID__ -DUSE_UPNP APP_LDFLAGS += -rdynamic -fPIE -pie @@ -21,7 +21,7 @@ endif # Forcing debug optimization. Use `ndk-build NDK_DEBUG=1` instead. #APP_OPTIM := debug -# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0 # git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/android-ifaddrs.git diff --git a/appveyor.yml b/appveyor.yml index f6eee9ab..8c4a1a9d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.29.0.{build} +version: 2.30.0.{build} pull_requests: do_not_increment_build_number: true branches: diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index a9cf3a83..f5c1280e 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -83,6 +83,7 @@ set (LIBI2PD_SRC "${LIBI2PD_SRC_DIR}/NTCP2.cpp" "${LIBI2PD_SRC_DIR}/Blinding.cpp" "${LIBI2PD_SRC_DIR}/Elligator.cpp" + "${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp" ) if (WITH_WEBSOCKETS) @@ -183,17 +184,17 @@ else() set( CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections" ) # -flto is added from above endif () -# check for c++11 support +# check for c++17 & c++11 support include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED) CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED) -CHECK_CXX_COMPILER_FLAG("-std=c++0x" CXX0X_SUPPORTED) -if (CXX11_SUPPORTED) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) -elseif (CXX0X_SUPPORTED) # gcc 4.6 - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x" ) -elseif (NOT MSVC) - message(SEND_ERROR "C++11 standard not seems to be supported by compiler. Too old version?") -endif () +if(CXX17_SUPPORTED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") +elseif(CXX11_SUPPORTED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +else() + message(SEND_ERROR "C++17 nor C++11 standard not seems to be supported by compiler. Too old version?") +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe") diff --git a/contrib/certificates/reseed/backup_at_mail.i2p.crt b/contrib/certificates/reseed/backup_at_mail.i2p.crt deleted file mode 100644 index 73b08eaa..00000000 --- a/contrib/certificates/reseed/backup_at_mail.i2p.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY -WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt -b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu -aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC -WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255 -bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls -LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d -roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr -omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H -uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV -Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA -3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo -dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ -HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv -TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl -/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV -exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe -jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl -1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T -5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa -0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME -Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge -vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S -DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O -lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs -cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA -FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG -1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/ -4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI -flpzWXkFM2D36OUaubfe9YY= ------END CERTIFICATE----- diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index 31b8d4a5..f9571ca0 100644 --- a/contrib/rpm/i2pd-git.spec +++ b/contrib/rpm/i2pd-git.spec @@ -1,7 +1,7 @@ %define git_hash %(git rev-parse HEAD | cut -c -7) Name: i2pd-git -Version: 2.29.0 +Version: 2.30.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -55,14 +55,22 @@ cd build %endif %endif +%if 0%{?mageia} > 7 +pushd build make %{?_smp_mflags} +popd +%else +make %{?_smp_mflags} +%endif %install -cd build +pushd build + %if 0%{?mageia} -cd build +pushd build %endif + chrpath -d i2pd %{__install} -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd %{__install} -D -m 755 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf @@ -110,6 +118,9 @@ getent passwd i2pd >/dev/null || \ %changelog +* Tue Feb 25 2020 orignal - 2.30.0 +- update to 2.30.0 + * Mon Oct 21 2019 orignal - 2.29.0 - update to 2.29.0 diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index 3db99af0..b3cca8ab 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -1,5 +1,5 @@ Name: i2pd -Version: 2.29.0 +Version: 2.30.0 Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -53,14 +53,22 @@ cd build %endif %endif +%if 0%{?mageia} > 7 +pushd build make %{?_smp_mflags} +popd +%else +make %{?_smp_mflags} +%endif %install -cd build +pushd build + %if 0%{?mageia} -cd build +pushd build %endif + chrpath -d i2pd install -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd install -D -m 755 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf @@ -108,6 +116,9 @@ getent passwd i2pd >/dev/null || \ %changelog +* Tue Feb 25 2020 orignal - 2.30.0 +- update to 2.30.0 + * Mon Oct 21 2019 orignal - 2.29.0 - update to 2.29.0 diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 7dc6a9d2..36e26785 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -54,6 +54,7 @@ namespace http { " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n" " a, .slide label { text-decoration: none; color: #894C84; }\r\n" " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n" + " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none; color: initial;}\r\n" " .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n" " .wrapper { margin: 0 auto; padding: 1em; max-width: 60em; }\r\n" " .left { float: left; position: absolute; }\r\n" @@ -64,10 +65,11 @@ namespace http { " .tunnel.building { color: #434343; }\r\n" " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n" " table { width: 100%; border-collapse: collapse; text-align: center; }\r\n" - " .slide p, .slide [type='checkbox']{ display:none; }\r\n" - " .slide [type='checkbox']:checked ~ p { display:block; margin-top: 0; padding: 0; }\r\n" + " .slide p, .slide [type='checkbox'] { display: none; }\r\n" + " .slide [type='checkbox']:checked ~ p { display: block; margin-top: 0; padding: 0; }\r\n" " .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n" " .enabled:after { color: #56B734; content: \"Enabled\" }\r\n" + " .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n" "\r\n"; const char HTTP_PAGE_TUNNELS[] = "tunnels"; @@ -89,10 +91,12 @@ namespace http { const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test"; const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config"; const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel"; + const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_ADDRESS[] = "address"; static std::string ConvertTime (uint64_t time); + std::map HTTPConnection::m_Tokens; static void ShowUptime (std::stringstream& s, int seconds) { @@ -203,10 +207,7 @@ namespace http { s << "ERROR: " << string << "
\r\n"; } - void ShowStatus ( - std::stringstream& s, - bool includeHiddenContent, - i2p::http::OutputFormatEnum outputFormat) + void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) { s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); @@ -253,12 +254,12 @@ namespace http { ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n"; - s << "
"; - if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { - s << "\r\n\r\n

\r\n"; - } - if(includeHiddenContent) { - s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; + s << "

"; + if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { + s << "\r\n\r\n

\r\n"; + } + if(includeHiddenContent) { + s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; s << "Router Family: " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; s << "Router Caps: " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; s << "Our external address:" << "
\r\n" ; @@ -292,12 +293,12 @@ namespace http { } s << address->host.to_string() << ":" << address->port << "
\r\n"; } - } + } s << "

\r\n
\r\n"; - if(outputFormat==OutputFormatEnum::forQtUi) { - s << "
"; - } - s << "Routers: " << i2p::data::netdb.GetNumRouters () << " "; + if(outputFormat==OutputFormatEnum::forQtUi) { + s << "
"; + } + s << "Routers: " << i2p::data::netdb.GetNumRouters () << " "; s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; @@ -308,17 +309,17 @@ namespace http { s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; - if(outputFormat==OutputFormatEnum::forWebConsole) { - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); - s << "\r\n"; - s << "
Services
ServiceState
" << "HTTP Proxy" << "
" << "SOCKS Proxy" << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
" << "I2PControl" << "
\r\n"; - } + if(outputFormat==OutputFormatEnum::forWebConsole) { + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); + s << "\r\n"; + s << "
Services
ServiceState
" << "HTTP Proxy" << "
" << "SOCKS Proxy" << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
" << "I2PControl" << "
\r\n"; + } } void ShowLocalDestinations (std::stringstream& s) @@ -352,7 +353,7 @@ namespace http { static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr dest) { - s << "Base64:
\r\n
\r\n
\r\n"; if (dest->IsEncryptedLeaseSet ()) { @@ -403,19 +404,21 @@ namespace http { s << "
\r\n"; } - void ShowLocalDestination (std::stringstream& s, const std::string& b32) + void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token) { s << "Local Destination:
\r\n
\r\n"; i2p::data::IdentHash ident; ident.FromBase32 (b32); auto dest = i2p::client::context.FindLocalDestination (ident); + if (dest) { ShowLeaseSetDestination (s, dest); // show streams - s << "\r\n"; - s << ""; - s << ""; + s << "
Streams
StreamIDDestination
\r\n\r\n\r\n"; + s << ""; + s << ""; s << ""; s << ""; s << ""; @@ -424,13 +427,20 @@ namespace http { s << ""; s << ""; s << ""; - s << "\r\n"; + s << "\r\n\r\n\r\n"; for (const auto& it: dest->GetAllStreams ()) { + auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()); s << ""; - s << ""; - s << ""; + s << ""; + if (it->GetRecvStreamID ()) { + s << ""; + } else { + s << ""; s << ""; s << ""; s << ""; @@ -441,7 +451,7 @@ namespace http { s << ""; s << "\r\n"; } - s << "
Streams
StreamID"; // Stream closing button column + s << "DestinationSentReceivedOutRTTWindowStatus
" << it->GetSendStreamID () << "" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "" << it->GetRecvStreamID () << ""; + } + s << "" << streamDest << "" << it->GetNumSentBytes () << "" << it->GetNumReceivedBytes () << "" << it->GetSendQueueSize () << "" << (int)it->GetStatus () << "
"; + s << "\r\n"; } } @@ -858,7 +868,8 @@ namespace http { m_Socket->close (); } - bool HTTPConnection::CheckAuth (const HTTPReq & req) { + bool HTTPConnection::CheckAuth (const HTTPReq & req) + { /* method #1: http://user:pass@127.0.0.1:7070/ */ if (req.uri.find('@') != std::string::npos) { URL url; @@ -920,7 +931,7 @@ namespace http { } else if (req.uri.find("cmd=") != std::string::npos) { HandleCommand (req, res, s); } else { - ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); + ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); res.add_header("Refresh", "10"); } ShowPageTail (s); @@ -930,7 +941,23 @@ namespace http { SendReply (res, content); } - std::map HTTPConnection::m_Tokens; + uint32_t HTTPConnection::CreateToken () + { + uint32_t token; + RAND_bytes ((uint8_t *)&token, 4); + token &= 0x7FFFFFFF; // clear first bit + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) + { + if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT) + it = m_Tokens.erase (it); + else + ++it; + } + m_Tokens[token] = ts; + return token; + } + void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s) { std::map params; @@ -947,18 +974,7 @@ namespace http { ShowTunnels (s); else if (page == HTTP_PAGE_COMMANDS) { - uint32_t token; - RAND_bytes ((uint8_t *)&token, 4); - token &= 0x7FFFFFFF; // clear first bit - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) - { - if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT) - it = m_Tokens.erase (it); - else - ++it; - } - m_Tokens[token] = ts; + uint32_t token = CreateToken (); ShowCommands (s, token); } else if (page == HTTP_PAGE_TRANSIT_TUNNELS) @@ -966,7 +982,10 @@ namespace http { else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) ShowLocalDestinations (s); else if (page == HTTP_PAGE_LOCAL_DESTINATION) - ShowLocalDestination (s, params["b32"]); + { + uint32_t token = CreateToken (); + ShowLocalDestination (s, params["b32"], token); + } else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION) ShowI2CPLocalDestination (s, params["i2cp_id"]); else if (page == HTTP_PAGE_SAM_SESSIONS) @@ -992,7 +1011,10 @@ namespace http { url.parse(req.uri); url.parse_query(params); + std::string webroot; i2p::config::GetOption("http.webroot", webroot); + std::string redirect = "5; url=" + webroot + "?page=commands"; std::string token = params["token"]; + if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) { ShowError(s, "Invalid token"); @@ -1008,36 +1030,74 @@ namespace http { i2p::context.SetAcceptsTunnels (true); else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT) i2p::context.SetAcceptsTunnels (false); - else if (cmd == HTTP_COMMAND_SHUTDOWN_START) { + else if (cmd == HTTP_COMMAND_SHUTDOWN_START) + { i2p::context.SetAcceptsTunnels (false); #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) Daemon.gracefulShutdownInterval = 10*60; #elif defined(WIN32_APP) i2p::win32::GracefulShutdown (); #endif - } else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) { + } + else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) + { i2p::context.SetAcceptsTunnels (true); #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) Daemon.gracefulShutdownInterval = 0; #elif defined(WIN32_APP) i2p::win32::StopGracefulShutdown (); #endif - } else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) { + } + else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) + { #ifndef WIN32_APP Daemon.running = false; #else i2p::win32::StopWin32App (); #endif - } else if (cmd == HTTP_COMMAND_LOGLEVEL){ + } + else if (cmd == HTTP_COMMAND_LOGLEVEL) + { std::string level = params["level"]; SetLogLevel (level); - } else { + } + else if (cmd == HTTP_COMMAND_KILLSTREAM) + { + std::string b32 = params["b32"]; + uint32_t streamID = std::stoul(params["streamID"], nullptr); + + i2p::data::IdentHash ident; + ident.FromBase32 (b32); + auto dest = i2p::client::context.FindLocalDestination (ident); + + if (streamID) + { + if (dest) + { + if(dest->DeleteStream (streamID)) + s << "SUCCESS: Stream closed

\r\n"; + else + s << "ERROR: Stream not found or already was closed

\r\n"; + } + else + s << "ERROR: Destination not found

\r\n"; + } + else + s << "ERROR: StreamID can be null

\r\n"; + + s << "Return to destination page
\r\n"; + s << "

You will be redirected back in 5 seconds"; + redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; + res.add_header("Refresh", redirect.c_str()); + return; + } + else + { res.code = 400; ShowError(s, "Unknown command: " + cmd); return; } - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - std::string redirect = "5; url=" + webroot + "?page=commands"; + s << "SUCCESS: Command accepted

\r\n"; s << "Back to commands list
\r\n"; s << "

You will be redirected in 5 seconds"; diff --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h index 3d32ed2b..f5ac95fc 100644 --- a/daemon/HTTPServer.h +++ b/daemon/HTTPServer.h @@ -35,6 +35,7 @@ namespace http void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data); void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data); void SendReply (HTTPRes & res, std::string & content); + uint32_t CreateToken (); private: diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index 572292ee..acf054f1 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -394,13 +394,15 @@ namespace client void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { + bool first = true; for (auto it = params.begin (); it != params.end (); it++) { LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first); auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { - if (it != params.begin ()) results << ","; + if (!first) results << ","; + else first = false; (this->*(it1->second))(results); } else diff --git a/debian/changelog b/debian/changelog index f5f98680..da177cbd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +i2pd (2.30.0-1) unstable; urgency=medium + + * updated to version 2.30.0/0.9.45 + + -- orignal Tue, 25 Feb 2020 16:00:00 +0000 + i2pd (2.29.0-1) unstable; urgency=medium * updated to version 2.29.0/0.9.43 diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index e2f4bdd0..521ebf65 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -60,7 +60,7 @@ namespace config { ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("ntcp", value()->default_value(false), "Enable NTCP transport (default: disabled)") ("ssu", value()->default_value(true), "Enable SSU transport (default: enabled)") - ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") + ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Windows service management ('install' or 'remove')") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") @@ -131,6 +131,7 @@ namespace config { ("sam.enabled", value()->default_value(true), "Enable or disable SAM Application bridge") ("sam.address", value()->default_value("127.0.0.1"), "SAM listen address") ("sam.port", value()->default_value(7656), "SAM listen port") + ("sam.singlethread", value()->default_value(true), "Sessions run in the SAM bridge's thread") ; options_description bob("BOB options"); @@ -190,9 +191,10 @@ namespace config { "https://reseed.i2p-projekt.de/," "https://i2p.mooo.com/netDb/," "https://netdb.i2p2.no/," + "https://reseed.i2p2.no/," + "https://reseed2.i2p2.no/," // "https://us.reseed.i2p2.no:444/," // mamoth's shit // "https://uk.reseed.i2p2.no:444/," // mamoth's shit - "https://download.xxlspeed.com/," "https://reseed-fr.i2pd.xyz/," "https://reseed.memcpy.io/," "https://reseed.onion.im/," @@ -237,6 +239,7 @@ namespace config { ("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)") ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") ("ntcp2.addressv6", value()->default_value("::"), "Address to bind NTCP2 on") + ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport") ; options_description nettime("Time sync options"); @@ -303,16 +306,16 @@ namespace config { std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; std::cout << m_OptionsDesc; exit(EXIT_SUCCESS); - } + } else if (m_Options.count("version")) { 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 / 100 % 1000 << "." // min. version << BOOST_VERSION % 100 // patch version << std::endl; -#if defined(OPENSSL_VERSION_TEXT) +#if defined(OPENSSL_VERSION_TEXT) std::cout << OPENSSL_VERSION_TEXT << std::endl; #endif #if defined(LIBRESSL_VERSION_TEXT) diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 33490c8a..32410daf 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -27,8 +27,8 @@ # define X509_getm_notAfter X509_get_notAfter #else # define LEGACY_OPENSSL 0 -# define OPENSSL_HKDF 1 # if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1 +# define OPENSSL_HKDF 1 # define OPENSSL_EDDSA 1 # define OPENSSL_X25519 1 # define OPENSSL_SIPHASH 1 diff --git a/libi2pd/CryptoKey.cpp b/libi2pd/CryptoKey.cpp index df5dd38f..878e984a 100644 --- a/libi2pd/CryptoKey.cpp +++ b/libi2pd/CryptoKey.cpp @@ -14,6 +14,7 @@ namespace crypto void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { + if (!ctx) return; ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding); } @@ -24,6 +25,7 @@ namespace crypto bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { + if (!ctx) return false; return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding); } @@ -151,11 +153,9 @@ namespace crypto memcpy (m_PublicKey, pub, 32); } - void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t * epriv, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding) + void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool) { - X25519Keys ep; - ep.SetPrivateKey (epriv); - ep.Agree (m_PublicKey, sharedSecret); + memcpy (pub, m_PublicKey, 32); } ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv) diff --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index 6412f4d3..701e9482 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -125,8 +125,8 @@ namespace crypto 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) + void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool); + // copies m_PublicKey to pub private: diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index ca60f0fb..d4620ed9 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -7,15 +7,15 @@ #include "Timestamp.h" #include "NetDb.hpp" #include "Destination.h" -#include "util.h" namespace i2p { namespace client { - LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map * params): - m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic), - m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), + LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service, + bool isPublic, const std::map * params): + m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), + m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service), m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE) { @@ -123,77 +123,36 @@ namespace client LeaseSetDestination::~LeaseSetDestination () { - if (m_IsRunning) - Stop (); if (m_Pool) i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); for (auto& it: m_LeaseSetRequests) it.second->Complete (nullptr); } - void LeaseSetDestination::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Destination: runtime exception: ", ex.what ()); - } - } - } - - bool LeaseSetDestination::Start () + void LeaseSetDestination::Start () { - if (!m_IsRunning) - { - if (m_Nickname.empty ()) - m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname - LoadTags (); - m_IsRunning = true; - m_Pool->SetLocalDestination (shared_from_this ()); - m_Pool->SetActive (true); - m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); - m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, - shared_from_this (), std::placeholders::_1)); - m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ())); - - return true; - } - else - return false; + if (m_Nickname.empty ()) + m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname + LoadTags (); + m_Pool->SetLocalDestination (shared_from_this ()); + m_Pool->SetActive (true); + m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); + m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, + shared_from_this (), std::placeholders::_1)); } - bool LeaseSetDestination::Stop () + void LeaseSetDestination::Stop () { - if (m_IsRunning) + m_CleanupTimer.cancel (); + m_PublishConfirmationTimer.cancel (); + m_PublishVerificationTimer.cancel (); + if (m_Pool) { - m_CleanupTimer.cancel (); - m_PublishConfirmationTimer.cancel (); - m_PublishVerificationTimer.cancel (); - - m_IsRunning = false; - if (m_Pool) - { - m_Pool->SetLocalDestination (nullptr); - i2p::tunnel::tunnels.StopTunnelPool (m_Pool); - } - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - SaveTags (); - CleanUp (); // GarlicDestination - return true; + m_Pool->SetLocalDestination (nullptr); + i2p::tunnel::tunnels.StopTunnelPool (m_Pool); } - else - return false; + SaveTags (); + CleanUp (); // GarlicDestination } bool LeaseSetDestination::Reconfigure(std::map params) @@ -353,30 +312,38 @@ namespace client void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr 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, std::shared_ptr from) + 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); + } + + bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { - uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET]; switch (typeID) { case eI2NPData: - HandleDataMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); + HandleDataMessage (payload, len); break; case eI2NPDeliveryStatus: // we assume tunnel tests non-encrypted - HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); + HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); break; case eI2NPDatabaseStore: - HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); + HandleDatabaseStoreMessage (payload, len); break; case eI2NPDatabaseSearchReply: - HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); + HandleDatabaseSearchReplyMessage (payload, len); break; 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) @@ -421,7 +388,7 @@ namespace client if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET) leaseSet = std::make_shared (buf + offset, len - offset); // LeaseSet else - leaseSet = std::make_shared (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset); // LeaseSet2 + leaseSet = std::make_shared (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetEncryptionType ()); // LeaseSet2 if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key) { if (leaseSet->GetIdentHash () != GetIdentHash ()) @@ -445,7 +412,7 @@ namespace client auto it2 = m_LeaseSetRequests.find (key); if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey) { - auto ls2 = std::make_shared (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr); + auto ls2 = std::make_shared (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr, GetEncryptionType ()); if (ls2->IsValid ()) { m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key @@ -511,9 +478,8 @@ namespace client LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); } - void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) + void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) { - uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); if (msgID == m_PublishReplyToken) { LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32()); @@ -525,7 +491,7 @@ namespace client shared_from_this (), std::placeholders::_1)); } else - i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg); + i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); } void LeaseSetDestination::SetLeaseSetUpdated () @@ -856,10 +822,12 @@ namespace client } } - ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): - LeaseSetDestination (isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), + ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, + bool isPublic, const std::map * params): + LeaseSetDestination (service, isPublic, params), + m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), m_DatagramDestination (nullptr), m_RefCounter (0), - m_ReadyChecker(GetService()) + m_ReadyChecker(service) { if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only @@ -872,11 +840,14 @@ namespace client if (it != params->end ()) 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 (); else i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey); + m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey); if (isPublic) LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); @@ -924,43 +895,33 @@ namespace client { } - bool ClientDestination::Start () + void ClientDestination::Start () { - if (LeaseSetDestination::Start ()) - { - m_StreamingDestination = std::make_shared (GetSharedFromThis ()); // TODO: - m_StreamingDestination->Start (); - for (auto& it: m_StreamingDestinationsByPorts) - it.second->Start (); - return true; - } - else - return false; + LeaseSetDestination::Start (); + m_StreamingDestination = std::make_shared (GetSharedFromThis ()); // TODO: + m_StreamingDestination->Start (); + for (auto& it: m_StreamingDestinationsByPorts) + it.second->Start (); } - bool ClientDestination::Stop () + void ClientDestination::Stop () { - if (LeaseSetDestination::Stop ()) + LeaseSetDestination::Stop (); + m_ReadyChecker.cancel(); + m_StreamingDestination->Stop (); + //m_StreamingDestination->SetOwner (nullptr); + m_StreamingDestination = nullptr; + for (auto& it: m_StreamingDestinationsByPorts) { - m_ReadyChecker.cancel(); - m_StreamingDestination->Stop (); - //m_StreamingDestination->SetOwner (nullptr); - m_StreamingDestination = nullptr; - for (auto& it: m_StreamingDestinationsByPorts) - { - it.second->Stop (); - //it.second->SetOwner (nullptr); - } - m_StreamingDestinationsByPorts.clear (); - if (m_DatagramDestination) - { - delete m_DatagramDestination; - m_DatagramDestination = nullptr; - } - return true; + it.second->Stop (); + //it.second->SetOwner (nullptr); + } + m_StreamingDestinationsByPorts.clear (); + if (m_DatagramDestination) + { + delete m_DatagramDestination; + m_DatagramDestination = nullptr; } - else - return false; } #ifdef I2LUA @@ -1165,8 +1126,8 @@ namespace client LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p"); memset (m_EncryptionPrivateKey, 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); if (f1) { f1.write ((char *)m_EncryptionPublicKey, 256); @@ -1229,5 +1190,46 @@ namespace client } } } + + bool ClientDestination::DeleteStream (uint32_t recvStreamID) + { + if (m_StreamingDestination->DeleteStream (recvStreamID)) + return true; + for (auto it: m_StreamingDestinationsByPorts) + if (it.second->DeleteStream (recvStreamID)) + return true; + return false; + } + + RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): + RunnableService ("Destination"), + ClientDestination (GetIOService (), keys, isPublic, params) + { + } + + RunnableClientDestination::~RunnableClientDestination () + { + if (IsRunning ()) + Stop (); + } + + void RunnableClientDestination::Start () + { + if (!IsRunning ()) + { + ClientDestination::Start (); + StartIOService (); + } + } + + void RunnableClientDestination::Stop () + { + if (IsRunning ()) + { + ClientDestination::Stop (); + StopIOService (); + } + } + } } diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index e4489fe0..23ed5188 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -20,6 +20,7 @@ #include "NetDb.hpp" #include "Streaming.h" #include "Datagram.h" +#include "util.h" namespace i2p { @@ -98,18 +99,17 @@ namespace client public: - LeaseSetDestination (bool isPublic, const std::map * params = nullptr); + LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map * params = nullptr); ~LeaseSetDestination (); const std::string& GetNickname () const { return m_Nickname; }; + boost::asio::io_service& GetService () { return m_Service; }; - virtual bool Start (); - virtual bool Stop (); + virtual void Start (); + virtual void Stop (); /** i2cp reconfigure */ virtual bool Reconfigure(std::map i2cpOpts); - bool IsRunning () const { return m_IsRunning; }; - boost::asio::io_service& GetService () { return m_Service; }; std::shared_ptr GetTunnelPool () { return m_Pool; }; bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; }; std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); @@ -121,7 +121,6 @@ namespace client // implements GarlicDestination std::shared_ptr GetLeaseSet (); std::shared_ptr GetTunnelPool () const { return m_Pool; } - void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); // override GarlicDestination bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); @@ -131,6 +130,10 @@ namespace client 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 newLeaseSet); int GetLeaseSetType () const { return m_LeaseSetType; }; void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; @@ -143,7 +146,6 @@ namespace client private: - void Run (); void UpdateLeaseSet (); std::shared_ptr GetLeaseSetMt (); void Publish (); @@ -152,7 +154,7 @@ namespace client 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 (std::shared_ptr msg); + void HandleDeliveryStatusMessage (uint32_t msgID); void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey = nullptr); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); @@ -162,9 +164,7 @@ namespace client private: - volatile bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; + boost::asio::io_service& m_Service; mutable std::mutex m_RemoteLeaseSetsMutex; std::map > m_RemoteLeaseSets; std::map > m_LeaseSetRequests; @@ -203,11 +203,12 @@ namespace client void Ready(ReadyPromise & p); #endif - ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); + ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, + bool isPublic, const std::map * params = nullptr); ~ClientDestination (); - virtual bool Start (); - virtual bool Stop (); + void Start (); + void Stop (); const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; @@ -280,7 +281,20 @@ namespace client // for HTTP only std::vector > GetAllStreams () const; + bool DeleteStream (uint32_t recvStreamID); }; + + class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination + { + public: + + RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); + ~RunnableClientDestination (); + + void Start (); + void Stop (); + }; + } } diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp new file mode 100644 index 00000000..5f2eb5c8 --- /dev/null +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -0,0 +1,576 @@ +#include +#include +#include "Log.h" +#include "Crypto.h" +#include "Elligator.h" +#include "Tag.h" +#include "I2PEndian.h" +#include "Timestamp.h" +#include "Tunnel.h" +#include "TunnelPool.h" +#include "ECIESX25519AEADRatchetSession.h" + +namespace i2p +{ +namespace garlic +{ + + void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k) + { + // DH_INITIALIZE(rootKey, k) + uint8_t keydata[64]; + i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) + // nextRootKey = keydata[0:31] + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf); + // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) + memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32); + m_NextSymmKeyIndex = 0; + } + + void RatchetTagSet::NextSessionTagRatchet () + { + i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) + memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32); + m_NextIndex = 0; + } + + uint64_t RatchetTagSet::GetNextSessionTag () + { + i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) + m_NextIndex++; + return m_KeyData.GetTag (); + } + + void RatchetTagSet::GetSymmKey (int index, uint8_t * key) + { + if (m_NextSymmKeyIndex > 0 && index >= m_NextSymmKeyIndex) + { + auto num = index + 1 - m_NextSymmKeyIndex; + for (int i = 0; i < num; i++) + i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); + m_NextSymmKeyIndex += num; + memcpy (key, m_CurrentSymmKeyCK + 32, 32); + } + else + CalculateSymmKeyCK (index, key); + } + + void RatchetTagSet::CalculateSymmKeyCK (int index, uint8_t * key) + { + // TODO: store intermediate keys + uint8_t currentSymmKeyCK[64]; + i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64) + for (int i = 0; i < index; i++) + i2p::crypto::HKDF (currentSymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64) + memcpy (key, currentSymmKeyCK + 32, 32); + } + + ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner): + GarlicRoutingSession (owner, true) + { + ResetKeys (); + } + + ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession () + { + } + + void ECIESX25519AEADRatchetSession::ResetKeys () + { + // 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); + } + + 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); + } + + void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + + bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) + { + for (int i = 0; i < 10; i++) + { + m_EphemeralKeys.GenerateKeys (); + if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf)) + return true; // success + } + return false; + } + + uint64_t ECIESX25519AEADRatchetSession::CreateNewSessionTag () const + { + uint8_t tagsetKey[32]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) + // Session Tag Ratchet + RatchetTagSet tagsetNsr; + tagsetNsr.DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) + tagsetNsr.NextSessionTagRatchet (); + return tagsetNsr.GetNextSessionTag (); + } + + bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) + { + if (!GetOwner ()) return false; + // we are Bob + // KDF1 + MixHash (GetOwner ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || bpk) + + if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) + { + LogPrint (eLogError, "Garlic: Can't decode elligator"); + return false; + } + buf += 32; len -= 32; + MixHash (m_Aepk, 32); // h = SHA256(h || aepk) + + uint8_t sharedSecret[32]; + GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr); // x25519(bsk, aepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + + // decrypt flags/static + uint8_t nonce[12], fs[32]; + CreateNonce (0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 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 payload (len - 16); + // KDF2 for payload + bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); + if (isStatic) + { + // static key, fs is apk + memcpy (m_RemoteStaticKey, fs, 32); + GetOwner ()->Decrypt (fs, sharedSecret, nullptr); // x25519(bsk, apk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + } + else // all zeros flags + CreateNonce (1, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 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) + m_State = eSessionStateNewSessionReceived; + GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); + + HandlePayload (payload.data (), len - 16); + + return true; + } + + void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len) + { + 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: + GetOwner ()->HandleECIESx25519GarlicClove (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; + } + } + + bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + ResetKeys (); + // we are Alice, bpk is m_RemoteStaticKey + size_t offset = 0; + if (!GenerateEphemeralKeysAndEncode (out + offset)) + { + LogPrint (eLogError, "Garlic: Can't encode elligator"); + return false; + } + offset += 32; + + // KDF1 + MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk) + MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk) + uint8_t sharedSecret[32]; + m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + // encrypt static key section + uint8_t nonce[12]; + CreateNonce (0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed "); + return false; + } + MixHash (out + offset, 48); // h = SHA256(h || ciphertext) + offset += 48; + // KDF2 + GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr); // x25519 (ask, bpk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } + MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) + + m_State = eSessionStateNewSessionSent; + if (GetOwner ()) + GetOwner ()->AddECIESx25519SessionTag (0, CreateNewSessionTag (), shared_from_this ()); + + return true; + } + + bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + // we are Bob + uint64_t tag = CreateNewSessionTag (); + + size_t offset = 0; + memcpy (out + offset, &tag, 8); + offset += 8; + if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk + { + LogPrint (eLogError, "Garlic: Can't encode elligator"); + return false; + } + offset += 32; + // KDF for Reply Key Section + MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) + MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) + m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + uint8_t nonce[12]; + CreateNonce (0, nonce); + // calulate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305 (sharedSecret /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + { + LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); + return false; + } + MixHash (out + offset, 16); // h = SHA256(h || ciphertext) + out += 16; + // KDF for payload + uint8_t keydata[64]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + // k_ab = keydata[0:31], k_ba = keydata[32:63] + m_ReceiveTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + m_ReceiveTagset.NextSessionTagRatchet (); + m_SendTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + m_SendTagset.NextSessionTagRatchet (); + GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, keydata, nonce, out + offset, len + 16, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } + m_State = eSessionStateEstablished; + + return true; + } + + bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len) + { + // we are Alice + LogPrint (eLogDebug, "Garlic: reply received"); + const uint8_t * tag = buf; + buf += 8; len -= 8; // tag + uint8_t bepk[32]; // Bob's ephemeral key + if (!i2p::crypto::GetElligator ()->Decode (buf, bepk)) + { + LogPrint (eLogError, "Garlic: Can't decode elligator"); + return false; + } + buf += 32; len -= 32; + // KDF for Reply Key Section + MixHash (tag, 8); // h = SHA256(h || tag) + MixHash (bepk, 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) + GetOwner ()->Decrypt (bepk, sharedSecret, nullptr); // x25519 (ask, bepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + uint8_t nonce[12]; + CreateNonce (0, nonce); + // calulate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anyting */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only + { + LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed"); + return false; + } + MixHash (buf, 16); // h = SHA256(h || ciphertext) + buf += 16; len -= 16; + // KDF for payload + uint8_t keydata[64]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + // k_ab = keydata[0:31], k_ba = keydata[32:63] + m_SendTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + m_SendTagset.NextSessionTagRatchet (); + m_ReceiveTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + m_ReceiveTagset.NextSessionTagRatchet (); + GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + // decrypt payload + std::vector payload (len - 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, payload.data (), len - 16, false)) // decrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); + return false; + } + + m_State = eSessionStateEstablished; + GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); + HandlePayload (payload.data (), len - 16); + + return true; + } + + bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + uint8_t nonce[12]; + auto index = m_SendTagset.GetNextIndex (); + CreateNonce (index, nonce); // tag's index + uint64_t tag = m_SendTagset.GetNextSessionTag (); + memcpy (out, &tag, 8); + // ad = The session tag, 8 bytes + // ciphertext = ENCRYPT(k, n, payload, ad) + uint8_t key[32]; + m_SendTagset.GetSymmKey (index, key); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } + return true; + } + + bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index) + { + uint8_t nonce[12]; + CreateNonce (index, nonce); // tag's index + len -= 8; // tag + std::vector payload (len - 16); + uint8_t key[32]; + m_ReceiveTagset.GetSymmKey (index, key); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, key, nonce, payload.data (), len - 16, false)) // decrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); + return false; + } + HandlePayload (payload.data (), len - 16); + if (m_NumReceiveTags > 0)m_NumReceiveTags--; + if (m_NumReceiveTags <= GetOwner ()->GetNumTags ()*2/3) + GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); + return true; + } + + bool ECIESX25519AEADRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len, int index) + { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + switch (m_State) + { + case eSessionStateEstablished: + return HandleExistingSessionMessage (buf, len, index); + case eSessionStateNew: + return HandleNewIncomingSession (buf, len); + case eSessionStateNewSessionSent: + return HandleNewOutgoingSessionReply (buf, len); + default: + return false; + } + return true; + } + + std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) + { + auto m = NewI2NPMessage (); + m->Align (12); // in order to get buf aligned to 16 (12 + 4) + uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + auto payload = CreatePayload (msg); + size_t len = payload.size (); + + switch (m_State) + { + case eSessionStateEstablished: + if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 24; + break; + case eSessionStateNew: + if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 96; + break; + case eSessionStateNewSessionReceived: + if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 72; + break; + default: + return nullptr; + } + + htobe32buf (m->GetPayload (), len); + m->len += len + 4; + m->FillI2NPMessageHeader (eI2NPGarlic); + return m; + } + + std::vector ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg) + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + size_t payloadLen = 7; // datatime + if (msg && m_Destination) + payloadLen += msg->GetPayloadLength () + 13 + 32; + auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()) : nullptr; + std::shared_ptr deliveryStatus; + if (leaseSet) + { + payloadLen += leaseSet->GetPayloadLength () + 13; + deliveryStatus = CreateEncryptedDeliveryStatusMsg (leaseSet->GetMsgID ()); + payloadLen += deliveryStatus->GetPayloadLength () + 49; + if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous + SetLeaseSetUpdateStatus (eLeaseSetSubmitted); + SetLeaseSetUpdateMsgID (leaseSet->GetMsgID ()); + SetLeaseSetSubmissionTime (ts); + } + uint8_t paddingSize; + RAND_bytes (&paddingSize, 1); + paddingSize &= 0x0F; paddingSize++; // 1 - 16 + payloadLen += paddingSize + 3; + std::vector v(payloadLen); + size_t offset = 0; + // DateTime + v[offset] = eECIESx25519BlkDateTime; offset++; + htobe16buf (v.data () + offset, 4); offset += 2; + htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds + // LeaseSet + if (leaseSet) + offset += CreateGarlicClove (leaseSet, v.data () + offset, payloadLen - offset); + // DeliveryStatus + if (deliveryStatus) + offset += CreateDeliveryStatusClove (deliveryStatus, v.data () + offset, payloadLen - offset); + // msg + if (msg && m_Destination) + offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true); + // padding + v[offset] = eECIESx25519BlkPadding; offset++; + htobe16buf (v.data () + offset, paddingSize); offset += 2; + memset (v.data () + offset, 0, paddingSize); offset += paddingSize; + return v; + } + + size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination) + { + if (!msg) return 0; + uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; + if (isDestination) cloveSize += 32; + if ((int)len < cloveSize + 3) return 0; + buf[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (buf + 1, cloveSize); // size + buf += 3; + if (isDestination) + { + *buf = (eGarlicDeliveryTypeDestination << 5); + memcpy (buf + 1, *m_Destination, 32); buf += 32; + } + else + *buf = 0; + buf++; // flag and delivery instructions + *buf = msg->GetTypeID (); // I2NP msg type + htobe32buf (buf + 1, msg->GetMsgID ()); // msgID + htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds + memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); + return cloveSize + 3; + } + + size_t ECIESX25519AEADRatchetSession::CreateDeliveryStatusClove (std::shared_ptr msg, uint8_t * buf, size_t len) + { + uint16_t cloveSize = msg->GetPayloadLength () + 9 + 37 /* delivery instruction */; + if ((int)len < cloveSize + 3) return 0; + buf[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (buf + 1, cloveSize); // size + buf += 3; + if (GetOwner ()) + { + auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel (); + if (inboundTunnel) + { + // delivery instructions + *buf = eGarlicDeliveryTypeTunnel << 5; buf++; // delivery instructions flag tunnel + // hash and tunnelID sequence is reversed for Garlic + memcpy (buf, inboundTunnel->GetNextIdentHash (), 32); buf += 32;// To Hash + htobe32buf (buf, inboundTunnel->GetNextTunnelID ()); buf += 4;// tunnelID + } + else + { + LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus"); + return 0; + } + htobe32buf (buf + 1, msg->GetMsgID ()); // msgID + htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds + memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); + } + else + return 0; + return cloveSize + 3; + } + + void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (int numTags) + { + for (int i = 0; i < numTags; i++) + { + auto index = m_ReceiveTagset.GetNextIndex (); + uint64_t tag = m_ReceiveTagset.GetNextSessionTag (); + GetOwner ()->AddECIESx25519SessionTag (index, tag, shared_from_this ()); + } + m_NumReceiveTags += numTags; + } +} +} + + diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h new file mode 100644 index 00000000..4d273c13 --- /dev/null +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -0,0 +1,128 @@ +#ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__ +#define ECIES_X25519_AEAD_RATCHET_SESSION_H__ + +#include +#include +#include +#include +#include +#include "Identity.h" +#include "Crypto.h" +#include "Garlic.h" + +namespace i2p +{ +namespace garlic +{ + class RatchetTagSet + { + public: + + void DHInitialize (const uint8_t * rootKey, const uint8_t * k); + void NextSessionTagRatchet (); + uint64_t GetNextSessionTag (); + int GetNextIndex () const { return m_NextIndex; }; + void GetSymmKey (int index, uint8_t * key); + + private: + + void CalculateSymmKeyCK (int index, uint8_t * key); + + private: + + union + { + uint64_t ll[8]; + uint8_t buf[64]; + + const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31] + const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63] + uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39] + + } m_KeyData; + uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64]; + int m_NextIndex, m_NextSymmKeyIndex; + }; + + enum ECIESx25519BlockType + { + eECIESx25519BlkDateTime = 0, + eECIESx25519BlkSessionID = 1, + eECIESx25519BlkTermination = 4, + eECIESx25519BlkOptions = 5, + eECIESx25519BlkNextSessionKey = 7, + eECIESx25519BlkGalicClove = 11, + eECIESx25519BlkPadding = 254 + }; + + + const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second of inactivity we should restart after + const int ECIESX25519_EXPIRATION_TIMEOUT = 600; // in seconds + + class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this + { + enum SessionState + { + eSessionStateNew =0, + eSessionStateNewSessionReceived, + eSessionStateNewSessionSent, + eSessionStateEstablished + }; + + public: + + ECIESX25519AEADRatchetSession (GarlicDestination * owner); + ~ECIESX25519AEADRatchetSession (); + + bool HandleNextMessage (const uint8_t * buf, size_t len, int index = 0); + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + + const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } + void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } + + void SetDestination (const i2p::data::IdentHash& dest) // TODO: + { + if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); + } + + bool IsExpired (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT; } + bool CanBeRestarted (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_RESTART_TIMEOUT; } + + private: + + void ResetKeys (); + void MixHash (const uint8_t * buf, size_t len); + void CreateNonce (uint64_t seqn, uint8_t * nonce); + bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes + uint64_t CreateNewSessionTag () const; + + bool HandleNewIncomingSession (const uint8_t * buf, size_t len); + bool HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len); + bool HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index); + void HandlePayload (const uint8_t * buf, size_t len); + + bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + bool NewSessionReplyMessage (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); + + std::vector CreatePayload (std::shared_ptr msg); + size_t CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination = false); + size_t CreateDeliveryStatusClove (std::shared_ptr msg, uint8_t * buf, size_t len); + + void GenerateMoreReceiveTags (int numTags); + + private: + + uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32]; + uint8_t m_Aepk[32]; // Alice's ephemeral keys TODO: for incoming only + i2p::crypto::X25519Keys m_EphemeralKeys; + SessionState m_State = eSessionStateNew; + uint64_t m_LastActivityTimestamp = 0; // incoming + RatchetTagSet m_SendTagset, m_ReceiveTagset; + int m_NumReceiveTags = 0; + std::unique_ptr m_Destination;// TODO: might not need it + }; +} +} + +#endif diff --git a/libi2pd/Elligator.cpp b/libi2pd/Elligator.cpp index b9471512..48a5a7ac 100644 --- a/libi2pd/Elligator.cpp +++ b/libi2pd/Elligator.cpp @@ -1,3 +1,4 @@ +#include #include "Crypto.h" #include "Elligator.h" @@ -39,8 +40,8 @@ namespace crypto BN_free (u); BN_free (iu); } - bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY) const - { + bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const + { bool ret = true; BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); @@ -61,7 +62,14 @@ namespace crypto BN_mod_mul (uxxA, uxxA, xA, p, ctx); if (Legendre (uxxA, ctx) != -1) - { + { + uint8_t randByte = 0; // random highest bits and high y + if (random) + { + RAND_bytes (&randByte, 1); + highY = randByte & 0x01; + } + BIGNUM * r = BN_CTX_get (ctx); if (highY) { @@ -77,7 +85,9 @@ namespace crypto SquareRoot (r, r, ctx); bn2buf (r, encoded, 32); - + + if (random) + encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte for (size_t i = 0; i < 16; i++) // To Little Endian { uint8_t tmp = encoded[i]; @@ -105,6 +115,7 @@ namespace crypto encoded1[i] = encoded[31 - i]; encoded1[31 - i] = encoded[i]; } + encoded1[0] &= 0x3F; // drop two highest bits BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r); diff --git a/libi2pd/Elligator.h b/libi2pd/Elligator.h index ca463568..7cdcbbfe 100644 --- a/libi2pd/Elligator.h +++ b/libi2pd/Elligator.h @@ -17,7 +17,7 @@ namespace crypto Elligator2 (); ~Elligator2 (); - bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false) 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: diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 1ee69df5..9ac5540f 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -1,10 +1,8 @@ #include -#include #include "I2PEndian.h" #include #include #include "Crypto.h" -#include "Elligator.h" #include "RouterContext.h" #include "I2NPProtocol.h" #include "Tunnel.h" @@ -13,30 +11,22 @@ #include "Timestamp.h" #include "Log.h" #include "FS.h" +#include "ECIESX25519AEADRatchetSession.h" #include "Garlic.h" namespace i2p { namespace garlic { - GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, - std::shared_ptr destination, int numTags, bool attachLeaseSet): - m_Owner (owner), m_Destination (destination), m_NumTags (numTags), - m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), + GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet): + m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) { - // create new session tags and session key - RAND_bytes (m_SessionKey, 32); - m_Encryption.SetKey (m_SessionKey); } - GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): - m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) + GarlicRoutingSession::GarlicRoutingSession (): + m_Owner (nullptr), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) { - memcpy (m_SessionKey, sessionKey, 32); - m_Encryption.SetKey (m_SessionKey); - m_SessionTags.push_back (sessionTag); - m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); } GarlicRoutingSession::~GarlicRoutingSession () @@ -68,88 +58,54 @@ namespace garlic m_SharedRoutingPath = path; } - GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags () + bool GarlicRoutingSession::MessageConfirmed (uint32_t msgID) { - auto tags = new UnconfirmedTags (m_NumTags); - tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); - for (int i = 0; i < m_NumTags; i++) + if (msgID == GetLeaseSetUpdateMsgID ()) { - RAND_bytes (tags->sessionTags[i], 32); - tags->sessionTags[i].creationTime = tags->tagsCreationTime; - } - return tags; - } - - void GarlicRoutingSession::MessageConfirmed (uint32_t msgID) - { - TagsConfirmed (msgID); - if (msgID == m_LeaseSetUpdateMsgID) - { - m_LeaseSetUpdateStatus = eLeaseSetUpToDate; - m_LeaseSetUpdateMsgID = 0; + SetLeaseSetUpdateStatus (eLeaseSetUpToDate); + SetLeaseSetUpdateMsgID (0); LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed"); + return true; } - else - CleanupExpiredTags (); - } + return false; + } - void GarlicRoutingSession::TagsConfirmed (uint32_t msgID) + std::shared_ptr GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg (uint32_t msgID) { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - auto it = m_UnconfirmedTagsMsgs.find (msgID); - if (it != m_UnconfirmedTagsMsgs.end ()) + auto msg = CreateDeliveryStatusMsg (msgID); + if (GetOwner ()) { - auto& tags = it->second; - if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - { - for (int i = 0; i < tags->numTags; i++) - m_SessionTags.push_back (tags->sessionTags[i]); - } - m_UnconfirmedTagsMsgs.erase (it); + //encrypt + uint8_t key[32], tag[32]; + RAND_bytes (key, 32); // random session key + RAND_bytes (tag, 32); // random session tag + GetOwner ()->SubmitSessionKey (key, tag); + ElGamalAESSession garlic (key, tag); + msg = garlic.WrapSingleMessage (msg); } - } - - bool GarlicRoutingSession::CleanupExpiredTags () + return msg; + } + + ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner, + std::shared_ptr destination, int numTags, bool attachLeaseSet): + GarlicRoutingSession (owner, attachLeaseSet), + m_Destination (destination), m_NumTags (numTags) { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) - { - if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - it = m_SessionTags.erase (it); - else - ++it; - } - CleanupUnconfirmedTags (); - if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) - { - if (m_Owner) - m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); - m_LeaseSetUpdateMsgID = 0; - } - return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty (); + // create new session tags and session key + RAND_bytes (m_SessionKey, 32); + m_Encryption.SetKey (m_SessionKey); } - bool GarlicRoutingSession::CleanupUnconfirmedTags () + ElGamalAESSession::ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag): + m_NumTags(1) { - bool ret = false; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - // delete expired unconfirmed tags - for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) - { - if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) - { - if (m_Owner) - m_Owner->RemoveDeliveryStatusSession (it->first); - it = m_UnconfirmedTagsMsgs.erase (it); - ret = true; - } - else - ++it; - } - return ret; + memcpy (m_SessionKey, sessionKey, 32); + m_Encryption.SetKey (m_SessionKey); + m_SessionTags.push_back (sessionTag); + m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); } - std::shared_ptr GarlicRoutingSession::WrapSingleMessage (std::shared_ptr msg) + std::shared_ptr ElGamalAESSession::WrapSingleMessage (std::shared_ptr msg) { auto m = NewI2NPMessage (); m->Align (12); // in order to get buf aligned to 16 (12 + 4) @@ -215,10 +171,10 @@ namespace garlic return m; } - size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) + size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) { size_t blockSize = 0; - bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); + bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count blockSize += 2; @@ -247,7 +203,7 @@ namespace garlic return blockSize; } - size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags) + size_t ElGamalAESSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint32_t msgID; @@ -257,17 +213,17 @@ namespace garlic *numCloves = 0; size++; - if (m_Owner) + if (GetOwner ()) { // resubmit non-confirmed LeaseSet - if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) + if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT) { - m_LeaseSetUpdateStatus = eLeaseSetUpdated; + SetLeaseSetUpdateStatus (eLeaseSetUpdated); SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed } // attach DeviveryStatus if necessary - if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated + if (newTags || GetLeaseSetUpdateStatus () == eLeaseSetUpdated) // new tags created or leaseset updated { // clove is DeliveryStatus auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); @@ -281,20 +237,20 @@ namespace garlic m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr(newTags))); newTags = nullptr; // got acquired } - m_Owner->DeliveryStatusSent (shared_from_this (), msgID); + GetOwner ()->DeliveryStatusSent (shared_from_this (), msgID); } else LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created"); } // attach LeaseSet - if (m_LeaseSetUpdateStatus == eLeaseSetUpdated) + if (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) { - if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous - m_LeaseSetUpdateStatus = eLeaseSetSubmitted; - m_LeaseSetUpdateMsgID = msgID; - m_LeaseSetSubmissionTime = ts; + if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous + SetLeaseSetUpdateStatus (eLeaseSetSubmitted); + SetLeaseSetUpdateMsgID (msgID); + SetLeaseSetSubmissionTime (ts); // clove if our leaseSet must be attached - auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); + auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()); size += CreateGarlicClove (payload + size, leaseSet, false); (*numCloves)++; } @@ -315,7 +271,7 @@ namespace garlic return size; } - size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination) + size_t ElGamalAESSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec size_t size = 0; @@ -345,12 +301,12 @@ namespace garlic return size; } - size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) + size_t ElGamalAESSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) { size_t size = 0; - if (m_Owner) + if (GetOwner ()) { - auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel (); + auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel (); if (inboundTunnel) { buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel @@ -361,19 +317,12 @@ namespace garlic htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID size += 4; // create msg - auto msg = CreateDeliveryStatusMsg (msgID); - if (m_Owner) - { - //encrypt - uint8_t key[32], tag[32]; - RAND_bytes (key, 32); // random session key - RAND_bytes (tag, 32); // random session tag - m_Owner->SubmitSessionKey (key, tag); - GarlicRoutingSession garlic (key, tag); - msg = garlic.WrapSingleMessage (msg); - } - memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); - size += msg->GetLength (); + auto msg = CreateEncryptedDeliveryStatusMsg (msgID); + if (msg) + { + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); + size += msg->GetLength (); + } // fill clove uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec uint32_t cloveID; @@ -394,6 +343,82 @@ namespace garlic return size; } + ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags () + { + auto tags = new UnconfirmedTags (m_NumTags); + tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); + for (int i = 0; i < m_NumTags; i++) + { + RAND_bytes (tags->sessionTags[i], 32); + tags->sessionTags[i].creationTime = tags->tagsCreationTime; + } + return tags; + } + + bool ElGamalAESSession::MessageConfirmed (uint32_t msgID) + { + TagsConfirmed (msgID); + if (!GarlicRoutingSession::MessageConfirmed (msgID)) + CleanupExpiredTags (); + return true; + } + + void ElGamalAESSession::TagsConfirmed (uint32_t msgID) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + auto it = m_UnconfirmedTagsMsgs.find (msgID); + if (it != m_UnconfirmedTagsMsgs.end ()) + { + auto& tags = it->second; + if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + for (int i = 0; i < tags->numTags; i++) + m_SessionTags.push_back (tags->sessionTags[i]); + } + m_UnconfirmedTagsMsgs.erase (it); + } + } + + bool ElGamalAESSession::CleanupExpiredTags () + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) + { + if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + it = m_SessionTags.erase (it); + else + ++it; + } + CleanupUnconfirmedTags (); + if (GetLeaseSetUpdateMsgID () && ts*1000LL > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT) + { + if (GetOwner ()) + GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); + SetLeaseSetUpdateMsgID (0); + } + return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty (); + } + + bool ElGamalAESSession::CleanupUnconfirmedTags () + { + bool ret = false; + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + // delete expired unconfirmed tags + for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) + { + if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) + { + if (GetOwner ()) + GetOwner ()->RemoveDeliveryStatusSession (it->first); + it = m_UnconfirmedTagsMsgs.erase (it); + ret = true; + } + else + ++it; + } + return ret; + } + GarlicDestination::GarlicDestination (): m_NumTags (32) // 32 tags by default { m_Ctx = BN_CTX_new (); @@ -409,6 +434,8 @@ namespace garlic m_Sessions.clear (); m_DeliveryStatusSessions.clear (); m_Tags.clear (); + m_ECIESx25519Sessions.clear (); + m_ECIESx25519Tags.clear (); } void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) { @@ -458,7 +485,7 @@ namespace garlic // tag not found. Handle depending on encryption type if (GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET) { - HandleECIESx25519 (buf, length - 4); + HandleECIESx25519 (buf, length); return; } // otherwise assume ElGamal/AES @@ -552,7 +579,7 @@ namespace garlic LogPrint (eLogError, "Garlic: message is too short"); break; } - HandleI2NPMessage (buf, len - offset, from); + HandleI2NPMessage (buf, len - offset); break; case eGarlicDeliveryTypeDestination: LogPrint (eLogDebug, "Garlic: type destination"); @@ -563,7 +590,7 @@ namespace garlic LogPrint (eLogError, "Garlic: message is too short"); break; } - HandleI2NPMessage (buf, len - offset, from); + HandleI2NPMessage (buf, len - offset); break; case eGarlicDeliveryTypeTunnel: { @@ -647,21 +674,41 @@ namespace garlic std::shared_ptr GarlicDestination::GetRoutingSession ( std::shared_ptr destination, bool attachLeaseSet) { - GarlicRoutingSessionPtr session; - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (destination->GetIdentHash ()); - if (it != m_Sessions.end ()) - session = it->second; - } - if (!session) - { - session = std::make_shared (this, destination, - attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests - std::unique_lock l(m_SessionsMutex); - m_Sessions[destination->GetIdentHash ()] = session; - } - return session; + if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET && + GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET) + { + ECIESX25519AEADRatchetSessionPtr session; + uint8_t staticKey[32]; + destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key + auto it = m_ECIESx25519Sessions.find (staticKey); + if (it != m_ECIESx25519Sessions.end ()) + session = it->second; + if (!session) + { + session = std::make_shared (this); + session->SetRemoteStaticKey (staticKey); + } + session->SetDestination (destination->GetIdentHash ()); // TODO: remove + return session; + } + else + { + ElGamalAESSessionPtr session; + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find (destination->GetIdentHash ()); + if (it != m_Sessions.end ()) + session = it->second; + } + if (!session) + { + session = std::make_shared (this, destination, + attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests + std::unique_lock l(m_SessionsMutex); + m_Sessions[destination->GetIdentHash ()] = session; + } + return session; + } } void GarlicDestination::CleanupExpiredTags () @@ -709,6 +756,25 @@ namespace garlic ++it; } } + // ECIESx25519 + for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) + { + if (ts > it->second.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) + it = m_ECIESx25519Tags.erase (it); + else + ++it; + } + + for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();) + { + if (it->second->IsExpired (ts)) + { + it->second->SetOwner (nullptr); + it = m_ECIESx25519Sessions.erase (it); + } + else + ++it; + } } void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) @@ -717,15 +783,14 @@ namespace garlic m_DeliveryStatusSessions.erase (msgID); } - void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID) + void GarlicDestination::DeliveryStatusSent (ElGamalAESSessionPtr session, uint32_t msgID) { std::unique_lock l(m_DeliveryStatusSessionsMutex); m_DeliveryStatusSessions[msgID] = session; } - void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) + void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID) { - uint32_t msgID = bufbe32toh (msg->GetPayload ()); GarlicRoutingSessionPtr session; { std::unique_lock l(m_DeliveryStatusSessionsMutex); @@ -745,8 +810,12 @@ namespace garlic void GarlicDestination::SetLeaseSetUpdated () { - std::unique_lock l(m_SessionsMutex); - for (auto& it: m_Sessions) + { + std::unique_lock l(m_SessionsMutex); + for (auto& it: m_Sessions) + it.second->SetLeaseSetUpdated (); + } + for (auto& it: m_ECIESx25519Sessions) it.second->SetLeaseSetUpdated (); } @@ -757,7 +826,8 @@ namespace garlic void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) { - HandleDeliveryStatusMessage (msg); + uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); + HandleDeliveryStatusMessage (msgID); } void GarlicDestination::SaveTags () @@ -833,102 +903,103 @@ namespace garlic void GarlicDestination::HandleECIESx25519 (const uint8_t * buf, size_t len) { - // KDF1 - // TODO : use precalculated hashes - static const char protocolName[41] = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"; // 40 bytes - uint8_t h[64], ck[32]; - SHA256 ((const uint8_t *)protocolName, 40, h); - memcpy (ck, h, 32); - SHA256 (h, 32, h); - // we are Bob - memcpy (h + 32, GetEncryptionPublicKey (), 32); - SHA256 (h, 64, h); // h = SHA256(h || bpk) + uint64_t tag; + memcpy (&tag, buf, 8); + ECIESX25519AEADRatchetSessionPtr session; + int index = 0; + auto it = m_ECIESx25519Tags.find (tag); + if (it != m_ECIESx25519Tags.end ()) + { + session = it->second.session; + index = it->second.index; + m_ECIESx25519Tags.erase (tag); + } + else + session = std::make_shared (this); // incoming - uint8_t aepk[32]; - if (!i2p::crypto::GetElligator ()->Decode (buf, aepk)) - { - LogPrint (eLogError, "Garlic: Can't decode elligator"); - return; - } - buf += 32; len -= 32; - memcpy (h + 32, aepk, 32); - SHA256 (h, 64, h); // h = SHA256(h || aepk) - - uint8_t sharedSecret[32], keyData[64]; - Decrypt (aepk, sharedSecret, m_Ctx); // x25519 - i2p::crypto::HKDF (ck, sharedSecret, 32, "", keyData); // keydata = HKDF(chainKey, sharedSecret, "", 64) - memcpy (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, h, 32, keyData + 32, nonce, fs, 32, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed "); - return; - } - memcpy (h + 32, buf, 32); - SHA256 (h, 64, h); // h = SHA256(h || ciphertext) - buf += 48; len -= 48; // 32 data + 16 poly - // decrypt payload - std::vector payload (len + 32); uint8_t h1[32]; - // KDF2 for payload - bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); - if (isStatic) - { - // static key, fs is apk - Decrypt (fs, sharedSecret, m_Ctx); // DH(bsk, apk) - i2p::crypto::HKDF (ck, sharedSecret, 32, "", keyData); // keydata = HKDF(chainKey, sharedSecret, "", 64) - memcpy (ck, keyData, 32); // chainKey = keydata[0:31] - memcpy (payload.data (), h, 32); - memcpy (payload.data () + 32, buf, len); // h || ciphertext - SHA256 (payload.data (), len + 32, h1); - } - else // all zeros flags - htole64buf (nonce + 4, 1); // n = 1 - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, h, 32, keyData + 32, nonce, payload.data () + 32, len - 16, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); - return; - } - if (isStatic) memcpy (h, h1, 32); // h = SHA256(h || ciphertext) - HandleECIESx25519Payload (payload.data () + 32, len - 16); - } - - void GarlicDestination::HandleECIESx25519Payload (const uint8_t * buf, size_t len) - { - 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) + if (!session->HandleNextMessage (buf, len, index)) + LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); + } + + 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 +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif + // no break here + case eGarlicDeliveryTypeLocal: { - LogPrint (eLogError, "Garlic: Unexpected block length ", size); + 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; } - switch (blk) + 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); + } + } + + void GarlicDestination::AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session) + { + m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexSession{index, session, i2p::util::GetSecondsSinceEpoch ()}); + } + + void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session) + { + i2p::data::Tag<32> staticKeyTag (staticKey); + auto it = m_ECIESx25519Sessions.find (staticKeyTag); + if (it != m_ECIESx25519Sessions.end ()) + { + if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ())) + m_ECIESx25519Sessions.erase (it); + else { - case eECIESx25519BlkGalicClove: - // TODO: - break; - case eECIESx25519BlkDateTime: - LogPrint (eLogDebug, "Garlic: datetime"); - break; - case eECIESx25519BlkOptions: - LogPrint (eLogDebug, "Garlic: options"); - break; - case eECIESx25519BlkPadding: - LogPrint (eLogDebug, "NTCP2: padding"); - break; - default: - LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk); - } - offset += size; + LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); + return; + } } + m_ECIESx25519Sessions.emplace (staticKeyTag, session); } } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 4054d88c..be0ac117 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -2,7 +2,7 @@ #define GARLIC_H__ #include -#include +#include #include #include #include @@ -85,8 +85,10 @@ namespace garlic }; class GarlicDestination; - class GarlicRoutingSession: public std::enable_shared_from_this + class GarlicRoutingSession { + protected: + enum LeaseSetUpdateStatus { eLeaseSetUpToDate = 0, @@ -95,27 +97,15 @@ namespace garlic eLeaseSetDoNotSend }; - struct UnconfirmedTags - { - UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; - ~UnconfirmedTags () { delete[] sessionTags; }; - uint32_t msgID; - int numTags; - SessionTag * sessionTags; - uint32_t tagsCreationTime; - }; - public: - GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr destination, - int numTags, bool attachLeaseSet); - GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption - ~GarlicRoutingSession (); - std::shared_ptr WrapSingleMessage (std::shared_ptr msg); - void MessageConfirmed (uint32_t msgID); - bool CleanupExpiredTags (); // returns true if something left - bool CleanupUnconfirmedTags (); // returns true if something has been deleted - + GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet); + GarlicRoutingSession (); + virtual ~GarlicRoutingSession (); + virtual std::shared_ptr WrapSingleMessage (std::shared_ptr msg) = 0; + virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession + virtual bool MessageConfirmed (uint32_t msgID); + void SetLeaseSetUpdated () { if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; @@ -127,11 +117,63 @@ namespace garlic std::shared_ptr GetSharedRoutingPath (); void SetSharedRoutingPath (std::shared_ptr path); - const GarlicDestination * GetOwner () const { return m_Owner; } + GarlicDestination * GetOwner () const { return m_Owner; } void SetOwner (GarlicDestination * owner) { m_Owner = owner; } - + + protected: + + LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } + void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; } + uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; } + void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; } + void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; } + + std::shared_ptr CreateEncryptedDeliveryStatusMsg (uint32_t msgID); + private: + GarlicDestination * m_Owner; + + LeaseSetUpdateStatus m_LeaseSetUpdateStatus; + uint32_t m_LeaseSetUpdateMsgID; + uint64_t m_LeaseSetSubmissionTime; // in milliseconds + + std::shared_ptr m_SharedRoutingPath; + + public: + // for HTTP only + virtual size_t GetNumOutgoingTags () const { return 0; }; + }; + //using GarlicRoutingSessionPtr = std::shared_ptr; + typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 + + class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this + { + struct UnconfirmedTags + { + UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; + ~UnconfirmedTags () { delete[] sessionTags; }; + uint32_t msgID; + int numTags; + SessionTag * sessionTags; + uint32_t tagsCreationTime; + }; + + public: + + ElGamalAESSession (GarlicDestination * owner, std::shared_ptr destination, + int numTags, bool attachLeaseSet); + ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption + ~ElGamalAESSession () {}; + + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + + bool MessageConfirmed (uint32_t msgID); + bool CleanupExpiredTags (); // returns true if something left + bool CleanupUnconfirmedTags (); // returns true if something has been deleted + + private: + size_t CreateAESBlock (uint8_t * buf, std::shared_ptr msg); size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags); size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination); @@ -139,42 +181,32 @@ namespace garlic void TagsConfirmed (uint32_t msgID); UnconfirmedTags * GenerateSessionTags (); + + private: + + std::shared_ptr m_Destination; - private: - - GarlicDestination * m_Owner; - std::shared_ptr m_Destination; - - i2p::crypto::AESKey m_SessionKey; + i2p::crypto::AESKey m_SessionKey; std::list m_SessionTags; int m_NumTags; std::map > m_UnconfirmedTagsMsgs; // msgID->tags - LeaseSetUpdateStatus m_LeaseSetUpdateStatus; - uint32_t m_LeaseSetUpdateMsgID; - uint64_t m_LeaseSetSubmissionTime; // in milliseconds + i2p::crypto::CBCEncryption m_Encryption; - i2p::crypto::CBCEncryption m_Encryption; - - std::shared_ptr m_SharedRoutingPath; - - public: + public: // for HTTP only - size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; + size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; + }; + typedef std::shared_ptr ElGamalAESSessionPtr; + + class ECIESX25519AEADRatchetSession; + typedef std::shared_ptr ECIESX25519AEADRatchetSessionPtr; + struct ECIESX25519AEADRatchetIndexSession + { + int index; + ECIESX25519AEADRatchetSessionPtr session; + uint64_t creationTime; // seconds since epoch }; - //using GarlicRoutingSessionPtr = std::shared_ptr; - typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 - - enum ECIESx25519BlockType - { - eECIESx25519BlkDateTime = 0, - eECIESx25519BlkSessionID = 1, - eECIESx25519BlkTermination = 4, - eECIESx25519BlkOptions = 5, - eECIESx25519BlkNextSessionKey = 7, - eECIESx25519BlkGalicClove = 11, - eECIESx25519BlkPadding = 254 - }; class GarlicDestination: public i2p::data::LocalDestination { @@ -185,6 +217,7 @@ namespace garlic void CleanUp (); void SetNumTags (int numTags) { m_NumTags = numTags; }; + int GetNumTags () const { return m_NumTags; }; std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); void CleanupExpiredTags (); void RemoveDeliveryStatusSession (uint32_t msgID); @@ -193,7 +226,10 @@ namespace garlic void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread - void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); + void DeliveryStatusSent (ElGamalAESSessionPtr session, uint32_t msgID); + void AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session); + void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); + void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len); virtual void ProcessGarlicMessage (std::shared_ptr msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); @@ -201,12 +237,13 @@ namespace garlic virtual std::shared_ptr GetLeaseSet () = 0; // TODO virtual std::shared_ptr GetTunnelPool () const = 0; - virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) = 0; 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 msg); - void HandleDeliveryStatusMessage (std::shared_ptr msg); + void HandleDeliveryStatusMessage (uint32_t msgID); void SaveTags (); void LoadTags (); @@ -219,7 +256,6 @@ namespace garlic // ECIES-X25519-AEAD-Ratchet void HandleECIESx25519 (const uint8_t * buf, size_t len); - void HandleECIESx25519Payload (const uint8_t * buf, size_t len); private: @@ -227,12 +263,14 @@ namespace garlic // outgoing sessions int m_NumTags; std::mutex m_SessionsMutex; - std::map m_Sessions; + std::unordered_map m_Sessions; + std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session // incoming - std::map > m_Tags; + std::unordered_map, std::hash > > m_Tags; + std::unordered_map m_ECIESx25519Tags; // session tag -> session // DeliveryStatus std::mutex m_DeliveryStatusSessionsMutex; - std::map m_DeliveryStatusSessions; // msgID -> session + std::unordered_map m_DeliveryStatusSessions; // msgID -> session public: diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index de94732f..3f9633ed 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -723,7 +723,10 @@ namespace data case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA512_4096: LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); - // no break here +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif + // no break here case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); break; diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index e6373dd9..8d1b9524 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -251,18 +251,19 @@ namespace data memcpy (m_Buffer, buf, len); } - LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases): - LeaseSet (storeLeases), m_StoreType (storeType) + LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto): + LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto) { - SetBuffer (buf, len); + SetBuffer (buf, len); if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ReadFromBufferEncrypted (buf, len, nullptr, nullptr); else ReadFromBuffer (buf, len); } - LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret): - LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) + LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, + const uint8_t * secret, CryptoKeyType preferredCrypto): + LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto) { ReadFromBufferEncrypted (buf, len, key, secret); } @@ -355,6 +356,8 @@ namespace data offset += propertiesLen; // skip for now. TODO: implement properties if (offset + 1 >= len) return 0; // key sections + CryptoKeyType preferredKeyType = m_EncryptionType; + bool preferredKeyFound = false; int numKeySections = buf[offset]; offset++; for (int i = 0; i < numKeySections; i++) { @@ -362,15 +365,15 @@ namespace data if (offset + 2 >= len) return 0; uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; if (offset + encryptionKeyLen >= len) return 0; - if (IsStoreLeases ()) // create encryptor with leases only + if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only { - // we pick first valid key, higher key type has higher priority 4-1-0 - // if two keys with of the same type, pick first + // we pick first valid key if preferred not found auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset); - if (encryptor && (!m_Encryptor || keyType > m_EncryptionType)) + if (encryptor && (!m_Encryptor || keyType == preferredKeyType)) { m_Encryptor = encryptor; // TODO: atomic m_EncryptionType = keyType; + if (keyType == preferredKeyType) preferredKeyFound = true; } } offset += encryptionKeyLen; diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index 84d87e17..a51570f7 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -136,8 +136,8 @@ namespace data { public: - 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 key, const uint8_t * secret = nullptr); // store type 5, called from local netdb only + LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); + LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only uint8_t GetStoreType () const { return m_StoreType; }; uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; }; bool IsPublic () const { return m_IsPublic; }; @@ -168,7 +168,7 @@ namespace data uint32_t m_PublishedTimestamp = 0; bool m_IsPublic = true, m_IsPublishedEncrypted = false; std::shared_ptr m_TransientVerifier; - CryptoKeyType m_EncryptionType = CRYPTO_KEY_TYPE_ELGAMAL; + CryptoKeyType m_EncryptionType; std::shared_ptr m_Encryptor; // for standardLS2 }; diff --git a/libi2pd/Log.h b/libi2pd/Log.h index b11c6763..ba10b5e9 100644 --- a/libi2pd/Log.h +++ b/libi2pd/Log.h @@ -161,6 +161,7 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept s << std::forward(arg); } +#if (__cplusplus < 201703L) // below C++ 17 /** internal usage only -- folding args array to single string */ template void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept @@ -168,6 +169,7 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept LogPrint (s, std::forward(arg)); LogPrint (s, std::forward(args)...); } +#endif /** * @brief Create log message and send it to queue @@ -184,7 +186,11 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept // fold message to single string std::stringstream ss(""); +#if (__cplusplus >= 201703L) // C++ 17 or higher + (LogPrint (ss, std::forward(args)), ...); +#else LogPrint (ss, std::forward(args)...); +#endif auto msg = std::make_shared(level, std::time(nullptr), ss.str()); msg->tid = std::this_thread::get_id(); diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index e2c4ae43..3dfd6250 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -1,12 +1,10 @@ /* -* Copyright (c) 2013-2018, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree * -* Kovri go write your own code -* */ #include @@ -22,34 +20,29 @@ #include "Transports.h" #include "NetDb.hpp" #include "NTCP2.h" +#include "HTTP.h" +#include "util.h" namespace i2p { namespace transport { NTCP2Establisher::NTCP2Establisher (): - m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) - { + m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) + { } - NTCP2Establisher::~NTCP2Establisher () - { - delete[] m_SessionRequestBuffer; + NTCP2Establisher::~NTCP2Establisher () + { + delete[] m_SessionRequestBuffer; delete[] m_SessionCreatedBuffer; delete[] m_SessionConfirmedBuffer; } void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial) { - // temp_key = HMAC-SHA256(ck, input_key_material) - uint8_t tempKey[32]; unsigned int len; - HMAC(EVP_sha256(), m_CK, 32, inputKeyMaterial, 32, tempKey, &len); - // ck = HMAC-SHA256(temp_key, byte(0x01)) - static uint8_t one[1] = { 1 }; - HMAC(EVP_sha256(), tempKey, 32, one, 1, m_CK, &len); - // derived = HMAC-SHA256(temp_key, ck || byte(0x02)) - m_CK[32] = 2; - HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, m_K, &len); + i2p::crypto::HKDF (m_CK, inputKeyMaterial, 32, "", m_CK); + // ck is m_CK[0:31], k is m_CK[32:63] } void NTCP2Establisher::MixHash (const uint8_t * buf, size_t len) @@ -63,22 +56,22 @@ namespace transport void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) { - static const uint8_t protocolNameHash[] = - { - 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, - 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 + static const uint8_t protocolNameHash[] = + { + 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, + 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 }; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256") - static const uint8_t hh[32] = - { - 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, - 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e + static const uint8_t hh[32] = + { + 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, + 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e }; // SHA256 (protocolNameHash) - memcpy (m_CK, protocolNameHash, 32); + memcpy (m_CK, protocolNameHash, 32); // h = SHA256(hh || rs) SHA256_CTX ctx; SHA256_Init (&ctx); - SHA256_Update (&ctx, hh, 32); - SHA256_Update (&ctx, rs, 32); + SHA256_Update (&ctx, hh, 32); + SHA256_Update (&ctx, rs, 32); SHA256_Final (m_H, &ctx); // h = SHA256(h || epub) MixHash (epub, 32); @@ -92,25 +85,25 @@ namespace transport { KeyDerivationFunction1 (m_RemoteStaticKey, m_EphemeralKeys, m_RemoteStaticKey, GetPub ()); } - + void NTCP2Establisher::KDF1Bob () { - KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetStaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); + KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetStaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); } void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) - { - MixHash (sessionRequest + 32, 32); // encrypted payload + { + MixHash (sessionRequest + 32, 32); // encrypted payload int paddingLength = sessionRequestLen - 64; if (paddingLength > 0) MixHash (sessionRequest + 64, paddingLength); - MixHash (epub, 32); + MixHash (epub, 32); // x25519 between remote pub and ephemaral priv uint8_t inputKeyMaterial[32]; m_EphemeralKeys.Agree (GetRemotePub (), inputKeyMaterial); - + MixKey (inputKeyMaterial); } @@ -118,7 +111,7 @@ namespace transport { KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ()); } - + void NTCP2Establisher::KDF2Bob () { KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); @@ -127,14 +120,14 @@ namespace transport void NTCP2Establisher::KDF3Alice () { uint8_t inputKeyMaterial[32]; - i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); + i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); MixKey (inputKeyMaterial); } void NTCP2Establisher::KDF3Bob () { uint8_t inputKeyMaterial[32]; - m_EphemeralKeys.Agree (m_RemoteStaticKey, inputKeyMaterial); + m_EphemeralKeys.Agree (m_RemoteStaticKey, inputKeyMaterial); MixKey (inputKeyMaterial); } @@ -155,40 +148,40 @@ namespace transport encryption.SetKey (m_RemoteIdentHash); encryption.SetIV (m_IV); encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X - encryption.GetIV (m_IV); // save IV for SessionCreated + encryption.GetIV (m_IV); // save IV for SessionCreated // encryption key for next block KDF1Alice (); // fill options uint8_t options[32]; // actual options size is 16 bytes memset (options, 0, 16); options[0] = i2p::context.GetNetID (); // network ID - options[1] = 2; // ver + options[1] = 2; // ver htobe16buf (options + 2, paddingLength); // padLen - // m3p2Len + // m3p2Len auto bufLen = i2p::context.GetRouterInfo ().GetBufferLen (); - m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options + m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options htobe16buf (options + 4, m3p2Len); - // fill m3p2 payload (RouterInfo block) + // fill m3p2 payload (RouterInfo block) m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; // m3p1 is 48 bytes uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; m3p2[0] = eNTCP2BlkRouterInfo; // block htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI - m3p2[3] = 0; // flag + m3p2[3] = 0; // flag memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex // 2 bytes reserved htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA // 4 bytes reserved - // sign and encrypt options, use m_H as AD + // sign and encrypt options, use m_H as AD uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt + i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt } void NTCP2Establisher::CreateSessionCreatedMessage () { auto paddingLen = rand () % (287 - 64); m_SessionCreatedBufferLen = paddingLen + 64; - m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen]; + m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen]; RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); // encrypt Y i2p::crypto::CBCEncryption encryption; @@ -196,15 +189,15 @@ namespace transport encryption.SetIV (m_IV); encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y // encryption key for next block (m_K) - KDF2Bob (); + KDF2Bob (); uint8_t options[16]; memset (options, 0, 16); htobe16buf (options + 2, paddingLen); // padLen htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsB - // sign and encrypt options, use m_H as AD + // sign and encrypt options, use m_H as AD uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt + i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt } @@ -214,24 +207,24 @@ namespace transport MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload int paddingLength = m_SessionCreatedBufferLen - 64; if (paddingLength > 0) - MixHash (m_SessionCreatedBuffer + 64, paddingLength); + MixHash (m_SessionCreatedBuffer + 64, paddingLength); - // part1 48 bytes - i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, m_H, 32, m_K, nonce, m_SessionConfirmedBuffer, 48, true); // encrypt + // part1 48 bytes + i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt } void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce) { // part 2 // update AD again - MixHash (m_SessionConfirmedBuffer, 48); + MixHash (m_SessionConfirmedBuffer, 48); // encrypt m3p2, it must be filled in SessionRequest - KDF3Alice (); + KDF3Alice (); uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; - i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2, m3p2Len, true); // encrypt + i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt // update h again MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext) - } + } bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen) { @@ -240,13 +233,13 @@ namespace transport decryption.SetKey (i2p::context.GetIdentHash ()); decryption.SetIV (i2p::context.GetNTCP2IV ()); decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ()); - decryption.GetIV (m_IV); // save IV for SessionCreated + decryption.GetIV (m_IV); // save IV for SessionCreated // decryption key for next block KDF1Bob (); // verify MAC and decrypt options block (32 bytes), use m_H as AD uint8_t nonce[12], options[16]; memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, m_H, 32, m_K, nonce, options, 16, false)) // decrypt + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt { // options if (options[0] && options[0] != i2p::context.GetNetID ()) @@ -254,7 +247,7 @@ namespace transport LogPrint (eLogWarning, "NTCP2: SessionRequest networkID ", (int)options[0], " mismatch. Expected ", i2p::context.GetNetID ()); return false; } - if (options[1] == 2) // ver is always 2 + if (options[1] == 2) // ver is always 2 { paddingLen = bufbe16toh (options + 2); m_SessionRequestBufferLen = paddingLen + 64; @@ -262,11 +255,11 @@ namespace transport if (m3p2Len < 16) { LogPrint (eLogWarning, "NTCP2: SessionRequest m3p2len=", m3p2Len, " is too short"); - return false; - } + return false; + } // check timestamp auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t tsA = bufbe32toh (options + 8); + uint32_t tsA = bufbe32toh (options + 8); if (tsA < ts - NTCP2_CLOCK_SKEW || tsA > ts + NTCP2_CLOCK_SKEW) { LogPrint (eLogWarning, "NTCP2: SessionRequest time difference ", (int)(ts - tsA), " exceeds clock skew"); @@ -283,8 +276,8 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); return false; - } - return true; + } + return true; } bool NTCP2Establisher::ProcessSessionCreatedMessage (uint16_t& paddingLen) @@ -301,13 +294,13 @@ namespace transport uint8_t payload[16]; uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, m_H, 32, m_K, nonce, payload, 16, false)) // decrypt + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, GetH (), 32, GetK (), nonce, payload, 16, false)) // decrypt { - // options + // options paddingLen = bufbe16toh(payload + 2); // check timestamp auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t tsB = bufbe32toh (payload + 8); + uint32_t tsB = bufbe32toh (payload + 8); if (tsB < ts - NTCP2_CLOCK_SKEW || tsB > ts + NTCP2_CLOCK_SKEW) { LogPrint (eLogWarning, "NTCP2: SessionCreated time difference ", (int)(ts - tsB), " exceeds clock skew"); @@ -315,10 +308,10 @@ namespace transport } } else - { + { LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); return false; - } + } return true; } @@ -329,8 +322,8 @@ namespace transport int paddingLength = m_SessionCreatedBufferLen - 64; if (paddingLength > 0) MixHash (m_SessionCreatedBuffer + 64, paddingLength); - - if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, m_H, 32, m_K, nonce, m_RemoteStaticKey, 32, false)) // decrypt S + + if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S { LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); return false; @@ -341,10 +334,10 @@ namespace transport bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf) { // update AD again - MixHash (m_SessionConfirmedBuffer, 48); + MixHash (m_SessionConfirmedBuffer, 48); - KDF3Bob (); - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt + KDF3Bob (); + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt { // caclulate new h again for KDF data memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext @@ -359,8 +352,8 @@ namespace transport } NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter): - TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), - m_Server (server), m_Socket (m_Server.GetService ()), + TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), + m_Server (server), m_Socket (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false), m_Establisher (new NTCP2Establisher), m_SendSipKey (nullptr), m_ReceiveSipKey (nullptr), @@ -380,7 +373,7 @@ namespace transport memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16); } else - LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); + LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); } } @@ -434,8 +427,8 @@ namespace transport void NTCP2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); } @@ -451,7 +444,7 @@ namespace transport memcpy (h + 32, "siphash", 7); i2p::crypto::HKDF (master, h, 39, "", master, 32); // sip_master = HKDF(ask_master, h || "siphash") i2p::crypto::HKDF (master, nullptr, 0, "", k); // sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen) - memcpy (m_Sipkeysab, k, 32); memcpy (m_Sipkeysba, k + 32, 32); + memcpy (m_Sipkeysab, k, 32); memcpy (m_Sipkeysba, k + 32, 32); } @@ -460,8 +453,8 @@ namespace transport m_Establisher->CreateSessionRequestMessage (); // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } void NTCP2Session::HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { @@ -509,7 +502,7 @@ namespace transport } else SendSessionCreated (); - } + } else Terminate (); } @@ -529,9 +522,9 @@ namespace transport void NTCP2Session::SendSessionCreated () { m_Establisher->CreateSessionCreatedMessage (); - // send message + // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void NTCP2Session::HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -586,9 +579,9 @@ namespace transport { uint8_t nonce[12]; CreateNonce (1, nonce); // set nonce to 1 - m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); + m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); memset (nonce, 0, 12); // set nonce back to 0 - m_Establisher->CreateSessionConfirmedMessagePart2 (nonce); + m_Establisher->CreateSessionConfirmedMessagePart2 (nonce); // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -603,12 +596,12 @@ namespace transport Terminate (); } else - { + { LogPrint (eLogDebug, "NTCP2: SessionConfirmed sent"); KeyDerivationFunctionDataPhase (); // Alice data phase keys m_SendKey = m_Kab; - m_ReceiveKey = m_Kba; + m_ReceiveKey = m_Kba; SetSipKeys (m_Sipkeysab, m_Sipkeysba); memcpy (m_ReceiveIV.buf, m_Sipkeysba + 16, 8); memcpy (m_SendIV.buf, m_Sipkeysab + 16, 8); @@ -618,7 +611,7 @@ namespace transport // TODO: remove // m_SendQueue.push_back (CreateDeliveryStatusMsg (1)); // SendQueue (); - } + } } void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -632,7 +625,7 @@ namespace transport else { LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); - m_Establisher->m_SessionConfirmedBuffer = new uint8_t[m_Establisher->m3p2Len + 48]; + m_Establisher->m_SessionConfirmedBuffer = new uint8_t[m_Establisher->m3p2Len + 48]; boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionConfirmedReceived , shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -661,7 +654,7 @@ namespace transport KeyDerivationFunctionDataPhase (); // Bob data phase keys m_SendKey = m_Kba; - m_ReceiveKey = m_Kab; + m_ReceiveKey = m_Kab; SetSipKeys (m_Sipkeysba, m_Sipkeysab); memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); @@ -669,8 +662,8 @@ namespace transport // process RI if (buf[0] != eNTCP2BlkRouterInfo) { - LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed"); - Terminate (); + LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed"); + Terminate (); return; } auto size = bufbe16toh (buf.data () + 1); @@ -684,33 +677,33 @@ namespace transport i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag if (ri.IsUnreachable ()) { - LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); + LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); return; } if (i2p::util::GetMillisecondsSinceEpoch () > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes { - LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2Message3Error); + LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2Message3Error); return; } auto addr = ri.GetNTCP2Address (false); // any NTCP2 address if (!addr) { - LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed"); + LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed"); Terminate (); return; } if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32)) { - LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2IncorrectSParameter); + LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2IncorrectSParameter); return; } i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice // TODO: process options - - // ready to communicate + + // ready to communicate auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ()); m_Server.AddNTCP2Session (shared_from_this (), true); @@ -729,13 +722,13 @@ namespace transport { #if OPENSSL_SIPHASH m_SendSipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, sendSipKey, 16); - m_SendMDCtx = EVP_MD_CTX_create (); + m_SendMDCtx = EVP_MD_CTX_create (); EVP_PKEY_CTX *ctx = nullptr; EVP_DigestSignInit (m_SendMDCtx, &ctx, nullptr, nullptr, m_SendSipKey); - EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); + EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); m_ReceiveSipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, receiveSipKey, 16); - m_ReceiveMDCtx = EVP_MD_CTX_create (); + m_ReceiveMDCtx = EVP_MD_CTX_create (); ctx = nullptr; EVP_DigestSignInit (m_ReceiveMDCtx, &ctx, NULL, NULL, m_ReceiveSipKey); EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); @@ -779,9 +772,9 @@ namespace transport { #if OPENSSL_SIPHASH EVP_DigestSignInit (m_ReceiveMDCtx, nullptr, nullptr, nullptr, nullptr); - EVP_DigestSignUpdate (m_ReceiveMDCtx, m_ReceiveIV.buf, 8); - size_t l = 8; - EVP_DigestSignFinal (m_ReceiveMDCtx, m_ReceiveIV.buf, &l); + EVP_DigestSignUpdate (m_ReceiveMDCtx, m_ReceiveIV.buf, 8); + size_t l = 8; + EVP_DigestSignFinal (m_ReceiveMDCtx, m_ReceiveIV.buf, &l); #else i2p::crypto::Siphash<8> (m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey); #endif @@ -789,7 +782,7 @@ namespace transport m_NextReceivedLen = be16toh (m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key); LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); if (m_NextReceivedLen >= 16) - { + { if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen]; boost::system::error_code ec; @@ -807,7 +800,7 @@ namespace transport { LogPrint (eLogError, "NTCP2: received length ", m_NextReceivedLen, " is too short"); Terminate (); - } + } } } @@ -834,7 +827,7 @@ namespace transport uint8_t nonce[12]; CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) - { + { LogPrint (eLogDebug, "NTCP2: received message decrypted"); ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16); delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; // we don't need received buffer anymore @@ -844,7 +837,7 @@ namespace transport { LogPrint (eLogWarning, "NTCP2: Received AEAD verification failed "); SendTerminationAndTerminate (eNTCP2DataPhaseAEADFailure); - } + } } } @@ -867,7 +860,7 @@ namespace transport { case eNTCP2BlkDateTime: LogPrint (eLogDebug, "NTCP2: datetime"); - break; + break; case eNTCP2BlkOptions: LogPrint (eLogDebug, "NTCP2: options"); break; @@ -890,11 +883,11 @@ namespace transport nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header memcpy (nextMsg->GetNTCP2Header (), frame + offset, size); nextMsg->FromNTCP2 (); - m_Handler.PutNextMessage (nextMsg); + m_Handler.PutNextMessage (nextMsg); break; } case eNTCP2BlkTermination: - if (size >= 9) + if (size >= 9) { LogPrint (eLogDebug, "NTCP2: termination. reason=", (int)(frame[offset + 8])); Terminate (); @@ -917,46 +910,46 @@ namespace transport { #if OPENSSL_SIPHASH EVP_DigestSignInit (m_SendMDCtx, nullptr, nullptr, nullptr, nullptr); - EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); - size_t l = 8; - EVP_DigestSignFinal (m_SendMDCtx, m_SendIV.buf, &l); + EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); + size_t l = 8; + EVP_DigestSignFinal (m_SendMDCtx, m_SendIV.buf, &l); #else i2p::crypto::Siphash<8> (m_SendIV.buf, m_SendIV.buf, 8, m_SendSipKey); #endif // length must be in BigEndian htobe16buf (lengthBuf, frameLen ^ le16toh (m_SendIV.key)); LogPrint (eLogDebug, "NTCP2: sent length ", frameLen); - } + } void NTCP2Session::SendI2NPMsgs (std::vector >& msgs) { if (msgs.empty () || IsTerminated ()) return; - + size_t totalLen = 0; std::vector > encryptBufs; - std::vector bufs; + std::vector bufs; std::shared_ptr first; uint8_t * macBuf = nullptr; - for (auto& it: msgs) - { + for (auto& it: msgs) + { it->ToNTCP2 (); auto buf = it->GetNTCP2Header (); auto len = it->GetNTCP2Length (); // block header - buf -= 3; + buf -= 3; buf[0] = eNTCP2BlkI2NPMessage; // blk htobe16buf (buf + 1, len); // size - len += 3; - totalLen += len; + len += 3; + totalLen += len; encryptBufs.push_back ( {buf, len} ); if (&it == &msgs.front ()) // first message { // allocate two bytes for length buf -= 2; len += 2; first = it; - } + } if (&it == &msgs.back () && it->len + 16 < it->maxLen) // last message - { + { // if it's long enough we add padding and MAC to it // create padding block auto paddingLen = CreatePaddingBlock (totalLen, buf + len, it->maxLen - it->len - 16); @@ -969,8 +962,8 @@ namespace transport macBuf = buf + len; // allocate 16 bytes for MAC len += 16; - } - + } + bufs.push_back (boost::asio::buffer (buf, len)); } @@ -982,42 +975,42 @@ namespace transport auto paddingLen = CreatePaddingBlock (totalLen, m_NextSendBuffer, 287 - 16); // and padding block to encrypt and send if (paddingLen) - encryptBufs.push_back ( {m_NextSendBuffer, paddingLen} ); + encryptBufs.push_back ( {m_NextSendBuffer, paddingLen} ); bufs.push_back (boost::asio::buffer (m_NextSendBuffer, paddingLen + 16)); macBuf = m_NextSendBuffer + paddingLen; - totalLen += paddingLen; + totalLen += paddingLen; } uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 5); // frame length right before first block - + // send buffers - m_IsSending = true; + m_IsSending = true; boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleI2NPMsgsSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); - } + } void NTCP2Session::HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) { HandleNextFrameSent (ecode, bytes_transferred); // msgs get destroyed here } - + void NTCP2Session::EncryptAndSendNextBuffer (size_t payloadLen) { - if (IsTerminated ()) + if (IsTerminated ()) { delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; - return; + return; } // encrypt uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; - i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2); + i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2); SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer); // send - m_IsSending = true; + m_IsSending = true; boost::asio::async_write (m_Socket, boost::asio::buffer (m_NextSendBuffer, payloadLen + 16 + 2), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleNextFrameSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -1026,23 +1019,23 @@ namespace transport { m_IsSending = false; delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; - + if (ecode) { if (ecode != boost::asio::error::operation_aborted) LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ()); Terminate (); - } + } else - { + { m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); m_NumSentBytes += bytes_transferred; i2p::transport::transports.UpdateSentBytes (bytes_transferred); LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred); SendQueue (); - } + } } - + void NTCP2Session::SendQueue () { if (!m_SendQueue.empty ()) @@ -1052,7 +1045,7 @@ namespace transport while (!m_SendQueue.empty ()) { auto msg = m_SendQueue.front (); - size_t len = msg->GetNTCP2Length (); + size_t len = msg->GetNTCP2Length (); if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header { msgs.push_back (msg); @@ -1068,12 +1061,12 @@ namespace transport break; } SendI2NPMsgs (msgs); - } + } } size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len) { - if (len < 3) return 0; + if (len < 3) return 0; len -= 3; if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; @@ -1082,10 +1075,10 @@ namespace transport if (paddingSize) paddingSize = rand () % paddingSize; buf[0] = eNTCP2BlkPadding; // blk htobe16buf (buf + 1, paddingSize); // size - memset (buf + 3, 0, paddingSize); + memset (buf + 3, 0, paddingSize); return paddingSize + 3; - } - + } + void NTCP2Session::SendRouterInfo () { if (!IsEstablished ()) return; @@ -1106,11 +1099,11 @@ namespace transport void NTCP2Session::SendTermination (NTCP2TerminationReason reason) { if (!m_SendKey || !m_SendSipKey) return; - m_NextSendBuffer = new uint8_t[49]; // 49 = 12 bytes message + 16 bytes MAC + 2 bytes size + up to 19 padding block + m_NextSendBuffer = new uint8_t[49]; // 49 = 12 bytes message + 16 bytes MAC + 2 bytes size + up to 19 padding block // termination block m_NextSendBuffer[2] = eNTCP2BlkTermination; m_NextSendBuffer[3] = 0; m_NextSendBuffer[4] = 9; // 9 bytes block size - htobe64buf (m_NextSendBuffer + 5, m_ReceiveSequenceNumber); + htobe64buf (m_NextSendBuffer + 5, m_ReceiveSequenceNumber); m_NextSendBuffer[13] = (uint8_t)reason; // padding block auto paddingSize = CreatePaddingBlock (12, m_NextSendBuffer + 14, 19); @@ -1134,13 +1127,13 @@ namespace transport if (m_IsTerminated) return; for (auto it: msgs) m_SendQueue.push_back (it); - if (!m_IsSending) - SendQueue (); + if (!m_IsSending) + SendQueue (); else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) { LogPrint (eLogWarning, "NTCP2: outgoing messages queue size exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); Terminate (); - } + } } void NTCP2Session::SendLocalRouterInfo () @@ -1150,8 +1143,8 @@ namespace transport } NTCP2Server::NTCP2Server (): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), - m_TerminationTimer (m_Service) + RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()), + m_Resolver(GetService ()) { } @@ -1162,49 +1155,70 @@ namespace transport void NTCP2Server::Start () { - if (!m_IsRunning) + if (!IsRunning ()) { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&NTCP2Server::Run, this)); - auto& addresses = context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) + StartIOService (); + if(UsingProxy()) + { + LogPrint(eLogError, "NTCP2: USING PROXY "); + // TODO: resolve proxy until it is resolved + boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort)); + boost::system::error_code e; + auto itr = m_Resolver.resolve(q, e); + if(e) + { + LogPrint(eLogError, "NTCP2: Failed to resolve proxy ", e.message()); + } + else + { + m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr)); + if (m_ProxyEndpoint) + LogPrint(eLogError, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint); + } + } + else { - if (!address) continue; - if (address->IsPublishedNTCP2 ()) + LogPrint(eLogError, "NTCP2: NOTUSING PROXY "); + auto& addresses = context.GetRouterInfo ().GetAddresses (); + for (const auto& address: addresses) { - if (address->host.is_v4()) + if (!address) continue; + if (address->IsPublishedNTCP2 ()) { - try + if (address->host.is_v4()) { - m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port))); - } - catch ( std::exception & ex ) - { - LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what()); - continue; + try + { + m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port))); + } + catch ( std::exception & ex ) + { + LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what()); + continue; + } + + LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port); + auto conn = std::make_shared(*this); + m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); } - - LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port); - auto conn = std::make_shared(*this); - m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); - } - else if (address->host.is_v6() && context.SupportsV6 ()) - { - m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service)); - try + else if (address->host.is_v6() && context.SupportsV6 ()) { - m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); - m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); - m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); - m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); - m_NTCP2V6Acceptor->listen (); - - LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port); - auto conn = std::make_shared (*this); - m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); - } catch ( std::exception & ex ) { - LogPrint(eLogError, "NTCP2: failed to bind to ip6 port ", address->port); - continue; + m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); + try + { + m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); + m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); + m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); + m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); + m_NTCP2V6Acceptor->listen (); + + LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port); + auto conn = std::make_shared (*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); + } catch ( std::exception & ex ) { + LogPrint(eLogError, "NTCP2: failed to bind to ip6 port ", address->port); + continue; + } } } } @@ -1225,39 +1239,18 @@ namespace transport } m_NTCP2Sessions.clear (); - if (m_IsRunning) + if (IsRunning ()) { - m_IsRunning = false; m_TerminationTimer.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - } - - void NTCP2Server::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "NTCP2: runtime exception: ", ex.what ()); - } + m_ProxyEndpoint = nullptr; } + StopIOService (); } bool NTCP2Server::AddNTCP2Session (std::shared_ptr session, bool incoming) { if (!session) return false; - if (incoming) + if (incoming) m_PendingIncomingSessions.remove (session); if (!session->GetRemoteIdentity ()) return false; auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); @@ -1289,15 +1282,15 @@ namespace transport void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn) { LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port); - m_Service.post([this, address, port, conn]() + GetService ().post([this, address, port, conn]() { if (this->AddNTCP2Session (conn)) { - auto timer = std::make_shared(m_Service); + auto timer = std::make_shared(GetService ()); auto timeout = NTCP2_CONNECT_TIMEOUT * 5; conn->SetTerminationTimeout(timeout * 2); timer->expires_from_now (boost::posix_time::seconds(timeout)); - timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) + timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { @@ -1423,6 +1416,231 @@ namespace transport ScheduleTermination (); } } + + void NTCP2Server::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port) + { + m_ProxyType = proxytype; + m_ProxyAddress = addr; + m_ProxyPort = port; + } + + void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) + { + if (ecode) + { + LogPrint(eLogWarning, "NTCP2: failed to connect to proxy ", ecode.message()); + timer->cancel(); + conn->Terminate(); + return; + } + switch (m_ProxyType) + { + case eSocksProxy: + { + // TODO: support username/password auth etc + static const uint8_t buff[3] = {0x05, 0x01, 0x00}; + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), + [] (const boost::system::error_code & ec, std::size_t transferred) + { + (void) transferred; + if(ec) + { + LogPrint(eLogWarning, "NTCP2: socks5 write error ", ec.message()); + } + }); + auto readbuff = std::make_shared >(2); + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2), + [this, readbuff, timer, conn, host, port, addrtype](const boost::system::error_code & ec, std::size_t transferred) + { + if(ec) + { + LogPrint(eLogError, "NTCP2: socks5 read error ", ec.message()); + timer->cancel(); + conn->Terminate(); + return; + } + else if(transferred == 2) + { + if((*readbuff)[1] == 0x00) + { + AfterSocksHandshake(conn, timer, host, port, addrtype); + return; + } + else if ((*readbuff)[1] == 0xff) + { + LogPrint(eLogError, "NTCP2: socks5 proxy rejected authentication"); + timer->cancel(); + conn->Terminate(); + return; + } + LogPrint(eLogError, "NTCP2:", (int)(*readbuff)[1]); + } + LogPrint(eLogError, "NTCP2: socks5 server gave invalid response"); + timer->cancel(); + conn->Terminate(); + }); + break; + } + case eHTTPProxy: + { + i2p::http::HTTPReq req; + req.method = "CONNECT"; + req.version ="HTTP/1.1"; + if(addrtype == eIP6Address) + req.uri = "[" + host + "]:" + std::to_string(port); + else + req.uri = host + ":" + std::to_string(port); + + boost::asio::streambuf writebuff; + std::ostream out(&writebuff); + out << req.to_string(); + + boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), + [](const boost::system::error_code & ec, std::size_t transferred) + { + (void) transferred; + if(ec) + LogPrint(eLogError, "NTCP2: http proxy write error ", ec.message()); + }); + + boost::asio::streambuf * readbuff = new boost::asio::streambuf; + boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", + [this, readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred) + { + if(ec) + { + LogPrint(eLogError, "NTCP2: http proxy read error ", ec.message()); + timer->cancel(); + conn->Terminate(); + } + else + { + readbuff->commit(transferred); + i2p::http::HTTPRes res; + if(res.parse(boost::asio::buffer_cast(readbuff->data()), readbuff->size()) > 0) + { + if(res.code == 200) + { + timer->cancel(); + conn->ClientLogin(); + delete readbuff; + return; + } + else + LogPrint(eLogError, "NTCP2: http proxy rejected request ", res.code); + } + else + LogPrint(eLogError, "NTCP2: http proxy gave malformed response"); + timer->cancel(); + conn->Terminate(); + delete readbuff; + } + }); + break; + } + default: + LogPrint(eLogError, "NTCP2: unknown proxy type, invalid state"); + } + } + + void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn) + { + if(!m_ProxyEndpoint) return; + GetService().post([this, host, port, addrtype, conn]() { + if (this->AddNTCP2Session (conn)) + { + + auto timer = std::make_shared(GetService()); + auto timeout = NTCP_CONNECT_TIMEOUT * 5; + conn->SetTerminationTimeout(timeout * 2); + timer->expires_from_now (boost::posix_time::seconds(timeout)); + timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); + i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); + conn->Terminate (); + } + }); + conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype)); + } + }); + } + + void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) + { + // build request + size_t sz = 0; + auto buff = std::make_shared >(256); + auto readbuff = std::make_shared >(256); + (*buff)[0] = 0x05; + (*buff)[1] = 0x01; + (*buff)[2] = 0x00; + + if(addrtype == eIP4Address) + { + (*buff)[3] = 0x01; + auto addr = boost::asio::ip::address::from_string(host).to_v4(); + auto addrbytes = addr.to_bytes(); + auto addrsize = addrbytes.size(); + memcpy(buff->data () + 4, addrbytes.data(), addrsize); + } + else if (addrtype == eIP6Address) + { + (*buff)[3] = 0x04; + auto addr = boost::asio::ip::address::from_string(host).to_v6(); + auto addrbytes = addr.to_bytes(); + auto addrsize = addrbytes.size(); + memcpy(buff->data () + 4, addrbytes.data(), addrsize); + } + else if (addrtype == eHostname) + { + (*buff)[3] = 0x03; + size_t addrsize = host.size(); + sz = addrsize + 1 + 4; + if (2 + sz > buff->size ()) + { + // too big + return; + } + (*buff)[4] = (uint8_t) addrsize; + memcpy(buff->data() + 5, host.c_str(), addrsize); + } + htobe16buf(buff->data () + sz, port); + sz += 2; + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(), + [](const boost::system::error_code & ec, std::size_t written) + { + if(ec) + { + LogPrint(eLogError, "NTCP2: failed to write handshake to socks proxy ", ec.message()); + return; + } + }); + + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10), + [timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred) + { + if(e) + { + LogPrint(eLogError, "NTCP2: socks proxy read error ", e.message()); + } + else if(transferred == sz) + { + if((*readbuff)[1] == 0x00) + { + timer->cancel(); + conn->ClientLogin(); + return; + } + } + if(!e) + i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); + timer->cancel(); + conn->Terminate(); + }); + } + } } - diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index c66ff697..11ae5995 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -1,12 +1,10 @@ /* -* Copyright (c) 2013-2018, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree * -* Kovri go write your own code -* */ #ifndef NTCP2_H__ #define NTCP2_H__ @@ -30,7 +28,7 @@ namespace i2p namespace transport { - const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; + const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; const int NTCP2_MAX_PADDING_RATIO = 6; // in % const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds @@ -38,7 +36,7 @@ namespace transport const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds - 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 enum NTCP2BlockType @@ -48,8 +46,8 @@ namespace transport eNTCP2BlkRouterInfo, // 2 eNTCP2BlkI2NPMessage, // 3 eNTCP2BlkTermination, // 4 - eNTCP2BlkPadding = 254 - }; + eNTCP2BlkPadding = 254 + }; enum NTCP2TerminationReason { @@ -71,21 +69,21 @@ namespace transport eNTCP2RouterInfoSignatureVerificationFail, // 15 eNTCP2IncorrectSParameter, // 16 eNTCP2Banned, // 17 - }; - + }; + // RouterInfo flags - const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; + const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; struct NTCP2Establisher { NTCP2Establisher (); ~NTCP2Establisher (); - + const uint8_t * GetPub () const { return m_EphemeralKeys.GetPublicKey (); }; const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set - const uint8_t * GetK () const { return m_K; }; + const uint8_t * GetK () const { return m_CK + 32; }; const uint8_t * GetCK () const { return m_CK; }; const uint8_t * GetH () const { return m_H; }; @@ -114,21 +112,22 @@ namespace transport i2p::crypto::X25519Keys m_EphemeralKeys; uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 - uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/; + uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/; i2p::data::IdentHash m_RemoteIdentHash; - uint16_t m3p2Len; + uint16_t m3p2Len; uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; - }; + }; class NTCP2Server; class NTCP2Session: public TransportSession, public std::enable_shared_from_this { public: - NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter = nullptr); + + NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter = nullptr); ~NTCP2Session (); void Terminate (); void TerminateByTimeout (); @@ -140,9 +139,9 @@ namespace transport bool IsEstablished () const { return m_IsEstablished; }; bool IsTerminated () const { return m_IsTerminated; }; - void ClientLogin (); // Alice + void ClientLogin (); // Alice void ServerLogin (); // Bob - + void SendLocalRouterInfo (); // after handshake void SendI2NPMessages (const std::vector >& msgs); @@ -195,15 +194,15 @@ namespace transport std::unique_ptr m_Establisher; // 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; -#if OPENSSL_SIPHASH +#if OPENSSL_SIPHASH EVP_PKEY * m_SendSipKey, * m_ReceiveSipKey; EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx; #else const uint8_t * m_SendSipKey, * m_ReceiveSipKey; #endif - uint16_t m_NextReceivedLen; + uint16_t m_NextReceivedLen; uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; union { @@ -218,31 +217,51 @@ namespace transport std::list > m_SendQueue; }; - class NTCP2Server + class NTCP2Server: private i2p::util::RunnableServiceWithWork { public: + enum RemoteAddressType + { + eIP4Address, + eIP6Address, + eHostname + }; + + enum ProxyType + { + eNoProxy, + eSocksProxy, + eHTTPProxy + }; + NTCP2Server (); ~NTCP2Server (); void Start (); void Stop (); + boost::asio::io_service& GetService () { return GetIOService (); }; bool AddNTCP2Session (std::shared_ptr session, bool incoming = false); void RemoveNTCP2Session (std::shared_ptr session); std::shared_ptr FindNTCP2Session (const i2p::data::IdentHash& ident); - boost::asio::io_service& GetService () { return m_Service; }; - + void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn); void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn); + void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype); + + + bool UsingProxy() const { return m_ProxyType != eNoProxy; }; + void UseProxy(ProxyType proxy, const std::string & address, uint16_t port); + private: - void Run (); void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); - void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); + void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); + void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype); // timer void ScheduleTermination (); @@ -250,15 +269,17 @@ namespace transport private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; boost::asio::deadline_timer m_TerminationTimer; std::unique_ptr m_NTCP2Acceptor, m_NTCP2V6Acceptor; - std::map > m_NTCP2Sessions; + std::map > m_NTCP2Sessions; std::list > m_PendingIncomingSessions; + ProxyType m_ProxyType =eNoProxy; + std::string m_ProxyAddress; + uint16_t m_ProxyPort; + boost::asio::ip::tcp::resolver m_Resolver; + std::unique_ptr m_ProxyEndpoint; + public: // for HTTP/I2PControl diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 4461c094..6d69f131 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -950,7 +950,7 @@ namespace data if (numTags) { const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag - i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag); + i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag); replyMsg = garlic.WrapSingleMessage (replyMsg); if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message"); } diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index bf3459d8..6c63ef79 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -27,7 +27,7 @@ namespace i2p void RouterContext::Init () { srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); - m_StartupTime = std::chrono::steady_clock::now(); + m_StartupTime = std::chrono::steady_clock::now(); if (!Load ()) CreateNewRouter (); @@ -338,7 +338,11 @@ namespace i2p { case low : /* not set */; break; case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P' - case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here, extra + high means 'X' + case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; + #if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; + #endif + // no break here, extra + high means 'X' case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; } m_RouterInfo.SetCaps (caps); @@ -692,9 +696,9 @@ namespace i2p return i2p::tunnel::tunnels.GetExploratoryPool (); } - void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr 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 msg) diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index f6aa281f..dfc05fe7 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -115,12 +115,17 @@ namespace i2p // implements GarlicDestination std::shared_ptr GetLeaseSet () { return nullptr; }; std::shared_ptr GetTunnelPool () const; - void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); // override GarlicDestination void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatusMessage (std::shared_ptr 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: void CreateNewRouter (); diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 4f943912..3efcfb99 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -86,13 +86,14 @@ namespace stream LogPrint (eLogDebug, "Streaming: Stream deleted"); } - void Stream::Terminate () + void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only { m_AckSendTimer.cancel (); m_ReceiveTimer.cancel (); m_ResendTimer.cancel (); //CleanUp (); /* Need to recheck - broke working on windows */ - m_LocalDestination.DeleteStream (shared_from_this ()); + if (deleteFromDestination) + m_LocalDestination.DeleteStream (shared_from_this ()); } void Stream::CleanUp () @@ -847,6 +848,9 @@ namespace stream break; case 2: m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif // no break here case 4: if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); @@ -989,6 +993,8 @@ namespace stream m_PendingIncomingStreams.clear (); { std::unique_lock l(m_StreamsMutex); + for (auto it: m_Streams) + it.second->Terminate (false); // we delete here m_Streams.clear (); m_IncomingStreams.clear (); } @@ -1123,6 +1129,15 @@ namespace stream } } + bool StreamingDestination::DeleteStream (uint32_t recvStreamID) + { + auto it = m_Streams.find (recvStreamID); + if (it == m_Streams.end ()) + return false; + DeleteStream (it->second); + return true; + } + void StreamingDestination::SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 49962507..985a7eca 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -180,8 +180,7 @@ namespace stream int GetWindowSize () const { return m_WindowSize; }; int GetRTT () const { return m_RTT; }; - /** don't call me */ - void Terminate (); + void Terminate (bool deleteFromDestination = true); private: @@ -251,6 +250,7 @@ namespace stream std::shared_ptr CreateNewOutgoingStream (std::shared_ptr remote, int port = 0); void DeleteStream (std::shared_ptr stream); + bool DeleteStream (uint32_t recvStreamID); void SetAcceptor (const Acceptor& acceptor); void ResetAcceptor (); bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; diff --git a/libi2pd/Tag.h b/libi2pd/Tag.h index 8af82241..010b160f 100644 --- a/libi2pd/Tag.h +++ b/libi2pd/Tag.h @@ -93,4 +93,16 @@ private: } // data } // i2p +namespace std +{ + // hash for std::unordered_map + template struct hash > + { + size_t operator()(const i2p::data::Tag& s) const + { + return s.GetLL ()[0]; + } + }; +} + #endif /* TAG_H__ */ diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 42a605a4..8a36c5aa 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -157,6 +157,7 @@ namespace transport m_IsRunning = true; m_Thread = new std::thread (std::bind (&Transports::Run, this)); std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy); + std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); i2p::http::URL proxyurl; uint16_t softLimit, hardLimit, threads; i2p::config::GetOption("limits.ntcpsoft", softLimit); @@ -196,13 +197,38 @@ namespace transport LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy); return; } - // create NTCP2. TODO: move to acceptor + // create NTCP2. TODO: move to acceptor bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) { - m_NTCP2Server = new NTCP2Server (); - m_NTCP2Server->Start (); - } + if(!ntcp2proxy.empty()) + { + if(proxyurl.parse(ntcp2proxy)) + { + if(proxyurl.schema == "socks" || proxyurl.schema == "http") + { + m_NTCP2Server = new NTCP2Server (); + NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy; + + if (proxyurl.schema == "http") + proxytype = NTCP2Server::eHTTPProxy; + + m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port) ; + m_NTCP2Server->Start(); + } + else + LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy); + } + else + LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy); + return; + } + else + { + m_NTCP2Server = new NTCP2Server (); + m_NTCP2Server->Start (); + } + } // create acceptors auto& addresses = context.GetRouterInfo ().GetAddresses (); @@ -405,24 +431,36 @@ namespace transport { if (peer.router) // we have RI already { - if (!peer.numAttempts) // NTCP2 - { - peer.numAttempts++; - if (m_NTCP2Server) // we support NTCP2 - { - // NTCP2 have priority over NTCP - auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only - if (address) - { - auto s = std::make_shared (*m_NTCP2Server, peer.router); - m_NTCP2Server->Connect (address->host, address->port, s); - return true; - } - } - } + if (!peer.numAttempts) // NTCP2 + { + peer.numAttempts++; + if (m_NTCP2Server) // we support NTCP2 + { + // NTCP2 have priority over NTCP + auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only + if (address) + { + auto s = std::make_shared (*m_NTCP2Server, peer.router); + + if(m_NTCP2Server->UsingProxy()) + { + NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address; + std::string addr = address->host.to_string(); + + if(address->host.is_v6()) + remote = NTCP2Server::eIP6Address; + + m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s); + } + else + m_NTCP2Server->Connect (address->host, address->port, s); + return true; + } + } + } if (peer.numAttempts == 1) // NTCP1 { - peer.numAttempts++; + peer.numAttempts++; auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); if (address && m_NTCPServer) { @@ -650,14 +688,14 @@ namespace transport { auto before = it->second.sessions.size (); it->second.sessions.remove (session); - if (it->second.sessions.empty ()) + if (it->second.sessions.empty ()) { 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); - } + } else { std::unique_lock l(m_PeersMutex); diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 66620717..00b0a12c 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -1,5 +1,6 @@ #include #include "I2PEndian.h" +#include #include #include #include @@ -45,7 +46,7 @@ namespace tunnel // shuffle records std::vector recordIndicies; for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::random_shuffle (recordIndicies.begin(), recordIndicies.end()); + std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()())); // create real records uint8_t * records = msg->GetPayload () + 1; diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 4f740a09..7a1325fe 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -1,4 +1,5 @@ #include +#include #include "I2PEndian.h" #include "Crypto.h" #include "Tunnel.h" @@ -441,7 +442,7 @@ namespace tunnel int size = m_ExplicitPeers->size (); std::vector peerIndicies; for (int i = 0; i < size; i++) peerIndicies.push_back(i); - std::random_shuffle (peerIndicies.begin(), peerIndicies.end()); + std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()())); int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; for (int i = 0; i < numHops; i++) diff --git a/libi2pd/api.cpp b/libi2pd/api.cpp index ca72ae49..0a76bda7 100644 --- a/libi2pd/api.cpp +++ b/libi2pd/api.cpp @@ -77,7 +77,7 @@ namespace api std::shared_ptr CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params) { - auto localDestination = std::make_shared (keys, isPublic, params); + auto localDestination = std::make_shared (keys, isPublic, params); localDestination->Start (); return localDestination; } @@ -86,7 +86,7 @@ namespace api const std::map * params) { i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); - auto localDestination = std::make_shared (keys, isPublic, params); + auto localDestination = std::make_shared (keys, isPublic, params); localDestination->Start (); return localDestination; } diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 448d1307..c1b741fc 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -57,6 +57,45 @@ namespace i2p { namespace util { + + void RunnableService::StartIOService () + { + if (!m_IsRunning) + { + m_IsRunning = true; + m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this))); + } + } + + void RunnableService::StopIOService () + { + if (m_IsRunning) + { + m_IsRunning = false; + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + m_Thread = nullptr; + } + } + } + + void RunnableService::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, m_Name, ": runtime exception: ", ex.what ()); + } + } + } + namespace net { #ifdef WIN32 @@ -206,10 +245,10 @@ namespace net #else std::string localAddressUniversal = localAddress.to_string(); #endif - - typedef int (* IPN)(int af, const char *src, void *dst); + + typedef int (* IPN)(int af, const char *src, void *dst); IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); - if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found + if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found if(localAddress.is_v4()) { @@ -285,7 +324,7 @@ namespace net int GetMTU(const boost::asio::ip::address& localAddress) { - const int fallback = 576; // fallback MTU + int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU #ifdef WIN32 return GetMTUWindows(localAddress, fallback); diff --git a/libi2pd/util.h b/libi2pd/util.h index eccdadf7..2202ccd9 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -122,6 +123,43 @@ namespace util std::mutex m_Mutex; }; + class RunnableService + { + protected: + + RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {} + virtual ~RunnableService () {} + + boost::asio::io_service& GetIOService () { return m_Service; } + bool IsRunning () const { return m_IsRunning; }; + + void StartIOService (); + void StopIOService (); + + private: + + void Run (); + + private: + + std::string m_Name; + volatile bool m_IsRunning; + std::unique_ptr m_Thread; + boost::asio::io_service m_Service; + }; + + class RunnableServiceWithWork: public RunnableService + { + protected: + + RunnableServiceWithWork (const std::string& name): + RunnableService (name), m_Work (GetIOService ()) {} + + private: + + boost::asio::io_service::work m_Work; + }; + namespace net { int GetMTU (const boost::asio::ip::address& localAddress); diff --git a/libi2pd/version.h b/libi2pd/version.h index 2e876fdb..36fbe2ca 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -7,7 +7,7 @@ #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 29 +#define I2PD_VERSION_MINOR 30 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) @@ -21,7 +21,7 @@ #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 44 +#define I2P_VERSION_MICRO 45 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index aba090dc..c7bbdb46 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -743,8 +743,8 @@ namespace client } BOBCommandChannel::BOBCommandChannel (const std::string& address, int port): - m_IsRunning (false), m_Thread (nullptr), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) + RunnableService ("BOB"), + m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) { // command -> handler m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; @@ -794,7 +794,8 @@ namespace client BOBCommandChannel::~BOBCommandChannel () { - Stop (); + if (IsRunning ()) + Stop (); for (const auto& it: m_Destinations) delete it.second; } @@ -802,38 +803,15 @@ namespace client void BOBCommandChannel::Start () { Accept (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this)); + StartIOService (); } void BOBCommandChannel::Stop () { - m_IsRunning = false; for (auto& it: m_Destinations) it.second->Stop (); m_Acceptor.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - - void BOBCommandChannel::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "BOB: runtime exception: ", ex.what ()); - } - } + StopIOService (); } void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest) diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h index 8f1af185..15a0afaf 100644 --- a/libi2pd_client/BOB.h +++ b/libi2pd_client/BOB.h @@ -7,6 +7,7 @@ #include #include #include +#include "util.h" #include "I2PTunnel.h" #include "I2PService.h" #include "Identity.h" @@ -231,7 +232,7 @@ namespace client }; typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); - class BOBCommandChannel + class BOBCommandChannel: private i2p::util::RunnableService { public: @@ -241,22 +242,18 @@ namespace client void Start (); void Stop (); - boost::asio::io_service& GetService () { return m_Service; }; + boost::asio::io_service& GetService () { return GetIOService (); }; void AddDestination (const std::string& name, BOBDestination * dest); void DeleteDestination (const std::string& name); BOBDestination * FindDestination (const std::string& name); private: - void Run (); void Accept (); void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session); private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; boost::asio::ip::tcp::acceptor m_Acceptor; std::map m_Destinations; std::map m_CommandHandlers; diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index d14491b2..d72f40a8 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -53,14 +53,19 @@ namespace client // SAM bool sam; i2p::config::GetOption("sam.enabled", sam); - if (sam) { + if (sam) + { std::string samAddr; i2p::config::GetOption("sam.address", samAddr); uint16_t samPort; i2p::config::GetOption("sam.port", samPort); + bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread); LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort); - try { - m_SamBridge = new SAMBridge (samAddr, samPort); - m_SamBridge->Start (); - } catch (std::exception& e) { + try + { + m_SamBridge = new SAMBridge (samAddr, samPort, singleThread); + m_SamBridge->Start (); + } + catch (std::exception& e) + { LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what()); } } @@ -305,21 +310,34 @@ namespace client const std::map * params) { i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); - auto localDestination = std::make_shared (keys, isPublic, params); - std::unique_lock l(m_DestinationsMutex); - m_Destinations[localDestination->GetIdentHash ()] = localDestination; - localDestination->Start (); + auto localDestination = std::make_shared (keys, isPublic, params); + AddLocalDestination (localDestination); + return localDestination; + } + + std::shared_ptr ClientContext::CreateNewLocalDestination ( + boost::asio::io_service& service, bool isPublic, + i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, + const std::map * params) + { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); + auto localDestination = std::make_shared (service, keys, isPublic, params); + AddLocalDestination (localDestination); return localDestination; } std::shared_ptr ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params) { - MatchedTunnelDestination * cl = new MatchedTunnelDestination(keys, name, params); - auto localDestination = std::shared_ptr(cl); + auto localDestination = std::make_shared(keys, name, params); + AddLocalDestination (localDestination); + return localDestination; + } + + void ClientContext::AddLocalDestination (std::shared_ptr localDestination) + { std::unique_lock l(m_DestinationsMutex); m_Destinations[localDestination->GetIdentHash ()] = localDestination; localDestination->Start (); - return localDestination; } void ClientContext::DeleteLocalDestination (std::shared_ptr destination) @@ -344,14 +362,26 @@ namespace client if (it != m_Destinations.end ()) { LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); - if (!it->second->IsRunning ()) - it->second->Start (); + it->second->Start (); // make sure to start return it->second; } - auto localDestination = std::make_shared (keys, isPublic, params); - std::unique_lock l(m_DestinationsMutex); - m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination; - localDestination->Start (); + auto localDestination = std::make_shared (keys, isPublic, params); + AddLocalDestination (localDestination); + return localDestination; + } + + std::shared_ptr ClientContext::CreateNewLocalDestination (boost::asio::io_service& service, + const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params) + { + auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ()); + if (it != m_Destinations.end ()) + { + LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); + it->second->Start (); // make sure to start + return it->second; + } + auto localDestination = std::make_shared (service, keys, isPublic, params); + AddLocalDestination (localDestination); return localDestination; } diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 94aa8594..a239035f 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -68,8 +68,15 @@ namespace client i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, const std::map * params = nullptr); // used by SAM only + std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, + bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, + const std::map * params = nullptr); // same as previous but on external io_service std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, const std::map * params = nullptr); + std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, + const i2p::data::PrivateKeys& keys, bool isPublic = true, + const std::map * params = nullptr); // same as previous but on external io_service std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params = nullptr); void DeleteLocalDestination (std::shared_ptr destination); std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; @@ -107,6 +114,7 @@ namespace client void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain void CreateNewSharedLocalDestination (); + void AddLocalDestination (std::shared_ptr localDestination); private: diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 78190c89..c09b7f4d 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -24,10 +24,35 @@ namespace client { I2CPDestination::I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params): - LeaseSetDestination (isPublic, ¶ms), m_Owner (owner), m_Identity (identity) + RunnableService ("I2CP"), LeaseSetDestination (GetIOService (), isPublic, ¶ms), + m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()) { } + I2CPDestination::~I2CPDestination () + { + if (IsRunning ()) + Stop (); + } + + void I2CPDestination::Start () + { + if (!IsRunning ()) + { + LeaseSetDestination::Start (); + StartIOService (); + } + } + + void I2CPDestination::Stop () + { + if (IsRunning ()) + { + LeaseSetDestination::Stop (); + StopIOService (); + } + } + void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) { memcpy (m_EncryptionPrivateKey, key, 256); @@ -556,7 +581,10 @@ namespace client } // TODO: support multiple keys if (currentKey) + { m_Destination->SetEncryptionPrivateKey (currentKey); + m_Destination->SetEncryptionType (currentKeyType); + } m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); } @@ -786,8 +814,11 @@ namespace client { m_IsRunning = false; m_Acceptor.cancel (); - for (auto& it: m_Sessions) - it.second->Stop (); + { + auto sessions = m_Sessions; + for (auto& it: sessions) + it.second->Stop (); + } m_Sessions.clear (); m_Service.stop (); if (m_Thread) diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 0d235161..68b4415a 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -15,6 +15,7 @@ #include #include #include +#include "util.h" #include "Destination.h" namespace i2p @@ -61,19 +62,25 @@ namespace client const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; class I2CPSession; - class I2CPDestination: public LeaseSetDestination + class I2CPDestination: private i2p::util::RunnableService, public LeaseSetDestination { public: I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params); - + ~I2CPDestination (); + + void Start (); + void Stop (); + void SetEncryptionPrivateKey (const uint8_t * key); + void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession // implements LocalDestination bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; + i2p::data::CryptoKeyType GetEncryptionType () const { return m_EncryptionKeyType; }; std::shared_ptr GetIdentity () const { return m_Identity; }; protected: @@ -93,6 +100,7 @@ namespace client std::shared_ptr m_Owner; std::shared_ptr m_Identity; uint8_t m_EncryptionPrivateKey[256]; + i2p::data::CryptoKeyType m_EncryptionKeyType; std::shared_ptr m_Decryptor; uint64_t m_LeaseSetExpirationTime; }; diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index fa08ec51..3d75a3ca 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -8,7 +8,7 @@ namespace i2p namespace client { MatchedTunnelDestination::MatchedTunnelDestination(const i2p::data::PrivateKeys & keys, const std::string & remoteName, const std::map * params) - : ClientDestination(keys, false, params), + : RunnableClientDestination(keys, false, params), m_RemoteName(remoteName) {} @@ -45,29 +45,19 @@ namespace client } - bool MatchedTunnelDestination::Start() + void MatchedTunnelDestination::Start() { - if(ClientDestination::Start()) - { - m_ResolveTimer = std::make_shared(GetService()); - GetTunnelPool()->SetCustomPeerSelector(this); - ResolveCurrentLeaseSet(); - return true; - } - else - return false; + ClientDestination::Start(); + m_ResolveTimer = std::make_shared(GetService()); + GetTunnelPool()->SetCustomPeerSelector(this); + ResolveCurrentLeaseSet(); } - bool MatchedTunnelDestination::Stop() + void MatchedTunnelDestination::Stop() { - if(ClientDestination::Stop()) - { - if(m_ResolveTimer) - m_ResolveTimer->cancel(); - return true; - } - else - return false; + ClientDestination::Stop(); + if(m_ResolveTimer) + m_ResolveTimer->cancel(); } diff --git a/libi2pd_client/MatchedDestination.h b/libi2pd_client/MatchedDestination.h index bbeeb503..67b874e6 100644 --- a/libi2pd_client/MatchedDestination.h +++ b/libi2pd_client/MatchedDestination.h @@ -10,12 +10,12 @@ namespace client /** client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination */ - class MatchedTunnelDestination : public ClientDestination, public i2p::tunnel::ITunnelPeerSelector + class MatchedTunnelDestination : public RunnableClientDestination, public i2p::tunnel::ITunnelPeerSelector { public: MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, const std::map * params = nullptr); - bool Start(); - bool Stop(); + void Start(); + void Stop(); bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound); bool OnBuildResult(const i2p::tunnel::Path & peers, bool inbound, i2p::tunnel::TunnelBuildResult result); diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 766a1940..d0942221 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1000,10 +1000,10 @@ namespace client } } - SAMBridge::SAMBridge (const std::string& address, int port): - m_IsRunning (false), m_Thread (nullptr), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), - m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint), + SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread): + RunnableService ("SAM"), m_IsSingleThread (singleThread), + m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), + m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), m_SignatureTypes { {"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1}, @@ -1020,7 +1020,7 @@ namespace client SAMBridge::~SAMBridge () { - if (m_IsRunning) + if (IsRunning ()) Stop (); } @@ -1028,14 +1028,11 @@ namespace client { Accept (); ReceiveDatagram (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&SAMBridge::Run, this)); + StartIOService (); } void SAMBridge::Stop () { - m_IsRunning = false; - try { m_Acceptor.cancel (); @@ -1045,31 +1042,13 @@ namespace client LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); } - for (auto& it: m_Sessions) - it.second->CloseStreams (); - m_Sessions.clear (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - - void SAMBridge::Run () - { - while (m_IsRunning) { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); - } + std::unique_lock l(m_SessionsMutex); + for (auto& it: m_Sessions) + it.second->CloseStreams (); + m_Sessions.clear (); } + StopIOService (); } void SAMBridge::Accept () @@ -1118,7 +1097,9 @@ namespace client { i2p::data::PrivateKeys keys; if (!keys.FromBase64 (destination)) return nullptr; - localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params); + localDestination = m_IsSingleThread ? + i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) : + i2p::client::context.CreateNewLocalDestination (keys, true, params); } else // transient { @@ -1146,7 +1127,9 @@ namespace client } } } - localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params); + localDestination = m_IsSingleThread ? + i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) : + i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params); } if (localDestination) { @@ -1178,6 +1161,15 @@ namespace client session->localDestination->Release (); session->localDestination->StopAcceptingStreams (); session->CloseStreams (); + if (m_IsSingleThread) + { + auto timer = std::make_shared(GetService ()); + timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds + timer->async_wait ([timer, session](const boost::system::error_code& ecode) + { + // session's destructor is called here + }); + } } } diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index aebdccb6..5a447c06 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -9,6 +9,7 @@ #include #include #include +#include "util.h" #include "Identity.h" #include "LeaseSet.h" #include "Streaming.h" @@ -174,17 +175,17 @@ namespace client void CloseStreams (); }; - class SAMBridge + class SAMBridge: private i2p::util::RunnableService { public: - SAMBridge (const std::string& address, int port); + SAMBridge (const std::string& address, int port, bool singleThread); ~SAMBridge (); void Start (); void Stop (); - boost::asio::io_service& GetService () { return m_Service; }; + boost::asio::io_service& GetService () { return GetIOService (); }; std::shared_ptr CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient const std::map * params); void CloseSession (const std::string& id); @@ -201,8 +202,6 @@ namespace client private: - void Run (); - void Accept (); void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); @@ -211,9 +210,7 @@ namespace client private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; + bool m_IsSingleThread; boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; boost::asio::ip::udp::socket m_DatagramSocket; diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index 8a67901c..8742d4c5 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -433,6 +433,9 @@ namespace proxy break; case CMD_UDP: if (m_socksv == SOCKS5) break; +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif default: LogPrint(eLogError, "SOCKS: invalid command: ", ((int)*sock_buff)); SocksRequestFailed(SOCKS5_GEN_FAIL); diff --git a/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml b/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml index e6cc81d3..00b6974a 100644 --- a/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml +++ b/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml @@ -35,6 +35,7 @@ + diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 0cb73a2f..3cd2c2e0 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -63,6 +63,7 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \ ../../libi2pd/TunnelPool.cpp \ ../../libi2pd/util.cpp \ ../../libi2pd/Elligator.cpp \ + ../../libi2pd/ECIESX25519AEADRatchetSession.cpp \ ../../libi2pd_client/AddressBook.cpp \ ../../libi2pd_client/BOB.cpp \ ../../libi2pd_client/ClientContext.cpp \ @@ -153,6 +154,7 @@ HEADERS += DaemonQT.h mainwindow.h \ ../../libi2pd/util.h \ ../../libi2pd/version.h \ ../../libi2pd/Elligator.h \ + ../../libi2pd/ECIESX25519AEADRatchetSession.h \ ../../libi2pd_client/AddressBook.h \ ../../libi2pd_client/BOB.h \ ../../libi2pd_client/ClientContext.h \ @@ -216,6 +218,9 @@ macx { LIBS += $$BOOSTROOT/lib/libboost_filesystem.a LIBS += $$BOOSTROOT/lib/libboost_program_options.a LIBS += $$UPNPROOT/lib/libminiupnpc.a + LIBS += -Wl,-dead_strip + LIBS += -Wl,-dead_strip_dylibs + LIBS += -Wl,-bind_at_load } linux:!android { diff --git a/tests/test-elligator.cpp b/tests/test-elligator.cpp index 94798e80..48c9e31a 100644 --- a/tests/test-elligator.cpp +++ b/tests/test-elligator.cpp @@ -58,14 +58,20 @@ const uint8_t key3[32] = 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; +const uint8_t failed_key[32] = +{ + 0xe6, 0xf6, 0x6f, 0xdf, 0x6e, 0x23, 0x0c, 0x60, 0x3c, 0x5e, 0x6e, 0x59, 0xa2, 0x54, 0xea, 0x14, + 0x76, 0xa1, 0x3e, 0xb9, 0x51, 0x1b, 0x95, 0x49, 0x84, 0x67, 0x81, 0xe1, 0x2e, 0x52, 0x23, 0x0a +}; + int main () { uint8_t buf[32]; i2p::crypto::Elligator2 el; // encoding tests - el.Encode (key, buf); + el.Encode (key, buf, false, false); assert(memcmp (buf, encoded_key, 32) == 0); - el.Encode (key, buf, true); // with highY + el.Encode (key, buf, true, false); // with highY assert(memcmp (buf, encoded_key_high_y, 32) == 0); // decoding tests el.Decode (encoded1, buf); @@ -74,4 +80,6 @@ int main () assert(memcmp (buf, key2, 32) == 0); el.Decode (encoded3, buf); assert(memcmp (buf, key3, 32) == 0); + // encoding fails + assert (!el.Encode (failed_key, buf)); } diff --git a/tests/test-x25519.cpp b/tests/test-x25519.cpp index 9f249dbd..2ab8ad6a 100644 --- a/tests/test-x25519.cpp +++ b/tests/test-x25519.cpp @@ -27,10 +27,13 @@ uint8_t p[32] = int main () { +#if !OPENSSL_X25519 +// we test it for openssl < 1.1.0 uint8_t buf[32]; BN_CTX * ctx = BN_CTX_new (); i2p::crypto::GetEd25519 ()->ScalarMul (u, k, buf, ctx); BN_CTX_free (ctx); assert(memcmp (buf, p, 32) == 0); +#endif }