diff --git a/.gitignore b/.gitignore index 353d839f..c2db70e0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ netDb /i2pd /libi2pd.a /libi2pdclient.a -i2pd.exe +*.exe # Autotools diff --git a/ChangeLog b/ChangeLog index d2bfe7bf..d8ab87de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,42 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.20.0] - 2018-08-23 +### Added +- Full implementation of NTCP2 +- Assets for android +### Changed +- armeabi-v7a and x86 in one apk for android +- NTCP2 is enabled by default +- Show lease's expiration time in readable format in the web console +### Fixed +- Correct names for transports in the web console + +## [2.19.0] - 2018-06-26 +### Added +- ECIES support for RouterInfo +- HTTP outproxy authorization +- AVX/AESNI runtime detection +- Initial implementation of NTCP2 +- I2CP session reconfigure +- I2CP method ClientServicesInfo +- Datagrams to websocks +### Changed +- RouterInfo uses EdDSA signature by default +- Remove stream bans +- Android build system changed to gradle +- Multiple changes in QT GUI +- Dockerfile +### Fixed +- zero tunnelID issue +- tunnels reload +- headers in webconsole +- XSS in webconsole from SAM session name +- build for gcc 8 +- cmake build scripts +- systemd service files +- some netbsd issues + ## [2.18.0] - 2018-01-30 ### Added - Show tunnel nicknames for I2CP destination in WebUI @@ -43,7 +79,7 @@ - NTCP soft and hard descriptors limits - Support full timestamps in logs ### Changed -- Faster implmentation of GOST R 34.11 hash +- Faster implementation of GOST R 34.11 hash - Reject routers with RSA signtures - Reload config and shudown from Windows GUI - Update tunnels address(destination) without restart @@ -143,7 +179,7 @@ - Initial iOS support ### Changed -- Reduced file descriptiors usage +- Reduced file descriptors usage - Strict reseed checks enabled by default ## Fixed diff --git a/Makefile b/Makefile index 6d56ec7d..c51018f9 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,14 @@ USE_AVX := yes USE_STATIC := no USE_MESHNET := no USE_UPNP := no +DEBUG := yes + +ifeq ($(DEBUG),yes) + CXX_DEBUG = -g +else + CXX_DEBUG = -Os + LD_DEBUG = -s +endif ifeq ($(WEBSOCKETS),1) NEEDED_CXXFLAGS += -DWITH_EVENTS diff --git a/Makefile.bsd b/Makefile.bsd index f2293540..39e5651a 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -1,5 +1,5 @@ CXX = clang++ -CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation +CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation ## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. ## For example, when adding 'hardening flags' to the build @@ -8,5 +8,5 @@ CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-ind ## custom FLAGS to work at build-time. NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 INCFLAGS = -I/usr/include/ -I/usr/local/include/ -LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib +LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread diff --git a/Makefile.homebrew b/Makefile.homebrew index a6e645ee..688fdaea 100644 --- a/Makefile.homebrew +++ b/Makefile.homebrew @@ -3,8 +3,9 @@ BREWROOT = /usr/local BOOSTROOT = ${BREWROOT}/opt/boost SSLROOT = ${BREWROOT}/opt/libressl UPNPROOT = ${BREWROOT}/opt/miniupnpc -CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX -Wno-overloaded-virtual +CXXFLAGS = ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX -Wno-overloaded-virtual INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include +LDFLAGS = ${LD_DEBUG} ifndef TRAVIS CXX = clang++ @@ -13,7 +14,7 @@ endif ifeq ($(USE_STATIC),yes) LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_date_time.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a -lpthread else - LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib + LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread endif @@ -34,7 +35,7 @@ endif # Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2 # Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic ifeq ($(USE_AESNI),yes) - CXXFLAGS += -maes -DAESNI + CXXFLAGS += -maes endif ifeq ($(USE_AVX),1) CXXFLAGS += -mavx diff --git a/Makefile.linux b/Makefile.linux index 4a82591a..b9a740ad 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,13 +1,13 @@ # set defaults instead redefine -CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -INCFLAGS ?= +CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation +LDFLAGS ?= ${LD_DEBUG} ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. ## For example, when adding 'hardening flags' to the build ## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove ## -std=c++11. If you want to remove this variable please do so in a way that allows setting -## custom FLAGS to work at build-time. +## custom FDLAGS to work at build-time. # detect proper flag for c++11 support by compilers CXXVER := $(shell $(CXX) -dumpversion) @@ -21,7 +21,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6 NEEDED_CXXFLAGS += -std=c++0x else ifeq ($(shell expr match ${CXXVER} "[5-7]\.[0-9]"),3) # gcc >= 5.0 NEEDED_CXXFLAGS += -std=c++11 -else ifeq ($(shell expr match ${CXXVER} "7"),1) # gcc 7 ubuntu +else ifeq ($(shell expr match ${CXXVER} "[7-8]"),1) # gcc 7 ubuntu or gcc 8 arch NEEDED_CXXFLAGS += -std=c++11 else # not supported $(error Compiler too old) @@ -60,7 +60,12 @@ endif ifeq ($(USE_AESNI),yes) #check if AES-NI is supported by CPU ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0) - CPU_FLAGS += -maes -DAESNI + machine := $(shell uname -m) + ifeq ($(machine), aarch64) + CXXFLAGS += -DARM64AES + else + CPU_FLAGS += -maes + endif endif endif diff --git a/Makefile.mingw b/Makefile.mingw index b40d0ada..5f3292d1 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,54 +1,54 @@ -USE_WIN32_APP=yes -CXX = g++ -WINDRES = windres -CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN -NEEDED_CXXFLAGS = -std=c++11 -BOOST_SUFFIX = -mt -INCFLAGS = -Idaemon -I. -LDFLAGS = -s -Wl,-rpath,/usr/local/lib -Wl,-Bstatic -static-libgcc -static-libstdc++ - -# UPNP Support -ifeq ($(USE_UPNP),yes) - CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB - LDLIBS = -lminiupnpc -endif - -LDLIBS += \ - -lboost_system$(BOOST_SUFFIX) \ - -lboost_date_time$(BOOST_SUFFIX) \ - -lboost_filesystem$(BOOST_SUFFIX) \ - -lboost_program_options$(BOOST_SUFFIX) \ - -lssl \ - -lcrypto \ - -lz \ - -lwsock32 \ - -lws2_32 \ - -lgdi32 \ - -liphlpapi \ - -lstdc++ \ - -lpthread - -ifeq ($(USE_WIN32_APP), yes) - CXXFLAGS += -DWIN32_APP - LDFLAGS += -mwindows - DAEMON_RC += Win32/Resource.rc - DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) -endif - -# don't change following line to ifeq ($(USE_AESNI),yes) !!! -ifeq ($(USE_AESNI),1) - CPU_FLAGS += -maes -DAESNI -else - CPU_FLAGS += -msse -endif - -ifeq ($(USE_AVX),1) - CPU_FLAGS += -mavx -endif - -ifeq ($(USE_ASLR),yes) - LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols -endif - -obj/%.o : %.rc - $(WINDRES) -i $< -o $@ +USE_WIN32_APP=yes +CXX = g++ +WINDRES = windres +CXXFLAGS := ${CXX_DEBUG} -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN +NEEDED_CXXFLAGS = -std=c++11 +BOOST_SUFFIX = -mt +INCFLAGS = -Idaemon -I. +LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc -static-libstdc++ + +# UPNP Support +ifeq ($(USE_UPNP),yes) + CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB + LDLIBS = -lminiupnpc +endif + +LDLIBS += \ + -lboost_system$(BOOST_SUFFIX) \ + -lboost_date_time$(BOOST_SUFFIX) \ + -lboost_filesystem$(BOOST_SUFFIX) \ + -lboost_program_options$(BOOST_SUFFIX) \ + -lssl \ + -lcrypto \ + -lz \ + -lwsock32 \ + -lws2_32 \ + -lgdi32 \ + -liphlpapi \ + -lstdc++ \ + -lpthread + +ifeq ($(USE_WIN32_APP), yes) + CXXFLAGS += -DWIN32_APP + LDFLAGS += -mwindows + DAEMON_RC += Win32/Resource.rc + DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) +endif + +# don't change following line to ifeq ($(USE_AESNI),yes) !!! +ifeq ($(USE_AESNI),1) + CPU_FLAGS += -maes +else + CPU_FLAGS += -msse +endif + +ifeq ($(USE_AVX),1) + CPU_FLAGS += -mavx +endif + +ifeq ($(USE_ASLR),yes) + LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols +endif + +obj/%.o : %.rc + $(WINDRES) -i $< -o $@ diff --git a/Makefile.osx b/Makefile.osx index 8bbf37f0..13376040 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -1,8 +1,7 @@ CXX = clang++ -CXXFLAGS = -Os -Wall -std=c++11 -DMAC_OSX -#CXXFLAGS = -g -O2 -Wall -std=c++11 +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 := ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib 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 @@ -21,7 +20,7 @@ ifeq ($(USE_UPNP),yes) endif ifeq ($(USE_AESNI),1) - CXXFLAGS += -maes -DAESNI + CXXFLAGS += -maes else CXXFLAGS += -msse endif @@ -32,6 +31,14 @@ endif # Disabled, since it will be the default make rule. I think its better # to define the default rule in Makefile and not Makefile. - torkel -#install: all -# test -d ${PREFIX} || mkdir -p ${PREFIX}/ -# cp -r i2p ${PREFIX}/ +install-brew: all + install -d ${PREFIX}/bin ${PREFIX}/etc/i2pd ${PREFIX}/share/doc/i2pd ${PREFIX}/share/i2pd ${PREFIX}/share/man/man1 ${PREFIX}/var/lib/i2pd + install -m 755 ${I2PD} ${PREFIX}/bin/ + install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd + @cp -R contrib/certificates ${PREFIX}/share/i2pd/ + install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd + @gzip debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1 + @ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/ + @ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf + @ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt + @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf diff --git a/Win32/installer.iss b/Win32/installer.iss index 15ce4372..e340c65f 100644 --- a/Win32/installer.iss +++ b/Win32/installer.iss @@ -1,5 +1,5 @@ #define I2Pd_AppName "i2pd" -#define I2Pd_ver "2.18.0" +#define I2Pd_ver "2.20.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff --git a/android/.gitignore b/android/.gitignore index 90cd315e..6e42311a 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,12 +1,15 @@ gen tests +bin +libs +log* +obj +.gradle .idea +.externalNativeBuild ant.properties local.properties build.sh -bin -log* -.gradle android.iml build gradle diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index cfc9d55b..9a2cffd5 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,11 +3,11 @@ package="org.purplei2p.i2pd" android:installLocation="auto" android:versionCode="1" - android:versionName="2.18.0"> + android:versionName="2.20.0"> + android:targetSdkVersion="28" /> @@ -53,4 +53,4 @@ - \ No newline at end of file + diff --git a/android/assets/certificates b/android/assets/certificates new file mode 120000 index 00000000..01b21e5d --- /dev/null +++ b/android/assets/certificates @@ -0,0 +1 @@ +../../contrib/certificates \ No newline at end of file diff --git a/android/assets/i2pd.conf b/android/assets/i2pd.conf new file mode 100644 index 00000000..cf113da2 --- /dev/null +++ b/android/assets/i2pd.conf @@ -0,0 +1,81 @@ +## Configuration file for a typical i2pd user +## See https://i2pd.readthedocs.org/en/latest/configuration.html +## for more options you can use in this file. + +#logfile = /sdcard/i2pd/i2pd.log +loglevel = none + +# host = 1.2.3.4 +# port = 4567 + +ipv4 = true +ipv6 = false + +# ntcp = true +# ntcpproxy = http://127.0.0.1:8118 +# ssu = true + +bandwidth = L +# share = 100 + +# notransit = true +# floodfill = true + +[ntcp2] +enabled = true + +[http] +enabled = true +address = 127.0.0.1 +port = 7070 +# auth = true +# user = i2pd +# pass = changeme + +[httpproxy] +enabled = true +address = 127.0.0.1 +port = 4444 +# keys = http-proxy-keys.dat +# addresshelper = true +# outproxy = http://false.i2p +## httpproxy section also accepts I2CP parameters, like "inbound.length" etc. + +[socksproxy] +enabled = true +address = 127.0.0.1 +port = 4447 +# keys = socks-proxy-keys.dat +# outproxy.enabled = false +# outproxy = 127.0.0.1 +# outproxyport = 9050 +## socksproxy section also accepts I2CP parameters, like "inbound.length" etc. + +[sam] +enabled = false +# address = 127.0.0.1 +# port = 7656 + +[precomputation] +elgamal = true + +[upnp] +enabled = true +# name = I2Pd + +[reseed] +verify = true +## Path to local reseed data file (.su3) for manual reseeding +# file = /path/to/i2pseeds.su3 +## or HTTPS URL to reseed from +# file = https://legit-website.com/i2pseeds.su3 +## Path to local ZIP file or HTTPS URL to reseed from +# zipfile = /path/to/netDb.zip +## If you run i2pd behind a proxy server, set proxy server for reseeding here +## Should be http://address:port or socks://address:port +# proxy = http://127.0.0.1:8118 +## Minimum number of known routers, below which i2pd triggers reseeding. 25 by default +# threshold = 25 + +[limits] +transittunnels = 50 diff --git a/android/assets/subscriptions.txt b/android/assets/subscriptions.txt new file mode 100644 index 00000000..8f4afb03 --- /dev/null +++ b/android/assets/subscriptions.txt @@ -0,0 +1,3 @@ +http://inr.i2p/export/alive-hosts.txt +http://stats.i2p/cgi-bin/newhosts.txt +http://i2p-projekt.i2p/hosts.txt diff --git a/android/assets/tunnels.conf b/android/assets/tunnels.conf new file mode 100644 index 00000000..e95fdf2e --- /dev/null +++ b/android/assets/tunnels.conf @@ -0,0 +1,33 @@ +[IRC-IRC2P] +#type = client +#address = 127.0.0.1 +#port = 6668 +#destination = irc.postman.i2p +#destinationport = 6667 +#keys = irc-keys.dat + +#[IRC-ILITA] +#type = client +#address = 127.0.0.1 +#port = 6669 +#destination = irc.ilita.i2p +#destinationport = 6667 +#keys = irc-keys.dat + +#[SMTP] +#type = client +#address = 127.0.0.1 +#port = 7659 +#destination = smtp.postman.i2p +#destinationport = 25 +#keys = smtp-keys.dat + +#[POP3] +#type = client +#address = 127.0.0.1 +#port = 7660 +#destination = pop.postman.i2p +#destinationport = 110 +#keys = pop3-keys.dat + +# see more examples at https://i2pd.readthedocs.io/en/latest/user-guide/tunnels/ diff --git a/android/build.gradle b/android/build.gradle index 4fe17d88..8be30645 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -18,17 +18,22 @@ repositories { } android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 28 + buildToolsVersion "28.0.1" defaultConfig { applicationId "org.purplei2p.i2pd" - targetSdkVersion 25 + targetSdkVersion 28 minSdkVersion 14 versionCode 1 - versionName "2.18.0" + versionName "2.20.0" ndk { abiFilters 'armeabi-v7a' - //abiFilters 'x86' + abiFilters 'x86' + } + externalNativeBuild { + ndkBuild { + arguments "-j4" + } } } sourceSets { @@ -37,6 +42,7 @@ android { java.srcDirs = ['src'] res.srcDirs = ['res'] jniLibs.srcDirs = ['libs'] + assets.srcDirs = ['assets'] } } signingConfigs { @@ -49,7 +55,7 @@ android { } buildTypes { release { - minifyEnabled false + minifyEnabled true signingConfig signingConfigs.orignal proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' } diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..af82e006 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.parallel=true \ No newline at end of file diff --git a/android/jni/Android.mk b/android/jni/Android.mk index d5a8f05f..80935236 100755 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -68,6 +68,6 @@ include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := miniupnpc -LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnp-2.0/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a -LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnp-2.0/include +LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnpc-2.1/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a +LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnpc-2.1/include include $(PREBUILT_STATIC_LIBRARY) diff --git a/android/jni/Application.mk b/android/jni/Application.mk index 0fa116c2..92622ee9 100755 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -1,8 +1,8 @@ #APP_ABI := all -#APP_ABI := armeabi-v7a x86 +APP_ABI := armeabi-v7a x86 #APP_ABI := x86 #APP_ABI := x86_64 -APP_ABI := armeabi-v7a +#APP_ABI := armeabi-v7a #can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there. APP_PLATFORM := android-14 @@ -19,7 +19,8 @@ ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) APP_CPPFLAGS += -DANDROID_ARM7A endif -APP_OPTIM := debug +# 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/OpenSSL-for-Android-Prebuilt.git diff --git a/android/jni/DaemonAndroid.cpp b/android/jni/DaemonAndroid.cpp index 75584740..5ea5e495 100644 --- a/android/jni/DaemonAndroid.cpp +++ b/android/jni/DaemonAndroid.cpp @@ -1,10 +1,13 @@ -#include "DaemonAndroid.h" -#include "Daemon.h" #include +#include +#include +#include #include #include -#include //#include "mainwindow.h" +#include "FS.h" +#include "DaemonAndroid.h" +#include "Daemon.h" namespace i2p { @@ -80,6 +83,17 @@ namespace android //mutex=new QMutex(QMutex::Recursive); //setRunningCallback(0); //m_IsRunning=false; + + // make sure assets are ready before proceed + i2p::fs::DetectDataDir("", false); + int numAttempts = 0; + do + { + if (i2p::fs::Exists (i2p::fs::DataDirPath("assets.ready"))) break; // assets ready + numAttempts++; + std::this_thread::sleep_for (std::chrono::seconds(1)); // otherwise wait for 1 more second + } + while (numAttempts <= 10); // 10 seconds max return Daemon.init(argc,argv); } diff --git a/android/project.properties b/android/project.properties index f72b9716..919ca9c3 100644 --- a/android/project.properties +++ b/android/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-25 +target=android-28 diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 00000000..2a620ef9 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "i2pd" diff --git a/android/src/org/purplei2p/i2pd/I2PDActivity.java b/android/src/org/purplei2p/i2pd/I2PDActivity.java index 99672eb7..bc6f7209 100755 --- a/android/src/org/purplei2p/i2pd/I2PDActivity.java +++ b/android/src/org/purplei2p/i2pd/I2PDActivity.java @@ -1,5 +1,13 @@ package org.purplei2p.i2pd; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.BufferedReader; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Timer; @@ -10,7 +18,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.res.AssetManager; import android.os.Bundle; +import android.os.Environment; import android.os.IBinder; import android.util.Log; import android.view.Menu; @@ -18,25 +28,31 @@ import android.view.MenuItem; import android.widget.TextView; import android.widget.Toast; -public class I2PDActivity extends Activity { - private static final String TAG = "i2pdActvt"; - public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; +// For future package update checking +import org.purplei2p.i2pd.BuildConfig; - private TextView textView; +public class I2PDActivity extends Activity { + private static final String TAG = "i2pdActvt"; + public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; + + private TextView textView; + private boolean assetsCopied; + private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/"; private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = - new DaemonSingleton.StateUpdateListener() { - + new DaemonSingleton.StateUpdateListener() { @Override - public void daemonStateUpdate() { + public void daemonStateUpdate() + { + processAssets(); runOnUiThread(new Runnable(){ @Override public void run() { try { - if(textView==null)return; + if(textView==null) return; Throwable tr = daemon.getLastThrowable(); if(tr!=null) { textView.setText(throwableToString(tr)); @@ -44,10 +60,10 @@ public class I2PDActivity extends Activity { } DaemonSingleton.State state = daemon.getState(); textView.setText( - String.valueOf(state)+ - (DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+ - (DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"") - ); + String.valueOf(state)+ + (DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+ + (DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"") + ); } catch (Throwable tr) { Log.e(TAG,"error ignored",tr); } @@ -55,121 +71,120 @@ public class I2PDActivity extends Activity { }); } }; - private static volatile long graceStartedMillis; - private static final Object graceStartedMillis_LOCK=new Object(); + private static volatile long graceStartedMillis; + private static final Object graceStartedMillis_LOCK=new Object(); - private static String formatGraceTimeRemaining() { - long remainingSeconds; - synchronized (graceStartedMillis_LOCK){ - remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D); - } - long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D); - long remSec=remainingSeconds-remainingMinutes*60; - return remainingMinutes+":"+(remSec/10)+remSec%10; - } + private static String formatGraceTimeRemaining() { + long remainingSeconds; + synchronized (graceStartedMillis_LOCK){ + remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D); + } + long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D); + long remSec=remainingSeconds-remainingMinutes*60; + return remainingMinutes+":"+(remSec/10)+remSec%10; + } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - textView = new TextView(this); - setContentView(textView); - daemon.addStateChangeListener(daemonStateUpdatedListener); - daemonStateUpdatedListener.daemonStateUpdate(); + textView = new TextView(this); + setContentView(textView); + daemon.addStateChangeListener(daemonStateUpdatedListener); + daemonStateUpdatedListener.daemonStateUpdate(); - //set the app be foreground - doBindService(); + // set the app be foreground + doBindService(); - final Timer gracefulQuitTimer = getGracefulQuitTimer(); - if(gracefulQuitTimer!=null){ - long gracefulStopAtMillis; - synchronized (graceStartedMillis_LOCK) { - gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; - } - rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis); - } - } + final Timer gracefulQuitTimer = getGracefulQuitTimer(); + if(gracefulQuitTimer!=null){ + long gracefulStopAtMillis; + synchronized (graceStartedMillis_LOCK) { + gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; + } + rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis); + } + } - @Override + @Override protected void onDestroy() { super.onDestroy(); - textView = null; - daemon.removeStateChangeListener(daemonStateUpdatedListener); - //cancelGracefulStop(); + textView = null; + daemon.removeStateChangeListener(daemonStateUpdatedListener); + //cancelGracefulStop(); try{ - doUnbindService(); - }catch(Throwable tr){ + doUnbindService(); + }catch(Throwable tr){ Log.e(TAG, "", tr); } } - private static void cancelGracefulStop() { - Timer gracefulQuitTimer = getGracefulQuitTimer(); - if(gracefulQuitTimer!=null) { - gracefulQuitTimer.cancel(); - setGracefulQuitTimer(null); - } - } - - private CharSequence throwableToString(Throwable tr) { - StringWriter sw = new StringWriter(8192); - PrintWriter pw = new PrintWriter(sw); - tr.printStackTrace(pw); - pw.close(); - return sw.toString(); + private static void cancelGracefulStop() { + Timer gracefulQuitTimer = getGracefulQuitTimer(); + if(gracefulQuitTimer!=null) { + gracefulQuitTimer.cancel(); + setGracefulQuitTimer(null); + } } -// private LocalService mBoundService; + private CharSequence throwableToString(Throwable tr) { + StringWriter sw = new StringWriter(8192); + PrintWriter pw = new PrintWriter(sw); + tr.printStackTrace(pw); + pw.close(); + return sw.toString(); + } - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - // This is called when the connection with the service has been - // established, giving us the service object we can use to - // interact with the service. Because we have bound to a explicit - // service that we know is running in our own process, we can - // cast its IBinder to a concrete class and directly access it. -// mBoundService = ((LocalService.LocalBinder)service).getService(); + // private LocalService mBoundService; - // Tell the user about this for our demo. -// Toast.makeText(Binding.this, R.string.local_service_connected, -// Toast.LENGTH_SHORT).show(); - } + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. Because we have bound to a explicit + // service that we know is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + // mBoundService = ((LocalService.LocalBinder)service).getService(); - public void onServiceDisconnected(ComponentName className) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - // Because it is running in our same process, we should never - // see this happen. -// mBoundService = null; -// Toast.makeText(Binding.this, R.string.local_service_disconnected, -// Toast.LENGTH_SHORT).show(); - } - }; + // Tell the user about this for our demo. + // Toast.makeText(Binding.this, R.string.local_service_connected, + // Toast.LENGTH_SHORT).show(); + } + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + // Because it is running in our same process, we should never + // see this happen. + // mBoundService = null; + // Toast.makeText(Binding.this, R.string.local_service_disconnected, + // Toast.LENGTH_SHORT).show(); + } + }; - private static volatile boolean mIsBound; + private static volatile boolean mIsBound; - private void doBindService() { - synchronized (I2PDActivity.class) { - if (mIsBound) return; - // Establish a connection with the service. We use an explicit - // class name because we want a specific service implementation that - // we know will be running in our own process (and thus won't be - // supporting component replacement by other applications). - bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE); - mIsBound = true; - } - } + private void doBindService() { + synchronized (I2PDActivity.class) { + if (mIsBound) return; + // Establish a connection with the service. We use an explicit + // class name because we want a specific service implementation that + // we know will be running in our own process (and thus won't be + // supporting component replacement by other applications). + bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + } + } - private void doUnbindService() { - synchronized (I2PDActivity.class) { - if (mIsBound) { - // Detach our existing connection. - unbindService(mConnection); - mIsBound = false; - } - } - } + private void doUnbindService() { + synchronized (I2PDActivity.class) { + if (mIsBound) { + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + } + } + } @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -186,100 +201,219 @@ public class I2PDActivity extends Activity { int id = item.getItemId(); switch(id){ - case R.id.action_stop: - i2pdStop(); - return true; - case R.id.action_graceful_stop: - i2pdGracefulStop(); - return true; - } + case R.id.action_stop: + i2pdStop(); + return true; + case R.id.action_graceful_stop: + i2pdGracefulStop(); + return true; + } return super.onOptionsItemSelected(item); } private void i2pdStop() { - cancelGracefulStop(); - new Thread(new Runnable(){ + cancelGracefulStop(); + new Thread(new Runnable(){ - @Override - public void run() { - Log.d(TAG, "stopping"); - try{ - daemon.stopDaemon(); - }catch (Throwable tr) { - Log.e(TAG, "", tr); - } - } + @Override + public void run() { + Log.d(TAG, "stopping"); + try{ + daemon.stopDaemon(); + }catch (Throwable tr) { + Log.e(TAG, "", tr); + } + } - },"stop").start(); - } + },"stop").start(); + } - private static volatile Timer gracefulQuitTimer; + private static volatile Timer gracefulQuitTimer; - private void i2pdGracefulStop() { - if(daemon.getState()==DaemonSingleton.State.stopped){ - Toast.makeText(this, R.string.already_stopped, - Toast.LENGTH_SHORT).show(); - return; - } - if(getGracefulQuitTimer()!=null){ - Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, - Toast.LENGTH_SHORT).show(); - return; - } - Toast.makeText(this, R.string.graceful_stop_is_in_progress, - Toast.LENGTH_SHORT).show(); - new Thread(new Runnable(){ + private void i2pdGracefulStop() { + if(daemon.getState()==DaemonSingleton.State.stopped){ + Toast.makeText(this, R.string.already_stopped, + Toast.LENGTH_SHORT).show(); + return; + } + if(getGracefulQuitTimer()!=null){ + Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, + Toast.LENGTH_SHORT).show(); + return; + } + Toast.makeText(this, R.string.graceful_stop_is_in_progress, + Toast.LENGTH_SHORT).show(); + new Thread(new Runnable(){ @Override public void run() { try{ Log.d(TAG, "grac stopping"); - if(daemon.isStartedOkay()) { - daemon.stopAcceptingTunnels(); - long gracefulStopAtMillis; - synchronized (graceStartedMillis_LOCK) { - graceStartedMillis = System.currentTimeMillis(); - gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; - } - rescheduleGraceStop(null,gracefulStopAtMillis); - }else{ - i2pdStop(); - } - } catch(Throwable tr) { + if(daemon.isStartedOkay()) { + daemon.stopAcceptingTunnels(); + long gracefulStopAtMillis; + synchronized (graceStartedMillis_LOCK) { + graceStartedMillis = System.currentTimeMillis(); + gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; + } + rescheduleGraceStop(null,gracefulStopAtMillis); + }else{ + i2pdStop(); + } + } catch(Throwable tr) { Log.e(TAG,"",tr); } } - },"gracInit").start(); - } + },"gracInit").start(); + } - private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) { - if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel(); - final Timer gracefulQuitTimer = new Timer(true); - setGracefulQuitTimer(gracefulQuitTimer); - gracefulQuitTimer.schedule(new TimerTask(){ + private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) { + if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel(); + final Timer gracefulQuitTimer = new Timer(true); + setGracefulQuitTimer(gracefulQuitTimer); + gracefulQuitTimer.schedule(new TimerTask(){ - @Override - public void run() { - i2pdStop(); - } + @Override + public void run() { + i2pdStop(); + } - }, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis())); - final TimerTask tickerTask = new TimerTask() { - @Override - public void run() { - daemonStateUpdatedListener.daemonStateUpdate(); - } - }; - gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/); - } + }, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis())); + final TimerTask tickerTask = new TimerTask() { + @Override + public void run() { + daemonStateUpdatedListener.daemonStateUpdate(); + } + }; + gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/); + } - private static Timer getGracefulQuitTimer() { - return gracefulQuitTimer; + private static Timer getGracefulQuitTimer() { + return gracefulQuitTimer; } private static void setGracefulQuitTimer(Timer gracefulQuitTimer) { - I2PDActivity.gracefulQuitTimer = gracefulQuitTimer; + I2PDActivity.gracefulQuitTimer = gracefulQuitTimer; + } + + /** + * Copy the asset at the specified path to this app's data directory. If the + * asset is a directory, its contents are also copied. + * + * @param path + * Path to asset, relative to app's assets directory. + */ + private void copyAsset(String path) { + AssetManager manager = getAssets(); + + // If we have a directory, we make it and recurse. If a file, we copy its + // contents. + try { + String[] contents = manager.list(path); + + // The documentation suggests that list throws an IOException, but doesn't + // say under what conditions. It'd be nice if it did so when the path was + // to a file. That doesn't appear to be the case. If the returned array is + // null or has 0 length, we assume the path is to a file. This means empty + // directories will get turned into files. + if (contents == null || contents.length == 0) + throw new IOException(); + + // Make the directory. + File dir = new File(i2pdpath, path); + dir.mkdirs(); + + // Recurse on the contents. + for (String entry : contents) { + copyAsset(path + "/" + entry); + } + } catch (IOException e) { + copyFileAsset(path); + } + } + + /** + * Copy the asset file specified by path to app's data directory. Assumes + * parent directories have already been created. + * + * @param path + * Path to asset, relative to app's assets directory. + */ + private void copyFileAsset(String path) { + File file = new File(i2pdpath, path); + if(!file.exists()) try { + InputStream in = getAssets().open(path); + OutputStream out = new FileOutputStream(file); + byte[] buffer = new byte[1024]; + int read = in.read(buffer); + while (read != -1) { + out.write(buffer, 0, read); + read = in.read(buffer); + } + out.close(); + in.close(); + } catch (IOException e) { + Log.e(TAG, "", e); + } + } + + private void deleteRecursive(File fileOrDirectory) { + if (fileOrDirectory.isDirectory()) { + for (File child : fileOrDirectory.listFiles()) { + deleteRecursive(child); + } + } + fileOrDirectory.delete(); + } + + private void processAssets() { + if (!assetsCopied) try { + assetsCopied = true; // prevent from running on every state update + + File holderfile = new File(i2pdpath, "assets.ready"); + String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX + StringBuilder text = new StringBuilder(); + + if (holderfile.exists()) try { // if holder file exists, read assets version string + BufferedReader br = new BufferedReader(new FileReader(holderfile)); + String line; + + while ((line = br.readLine()) != null) { + text.append(line); + } + br.close(); + } + catch (IOException e) { + Log.e(TAG, "", e); + } + + // if version differs from current app version or null, try to delete certificates folder + if (!text.toString().contains(versionName)) try { + holderfile.delete(); + File certpath = new File(i2pdpath, "certificates"); + deleteRecursive(certpath); + } + catch (Throwable tr) { + Log.e(TAG, "", tr); + } + + // copy assets. If processed file exists, it won't be overwrited + copyAsset("certificates"); + copyAsset("i2pd.conf"); + copyAsset("subscriptions.txt"); + copyAsset("tunnels.conf"); + + // update holder file about successful copying + FileWriter writer = new FileWriter(holderfile); + writer.append(versionName); + writer.flush(); + writer.close(); + } + catch (Throwable tr) + { + Log.e(TAG,"copy assets",tr); + } } } diff --git a/android_binary_only/.gitignore b/android_binary_only/.gitignore new file mode 100644 index 00000000..6e42311a --- /dev/null +++ b/android_binary_only/.gitignore @@ -0,0 +1,18 @@ +gen +tests +bin +libs +log* +obj +.gradle +.idea +.externalNativeBuild +ant.properties +local.properties +build.sh +android.iml +build +gradle +gradlew +gradlew.bat + diff --git a/android_binary_only/jni/Android.mk b/android_binary_only/jni/Android.mk new file mode 100755 index 00000000..fa677c83 --- /dev/null +++ b/android_binary_only/jni/Android.mk @@ -0,0 +1,74 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := i2pd +LOCAL_CPP_FEATURES := rtti exceptions +LOCAL_C_INCLUDES += $(IFADDRS_PATH) $(LIB_SRC_PATH) $(LIB_CLIENT_SRC_PATH) $(DAEMON_SRC_PATH) +LOCAL_STATIC_LIBRARIES := \ + boost_system \ + boost_date_time \ + boost_filesystem \ + boost_program_options \ + crypto ssl \ + miniupnpc +LOCAL_LDLIBS := -lz + +LOCAL_SRC_FILES := $(IFADDRS_PATH)/ifaddrs.c \ + $(wildcard $(LIB_SRC_PATH)/*.cpp)\ + $(wildcard $(LIB_CLIENT_SRC_PATH)/*.cpp)\ + $(DAEMON_SRC_PATH)/UnixDaemon.cpp \ + $(DAEMON_SRC_PATH)/Daemon.cpp \ + $(DAEMON_SRC_PATH)/UPnP.cpp \ + $(DAEMON_SRC_PATH)/HTTPServer.cpp \ + $(DAEMON_SRC_PATH)/I2PControl.cpp \ + $(DAEMON_SRC_PATH)/i2pd.cpp +include $(BUILD_EXECUTABLE) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := boost_system +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_62_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_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_62_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_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_62_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_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_62_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_0/include +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := crypto +LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.0e/$(TARGET_ARCH_ABI)/lib/libcrypto.a +LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.0e/include +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := ssl +LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.0e/$(TARGET_ARCH_ABI)/lib/libssl.a +LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.0e/include +LOCAL_STATIC_LIBRARIES := crypto +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := miniupnpc +LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnpc-2.1/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a +LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnpc-2.1/include +include $(PREBUILT_STATIC_LIBRARY) diff --git a/android_binary_only/jni/Application.mk b/android_binary_only/jni/Application.mk new file mode 100755 index 00000000..b8cdc2ab --- /dev/null +++ b/android_binary_only/jni/Application.mk @@ -0,0 +1,43 @@ +#APP_ABI := all +#APP_ABI := armeabi-v7a x86 +#APP_ABI := x86 +#APP_ABI := x86_64 +APP_ABI := armeabi-v7a +#can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there. +APP_PLATFORM := android-14 + +# http://stackoverflow.com/a/21386866/529442 http://stackoverflow.com/a/15616255/529442 to enable c++11 support in Eclipse +NDK_TOOLCHAIN_VERSION := 4.9 +# APP_STL := stlport_shared --> does not seem to contain C++11 features +#APP_STL := gnustl_shared +APP_STL := gnustl_static + +# Enable c++11 extensions in source code +APP_CPPFLAGS += -std=c++11 -fvisibility=default -fPIE + +APP_CPPFLAGS += -DANDROID_BINARY -DANDROID -D__ANDROID__ -DUSE_UPNP +APP_LDFLAGS += -rdynamic -fPIE -pie +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) +APP_CPPFLAGS += -DANDROID_ARM7A +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/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 +# change to your own +I2PD_LIBS_PATH = /path/to/libraries +BOOST_PATH = $(I2PD_LIBS_PATH)/Boost-for-Android-Prebuilt +OPENSSL_PATH = $(I2PD_LIBS_PATH)/OpenSSL-for-Android-Prebuilt +MINIUPNP_PATH = $(I2PD_LIBS_PATH)/MiniUPnP-for-Android-Prebuilt +IFADDRS_PATH = $(I2PD_LIBS_PATH)/android-ifaddrs + +# don't change me +I2PD_SRC_PATH = $(PWD)/.. + +LIB_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd +LIB_CLIENT_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd_client +DAEMON_SRC_PATH = $(I2PD_SRC_PATH)/daemon diff --git a/appveyor.yml b/appveyor.yml index f663c86a..e0c09fc1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.18.{build} +version: 2.20.{build} pull_requests: do_not_increment_build_number: true branches: @@ -17,7 +17,7 @@ environment: - MSYSTEM: MINGW32 install: -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc catgets" +- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc" - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu " - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index fc9ca417..5876a0f7 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -39,7 +39,7 @@ include_directories(${LIBI2PD_CLIENT_SRC_DIR}) set (LIBI2PD_SRC "${LIBI2PD_SRC_DIR}/BloomFilter.cpp" "${LIBI2PD_SRC_DIR}/Config.cpp" - "${LIBI2PD_SRC_DIR}/CPU.cpp" + "${LIBI2PD_SRC_DIR}/CPU.cpp" "${LIBI2PD_SRC_DIR}/Crypto.cpp" "${LIBI2PD_SRC_DIR}/CryptoKey.cpp" "${LIBI2PD_SRC_DIR}/Garlic.cpp" @@ -77,6 +77,10 @@ set (LIBI2PD_SRC "${LIBI2PD_SRC_DIR}/api.cpp" "${LIBI2PD_SRC_DIR}/Event.cpp" "${LIBI2PD_SRC_DIR}/Gost.cpp" + "${LIBI2PD_SRC_DIR}/ChaCha20.cpp" + "${LIBI2PD_SRC_DIR}/Poly1305.cpp" + "${LIBI2PD_SRC_DIR}/Ed25519.cpp" + "${LIBI2PD_SRC_DIR}/NTCP2.cpp" ) if (WITH_WEBSOCKETS) @@ -186,7 +190,7 @@ if (CXX11_SUPPORTED) elseif (CXX0X_SUPPORTED) # gcc 4.6 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x" ) elseif (NOT MSVC) - message(SEND_ERROR "C++11 standart not seems to be supported by compiler. Too old version?") + message(SEND_ERROR "C++11 standard not seems to be supported by compiler. Too old version?") endif () if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -198,9 +202,11 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif () elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # more tweaks - if (NOT (MSVC OR MSYS OR APPLE)) + if (LINUX) set (CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libstdc++" ) # required for list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++") # required to link with -stdlib=libstdc++ + endif() + if (NOT (MSVC OR MSYS OR APPLE)) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-const-variable -Wno-overloaded-virtual -Wno-c99-extensions" ) endif() endif () @@ -230,7 +236,6 @@ endif () if (WITH_AESNI) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes" ) - add_definitions ( -DAESNI ) endif() if (WITH_AVX) @@ -335,7 +340,7 @@ target_link_libraries(libi2pdclient libi2pd) find_package ( Boost COMPONENTS system filesystem program_options date_time REQUIRED ) if(NOT DEFINED Boost_INCLUDE_DIRS) - message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!") + message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!") endif() find_package ( OpenSSL REQUIRED ) diff --git a/build/build_mingw.cmd b/build/build_mingw.cmd index cc6a15fa..7f163878 100644 --- a/build/build_mingw.cmd +++ b/build/build_mingw.cmd @@ -16,7 +16,8 @@ REM Note: if you installed MSYS64 to different path, edit WD variable (only C:\m set "WD=C:\msys64\usr\bin\" set MSYS2_PATH_TYPE=inherit set CHERE_INVOKING=enabled_from_arguments -set MSYSTEM=MSYS +REM set MSYSTEM=MSYS +set MSYSTEM=MINGW32 set "xSH=%WD%bash -lc" @@ -61,12 +62,12 @@ exit /b 0 %xSH% "make clean" >> nul echo Building i2pd %tag% for win%bitness%: echo Build AVX+AESNI... -%xSH% "make USE_UPNP=yes USE_AVX=1 USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx_aesni.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx_aesni.log 2>&1 +%xSH% "make DEBUG=no USE_UPNP=yes USE_AVX=1 USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx_aesni.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx_aesni.log 2>&1 echo Build AVX... -%xSH% "make USE_UPNP=yes USE_AVX=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx.log 2>&1 +%xSH% "make DEBUG=no USE_UPNP=yes USE_AVX=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_avx.log 2>&1 echo Build AESNI... -%xSH% "make USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_aesni.log 2>&1 +%xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%_aesni.log 2>&1 echo Build without extensions... -%xSH% "make USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%.log 2>&1 +%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates && make clean" > build/build_win%bitness%.log 2>&1 :EOF \ No newline at end of file diff --git a/contrib/apparmor/usr.sbin.i2pd b/contrib/apparmor/usr.sbin.i2pd index 4349e07b..afa59563 100644 --- a/contrib/apparmor/usr.sbin.i2pd +++ b/contrib/apparmor/usr.sbin.i2pd @@ -17,14 +17,16 @@ /etc/host.conf r, /etc/hosts r, /etc/nsswitch.conf r, + /etc/resolv.conf r, /run/resolvconf/resolv.conf r, + /run/systemd/resolve/stub-resolv.conf r, # path specific (feel free to modify if you have another paths) /etc/i2pd/** r, - /run/i2pd/i2pd.pid rw, + /run/i2pd/i2pd.pid rwk, /var/lib/i2pd/** rw, /var/log/i2pd/i2pd.log w, - /var/run/i2pd/i2pd.pid rw, + /var/run/i2pd/i2pd.pid rwk, /usr/sbin/i2pd mr, /usr/share/i2pd/** r, diff --git a/contrib/certificates/reseed/hottuna_at_mail.i2p.crt b/contrib/certificates/reseed/hottuna_at_mail.i2p.crt new file mode 100644 index 00000000..d0ff7c33 --- /dev/null +++ b/contrib/certificates/reseed/hottuna_at_mail.i2p.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFxzCCA6+gAwIBAgIQZfqn0yiJL3dGgCjeOeWS6DANBgkqhkiG9w0BAQsFADBw +MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK +ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQ +aG90dHVuYUBtYWlsLmkycDAeFw0xNjExMDkwMzE1MzJaFw0yNjExMDkwMzE1MzJa +MHAxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgxHjAcBgNV +BAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQD +DBBob3R0dW5hQG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEA21Bfgcc9VVH4l2u1YvYlTw2OPUyQb16X2IOW0PzdsUO5W78Loueu974BkiKi +84lQZanLr0OwEopdfutGc6gegSLmwaWx5YCG5uwpLOPkDiObfX+nptH6As/B1cn+ +mzejYdVKRnWd7EtHW0iseSsILBK1YbGw4AGpXJ8k18DJSzUt2+spOkpBW6XqectN +8y2JDSTns8yiNxietVeRN/clolDXT9ZwWHkd+QMHTKhgl3Uz1knOffU0L9l4ij4E +oFgPfQo8NL63kLM24hF1hM/At7XvE4iOlObFwPXE+H5EGZpT5+A7Oezepvd/VMzM +tCJ49hM0OlR393tKFONye5GCYeSDJGdPEB6+rBptpRrlch63tG9ktpCRrg2wQWgC +e3aOE1xVRrmwiTZ+jpfsOCbZrrSA/C4Bmp6AfGchyHuDGGkRU/FJwa1YLJe0dkWG +ITLWeh4zeVuAS5mctdv9NQ5wflSGz9S8HjsPBS5+CDOFHh4cexXRG3ITfk6aLhuY +KTMlkIO4SHKmnwAvy1sFlsqj6PbfVjpHPLg625fdNxBpe57TLxtIdBB3C7ccQSRW ++UG6Cmbcmh80PbsSR132NLMlzLhbaOjxeCWWJRo6cLuHBptAFMNwqsXt8xVf9M0N +NdJoKUmblyvjnq0N8aMEqtQ1uGMTaCB39cutHQq+reD/uzsCAwEAAaNdMFswDgYD +VR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNV +HRMBAf8EBTADAQH/MBkGA1UdDgQSBBBob3R0dW5hQG1haWwuaTJwMA0GCSqGSIb3 +DQEBCwUAA4ICAQCibFV8t4pajP176u3jx31x1kgqX6Nd+0YFARPZQjq99kUyoZer +GyHGsMWgM281RxiZkveHxR7Hm7pEd1nkhG3rm+d7GdJ2p2hujr9xUvl0zEqAAqtm +lkYI6uJ13WBjFc9/QuRIdeIeSUN+eazSXNg2nJhoV4pF9n2Q2xDc9dH4GWO93cMX +JPKVGujT3s0b7LWsEguZBPdaPW7wwZd902Cg/M5fE1hZQ8/SIAGUtylb/ZilVeTS +spxWP1gX3NT1SSvv0s6oL7eADCgtggWaMxEjZhi6WMnPUeeFY8X+6trkTlnF9+r/ +HiVvvzQKrPPtB3j1xfQCAF6gUKN4iY+2AOExv4rl/l+JJbPhpd/FuvD8AVkLMZ8X +uPe0Ew2xv30cc8JjGDzQvoSpBmVTra4f+xqH+w8UEmxnx97Ye2aUCtnPykACnFte +oT97K5052B1zq+4fu4xaHZnEzPYVK5POzOufNLPgciJsWrR5GDWtHd+ht/ZD37+b ++j1BXpeBWUBQgluFv+lNMVNPJxc2OMELR1EtEwXD7mTuuUEtF5Pi63IerQ5LzD3G +KBvXhMB0XhpE6WG6pBwAvkGf5zVv/CxClJH4BQbdZwj9HYddfEQlPl0z/XFR2M0+ +9/8nBfGSPYIt6KeHBCeyQWTdE9gqSzMwTMFsennXmaT8gyc7eKqKF6adqw== +-----END CERTIFICATE----- diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index e7e60f37..22990995 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -2,7 +2,7 @@ FROM alpine:latest LABEL authors "Mikal Villa , Darknet Villain " # Expose git branch, tag and URL variables as arguments -ARG GIT_BRANCH="master" +ARG GIT_BRANCH="openssl" ENV GIT_BRANCH=${GIT_BRANCH} ARG GIT_TAG="" ENV GIT_TAG=${GIT_TAG} diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index c87a2c0b..123df754 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -26,15 +26,11 @@ ## Log messages above this level (debug, *info, warn, error, none) ## If you set it to none, logging will be disabled # loglevel = info - -## Path to storage of i2pd data (RI, keys, peer profiles, ...) -## Default: ~/.i2pd or /var/lib/i2pd -# datadir = /var/lib/i2pd +## Write full CLF-formatted date and time to log (default: write only time) +# logclftime = true ## Daemon mode. Router will go to background after start # daemon = true -## Run as a service. Router will use system folders like ‘/var/lib/i2pd’ -# service = true ## Specify a family, router belongs to (default - none) # family = @@ -55,9 +51,15 @@ ipv6 = false ## Network interface to bind to # ifname = +## You can specify different interfaces for IPv4 and IPv6 +# ifname4 = +# ifname6 = ## Enable NTCP transport (default = true) # ntcp = true +## If you run i2pd behind a proxy server, you can only use NTCP transport with ntcpproxy option +## Should be http://address:port or socks://address:port +# ntcpproxy = http://127.0.0.1:8118 ## Enable SSU transport (default = true) # ssu = true @@ -69,6 +71,8 @@ ipv6 = false ## X - unlimited ## Default is X for floodfill, L for regular node # bandwidth = L +## Max % of bandwidth limit for transit. 0-100. 100 by default +# share = 100 ## Router will not accept transit tunnels, disabling transit traffic completely ## (default = false) @@ -77,46 +81,17 @@ ipv6 = false ## Router will be floodfill # floodfill = true -[limits] -## Maximum active transit sessions (default:2500) -# transittunnels = 2500 - -[precomputation] -## Enable or disable elgamal precomputation table -## By default, enabled on i386 hosts -# elgamal = true - -[upnp] -## Enable or disable UPnP: automatic port forwarding (enabled by default in WINDOWS, ANDROID) -# enabled = false - -## Name i2pd appears in UPnP forwardings list (default = I2Pd) -# name = I2Pd - -[reseed] -## Enable or disable reseed data verification. -verify = true -## URLs to request reseed data from, separated by comma -## Default: "mainline" I2P Network reseeds -# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/ -## Path to local reseed data file (.su3) for manual reseeding -# file = /path/to/i2pseeds.su3 -## or HTTPS URL to reseed from -# file = https://legit-website.com/i2pseeds.su3 - -[addressbook] -## AddressBook subscription URL for initial setup -## Default: inr.i2p at "mainline" I2P Network -# defaulturl = http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt -## Optional subscriptions URLs, separated by comma -# subscriptions = http://inr.i2p/export/alive-hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt - [http] +## Web Console settings ## Uncomment and set to 'false' to disable Web Console # enabled = true ## Address and port service will listen on address = 127.0.0.1 port = 7070 +## Uncomment following lines to enable Web Console authentication +# auth = true +# user = i2pd +# pass = changeme [httpproxy] ## Uncomment and set to 'false' to disable HTTP Proxy @@ -126,6 +101,11 @@ address = 127.0.0.1 port = 4444 ## Optional keys file for proxy local destination # keys = http-proxy-keys.dat +## Enable address helper for adding .i2p domains with "jump URLs" (default: true) +# addresshelper = true +## Address of a proxy server inside I2P, which is used to visit regular Internet +# outproxy = http://false.i2p +## httpproxy section also accepts I2CP parameters, like "inbound.length" etc. [socksproxy] ## Uncomment and set to 'false' to disable SOCKS Proxy @@ -135,13 +115,13 @@ address = 127.0.0.1 port = 4447 ## Optional keys file for proxy local destination # keys = socks-proxy-keys.dat - ## Socks outproxy. Example below is set to use Tor for all connections except i2p ## Uncomment and set to 'true' to enable using of SOCKS outproxy # outproxy.enabled = false ## Address and port of outproxy # outproxy = 127.0.0.1 # outproxyport = 9050 +## socksproxy section also accepts I2CP parameters, like "inbound.length" etc. [sam] ## Uncomment and set to 'true' to enable SAM Bridge @@ -170,3 +150,71 @@ enabled = true ## Address and port service will listen on # address = 127.0.0.1 # port = 7650 +## Authentication password. "itoopie" by default +# password = itoopie + +[precomputation] +## Enable or disable elgamal precomputation table +## By default, enabled on i386 hosts +# elgamal = true + +[upnp] +## Enable or disable UPnP: automatic port forwarding (enabled by default in WINDOWS, ANDROID) +# enabled = false +## Name i2pd appears in UPnP forwardings list (default = I2Pd) +# name = I2Pd + +[reseed] +## Options for bootstrapping into I2P network, aka reseeding +## Enable or disable reseed data verification. +verify = true +## URLs to request reseed data from, separated by comma +## Default: "mainline" I2P Network reseeds +# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/ +## Path to local reseed data file (.su3) for manual reseeding +# file = /path/to/i2pseeds.su3 +## or HTTPS URL to reseed from +# file = https://legit-website.com/i2pseeds.su3 +## Path to local ZIP file or HTTPS URL to reseed from +# zipfile = /path/to/netDb.zip +## If you run i2pd behind a proxy server, set proxy server for reseeding here +## Should be http://address:port or socks://address:port +# proxy = http://127.0.0.1:8118 +## Minimum number of known routers, below which i2pd triggers reseeding. 25 by default +# threshold = 25 + +[addressbook] +## AddressBook subscription URL for initial setup +## Default: inr.i2p at "mainline" I2P Network +# defaulturl = http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt +## Optional subscriptions URLs, separated by comma +# subscriptions = http://inr.i2p/export/alive-hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt + +[limits] +## Maximum active transit sessions (default:2500) +# transittunnels = 2500 +## Limit number of open file descriptors (0 - use system limit) +# openfiles = 0 +## Maximum size of corefile in Kb (0 - use system limit) +# coresize = 0 +## Threshold to start probabalistic backoff with ntcp sessions (0 - use system limit) +# ntcpsoft = 0 +## Maximum number of ntcp sessions (0 - use system limit) +# ntcphard = 0 + +[trust] +## Enable explicit trust options. false by default +# enabled = true +## Make direct I2P connections only to routers in specified Family. +# family = MyFamily +## Make direct I2P connections only to routers specified here. Comma separated list of base64 identities. +# routers = +## Should we hide our router from other routers? false by default +# hidden = true + +[exploratory] +## Exploratory tunnels settings with default values +# inbound.length = 2 +# inbound.quantity = 3 +# outbound.length = 2 +# outbound.quantity = 3 diff --git a/contrib/i2pd.service b/contrib/i2pd.service index 3f53bfb8..debd49b0 100644 --- a/contrib/i2pd.service +++ b/contrib/i2pd.service @@ -24,7 +24,7 @@ KillSignal=SIGQUIT #TimeoutStopSec=10m # If you have problems with hanging i2pd, you can try enable this -#LimitNOFILE=4096 +LimitNOFILE=4096 PrivateDevices=yes [Install] diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index 6e02779d..3539c19b 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.18.0 +Version: 2.20.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -99,4 +99,4 @@ getent passwd i2pd >/dev/null || \ %changelog * Thu Feb 01 2018 r4sas - 2.18.0 -- Initial i2pd-git based on i2pd 2.18.0-1 spec \ No newline at end of file +- Initial i2pd-git based on i2pd 2.18.0-1 spec diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index 22c31192..edad8228 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -1,6 +1,6 @@ Name: i2pd -Version: 2.18.0 -Release: 2%{?dist} +Version: 2.20.0 +Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -96,6 +96,12 @@ getent passwd i2pd >/dev/null || \ %changelog +* Thu Aug 23 2018 orignal - 2.20.0 +- update to 2.20.0 + +* Tue Jun 26 2018 orignal - 2.19.0 +- update to 2.19.0 + * Mon Feb 05 2018 r4sas - 2.18.0-2 - Fixed blocking system shutdown for 10 minutes (#1089) diff --git a/contrib/tunnels.conf b/contrib/tunnels.conf index be8681dc..3358ffc4 100644 --- a/contrib/tunnels.conf +++ b/contrib/tunnels.conf @@ -30,4 +30,4 @@ keys = irc-keys.dat #destinationport = 110 #keys = pop3-keys.dat -# see more examples in /usr/share/doc/i2pd/configuration.md.gz +# see more examples at https://i2pd.readthedocs.io/en/latest/user-guide/tunnels/ diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index d077644d..0b88e983 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -60,8 +60,12 @@ namespace i2p return service; } - bool Daemon_Singleton::init(int argc, char* argv[]) - { + bool Daemon_Singleton::init(int argc, char* argv[]) { + return init(argc, argv, nullptr); + } + + bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr logstream) + { i2p::config::Init(); i2p::config::ParseCmdline(argc, argv); @@ -104,7 +108,10 @@ namespace i2p logs = "file"; i2p::log::Logger().SetLogLevel(loglevel); - if (logs == "file") { + if (logstream) { + LogPrint(eLogInfo, "Log: will send messages to std::ostream"); + i2p::log::Logger().SendTo (logstream); + } else if (logs == "file") { if (logfile == "") logfile = i2p::fs::DataDirPath("i2pd.log"); LogPrint(eLogInfo, "Log: will send messages to ", logfile); @@ -145,6 +152,19 @@ namespace i2p i2p::context.SetSupportsV6 (ipv6); i2p::context.SetSupportsV4 (ipv4); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) + { + bool published; i2p::config::GetOption("ntcp2.published", published); + if (published) + { + uint16_t port; i2p::config::GetOption("ntcp2.port", port); + i2p::context.PublishNTCP2Address (port, true); // publish + } + else + i2p::context.PublishNTCP2Address (port, false); // unpublish + } + bool transit; i2p::config::GetOption("notransit", transit); i2p::context.SetAcceptsTunnels (!transit); uint16_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); @@ -269,9 +289,10 @@ namespace i2p if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled"); i2p::transport::transports.Start(ntcp, ssu); - if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU()) { + if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) LogPrint(eLogInfo, "Daemon: Transports started"); - } else { + else + { LogPrint(eLogError, "Daemon: failed to start Transports"); /** shut down netdb right away */ i2p::transport::transports.Stop(); diff --git a/daemon/Daemon.h b/daemon/Daemon.h index 48301e73..1745b980 100644 --- a/daemon/Daemon.h +++ b/daemon/Daemon.h @@ -3,6 +3,7 @@ #include #include +#include namespace i2p { @@ -12,8 +13,9 @@ namespace util class Daemon_Singleton { public: - virtual bool init(int argc, char* argv[]); - virtual bool start(); + virtual bool init(int argc, char* argv[], std::shared_ptr logstream); + virtual bool init(int argc, char* argv[]); + virtual bool start(); virtual bool stop(); virtual void run () {}; @@ -44,19 +46,6 @@ namespace util } }; -#elif defined(ANDROID) -#define Daemon i2p::util::DaemonAndroid::Instance() - // dummy, invoked from android/jni/DaemonAndroid.* - class DaemonAndroid: public i2p::util::Daemon_Singleton - { - public: - static DaemonAndroid& Instance() - { - static DaemonAndroid instance; - return instance; - } - }; - #elif defined(_WIN32) #define Daemon i2p::util::DaemonWin32::Instance() class DaemonWin32 : public Daemon_Singleton @@ -77,7 +66,18 @@ namespace util DaemonWin32 ():isGraceful(false) {} }; - +#elif (defined(ANDROID) && !defined(ANDROID_BINARY)) +#define Daemon i2p::util::DaemonAndroid::Instance() + // dummy, invoked from android/jni/DaemonAndroid.* + class DaemonAndroid: public i2p::util::Daemon_Singleton + { + public: + static DaemonAndroid& Instance() + { + static DaemonAndroid instance; + return instance; + } + }; #else #define Daemon i2p::util::DaemonLinux::Instance() class DaemonLinux : public Daemon_Singleton diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 6f884a9b..09b00707 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "Base.h" #include "FS.h" @@ -91,6 +92,8 @@ namespace http { const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_ADDRESS[] = "address"; + static std::string ConvertTime (uint64_t time); + static void ShowUptime (std::stringstream& s, int seconds) { int num; @@ -198,7 +201,10 @@ namespace http { s << "ERROR: " << string << "
\r\n"; } - void ShowStatus (std::stringstream& s, bool includeHiddenContent) + void ShowStatus ( + std::stringstream& s, + bool includeHiddenContent, + i2p::http::OutputFormatEnum outputFormat) { s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); @@ -224,7 +230,7 @@ namespace http { default: s << "Unknown"; } s << "
\r\n"; -#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) +#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) if (auto remains = Daemon.gracefulShutdownInterval) { s << "Stopping in: "; s << remains << " seconds"; @@ -245,25 +251,37 @@ 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 << "
\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" ; for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) { + if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ()) + { + s << "NTCP2"; + if (address->host.is_v6 ()) s << "v6"; + s << "   supported
\r\n"; + continue; + } switch (address->transportStyle) { case i2p::data::RouterInfo::eTransportNTCP: - if (address->host.is_v6 ()) - s << "NTCP6  "; - else - s << "NTCP  "; - break; + { + s << "NTCP"; + if (address->IsPublishedNTCP2 ()) s << "2"; + if (address->host.is_v6 ()) s << "v6"; + s << "  "; + break; + } case i2p::data::RouterInfo::eTransportSSU: if (address->host.is_v6 ()) - s << "SSU6     "; + s << "SSUv6     "; else s << "SSU     "; break; @@ -272,9 +290,12 @@ namespace http { } s << address->host.to_string() << ":" << address->port << "
\r\n"; } - } + } s << "

\r\n
\r\n"; - 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"; @@ -285,15 +306,17 @@ namespace http { s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; - 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) @@ -443,14 +466,14 @@ namespace http { s << "
!! Invalid !!
\r\n"; s << "
\r\n"; s << "\r\n

\r\n"; - s << "Expires: " << ls.GetExpirationTime() << "
\r\n"; + s << "Expires: " << ConvertTime(ls.GetExpirationTime()) << "
\r\n"; auto leases = ls.GetNonExpiredLeases(); s << "Non Expired Leases: " << leases.size() << "
\r\n"; for ( auto & l : leases ) { s << "Gateway: " << l->tunnelGateway.ToBase64() << "
\r\n"; s << "TunnelID: " << l->tunnelID << "
\r\n"; - s << "EndDate: " << l->endDate << "
\r\n"; + s << "EndDate: " << ConvertTime(l->endDate) << "
\r\n"; } s << "

\r\n
\r\n
\r\n"; } @@ -493,7 +516,7 @@ namespace http { s << " Decline transit tunnels
\r\n"; else s << " Accept transit tunnels
\r\n"; -#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) +#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) if (Daemon.gracefulShutdownInterval) s << " Cancel graceful shutdown
"; else @@ -529,6 +552,46 @@ namespace http { } } + template + static void ShowNTCPTransports (std::stringstream& s, const Sessions& sessions, const std::string name) + { + std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; + for (const auto& it: sessions ) + { + if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) + { + // incoming connection doesn't have remote RI + if (it.second->IsOutgoing ()) tmp_s << " ⇒ "; + tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " + << it.second->GetSocket ().remote_endpoint().address ().to_string (); + if (!it.second->IsOutgoing ()) tmp_s << " ⇒ "; + tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + tmp_s << "
\r\n" << std::endl; + cnt++; + } + if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) + { + if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; + tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " + << "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]"; + if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; + tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + tmp_s6 << "
\r\n" << std::endl; + cnt6++; + } + } + if (!tmp_s.str ().empty ()) + { + s << "
\r\n\r\n

"; + s << tmp_s.str () << "

\r\n
\r\n"; + } + if (!tmp_s6.str ().empty ()) + { + s << "
\r\n\r\n

"; + s << tmp_s6.str () << "

\r\n
\r\n"; + } + } + void ShowTransports (std::stringstream& s) { s << "Transports:
\r\n
\r\n"; @@ -537,43 +600,14 @@ namespace http { { auto sessions = ntcpServer->GetNTCPSessions (); if (!sessions.empty ()) - { - std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; - for (const auto& it: sessions ) - { - if (it.second && it.second->IsEstablished () && !it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) - { - // incoming connection doesn't have remote RI - if (it.second->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << it.second->GetSocket ().remote_endpoint().address ().to_string (); - if (!it.second->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - tmp_s << "
\r\n" << std::endl; - cnt++; - } - if (it.second && it.second->IsEstablished () && it.second->GetSocket ().remote_endpoint ().address ().is_v6 ()) - { - if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << "[" << it.second->GetSocket ().remote_endpoint().address ().to_string () << "]"; - if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - tmp_s6 << "
\r\n" << std::endl; - cnt6++; - } - } - if (!tmp_s.str ().empty ()) - { - s << "
\r\n\r\n

"; - s << tmp_s.str () << "

\r\n
\r\n"; - } - if (!tmp_s6.str ().empty ()) - { - s << "
\r\n\r\n

"; - s << tmp_s6.str () << "

\r\n
\r\n"; - } - } + ShowNTCPTransports (s, sessions, "NTCP"); + } + auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); + if (ntcp2Server) + { + auto sessions = ntcp2Server->GetNTCP2Sessions (); + if (!sessions.empty ()) + ShowNTCPTransports (s, sessions, "NTCP2"); } auto ssuServer = i2p::transport::transports.GetSSUServer (); if (ssuServer) @@ -598,7 +632,7 @@ namespace http { auto sessions6 = ssuServer->GetSessionsV6 (); if (!sessions6.empty ()) { - s << "
\r\n\r\n

"; + s << "

\r\n\r\n

"; for (const auto& it: sessions6) { auto endpoint = it.second->GetRemoteEndpoint (); @@ -649,7 +683,7 @@ namespace http { s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
\r\n"; s << "
\r\n"; s << "Streams:
\r\n"; - for (const auto& it: session->ListSockets()) + for (const auto& it: sam->ListSockets(id)) { switch (it->GetSocketType ()) { @@ -733,6 +767,16 @@ namespace http { } } + std::string ConvertTime (uint64_t time) + { + ldiv_t divTime = ldiv(time,1000); + time_t t = divTime.quot; + struct tm *tm = localtime(&t); + char date[128]; + snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03ld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); + return date; + } + HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr socket): m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0), expected_host(hostname) @@ -851,7 +895,7 @@ namespace http { { /* deny request as it's from a non whitelisted hostname */ res.code = 403; - content = "host missmatch"; + content = "host mismatch"; SendReply(res, content); return; } @@ -863,7 +907,7 @@ namespace http { } else if (req.uri.find("cmd=") != std::string::npos) { HandleCommand (req, res, s); } else { - ShowStatus (s, true); + ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); res.add_header("Refresh", "10"); } ShowPageTail (s); @@ -953,14 +997,14 @@ namespace http { i2p::context.SetAcceptsTunnels (false); else if (cmd == HTTP_COMMAND_SHUTDOWN_START) { i2p::context.SetAcceptsTunnels (false); -#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) +#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) { i2p::context.SetAcceptsTunnels (true); -#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) +#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) Daemon.gracefulShutdownInterval = 0; #elif defined(WIN32_APP) i2p::win32::StopGracefulShutdown (); diff --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h index 46477dae..a1b82875 100644 --- a/daemon/HTTPServer.h +++ b/daemon/HTTPServer.h @@ -80,7 +80,8 @@ namespace http }; //all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml - void ShowStatus (std::stringstream& s, bool includeHiddenContent); + enum OutputFormatEnum { forWebConsole, forQtUi }; + void ShowStatus (std::stringstream& s, bool includeHiddenContent, OutputFormatEnum outputFormat); void ShowLocalDestinations (std::stringstream& s); void ShowLeasesSets(std::stringstream& s); void ShowTunnels (std::stringstream& s); diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index fcff78cd..6ac87cbb 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -727,7 +727,7 @@ namespace client sam_session.put("name", name); sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - for (const auto& socket: it.second->ListSockets()) + for (const auto& socket: sam->ListSockets(it.first)) { boost::property_tree::ptree stream; stream.put("type", socket->GetSocketType ()); diff --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp index ba7b408c..3aad19c6 100644 --- a/daemon/UPnP.cpp +++ b/daemon/UPnP.cpp @@ -131,8 +131,8 @@ namespace transport const auto& a = context.GetRouterInfo().GetAddresses(); for (const auto& address : a) { - if (!address->host.is_v6 ()) - TryPortMapping (address); + if (!address->host.is_v6 () && address->port) + TryPortMapping (address); } m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes m_Timer.async_wait ([this](const boost::system::error_code& ecode) @@ -148,7 +148,7 @@ namespace transport const auto& a = context.GetRouterInfo().GetAddresses(); for (const auto& address : a) { - if (!address->host.is_v6 ()) + if (!address->host.is_v6 () && address->port) CloseMapping (address); } } diff --git a/daemon/UnixDaemon.cpp b/daemon/UnixDaemon.cpp index a9c48fee..3dd38fba 100644 --- a/daemon/UnixDaemon.cpp +++ b/daemon/UnixDaemon.cpp @@ -138,11 +138,14 @@ namespace i2p LogPrint(eLogError, "Daemon: could not create pid file ", pidfile, ": ", strerror(errno)); return false; } + +#ifndef ANDROID if (lockf(pidFH, F_TLOCK, 0) != 0) { LogPrint(eLogError, "Daemon: could not lock pid file ", pidfile, ": ", strerror(errno)); return false; } +#endif char pid[10]; sprintf(pid, "%d\n", getpid()); ftruncate(pidFH, 0); diff --git a/debian/changelog b/debian/changelog index afddc797..9d721eb8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,20 @@ +i2pd (2.20.0-1) unstable; urgency=medium + + * updated to version 2.20.0/0.9.36 + + -- orignal Thu, 23 Aug 2018 16:00:00 +0000 + +i2pd (2.19.0-1) unstable; urgency=medium + + * updated to version 2.19.0/0.9.35 + * update manpage (1) + * update docfiles + * update build rules + * fixes in systemd unit (#1089, #1142, #1154, #1155) + * package now building with systemd support + + -- R4SAS Tue, 26 Jun 2018 16:27:45 +0000 + i2pd (2.18.0-1) unstable; urgency=low * updated to version 2.18.0/0.9.33 diff --git a/debian/control b/debian/control index 7bd18ebb..8ef0b08c 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: i2pd Section: net Priority: optional Maintainer: R4SAS -Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.16.1~), gcc (>= 4.7) | clang (>= 3.3), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev, dh-apparmor +Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.17.2~), gcc (>= 4.7) | clang (>= 3.3), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev Standards-Version: 3.9.6 Homepage: http://i2pd.website/ Vcs-Git: git://github.com/PurpleI2P/i2pd.git @@ -11,9 +11,8 @@ Vcs-Browser: https://github.com/PurpleI2P/i2pd Package: i2pd Architecture: any Pre-Depends: adduser -Depends: ${shlibs:Depends}, ${misc:Depends} -Suggests: tor, privoxy, apparmor -Description: A full-featured C++ implementation of I2P client. +Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, +Description: Full-featured C++ implementation of I2P client. I2P (Invisible Internet Protocol) is a universal anonymous network layer. All communications over I2P are anonymous and end-to-end encrypted, participants don't reveal their real IP addresses. @@ -25,7 +24,6 @@ Architecture: any Priority: extra Section: debug Depends: i2pd (= ${binary:Version}), ${misc:Depends} -Suggests: gdb Description: i2pd debugging symbols I2P (Invisible Internet Protocol) is a universal anonymous network layer. All communications over I2P are anonymous and end-to-end encrypted, participants diff --git a/debian/docs b/debian/docs index b43bf86b..b67deb18 100644 --- a/debian/docs +++ b/debian/docs @@ -1 +1,4 @@ README.md +contrib/i2pd.conf +contrib/subscriptions.txt +contrib/tunnels.conf diff --git a/debian/i2pd.1 b/debian/i2pd.1 index e1390891..9dd44093 100644 --- a/debian/i2pd.1 +++ b/debian/i2pd.1 @@ -1,22 +1,19 @@ -.TH I2PD "1" "March 31, 2015" +.TH "I2PD" "1" "June 20, 2018" -.SH NAME -i2pd \- Load-balanced unspoofable packet switching network - -.SH SYNOPSIS +.SH "NAME" +i2pd \- Full-featured C++ implementation of I2P client. +.SH "SYNOPSIS" .B i2pd [\fIOPTION1\fR] [\fIOPTION2\fR]... - -.SH DESCRIPTION +.SH "DESCRIPTION" i2pd is a C++ implementation of the router for the I2P anonymizing network, offering a simple layer that identity-sensitive applications can use to securely communicate. All data is wrapped with several layers of encryption, and the network is both distributed and dynamic, with no trusted parties. - .PP Any of the configuration options below can be used in the \fBDAEMON_ARGS\fR variable in \fI/etc/default/i2pd\fR. -.BR +.SH "OPTIONS" .TP \fB\-\-help\fR Show available options. @@ -36,11 +33,14 @@ Where to write pidfile (don\'t write by default) \fB\-\-log=\fR Logs destination: \fIstdout\fR, \fIfile\fR, \fIsyslog\fR (\fIstdout\fR if not set, \fIfile\fR - otherwise, for compatibility) .TP -\fB\-\-logfile\fR +\fB\-\-logfile=\fR Path to logfile (default - autodetect) .TP \fB\-\-loglevel=\fR -Log messages above this level (\fIdebug\fR, \fBinfo\fR, \fIwarn\fR, \fIerror\fR) +Log messages above this level (\fIdebug\fR, \fBinfo\fR, \fIwarn\fR, \fIerror\fR, \fInone\fR) +.TP +\fB\-\-logclftime\fR +Log messages with full CLF-formatted date and time (\fIdisabled\fR by default) .TP \fB\-\-datadir=\fR Path to storage of i2pd data (RI, keys, peer profiles, ...) @@ -51,35 +51,58 @@ The external IP address \fB\-\-port=\fR The port to listen on for incoming connections .TP -\fB\-\-daemon\fR -Router will go to background after start +\fB\-\-ifname=\fR +The network interface to bind to .TP -\fB\-\-service\fR -Router will use system folders like \fI/var/lib/i2pd\fR +\fB\-\-ifname4=\fR +The network interface to bind to for IPv4 connections +.TP +\fB\-\-ifname6=\fR +The network interface to bind to for IPv6 connections +.TP +\fB\-\-ipv4=\fR +Enable communication through ipv6 (\fIenabled\fR by default) .TP \fB\-\-ipv6\fR -Enable communication through ipv6. false by default +Enable communication through ipv6 (\fIdisabled\fR by default) +.TP +\fB\-\-ntcp=\fR +Enable usage of NTCP transport (\fIenabled\fR by default) +.TP +\fB\-\-ntcpproxy=\fR +Set proxy URL for NTCP transport +.TP +\fB\-\-ssu=\fR +Enable usage of SSU transport (\fIenabled\fR by default) .TP \fB\-\-notransit\fR -Router will not accept transit tunnels at startup +Router will not accept transit tunnels at startup (\fIdisabled\fR by default) .TP \fB\-\-floodfill\fR -Router will be floodfill +Router will be floodfill (\fIdisabled\fR by default) .TP \fB\-\-bandwidth=\fR -Bandwidth limit: integer in KBps or letter aliases: \fIL (32KBps)\fR, O (256), P (2048), X (>9000) +Bandwidth limit: integer in KBps or letter aliases: \fBL (32KBps)\fR, \fIO (256)\fR, \fIP (2048)\fR, \fIX (>9000)\fR +.TP +\fB\-\-share=\fR +Limit of transit traffic from max bandwidth in percents. (default: 100) +.TP +\fB\-\-daemon\fR +Router will go to background after start (\fIdisabled\fR by default) +.TP +\fB\-\-service\fR +Router will use system folders like \fI/var/lib/i2pd\fR (\fIdisabled\fR by default) .TP \fB\-\-family=\fR Name of a family, router belongs to. .PP -See service-specific parameters in example config file \fIcontrib/i2pd.conf\fR - -.SH FILES -.PP +Switches, which enabled by default (like \fB\-\-ssu\fR, \fB\-\-ntcp\fR, etc.), can be disabled in config file. +.RE +See service-specific parameters in example config file \fI/usr/share/doc/i2pd/i2pd.conf.gz\fR +.SH "FILES" /etc/i2pd/i2pd.conf, /etc/i2pd/tunnels.conf, /etc/default/i2pd .RS 4 i2pd configuration files (when running as a system service) - .RE .PP /var/lib/i2pd/ @@ -90,16 +113,15 @@ i2pd profile directory (when running as a system service, see \fB\-\-service\fR $HOME/.i2pd/ .RS 4 i2pd profile directory (when running as a normal user) +.SH "SEE ALSO" +Documentation at Read the Docs: \m[blue]\fBhttps://i2pd\&.readthedocs\&.io/en/latest/\fR\m[] +.SH "AUTHOR" +This manual page was written by kytv <\m[blue]\fBkillyourtv@i2pmail\&.org\fR\m[]> for the Debian system (but may be used by others). .RE +Updated by hagen <\m[blue]\fBhagen@i2pmail\&.org\fR\m[]> in 2016. +.RE +Updated by R4SAS <\m[blue]\fBr4sas@i2pmail\&.org\fR\m[]> in 2018. .PP -/usr/share/doc/i2pd/examples/hosts.txt.gz -.RS 4 -default I2P hosts file -.SH AUTHOR -This manual page was written by kytv for the Debian system (but may be used by others). -.PP -Updated by hagen in 2016. -.PP -Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or any later version published by the Free Software Foundation -.BR +Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or any later version published by the Free Software Foundation. +.RE On Debian systems, the complete text of the GNU General Public License can be found in \fI/usr/share/common-licenses/GPL\fR diff --git a/debian/i2pd.service b/debian/i2pd.service new file mode 120000 index 00000000..57d6b4da --- /dev/null +++ b/debian/i2pd.service @@ -0,0 +1 @@ +../contrib/debian/i2pd.service \ No newline at end of file diff --git a/debian/i2pd.tmpfile b/debian/i2pd.tmpfile new file mode 120000 index 00000000..22dfb4cf --- /dev/null +++ b/debian/i2pd.tmpfile @@ -0,0 +1 @@ +../contrib/debian/i2pd.tmpfile \ No newline at end of file diff --git a/debian/rules b/debian/rules index 4654ae6c..77ecd7b7 100755 --- a/debian/rules +++ b/debian/rules @@ -5,17 +5,18 @@ #export DH_VERBOSE=1 DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow -DPKG_EXPORT_BUILDFLAGS = 1 -include /usr/share/dpkg/buildflags.mk -CXXFLAGS+=$(CPPFLAGS) -PREFIX=/usr +#DPKG_EXPORT_BUILDFLAGS = 1 +#include /usr/share/dpkg/buildflags.mk +#CXXFLAGS+=$(CPPFLAGS) +#PREFIX=/usr %: dh $@ --parallel - dh_apparmor --profile-name=usr.sbin.i2pd -pi2pd +# dh_apparmor --profile-name=usr.sbin.i2pd -pi2pd override_dh_strip: dh_strip --dbg-package=i2pd-dbg -override_dh_shlibdeps: - dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info +## uncomment this if you have "missing info" problem when building package +#override_dh_shlibdeps: +# dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info diff --git a/docs/Doxyfile b/docs/Doxyfile index c434d2dc..275d668e 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1094,7 +1094,7 @@ HTML_STYLESHEET = # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra stylesheet files is of importance (e.g. the last # stylesheet in the list overrules the setting of the previous ones in the @@ -1637,7 +1637,7 @@ EXTRA_PACKAGES = # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, # $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty string, # for the replacement values of the other commands the user is referred to # HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. diff --git a/libi2pd/CPU.cpp b/libi2pd/CPU.cpp index d2868a20..a707c3dc 100644 --- a/libi2pd/CPU.cpp +++ b/libi2pd/CPU.cpp @@ -21,23 +21,35 @@ namespace cpu void Detect() { +#if defined(__AES__) || defined(__AVX__) + #if defined(__x86_64__) || defined(__i386__) int info[4]; __cpuid(0, info[0], info[1], info[2], info[3]); if (info[0] >= 0x00000001) { __cpuid(0x00000001, info[0], info[1], info[2], info[3]); +#ifdef __AES__ aesni = info[2] & bit_AES; // AESNI +#endif // __AES__ +#ifdef __AVX__ avx = info[2] & bit_AVX; // AVX +#endif // __AVX__ } -#endif +#endif // defined(__x86_64__) || defined(__i386__) + +#ifdef __AES__ if(aesni) { LogPrint(eLogInfo, "AESNI enabled"); } +#endif // __AES__ +#ifdef __AVX__ if(avx) { LogPrint(eLogInfo, "AVX enabled"); } +#endif // __AVX__ +#endif // defined(__AES__) || defined(__AVX__) } } } diff --git a/libi2pd/ChaCha20.cpp b/libi2pd/ChaCha20.cpp new file mode 100644 index 00000000..e43f1514 --- /dev/null +++ b/libi2pd/ChaCha20.cpp @@ -0,0 +1,147 @@ +#include "ChaCha20.h" + +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +namespace i2p +{ +namespace crypto +{ +namespace chacha +{ +constexpr int rounds = 20; +constexpr std::size_t blocksize = 64; + +void u32t8le(uint32_t v, uint8_t * p) +{ + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +uint32_t u8t32le(const uint8_t * p) +{ + uint32_t value = p[3]; + + value = (value << 8) | p[2]; + value = (value << 8) | p[1]; + value = (value << 8) | p[0]; + + return value; +} + +uint32_t rotl32(uint32_t x, int n) +{ + return x << n | (x >> (-n & 31)); +} + +void quarterround(uint32_t *x, int a, int b, int c, int d) +{ + x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); + x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); + x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); + x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); +} + + struct State_t + { + State_t() {}; + State_t(State_t &&) = delete; + + State_t & operator += (const State_t & other) + { + for(int i = 0; i < 16; i++) + data[i] += other.data[i]; + return *this; + } + + void Copy(const State_t & other) + { + memcpy(data, other.data, sizeof(uint32_t) * 16); + } + uint32_t data[16]; + }; + + struct Block_t + { + Block_t() {}; + Block_t(Block_t &&) = delete; + + uint8_t data[blocksize]; + + void operator << (const State_t & st) + { + int i; + for (i = 0; i < 16; i++) + u32t8le(st.data[i], data + (i << 2)); + } + }; + +void block(const State_t &input, Block_t & block, int rounds) +{ + int i; + State_t x; + x.Copy(input); + + for (i = rounds; i > 0; i -= 2) + { + quarterround(x.data, 0, 4, 8, 12); + quarterround(x.data, 1, 5, 9, 13); + quarterround(x.data, 2, 6, 10, 14); + quarterround(x.data, 3, 7, 11, 15); + quarterround(x.data, 0, 5, 10, 15); + quarterround(x.data, 1, 6, 11, 12); + quarterround(x.data, 2, 7, 8, 13); + quarterround(x.data, 3, 4, 9, 14); + } + x += input; + block << x; + +} +} // namespace chacha + + + + + +void chacha20(uint8_t * buf, size_t sz, const uint8_t * nonce, const uint8_t * key, uint32_t counter) +{ + chacha::State_t state; + chacha::Block_t block; + size_t i, j; + + state.data[0] = 0x61707865; + state.data[1] = 0x3320646e; + state.data[2] = 0x79622d32; + state.data[3] = 0x6b206574; + + for (i = 0; i < 8; i++) + state.data[4 + i] = chacha::u8t32le(key + i * 4); + + + state.data[12] = counter; + + for (i = 0; i < 3; i++) + state.data[13 + i] = chacha::u8t32le(nonce + i * 4); + + + for (i = 0; i < sz; i += chacha::blocksize) + { + chacha::block(state, block, chacha::rounds); + state.data[12]++; + for (j = i; j < i + chacha::blocksize; j++) + { + if (j >= sz) break; + buf[j] ^= block.data[j - i]; + } + } + +} + +} +} \ No newline at end of file diff --git a/libi2pd/ChaCha20.h b/libi2pd/ChaCha20.h new file mode 100644 index 00000000..c88325d5 --- /dev/null +++ b/libi2pd/ChaCha20.h @@ -0,0 +1,26 @@ +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +#ifndef LIBI2PD_CHACHA20_H +#define LIBI2PD_CHACHA20_H +#include +#include + +namespace i2p +{ +namespace crypto +{ + const std::size_t CHACHA20_KEY_BYTES = 32; + const std::size_t CHACHA20_NOUNCE_BYTES = 12; + + /** encrypt buf in place with chacha20 */ + void chacha20(uint8_t * buf, size_t sz, const uint8_t * nonce, const uint8_t * key, uint32_t counter=1); + +} +} + +#endif diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index d9cc6dec..9eaaf068 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -37,32 +37,32 @@ namespace config { ("pidfile", value()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") ("log", value()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)") ("logfile", value()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") - ("loglevel", value()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error)") - ("logclftime", value()->default_value(false), "Write full CLF-formatted date and time to log (default: write only time)") + ("loglevel", value()->default_value("info"), "Set the minimal level of log messages (debug, info, warn, error, none)") + ("logclftime", bool_switch()->default_value(false), "Write full CLF-formatted date and time to log (default: disabled, write only time)") ("family", value()->default_value(""), "Specify a family, router belongs to") ("datadir", value()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)") ("host", value()->default_value("0.0.0.0"), "External IP") ("ifname", value()->default_value(""), "Network interface to bind to") ("ifname4", value()->default_value(""), "Network interface to bind to for ipv4") ("ifname6", value()->default_value(""), "Network interface to bind to for ipv6") - ("nat", value()->default_value(true), "Should we assume we are behind NAT?") + ("nat", value()->default_value(true), "Should we assume we are behind NAT? (default: enabled)") ("port", value()->default_value(0), "Port to listen for incoming connections (default: auto)") - ("ipv4", value()->default_value(true), "Enable communication through ipv4") - ("ipv6", value()->zero_tokens()->default_value(false), "Enable communication through ipv6") + ("ipv4", value()->default_value(true), "Enable communication through ipv4 (default: enabled)") + ("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)") ("netid", value()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2") - ("daemon", value()->zero_tokens()->default_value(false), "Router will go to background after start") - ("service", value()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'") - ("notransit", value()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup") - ("floodfill", value()->zero_tokens()->default_value(false), "Router will be floodfill") + ("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)") + ("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)") + ("notransit", bool_switch()->default_value(false), "Router will not accept transit tunnels at startup (default: disabled)") + ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("bandwidth", value()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") - ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100") - ("ntcp", value()->default_value(true), "Enable NTCP transport") - ("ssu", value()->default_value(true), "Enable SSU transport") + ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") + ("ntcp", value()->default_value(true), "Enable NTCP transport (default: enabled)") + ("ssu", value()->default_value(true), "Enable SSU transport (default: enabled)") ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Windows service management ('install' or 'remove')") - ("insomnia", value()->zero_tokens()->default_value(false), "Prevent system from sleeping") - ("close", value()->default_value("ask"), "Action on close: minimize, exit, ask") // TODO: add custom validator or something + ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") + ("close", value()->default_value("ask"), "Action on close: minimize, exit, ask") #endif ; @@ -78,14 +78,14 @@ namespace config { options_description httpserver("HTTP Server options"); httpserver.add_options() - ("http.enabled", value()->default_value(true), "Enable or disable webconsole") - ("http.address", value()->default_value("127.0.0.1"), "Webconsole listen address") - ("http.port", value()->default_value(7070), "Webconsole listen port") - ("http.auth", value()->default_value(false), "Enable Basic HTTP auth for webconsole") - ("http.user", value()->default_value("i2pd"), "Username for basic auth") - ("http.pass", value()->default_value(""), "Password for basic auth (default: random, see logs)") - ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") - ("http.hostname", value()->default_value("localhost"),"Expected hostname for WebUI") + ("http.enabled", value()->default_value(true), "Enable or disable webconsole") + ("http.address", value()->default_value("127.0.0.1"), "Webconsole listen address") + ("http.port", value()->default_value(7070), "Webconsole listen port") + ("http.auth", value()->default_value(false), "Enable Basic HTTP auth for webconsole") + ("http.user", value()->default_value("i2pd"), "Username for basic auth") + ("http.pass", value()->default_value(""), "Password for basic auth (default: random, see logs)") + ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") + ("http.hostname", value()->default_value("localhost"), "Expected hostname for WebUI") ; options_description httpproxy("HTTP Proxy options"); @@ -191,7 +191,7 @@ namespace config { // "https://uk.reseed.i2p2.no:444/," // mamoth's shit "https://i2p-0.manas.ca:8443/," "https://download.xxlspeed.com/," - "https://reseed-ru.lngserv.ru/," + "https://reseed-fr.i2pd.xyz/," "https://reseed.atomike.ninja/," "https://reseed.memcpy.io/," "https://reseed.onion.im/," @@ -231,6 +231,13 @@ namespace config { ("exploratory.outbound.quantity", value()->default_value(3), "Exploratory outbound tunnels quantity") ; + options_description ntcp2("NTCP2 Options"); + ntcp2.add_options() + ("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)") + ("ntcp2.published", value()->default_value(false), "Publish NTCP2 (default: disabled)") + ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") + ; + m_OptionsDesc .add(general) .add(limits) @@ -248,6 +255,7 @@ namespace config { .add(trust) .add(websocket) .add(exploratory) + .add(ntcp2) ; } @@ -330,4 +338,3 @@ namespace config { } // namespace config } // namespace i2p - diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 5ba3334d..7a947d34 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -8,8 +8,15 @@ #include #include "TunnelBase.h" #include -#include "Log.h" #include "Crypto.h" +#if LEGACY_OPENSSL +#include "ChaCha20.h" +#include "Poly1305.h" +#else +#include +#endif +#include "I2PEndian.h" +#include "Log.h" namespace i2p { @@ -392,7 +399,7 @@ namespace crypto bn2buf (x, encrypted + 1, len); bn2buf (y, encrypted + 1 + len, len); RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); - } + } else { bn2buf (x, encrypted, len); @@ -461,10 +468,10 @@ namespace crypto CBCDecryption decryption; decryption.SetKey (shared); decryption.SetIV (iv); - if (zeroPadding) + if (zeroPadding) decryption.Decrypt (encrypted + 258, 256, m); else - decryption.Decrypt (encrypted + 256, 256, m); + decryption.Decrypt (encrypted + 256, 256, m); // verify and copy uint8_t hash[32]; SHA256 (m + 33, 222, hash); @@ -515,9 +522,9 @@ namespace crypto { uint64_t buf[256]; uint64_t hash[12]; // 96 bytes +#ifdef __AVX__ if(i2p::cpu::avx) { -#ifdef AVX __asm__ ( "vmovups %[key], %%ymm0 \n" @@ -536,30 +543,9 @@ namespace crypto [buf]"r"(buf), [hash]"r"(hash) : "memory", "%xmm0" // TODO: change to %ymm0 later ); -#else - // ikeypad - buf[0] = key.GetLL ()[0] ^ IPAD; - buf[1] = key.GetLL ()[1] ^ IPAD; - buf[2] = key.GetLL ()[2] ^ IPAD; - buf[3] = key.GetLL ()[3] ^ IPAD; - buf[4] = IPAD; - buf[5] = IPAD; - buf[6] = IPAD; - buf[7] = IPAD; - // okeypad - hash[0] = key.GetLL ()[0] ^ OPAD; - hash[1] = key.GetLL ()[1] ^ OPAD; - hash[2] = key.GetLL ()[2] ^ OPAD; - hash[3] = key.GetLL ()[3] ^ OPAD; - hash[4] = OPAD; - hash[5] = OPAD; - hash[6] = OPAD; - hash[7] = OPAD; - // fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P) - memset (hash + 10, 0, 16); -#endif } else +#endif { // ikeypad buf[0] = key.GetLL ()[0] ^ IPAD; @@ -593,7 +579,14 @@ namespace crypto } // AES -#ifdef AESNI +#ifdef __AES__ + #ifdef ARM64AES + void init_aesenc(void){ + // TODO: Implementation + } + + #endif + #define KeyExpansion256(round0,round1) \ "pshufd $0xff, %%xmm2, %%xmm2 \n" \ "movaps %%xmm1, %%xmm4 \n" \ @@ -618,7 +611,7 @@ namespace crypto "movaps %%xmm3, "#round1"(%[sched]) \n" #endif -#ifdef AESNI +#ifdef __AES__ void ECBCryptoAESNI::ExpandKey (const AESKey& key) { __asm__ @@ -655,11 +648,11 @@ namespace crypto : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged ); - } + } #endif -#if AESNI +#ifdef __AES__ #define EncryptAES256(sched) \ "pxor (%["#sched"]), %%xmm0 \n" \ "aesenc 16(%["#sched"]), %%xmm0 \n" \ @@ -677,12 +670,12 @@ namespace crypto "aesenc 208(%["#sched"]), %%xmm0 \n" \ "aesenclast 224(%["#sched"]), %%xmm0 \n" #endif - + void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[in]), %%xmm0 \n" @@ -690,17 +683,15 @@ namespace crypto "movups %%xmm0, (%[out]) \n" : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" ); -#else - AES_encrypt (in->buf, out->buf, &m_Key); -#endif } else +#endif { AES_encrypt (in->buf, out->buf, &m_Key); - } + } } -#ifdef AESNI +#ifdef __AES__ #define DecryptAES256(sched) \ "pxor 224(%["#sched"]), %%xmm0 \n" \ "aesdec 208(%["#sched"]), %%xmm0 \n" \ @@ -718,12 +709,12 @@ namespace crypto "aesdec 16(%["#sched"]), %%xmm0 \n" \ "aesdeclast (%["#sched"]), %%xmm0 \n" #endif - + void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[in]), %%xmm0 \n" @@ -731,17 +722,15 @@ namespace crypto "movups %%xmm0, (%[out]) \n" : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" ); -#else - AES_decrypt (in->buf, out->buf, &m_Key); -#endif } else +#endif { AES_decrypt (in->buf, out->buf, &m_Key); } } -#ifdef AESNI +#ifdef __AES__ #define CallAESIMC(offset) \ "movaps "#offset"(%[shed]), %%xmm0 \n" \ "aesimc %%xmm0, %%xmm0 \n" \ @@ -750,25 +739,23 @@ namespace crypto void ECBEncryption::SetKey (const AESKey& key) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI - ExpandKey (key); -#else - AES_set_encrypt_key (key, 256, &m_Key); -#endif + ExpandKey (key); } else +#endif { AES_set_encrypt_key (key, 256, &m_Key); } } - + void ECBDecryption::SetKey (const AESKey& key) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI ExpandKey (key); // expand encryption key first // then invert it using aesimc __asm__ @@ -788,11 +775,9 @@ namespace crypto CallAESIMC(208) : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" ); -#else - AES_set_decrypt_key (key, 256, &m_Key); -#endif } else +#endif { AES_set_decrypt_key (key, 256, &m_Key); } @@ -801,9 +786,9 @@ namespace crypto void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -823,16 +808,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) : "%xmm0", "%xmm1", "cc", "memory" ); -#else - for (int i = 0; i < numBlocks; i++) - { - *m_LastBlock.GetChipherBlock () ^= in[i]; - m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ()); - out[i] = *m_LastBlock.GetChipherBlock (); - } -#endif } else +#endif { for (int i = 0; i < numBlocks; i++) { @@ -853,9 +831,9 @@ namespace crypto void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -869,19 +847,17 @@ namespace crypto [in]"r"(in), [out]"r"(out) : "%xmm0", "%xmm1", "memory" ); -#else - Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); -#endif } else +#endif Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); } void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -902,17 +878,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" ); -#else - for (int i = 0; i < numBlocks; i++) - { - ChipherBlock tmp = in[i]; - m_ECBDecryption.Decrypt (in + i, out + i); - out[i] ^= *m_IV.GetChipherBlock (); - *m_IV.GetChipherBlock () = tmp; - } -#endif } else +#endif { for (int i = 0; i < numBlocks; i++) { @@ -933,9 +901,9 @@ namespace crypto void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( "movups (%[iv]), %%xmm1 \n" @@ -949,19 +917,17 @@ namespace crypto [in]"r"(in), [out]"r"(out) : "%xmm0", "%xmm1", "memory" ); -#else - Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); -#endif } else +#endif Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); } void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( // encrypt IV @@ -987,14 +953,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes : "%xmm0", "%xmm1", "cc", "memory" ); -#else - m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerEncryption.SetIV (out); - m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv -#endif } else +#endif { m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv m_LayerEncryption.SetIV (out); @@ -1005,9 +966,9 @@ namespace crypto void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out) { +#ifdef __AES__ if(i2p::cpu::aesni) { -#ifdef AESNI __asm__ ( // decrypt IV @@ -1034,14 +995,9 @@ namespace crypto [in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" ); -#else - m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv - m_LayerDecryption.SetIV (out); - m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data - m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv -#endif } else +#endif { m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv m_LayerDecryption.SetIV (out); @@ -1050,6 +1006,103 @@ namespace crypto } } +// AEAD/ChaCha20/Poly1305 + + bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt) + { + if (len < msgLen) return false; + if (encrypt && len < msgLen + 16) return false; + bool ret = true; +#if LEGACY_OPENSSL + // generate one time poly key + uint8_t polyKey[64]; + memset(polyKey, 0, sizeof(polyKey)); + chacha20 (polyKey, 64, nonce, key, 0); + + // create Poly1305 message + if (!ad) adLen = 0; + std::vector polyMsg(adLen + msgLen + 3*16); + size_t offset = 0; + uint8_t padding[16]; memset (padding, 0, 16); + if (ad) + { + memcpy (polyMsg.data (), ad, adLen); offset += adLen; // additional authenticated data + auto rem = adLen & 0x0F; // %16 + if (rem) + { + // padding1 + rem = 16 - rem; + memcpy (polyMsg.data () + offset, padding, rem); offset += rem; + } + } + // encrypt/decrypt data and add to hash + if (buf != msg) + memcpy (buf, msg, msgLen); + if (encrypt) + { + chacha20 (buf, msgLen, nonce, key, 1); // encrypt + memcpy (polyMsg.data () + offset, buf, msgLen); // after encryption + } + else + { + memcpy (polyMsg.data () + offset, buf, msgLen); // before decryption + chacha20 (buf, msgLen, nonce, key, 1); // decrypt + } + offset += msgLen; // encrypted data + + auto rem = msgLen & 0x0F; // %16 + if (rem) + { + // padding2 + rem = 16 - rem; + memcpy (polyMsg.data () + offset, padding, rem); offset += rem; + } + htole64buf (polyMsg.data () + offset, adLen); offset += 8; + htole64buf (polyMsg.data () + offset, msgLen); offset += 8; + + if (encrypt) + { + // calculate Poly1305 tag and write in after encrypted data + Poly1305HMAC ((uint32_t *)(buf + msgLen), (uint32_t *)polyKey, polyMsg.data (), offset); + } + else + { + uint32_t tag[8]; + // calculate Poly1305 tag + Poly1305HMAC (tag, (uint32_t *)polyKey, polyMsg.data (), offset); + if (memcmp (tag, msg + msgLen, 16)) ret = false; // compare with provided + } +#else + int outlen = 0; + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); + if (encrypt) + { + EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); + EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); + EVP_EncryptUpdate(ctx, NULL, &outlen, ad, adLen); + EVP_EncryptUpdate(ctx, buf, &outlen, msg, msgLen); + EVP_EncryptFinal_ex(ctx, buf, &outlen); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, buf + msgLen); + } + else + { + EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (uint8_t *)(msg + msgLen)); + EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce); + EVP_DecryptUpdate(ctx, NULL, &outlen, ad, adLen); + EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen); + ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0; + } + + EVP_CIPHER_CTX_free (ctx); +#endif + return ret; + } + +// init and terminate + /* std::vector > m_OpenSSLMutexes; static void OpensslLockingCallback(int mode, int type, const char * file, int line) { diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 6e4ddb3d..43f1def9 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -69,9 +69,9 @@ namespace crypto void operator^=(const ChipherBlock& other) // XOR { +#ifdef __AVX__ if (i2p::cpu::avx) { -#ifdef AVX __asm__ ( "vmovups (%[buf]), %%xmm0 \n" @@ -82,12 +82,9 @@ namespace crypto : [buf]"r"(buf), [other]"r"(other.buf) : "%xmm0", "%xmm1", "memory" ); -#else - for (int i = 0; i < 16; i++) - buf[i] ^= other.buf[i]; -#endif } else +#endif { // TODO: implement it better for (int i = 0; i < 16; i++) @@ -123,7 +120,10 @@ namespace crypto }; -#ifdef AESNI +#ifdef __AES__ + #ifdef ARM64AES + void init_aesenc(void) __attribute__((constructor)); + #endif class ECBCryptoAESNI { public: @@ -140,7 +140,7 @@ namespace crypto }; #endif -#ifdef AESNI +#ifdef __AES__ class ECBEncryption: public ECBCryptoAESNI #else class ECBEncryption @@ -149,14 +149,14 @@ namespace crypto public: void SetKey (const AESKey& key); - + void Encrypt(const ChipherBlock * in, ChipherBlock * out); private: AES_KEY m_Key; }; -#ifdef AESNI +#ifdef __AES__ class ECBDecryption: public ECBCryptoAESNI #else class ECBDecryption @@ -178,13 +178,14 @@ namespace crypto void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes + void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); }; void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); void Encrypt (const uint8_t * in, uint8_t * out); // one block ECBEncryption & ECB() { return m_ECBEncryption; } - + private: AESAlignedBuffer<16> m_LastBlock; @@ -200,13 +201,14 @@ namespace crypto void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes + void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); }; void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); void Decrypt (const uint8_t * in, uint8_t * out); // one block ECBDecryption & ECB() { return m_ECBDecryption; } - + private: AESAlignedBuffer<16> m_IV; @@ -249,6 +251,10 @@ namespace crypto CBCDecryption m_LayerDecryption; }; +// AEAD/ChaCha20/Poly1305 + bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag + +// init and terminate void InitCrypto (bool precomputation); void TerminateCrypto (); } @@ -256,7 +262,13 @@ namespace crypto // take care about openssl version #include -#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL +#if ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL +# define LEGACY_OPENSSL 1 +#else +# define LEGACY_OPENSSL 0 +#endif + +#if LEGACY_OPENSSL // define getters and setters introduced in 1.1.0 inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) { diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index b7c2ee32..ad7725fc 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -64,7 +64,7 @@ namespace client { it = params->find (I2CP_PARAM_OUTBOUND_NICKNAME); if (it != params->end ()) m_Nickname = it->second; - // otherwise we set deafult nickname in Start when we know local address + // otherwise we set default nickname in Start when we know local address } } } @@ -329,17 +329,17 @@ namespace client switch (typeID) { case eI2NPData: - HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + HandleDataMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); break; case eI2NPDeliveryStatus: // we assume tunnel tests non-encrypted HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); break; case eI2NPDatabaseStore: - HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); break; case eI2NPDatabaseSearchReply: - HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); break; default: i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); @@ -859,6 +859,11 @@ namespace client void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) { uint32_t length = bufbe32toh (buf); + if(length > len - 4) + { + LogPrint(eLogError, "Destination: Data message length ", length, " exceeds buffer length ", len); + return; + } buf += 4; // we assume I2CP payload uint16_t fromPort = bufbe16toh (buf + 4), // source diff --git a/libi2pd/Ed25519.cpp b/libi2pd/Ed25519.cpp new file mode 100644 index 00000000..17264926 --- /dev/null +++ b/libi2pd/Ed25519.cpp @@ -0,0 +1,515 @@ +#include +#include "Log.h" +#include "Crypto.h" +#include "Ed25519.h" + +namespace i2p +{ +namespace crypto +{ + Ed25519::Ed25519 () + { + BN_CTX * ctx = BN_CTX_new (); + BIGNUM * tmp = BN_new (); + + q = BN_new (); + // 2^255-19 + BN_set_bit (q, 255); // 2^255 + BN_sub_word (q, 19); + + l = BN_new (); + // 2^252 + 27742317777372353535851937790883648493 + BN_set_bit (l, 252); + two_252_2 = BN_dup (l); + BN_dec2bn (&tmp, "27742317777372353535851937790883648493"); + BN_add (l, l, tmp); + BN_sub_word (two_252_2, 2); // 2^252 - 2 + + // -121665*inv(121666) + d = BN_new (); + BN_set_word (tmp, 121666); + BN_mod_inverse (tmp, tmp, q, ctx); + BN_set_word (d, 121665); + BN_set_negative (d, 1); + BN_mul (d, d, tmp, ctx); + + // 2^((q-1)/4) + I = BN_new (); + BN_free (tmp); + tmp = BN_dup (q); + BN_sub_word (tmp, 1); + BN_div_word (tmp, 4); + BN_set_word (I, 2); + BN_mod_exp (I, I, tmp, q, ctx); + BN_free (tmp); + + // 4*inv(5) + BIGNUM * By = BN_new (); + BN_set_word (By, 5); + BN_mod_inverse (By, By, q, ctx); + BN_mul_word (By, 4); + BIGNUM * Bx = RecoverX (By, ctx); + BN_mod (Bx, Bx, q, ctx); // % q + BN_mod (By, By, q, ctx); // % q + + // precalculate Bi256 table + Bi256Carry = { Bx, By }; // B + for (int i = 0; i < 32; i++) + { + Bi256[i][0] = Bi256Carry; // first point + for (int j = 1; j < 128; j++) + Bi256[i][j] = Sum (Bi256[i][j-1], Bi256[i][0], ctx); // (256+j+1)^i*B + Bi256Carry = Bi256[i][127]; + for (int j = 0; j < 128; j++) // add first point 128 more times + Bi256Carry = Sum (Bi256Carry, Bi256[i][0], ctx); + } + + BN_CTX_free (ctx); + } + + Ed25519::Ed25519 (const Ed25519& other): q (BN_dup (other.q)), l (BN_dup (other.l)), + d (BN_dup (other.d)), I (BN_dup (other.I)), two_252_2 (BN_dup (other.two_252_2)), + Bi256Carry (other.Bi256Carry) + { + for (int i = 0; i < 32; i++) + for (int j = 0; j < 128; j++) + Bi256[i][j] = other.Bi256[i][j]; + } + + Ed25519::~Ed25519 () + { + BN_free (q); + BN_free (l); + BN_free (d); + BN_free (I); + BN_free (two_252_2); + } + + + EDDSAPoint Ed25519::GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const + { + return MulB (expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian + } + + EDDSAPoint Ed25519::DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const + { + return DecodePoint (buf, ctx); + } + + void Ed25519::EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const + { + EncodePoint (Normalize (publicKey, ctx), buf); + } + + bool Ed25519::Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const + { + BN_CTX * ctx = BN_CTX_new (); + BIGNUM * h = DecodeBN<64> (digest); + // signature 0..31 - R, 32..63 - S + // B*S = R + PK*h => R = B*S - PK*h + // we don't decode R, but encode (B*S - PK*h) + auto Bs = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx); // B*S; + BN_mod (h, h, l, ctx); // public key is multiple of B, but B%l = 0 + auto PKh = Mul (publicKey, h, ctx); // PK*h + uint8_t diff[32]; + EncodePoint (Normalize (Sum (Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded + bool passed = !memcmp (signature, diff, 32); // R + BN_free (h); + BN_CTX_free (ctx); + if (!passed) + LogPrint (eLogError, "25519 signature verification failed"); + return passed; + } + + void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, + uint8_t * signature) const + { + BN_CTX * bnCtx = BN_CTX_new (); + // calculate r + SHA512_CTX ctx; + SHA512_Init (&ctx); + SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key + SHA512_Update (&ctx, buf, len); // data + uint8_t digest[64]; + SHA512_Final (digest, &ctx); + BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors + // calculate R + uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf + EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors + // calculate S + SHA512_Init (&ctx); + SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R + SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key + SHA512_Update (&ctx, buf, len); // data + SHA512_Final (digest, &ctx); + BIGNUM * h = DecodeBN<64> (digest); + // S = (r + h*a) % l + BIGNUM * a = DecodeBN (expandedPrivateKey); // left half of expanded key + BN_mod_mul (h, h, a, l, bnCtx); // %l + BN_mod_add (h, h, r, l, bnCtx); // %l + memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2); + EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S + BN_free (r); BN_free (h); BN_free (a); + BN_CTX_free (bnCtx); + } + + EDDSAPoint Ed25519::Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const + { + // x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2) + // y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2) + // z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2) + // t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2) + BIGNUM * x3 = BN_new (), * y3 = BN_new (), * z3 = BN_new (), * t3 = BN_new (); + + BN_mul (x3, p1.x, p2.x, ctx); // A = x1*x2 + BN_mul (y3, p1.y, p2.y, ctx); // B = y1*y2 + + BN_CTX_start (ctx); + BIGNUM * t1 = p1.t, * t2 = p2.t; + if (!t1) { t1 = BN_CTX_get (ctx); BN_mul (t1, p1.x, p1.y, ctx); } + if (!t2) { t2 = BN_CTX_get (ctx); BN_mul (t2, p2.x, p2.y, ctx); } + BN_mul (t3, t1, t2, ctx); + BN_mul (t3, t3, d, ctx); // C = d*t1*t2 + + if (p1.z) + { + if (p2.z) + BN_mul (z3, p1.z, p2.z, ctx); // D = z1*z2 + else + BN_copy (z3, p1.z); // D = z1 + } + else + { + if (p2.z) + BN_copy (z3, p2.z); // D = z2 + else + BN_one (z3); // D = 1 + } + + BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx); + BN_add (E, p1.x, p1.y); + BN_add (F, p2.x, p2.y); + BN_mul (E, E, F, ctx); // (x1 + y1)*(x2 + y2) + BN_sub (E, E, x3); + BN_sub (E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B + BN_sub (F, z3, t3); // F = D - C + BN_add (G, z3, t3); // G = D + C + BN_add (H, y3, x3); // H = B + A + + BN_mod_mul (x3, E, F, q, ctx); // x3 = E*F + BN_mod_mul (y3, G, H, q, ctx); // y3 = G*H + BN_mod_mul (z3, F, G, q, ctx); // z3 = F*G + BN_mod_mul (t3, E, H, q, ctx); // t3 = E*H + + BN_CTX_end (ctx); + + return EDDSAPoint {x3, y3, z3, t3}; + } + + void Ed25519::Double (EDDSAPoint& p, BN_CTX * ctx) const + { + BN_CTX_start (ctx); + BIGNUM * x2 = BN_CTX_get (ctx), * y2 = BN_CTX_get (ctx), * z2 = BN_CTX_get (ctx), * t2 = BN_CTX_get (ctx); + + BN_sqr (x2, p.x, ctx); // x2 = A = x^2 + BN_sqr (y2, p.y, ctx); // y2 = B = y^2 + if (p.t) + BN_sqr (t2, p.t, ctx); // t2 = t^2 + else + { + BN_mul (t2, p.x, p.y, ctx); // t = x*y + BN_sqr (t2, t2, ctx); // t2 = t^2 + } + BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2 + if (p.z) + BN_sqr (z2, p.z, ctx); // z2 = D = z^2 + else + BN_one (z2); // z2 = 1 + + BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx); + // E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy + BN_mul (E, p.x, p.y, ctx); + BN_lshift1 (E, E); // E =2*x*y + BN_sub (F, z2, t2); // F = D - C + BN_add (G, z2, t2); // G = D + C + BN_add (H, y2, x2); // H = B + A + + BN_mod_mul (p.x, E, F, q, ctx); // x2 = E*F + BN_mod_mul (p.y, G, H, q, ctx); // y2 = G*H + if (!p.z) p.z = BN_new (); + BN_mod_mul (p.z, F, G, q, ctx); // z2 = F*G + if (!p.t) p.t = BN_new (); + BN_mod_mul (p.t, E, H, q, ctx); // t2 = E*H + + BN_CTX_end (ctx); + } + + EDDSAPoint Ed25519::Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const + { + BIGNUM * zero = BN_new (), * one = BN_new (); + BN_zero (zero); BN_one (one); + EDDSAPoint res {zero, one}; + if (!BN_is_zero (e)) + { + int bitCount = BN_num_bits (e); + for (int i = bitCount - 1; i >= 0; i--) + { + Double (res, ctx); + if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx); + } + } + return res; + } + + EDDSAPoint Ed25519::MulB (const uint8_t * e, BN_CTX * ctx) const // B*e, e is 32 bytes Little Endian + { + BIGNUM * zero = BN_new (), * one = BN_new (); + BN_zero (zero); BN_one (one); + EDDSAPoint res {zero, one}; + bool carry = false; + for (int i = 0; i < 32; i++) + { + uint8_t x = e[i]; + if (carry) + { + if (x < 255) + { + x++; + carry = false; + } + else + x = 0; + } + if (x > 0) + { + if (x <= 128) + res = Sum (res, Bi256[i][x-1], ctx); + else + { + res = Sum (res, -Bi256[i][255-x], ctx); // -Bi[256-x] + carry = true; + } + } + } + if (carry) res = Sum (res, Bi256Carry, ctx); + return res; + } + + EDDSAPoint Ed25519::Normalize (const EDDSAPoint& p, BN_CTX * ctx) const + { + if (p.z) + { + BIGNUM * x = BN_new (), * y = BN_new (); + BN_mod_inverse (y, p.z, q, ctx); + BN_mod_mul (x, p.x, y, q, ctx); // x = x/z + BN_mod_mul (y, p.y, y, q, ctx); // y = y/z + return EDDSAPoint{x, y}; + } + else + return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)}; + } + + bool Ed25519::IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const + { + BN_CTX_start (ctx); + BIGNUM * x2 = BN_CTX_get (ctx), * y2 = BN_CTX_get (ctx), * tmp = BN_CTX_get (ctx); + BN_sqr (x2, p.x, ctx); // x^2 + BN_sqr (y2, p.y, ctx); // y^2 + // y^2 - x^2 - 1 - d*x^2*y^2 + BN_mul (tmp, d, x2, ctx); + BN_mul (tmp, tmp, y2, ctx); + BN_sub (tmp, y2, tmp); + BN_sub (tmp, tmp, x2); + BN_sub_word (tmp, 1); + BN_mod (tmp, tmp, q, ctx); // % q + bool ret = BN_is_zero (tmp); + BN_CTX_end (ctx); + return ret; + } + + BIGNUM * Ed25519::RecoverX (const BIGNUM * y, BN_CTX * ctx) const + { + BN_CTX_start (ctx); + BIGNUM * y2 = BN_CTX_get (ctx), * xx = BN_CTX_get (ctx); + BN_sqr (y2, y, ctx); // y^2 + // xx = (y^2 -1)*inv(d*y^2 +1) + BN_mul (xx, d, y2, ctx); + BN_add_word (xx, 1); + BN_mod_inverse (xx, xx, q, ctx); + BN_sub_word (y2, 1); + BN_mul (xx, y2, xx, ctx); + // x = srqt(xx) = xx^(2^252-2) + BIGNUM * x = BN_new (); + BN_mod_exp (x, xx, two_252_2, q, ctx); + // check (x^2 -xx) % q + BN_sqr (y2, x, ctx); + BN_mod_sub (y2, y2, xx, q, ctx); + if (!BN_is_zero (y2)) + BN_mod_mul (x, x, I, q, ctx); + if (BN_is_odd (x)) + BN_sub (x, q, x); + BN_CTX_end (ctx); + return x; + } + + EDDSAPoint Ed25519::DecodePoint (const uint8_t * buf, BN_CTX * ctx) const + { + // buf is 32 bytes Little Endian, convert it to Big Endian + uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH]; + for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH/2; i++) // invert bytes + { + buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i]; + buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i]; + } + bool isHighestBitSet = buf1[0] & 0x80; + if (isHighestBitSet) + buf1[0] &= 0x7f; // clear highest bit + BIGNUM * y = BN_new (); + BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y); + BIGNUM * x = RecoverX (y, ctx); + if (BN_is_bit_set (x, 0) != isHighestBitSet) + BN_sub (x, q, x); // x = q - x + BIGNUM * z = BN_new (), * t = BN_new (); + BN_one (z); BN_mod_mul (t, x, y, q, ctx); // pre-calculate t + EDDSAPoint p {x, y, z, t}; + if (!IsOnCurve (p, ctx)) + LogPrint (eLogError, "Decoded point is not on 25519"); + return p; + } + + void Ed25519::EncodePoint (const EDDSAPoint& p, uint8_t * buf) const + { + EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH); + if (BN_is_bit_set (p.x, 0)) // highest bit + buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit + } + + template + BIGNUM * Ed25519::DecodeBN (const uint8_t * buf) const + { + // buf is Little Endian convert it to Big Endian + uint8_t buf1[len]; + for (size_t i = 0; i < len/2; i++) // invert bytes + { + buf1[i] = buf[len -1 - i]; + buf1[len -1 - i] = buf[i]; + } + BIGNUM * res = BN_new (); + BN_bin2bn (buf1, len, res); + return res; + } + + void Ed25519::EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const + { + bn2buf (bn, buf, len); + // To Little Endian + for (size_t i = 0; i < len/2; i++) // invert bytes + { + uint8_t tmp = buf[i]; + buf[i] = buf[len -1 - i]; + buf[len -1 - i] = tmp; + } + } + + BIGNUM * Ed25519::ScalarMul (const BIGNUM * u, const BIGNUM * k, BN_CTX * ctx) const + { + BN_CTX_start (ctx); + auto x1 = BN_CTX_get (ctx); BN_copy (x1, u); + auto x2 = BN_CTX_get (ctx); BN_one (x2); + auto z2 = BN_CTX_get (ctx); BN_zero (z2); + auto x3 = BN_CTX_get (ctx); BN_copy (x3, u); + auto z3 = BN_CTX_get (ctx); BN_one (z3); + auto c121666 = BN_CTX_get (ctx); BN_set_word (c121666, 121666); + auto tmp0 = BN_CTX_get (ctx); auto tmp1 = BN_CTX_get (ctx); + unsigned int swap = 0; + auto bits = BN_num_bits (k); + while(bits) + { + --bits; + auto k_t = BN_is_bit_set(k, bits) ? 1 : 0; + swap ^= k_t; + if (swap) + { + std::swap (x2, x3); + std::swap (z2, z3); + } + swap = k_t; + BN_mod_sub(tmp0, x3, z3, q, ctx); + BN_mod_sub(tmp1, x2, z2, q, ctx); + BN_mod_add(x2, x2, z2, q, ctx); + BN_mod_add(z2, x3, z3, q, ctx); + BN_mod_mul(z3, tmp0, x2, q, ctx); + BN_mod_mul(z2, z2, tmp1, q, ctx); + BN_mod_sqr(tmp0, tmp1, q, ctx); + BN_mod_sqr(tmp1, x2, q, ctx); + BN_mod_add(x3, z3, z2, q, ctx); + BN_mod_sub(z2, z3, z2, q, ctx); + BN_mod_mul(x2, tmp1, tmp0, q, ctx); + BN_mod_sub(tmp1, tmp1, tmp0, q, ctx); + BN_mod_sqr(z2, z2, q, ctx); + BN_mod_mul(z3, tmp1, c121666, q, ctx); + BN_mod_sqr(x3, x3, q, ctx); + BN_mod_add(tmp0, tmp0, z3, q, ctx); + BN_mod_mul(z3, x1, z2, q, ctx); + BN_mod_mul(z2, tmp1, tmp0, q, ctx); + } + if (swap) + { + std::swap (x2, x3); + std::swap (z2, z3); + } + BN_mod_inverse (z2, z2, q, ctx); + BIGNUM * res = BN_new (); // not from ctx + BN_mod_mul(res, x2, z2, q, ctx); + BN_CTX_end (ctx); + return res; + } + + void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const + { + BIGNUM * p1 = DecodeBN<32> (p); + uint8_t k[32]; + memcpy (k, e, 32); + k[0] &= 248; k[31] &= 127; k[31] |= 64; + BIGNUM * n = DecodeBN<32> (k); + BIGNUM * q1 = ScalarMul (p1, n, ctx); + EncodeBN (q1, buf, 32); + BN_free (p1); BN_free (n); BN_free (q1); + } + + void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const + { + BIGNUM *p1 = BN_new (); BN_set_word (p1, 9); + uint8_t k[32]; + memcpy (k, e, 32); + k[0] &= 248; k[31] &= 127; k[31] |= 64; + BIGNUM * n = DecodeBN<32> (k); + BIGNUM * q1 = ScalarMul (p1, n, ctx); + EncodeBN (q1, buf, 32); + BN_free (p1); BN_free (n); BN_free (q1); + } + + void Ed25519::ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey) + { + SHA512 (key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey); + expandedKey[0] &= 0xF8; // drop last 3 bits + expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits + expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit + } + + static std::unique_ptr g_Ed25519; + std::unique_ptr& GetEd25519 () + { + if (!g_Ed25519) + { + auto c = new Ed25519(); + if (!g_Ed25519) // make sure it was not created already + g_Ed25519.reset (c); + else + delete c; + } + return g_Ed25519; + } +} +} + diff --git a/libi2pd/Ed25519.h b/libi2pd/Ed25519.h new file mode 100644 index 00000000..fc23a457 --- /dev/null +++ b/libi2pd/Ed25519.h @@ -0,0 +1,124 @@ +#ifndef ED25519_H__ +#define ED25519_H__ + +#include +#include + +namespace i2p +{ +namespace crypto +{ + struct EDDSAPoint + { + BIGNUM * x {nullptr}; + BIGNUM * y {nullptr}; + BIGNUM * z {nullptr}; + BIGNUM * t {nullptr}; // projective coordinates + + EDDSAPoint () {} + EDDSAPoint (const EDDSAPoint& other) { *this = other; } + EDDSAPoint (EDDSAPoint&& other) { *this = std::move (other); } + EDDSAPoint (BIGNUM * x1, BIGNUM * y1, BIGNUM * z1 = nullptr, BIGNUM * t1 = nullptr) + : x(x1) + , y(y1) + , z(z1) + , t(t1) + {} + ~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); } + + EDDSAPoint& operator=(EDDSAPoint&& other) + { + if (this != &other) + { + BN_free (x); x = other.x; other.x = nullptr; + BN_free (y); y = other.y; other.y = nullptr; + BN_free (z); z = other.z; other.z = nullptr; + BN_free (t); t = other.t; other.t = nullptr; + } + return *this; + } + + EDDSAPoint& operator=(const EDDSAPoint& other) + { + if (this != &other) + { + BN_free (x); x = other.x ? BN_dup (other.x) : nullptr; + BN_free (y); y = other.y ? BN_dup (other.y) : nullptr; + BN_free (z); z = other.z ? BN_dup (other.z) : nullptr; + BN_free (t); t = other.t ? BN_dup (other.t) : nullptr; + } + return *this; + } + + EDDSAPoint operator-() const + { + BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL; + if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); }; + if (y) y1 = BN_dup (y); + if (z) z1 = BN_dup (z); + if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); }; + return EDDSAPoint {x1, y1, z1, t1}; + } + }; + + const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; + const size_t EDDSA25519_SIGNATURE_LENGTH = 64; + const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; + class Ed25519 + { + public: + + Ed25519 (); + Ed25519 (const Ed25519& other); + ~Ed25519 (); + + EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const; + EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const; + void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const; + void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519 + void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; + + bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const; + void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; + + static void ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey); // key - 32 bytes, expandedKey - 64 bytes + + private: + + EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const; + void Double (EDDSAPoint& p, BN_CTX * ctx) const; + EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const; + EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const; // B*e, e is 32 bytes Little Endian + EDDSAPoint Normalize (const EDDSAPoint& p, BN_CTX * ctx) const; + + bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const; + BIGNUM * RecoverX (const BIGNUM * y, BN_CTX * ctx) const; + EDDSAPoint DecodePoint (const uint8_t * buf, BN_CTX * ctx) const; + void EncodePoint (const EDDSAPoint& p, uint8_t * buf) const; + + template + BIGNUM * DecodeBN (const uint8_t * buf) const; + void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const; + + // for x25519 + BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const; + + private: + + BIGNUM * q, * l, * d, * I; + // transient values + BIGNUM * two_252_2; // 2^252-2 + EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes + // if j > 128 we use 256 - j and carry 1 to next byte + // Bi256[0][0] = B, base point + EDDSAPoint Bi256Carry; // Bi256[32][0] + }; + + std::unique_ptr& GetEd25519 (); + +} +} + + +#endif + diff --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index 41d7903b..985e1c22 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -96,7 +96,7 @@ namespace http { pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */ if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) { std::size_t delim = url.find(':', pos_p); - if (delim != std::string::npos && delim < pos_c) { + if (delim && delim != std::string::npos && delim < pos_c) { user = url.substr(pos_p, delim - pos_p); delim += 1; pass = url.substr(delim, pos_c - delim); diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index f394d284..deaa2292 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -26,6 +26,9 @@ namespace i2p const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1; const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; + // I2NP NTCP2 header + const size_t I2NP_NTCP2_HEADER_SIZE = I2NP_HEADER_EXPIRATION_OFFSET + 4; + // Tunnel Gateway header const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; @@ -194,6 +197,24 @@ namespace tunnel len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET); return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET); } + // for NTCP2 only + uint8_t * GetNTCP2Header () { return GetPayload () - I2NP_NTCP2_HEADER_SIZE; }; + size_t GetNTCP2Length () const { return GetPayloadLength () + I2NP_NTCP2_HEADER_SIZE; }; + void FromNTCP2 () + { + const uint8_t * ntcp2 = GetNTCP2Header (); + memcpy (GetHeader () + I2NP_HEADER_TYPEID_OFFSET, ntcp2 + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid + SetExpiration (bufbe32toh (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET)*1000LL); + SetSize (len - offset - I2NP_HEADER_SIZE); + SetChks (0); + } + + void ToNTCP2 () + { + uint8_t * ntcp2 = GetNTCP2Header (); + htobe32buf (ntcp2 + I2NP_HEADER_EXPIRATION_OFFSET, bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL); + memcpy (ntcp2 + I2NP_HEADER_TYPEID_OFFSET, GetHeader () + I2NP_HEADER_TYPEID_OFFSET, 5); // typeid + msgid + } void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0); void RenewI2NPMessageHeader (); diff --git a/libi2pd/I2PEndian.h b/libi2pd/I2PEndian.h index d2250768..0798f688 100644 --- a/libi2pd/I2PEndian.h +++ b/libi2pd/I2PEndian.h @@ -113,7 +113,20 @@ inline void htobe64buf(void *buf, uint64_t big64) htobuf64(buf, htobe64(big64)); } +inline void htole16buf(void *buf, uint16_t big16) +{ + htobuf16(buf, htole16(big16)); +} +inline void htole32buf(void *buf, uint32_t big32) +{ + htobuf32(buf, htole32(big32)); +} + +inline void htole64buf(void *buf, uint64_t big64) +{ + htobuf64(buf, htole64(big64)); +} #endif // I2PENDIAN_H__ diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index 3fcd16ad..7f64d931 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -719,24 +719,29 @@ namespace data XORMetric operator^(const IdentHash& key1, const IdentHash& key2) { XORMetric m; -#if defined(__AVX__) // for AVX - __asm__ - ( - "vmovups %1, %%ymm0 \n" - "vmovups %2, %%ymm1 \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, %0 \n" - : "=m"(*m.metric) - : "m"(*key1), "m"(*key2) - : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler - ); -#else - const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); - m.metric_ll[0] = hash1[0] ^ hash2[0]; - m.metric_ll[1] = hash1[1] ^ hash2[1]; - m.metric_ll[2] = hash1[2] ^ hash2[2]; - m.metric_ll[3] = hash1[3] ^ hash2[3]; +#ifdef __AVX__ + if(i2p::cpu::avx) + { + __asm__ + ( + "vmovups %1, %%ymm0 \n" + "vmovups %2, %%ymm1 \n" + "vxorps %%ymm0, %%ymm1, %%ymm1 \n" + "vmovups %%ymm1, %0 \n" + : "=m"(*m.metric) + : "m"(*key1), "m"(*key2) + : "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler + ); + } + else #endif + { + const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); + m.metric_ll[0] = hash1[0] ^ hash2[0]; + m.metric_ll[1] = hash1[1] ^ hash2[1]; + m.metric_ll[2] = hash1[2] ^ hash2[2]; + m.metric_ll[3] = hash1[3] ^ hash2[3]; + } return m; } diff --git a/libi2pd/Log.cpp b/libi2pd/Log.cpp index b664a5d9..79b4a511 100644 --- a/libi2pd/Log.cpp +++ b/libi2pd/Log.cpp @@ -8,6 +8,9 @@ #include "Log.h" +//for std::transform +#include + namespace i2p { namespace log { static Log logger; @@ -107,7 +110,18 @@ namespace log { } } - void Log::SetLogLevel (const std::string& level) { + std::string str_tolower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + // static_cast(std::tolower) // wrong + // [](int c){ return std::tolower(c); } // wrong + // [](char c){ return std::tolower(c); } // wrong + [](unsigned char c){ return std::tolower(c); } // correct + ); + return s; + } + + void Log::SetLogLevel (const std::string& level_) { + std::string level=str_tolower(level_); if (level == "none") { m_MinLevel = eLogNone; } else if (level == "error") { m_MinLevel = eLogError; } else if (level == "warn") { m_MinLevel = eLogWarning; } diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp new file mode 100644 index 00000000..58f928b4 --- /dev/null +++ b/libi2pd/NTCP2.cpp @@ -0,0 +1,1279 @@ +/* +* Copyright (c) 2013-2018, 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 +#include +#include +#include +#include +#include "Log.h" +#include "I2PEndian.h" +#include "Crypto.h" +#include "Ed25519.h" +#include "Siphash.h" +#include "RouterContext.h" +#include "Transports.h" +#include "NetDb.hpp" +#include "NTCP2.h" + +namespace i2p +{ +namespace transport +{ + NTCP2Establisher::NTCP2Establisher (): + m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) + { + m_Ctx = BN_CTX_new (); + CreateEphemeralKey (); + } + + NTCP2Establisher::~NTCP2Establisher () + { + BN_CTX_free (m_Ctx); + delete[] m_SessionRequestBuffer; + delete[] m_SessionCreatedBuffer; + delete[] m_SessionConfirmedBuffer; + } + + void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived) + { + // 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, derived, &len); + } + + void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * 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 + }; // 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 + }; // SHA256 (protocolNameHash) + 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_Final (m_H, &ctx); + // h = SHA256(h || epub) + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, epub, 32); + SHA256_Final (m_H, &ctx); + // x25519 between rs and priv + uint8_t inputKeyMaterial[32]; + i2p::crypto::GetEd25519 ()->ScalarMul (pub, priv, inputKeyMaterial, m_Ctx); // rs*priv + MixKey (inputKeyMaterial, m_K); + } + + void NTCP2Establisher::KDF1Alice () + { + KeyDerivationFunction1 (m_RemoteStaticKey, GetPriv (), m_RemoteStaticKey, GetPub ()); + } + + void NTCP2Establisher::KDF1Bob () + { + KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); + } + + void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) + { + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, sessionRequest + 32, 32); // encrypted payload + SHA256_Final (m_H, &ctx); + + int paddingLength = sessionRequestLen - 64; + if (paddingLength > 0) + { + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, sessionRequest + 64, paddingLength); + SHA256_Final (m_H, &ctx); + } + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, epub, 32); + SHA256_Final (m_H, &ctx); + + // x25519 between remote pub and priv + uint8_t inputKeyMaterial[32]; + i2p::crypto::GetEd25519 ()->ScalarMul (GetRemotePub (), GetPriv (), inputKeyMaterial, m_Ctx); + MixKey (inputKeyMaterial, m_K); + } + + void NTCP2Establisher::KDF2Alice () + { + KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ()); + } + + void NTCP2Establisher::KDF2Bob () + { + KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); + } + + void NTCP2Establisher::KDF3Alice () + { + uint8_t inputKeyMaterial[32]; + i2p::crypto::GetEd25519 ()->ScalarMul (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), inputKeyMaterial, m_Ctx); + MixKey (inputKeyMaterial, m_K); + } + + void NTCP2Establisher::KDF3Bob () + { + uint8_t inputKeyMaterial[32]; + i2p::crypto::GetEd25519 ()->ScalarMul (m_RemoteStaticKey, m_EphemeralPrivateKey, inputKeyMaterial, m_Ctx); + MixKey (inputKeyMaterial, m_K); + } + + void NTCP2Establisher::CreateEphemeralKey () + { + RAND_bytes (m_EphemeralPrivateKey, 32); + i2p::crypto::GetEd25519 ()->ScalarMulB (m_EphemeralPrivateKey, m_EphemeralPublicKey, m_Ctx); + } + + void NTCP2Establisher::CreateSessionRequestMessage () + { + // create buffer and fill padding + auto paddingLength = rand () % (287 - 64); // message length doesn't exceed 287 bytes + m_SessionRequestBufferLen = paddingLength + 64; + m_SessionRequestBuffer = new uint8_t[m_SessionRequestBufferLen]; + RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); + // encrypt X + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (m_RemoteIdentHash); + encryption.SetIV (m_IV); + encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X + 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[1] = 2; // ver + htobe16buf (options + 2, paddingLength); // padLen + m3p2Len = i2p::context.GetRouterInfo ().GetBufferLen () + 20; // (RI header + RI + MAC for now) TODO: implement options + htobe16buf (options + 4, m3p2Len); + // 2 bytes reserved + htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA + // 4 bytes reserved + // 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 + } + + void NTCP2Establisher::CreateSessionCreatedMessage () + { + auto paddingLen = rand () % (287 - 64); + m_SessionCreatedBufferLen = paddingLen + 64; + m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen]; + RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); + // encrypt Y + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (i2p::context.GetIdentHash ()); + encryption.SetIV (m_IV); + encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y + // encryption key for next block (m_K) + 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 + 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 + + } + + void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce) + { + // update AD + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionCreatedBuffer + 32, 32); // encrypted payload + SHA256_Final (m_H, &ctx); + + int paddingLength = m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + { + SHA256_CTX ctx1; + SHA256_Init (&ctx1); + SHA256_Update (&ctx1, m_H, 32); + SHA256_Update (&ctx1, m_SessionCreatedBuffer + 64, paddingLength); + SHA256_Final (m_H, &ctx1); + } + // part1 48 bytes + m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; + i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, m_H, 32, m_K, nonce, m_SessionConfirmedBuffer, 48, true); // encrypt + } + + void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce) + { + // part 2 + // update AD again + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionConfirmedBuffer, 48); + SHA256_Final (m_H, &ctx); + // fill and encrypt + uint8_t * buf = m_SessionConfirmedBuffer + 48; + buf[0] = eNTCP2BlkRouterInfo; // block + htobe16buf (buf + 1, i2p::context.GetRouterInfo ().GetBufferLen () + 1); // flag + RI + buf[3] = 0; // flag + memcpy (buf + 4, i2p::context.GetRouterInfo ().GetBuffer (), i2p::context.GetRouterInfo ().GetBufferLen ()); + KDF3Alice (); + i2p::crypto::AEADChaCha20Poly1305 (buf, m3p2Len - 16, m_H, 32, m_K, nonce, buf, m3p2Len, true); // encrypt + // update h again + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, buf, m3p2Len); + SHA256_Final (m_H, &ctx); //h = SHA256(h || ciphertext) + } + + bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen) + { + // decrypt X + i2p::crypto::CBCDecryption decryption; + 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 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 + { + // options + if (options[1] == 2) // ver is always 2 + { + paddingLen = bufbe16toh (options + 2); + m_SessionRequestBufferLen = paddingLen + 64; + m3p2Len = bufbe16toh (options + 4); + if (m3p2Len < 16) + { + LogPrint (eLogWarning, "NTCP2: SessionRequest m3p2len=", m3p2Len, " is too short"); + return false; + } + // check timestamp + auto ts = i2p::util::GetSecondsSinceEpoch (); + 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"); + return false; + } + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", (int)options[1]); + return false; + } + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); + return false; + } + return true; + } + + bool NTCP2Establisher::ProcessSessionCreatedMessage (uint16_t& paddingLen) + { + m_SessionCreatedBufferLen = 64; + // decrypt Y + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (m_RemoteIdentHash); + decryption.SetIV (m_IV); + decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ()); + // decryption key for next block (m_K) + KDF2Alice (); + // decrypt and verify MAC + 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 + { + // options + paddingLen = bufbe16toh(payload + 2); + // check timestamp + auto ts = i2p::util::GetSecondsSinceEpoch (); + 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"); + return false; + } + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); + return false; + } + return true; + } + + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce) + { + // update AD + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionCreatedBuffer + 32, 32); // encrypted payload + SHA256_Final (m_H, &ctx); + + int paddingLength = m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + { + SHA256_CTX ctx1; + SHA256_Init (&ctx1); + SHA256_Update (&ctx1, m_H, 32); + SHA256_Update (&ctx1, m_SessionCreatedBuffer + 64, paddingLength); + SHA256_Final (m_H, &ctx1); + } + if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, m_H, 32, m_K, nonce, m_RemoteStaticKey, 32, false)) // decrypt S + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); + return false; + } + return true; + } + + bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf) + { + // update AD again + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, m_SessionConfirmedBuffer, 48); + SHA256_Final (m_H, &ctx); + + KDF3Bob (); + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt + { + // caclulate new h again for KDF data + memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext + SHA256 (m_SessionConfirmedBuffer + 16, m3p2Len + 32, m_H); //h = SHA256(h || ciphertext); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); + return false; + } + return true; + } + + NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter): + TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), + m_Server (server), m_Socket (m_Server.GetService ()), + m_IsEstablished (false), m_IsTerminated (false), + m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), + m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false) + { + m_Establisher.reset (new NTCP2Establisher); + if (in_RemoteRouter) // Alice + { + m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash (); + auto addr = in_RemoteRouter->GetNTCP2Address (true); // we need a published address + if (addr) + { + memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32); + memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16); + } + else + LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); + } + } + + NTCP2Session::~NTCP2Session () + { + delete[] m_NextReceivedBuffer; + delete[] m_NextSendBuffer; + } + + void NTCP2Session::Terminate () + { + if (!m_IsTerminated) + { + m_IsTerminated = true; + m_IsEstablished = false; + m_Socket.close (); + transports.PeerDisconnected (shared_from_this ()); + m_Server.RemoveNTCP2Session (shared_from_this ()); + m_SendQueue.clear (); + LogPrint (eLogDebug, "NTCP2: session terminated"); + } + } + + void NTCP2Session::TerminateByTimeout () + { + SendTerminationAndTerminate (eNTCP2IdleTimeout); + } + + void NTCP2Session::Done () + { + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); + } + + void NTCP2Session::Established () + { + m_IsEstablished = true; + m_Establisher.reset (nullptr); + SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT); + transports.PeerConnected (shared_from_this ()); + } + + void NTCP2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + + + void NTCP2Session::KeyDerivationFunctionDataPhase () + { + uint8_t tempKey[32]; unsigned int len; + HMAC(EVP_sha256(), m_Establisher->GetCK (), 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(ck, zerolen) + static uint8_t one[1] = { 1 }; + HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Kab, &len); // k_ab = HMAC-SHA256(temp_key, byte(0x01)). + m_Kab[32] = 2; + HMAC(EVP_sha256(), tempKey, 32, m_Kab, 33, m_Kba, &len); // k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02)) + static uint8_t ask[4] = { 'a', 's', 'k', 1 }, master[32]; + HMAC(EVP_sha256(), tempKey, 32, ask, 4, master, &len); // ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01)) + uint8_t h[39]; + memcpy (h, m_Establisher->GetH (), 32); + memcpy (h + 32, "siphash", 7); + HMAC(EVP_sha256(), master, 32, h, 39, tempKey, &len); // temp_key = HMAC-SHA256(ask_master, h || "siphash") + HMAC(EVP_sha256(), tempKey, 32, one, 1, master, &len); // sip_master = HMAC-SHA256(temp_key, byte(0x01)) + HMAC(EVP_sha256(), master, 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(sip_master, zerolen) + HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Sipkeysab, &len); // sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)). + m_Sipkeysab[32] = 2; + HMAC(EVP_sha256(), tempKey, 32, m_Sipkeysab, 33, m_Sipkeysba, &len); // sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02)) + } + + + void NTCP2Session::SendSessionRequest () + { + 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)); + } + + void NTCP2Session::HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + (void) bytes_transferred; + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: couldn't send SessionRequest message: ", ecode.message ()); + Terminate (); + } + else + { + m_Establisher->m_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size + // we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, 64), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionCreatedReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + } + + void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + (void) bytes_transferred; + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred); + uint16_t paddingLen = 0; + if (m_Establisher->ProcessSessionRequestMessage (paddingLen)) + { + if (paddingLen > 0) + { + if (paddingLen <= 287 - 64) // session request is 287 bytes max + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); + Terminate (); + } + } + else + SendSessionCreated (); + } + else + Terminate (); + } + } + + void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionRequest padding read error: ", ecode.message ()); + Terminate (); + } + else + SendSessionCreated (); + } + + void NTCP2Session::SendSessionCreated () + { + m_Establisher->CreateSessionCreatedMessage (); + // 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)); + } + + void NTCP2Session::HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionCreated read error: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred); + uint16_t paddingLen = 0; + if (m_Establisher->ProcessSessionCreatedMessage (paddingLen)) + { + if (paddingLen > 0) + { + if (paddingLen <= 287 - 64) // session created is 287 bytes max + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); + Terminate (); + } + } + else + SendSessionConfirmed (); + } + else + Terminate (); + } + } + + void NTCP2Session::HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionCreated padding read error: ", ecode.message ()); + Terminate (); + } + else + { + m_Establisher->m_SessionCreatedBufferLen += bytes_transferred; + SendSessionConfirmed (); + } + } + + void NTCP2Session::SendSessionConfirmed () + { + uint8_t nonce[12]; + CreateNonce (1, nonce); // set nonce to 1 + m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); + memset (nonce, 0, 12); // set nonce back to 0 + 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)); + } + + void NTCP2Session::HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + LogPrint (eLogDebug, "NTCP2: SessionConfirmed sent"); + KeyDerivationFunctionDataPhase (); + // Alice data phase keys + m_SendKey = m_Kab; + m_ReceiveKey = m_Kba; + m_SendSipKey = m_Sipkeysab; + m_ReceiveSipKey = m_Sipkeysba; + memcpy (m_ReceiveIV.buf, m_Sipkeysba + 16, 8); + memcpy (m_SendIV.buf, m_Sipkeysab + 16, 8); + Established (); + ReceiveLength (); + + // TODO: remove + // m_SendQueue.push_back (CreateDeliveryStatusMsg (1)); + // SendQueue (); + } + + void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + (void) bytes_transferred; + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: couldn't send SessionCreated message: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); + 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)); + } + } + + void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: SessionConfirmed received"); + // part 1 + uint8_t nonce[12]; + CreateNonce (1, nonce); + if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce)) + { + // part 2 + std::vector buf(m_Establisher->m3p2Len - 16); // -MAC + memset (nonce, 0, 12); // set nonce to 0 again + if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ())) + { + KeyDerivationFunctionDataPhase (); + // Bob data phase keys + m_SendKey = m_Kba; + m_ReceiveKey = m_Kab; + m_SendSipKey = m_Sipkeysba; + m_ReceiveSipKey = m_Sipkeysab; + memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); + memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); + // payload + // process RI + if (buf[0] != eNTCP2BlkRouterInfo) + { + LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed"); + Terminate (); + return; + } + auto size = bufbe16toh (buf.data () + 1); + if (size > buf.size () - 3) + { + LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed"); + Terminate (); + return; + } + // TODO: check flag + 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); + return; + } + auto addr = ri.GetNTCP2Address (false); // any NTCP2 address + if (!addr) + { + 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 mistmatch in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2IncorrectSParameter); + return; + } + + i2p::data::netdb.AddRouterInfo (buf.data () + 4, size - 1); // TODO: should insert ri and not parse it twice + // TODO: process options + + // 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 ()); + Established (); + ReceiveLength (); + } + else + Terminate (); + } + else + Terminate (); + } + } + + void NTCP2Session::ClientLogin () + { + SendSessionRequest (); + } + + void NTCP2Session::ServerLogin () + { + m_Establisher->m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now + boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void NTCP2Session::ReceiveLength () + { + if (IsTerminated ()) return; + boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void NTCP2Session::HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogWarning, "NTCP2: receive length read error: ", ecode.message ()); + Terminate (); + } + else + { + i2p::crypto::Siphash<8> (m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey); + // m_NextReceivedLen comes from the network in BigEndian + 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]; + Receive (); + } + else + { + LogPrint (eLogError, "NTCP2: received length ", m_NextReceivedLen, " is too short"); + Terminate (); + } + } + } + + void NTCP2Session::Receive () + { + if (IsTerminated ()) return; + boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void NTCP2Session::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogWarning, "NTCP2: receive read error: ", ecode.message ()); + Terminate (); + } + else + { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumReceivedBytes += bytes_transferred + 2; // + length + i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); + 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 + ReceiveLength (); + } + else + { + LogPrint (eLogWarning, "NTCP2: Received AEAD verification failed "); + SendTerminationAndTerminate (eNTCP2DataPhaseAEADFailure); + } + } + } + + void NTCP2Session::ProcessNextFrame (const uint8_t * frame, size_t len) + { + size_t offset = 0; + while (offset < len) + { + uint8_t blk = frame[offset]; + offset++; + auto size = bufbe16toh (frame + offset); + offset += 2; + LogPrint (eLogDebug, "NTCP2: Block type ", (int)blk, " of size ", size); + if (size > len) + { + LogPrint (eLogError, "NTCP2: Unexpected block length ", size); + break; + } + switch (blk) + { + case eNTCP2BlkDateTime: + LogPrint (eLogDebug, "NTCP2: datetime"); + break; + case eNTCP2BlkOptions: + LogPrint (eLogDebug, "NTCP2: options"); + break; + case eNTCP2BlkRouterInfo: + { + LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]); + i2p::data::netdb.AddRouterInfo (frame + offset + 1, size - 1); + break; + } + case eNTCP2BlkI2NPMessage: + { + LogPrint (eLogDebug, "NTCP2: I2NP"); + auto nextMsg = NewI2NPMessage (size); + 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); + break; + } + case eNTCP2BlkTermination: + if (size >= 9) + { + LogPrint (eLogDebug, "NTCP2: termination. reason=", (int)(frame[offset + 8])); + Terminate (); + } + else + LogPrint (eLogWarning, "NTCP2: Unexpected temination block size ", size); + break; + case eNTCP2BlkPadding: + LogPrint (eLogDebug, "NTCP2: padding"); + break; + default: + LogPrint (eLogWarning, "NTCP2: Unknown block type ", (int)blk); + } + offset += size; + } + m_Handler.Flush (); + } + + void NTCP2Session::SendNextFrame (const uint8_t * payload, size_t len) + { + if (IsTerminated ()) return; + uint8_t nonce[12]; + CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; + m_NextSendBuffer = new uint8_t[len + 16 + 2]; + i2p::crypto::AEADChaCha20Poly1305 (payload, len, nullptr, 0, m_SendKey, nonce, m_NextSendBuffer + 2, len + 16, true); + i2p::crypto::Siphash<8> (m_SendIV.buf, m_SendIV.buf, 8, m_SendSipKey); + // length must be in BigEndian + htobe16buf (m_NextSendBuffer, (len + 16) ^ le16toh (m_SendIV.key)); + LogPrint (eLogDebug, "NTCP2: sent length ", len + 16); + + // send message + m_IsSending = true; + boost::asio::async_write (m_Socket, boost::asio::buffer (m_NextSendBuffer, len + 16 + 2), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleNextFrameSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void NTCP2Session::HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + m_IsSending = false; + delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; + + if (ecode) + { + LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ()); + } + else + { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumSentBytes += bytes_transferred; + i2p::transport::transports.UpdateSentBytes (bytes_transferred); + LogPrint (eLogDebug, "NTCP2: Next frame sent"); + SendQueue (); + } + } + + void NTCP2Session::SendQueue () + { + if (!m_SendQueue.empty ()) + { + auto buf = m_Server.NewNTCP2FrameBuffer (); + uint8_t * payload = buf->data (); + size_t s = 0; + // add I2NP blocks + while (!m_SendQueue.empty ()) + { + auto msg = m_SendQueue.front (); + size_t len = msg->GetNTCP2Length (); + if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header + { + payload[s] = eNTCP2BlkI2NPMessage; // blk + htobe16buf (payload + s + 1, len); // size + s += 3; + msg->ToNTCP2 (); + memcpy (payload + s, msg->GetNTCP2Header (), len); + s += len; + m_SendQueue.pop_front (); + } + else + break; + } + // add padding block + int paddingSize = (s*NTCP2_MAX_PADDING_RATIO)/100; + if (s + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - s -3; + if (paddingSize) paddingSize = rand () % paddingSize; + payload[s] = eNTCP2BlkPadding; // blk + htobe16buf (payload + s + 1, paddingSize); // size + s += 3; + memset (payload + s, 0, paddingSize); + s += paddingSize; + // send + SendNextFrame (payload, s); + m_Server.DeleteNTCP2FrameBuffer (buf); + } + } + + void NTCP2Session::SendRouterInfo () + { + if (!IsEstablished ()) return; + auto riLen = i2p::context.GetRouterInfo ().GetBufferLen (); + int paddingSize = (riLen*NTCP2_MAX_PADDING_RATIO)/100; + size_t payloadLen = riLen + paddingSize + 7; // 7 = 2*3 bytes header + 1 byte RI flag + uint8_t * payload = new uint8_t[payloadLen]; + payload[0] = eNTCP2BlkRouterInfo; + htobe16buf (payload + 1, riLen + 1); // size + payload[3] = 0; // flag + memcpy (payload + 4, i2p::context.GetRouterInfo ().GetBuffer (), riLen); + payload[riLen + 4] = eNTCP2BlkPadding; + htobe16buf (payload + riLen + 5, paddingSize); + RAND_bytes (payload + riLen + 7, paddingSize); + SendNextFrame (payload, payloadLen); + delete[] payload; + } + + void NTCP2Session::SendTermination (NTCP2TerminationReason reason) + { + if (!IsEstablished ()) return; + uint8_t payload[12] = { eNTCP2BlkTermination, 0, 9 }; + htobe64buf (payload + 3, m_ReceiveSequenceNumber); + payload[11] = (uint8_t)reason; + SendNextFrame (payload, 12); + } + + void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason reason) + { + SendTermination (reason); + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go + } + + void NTCP2Session::SendI2NPMessages (const std::vector >& msgs) + { + m_Server.GetService ().post (std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this (), msgs)); + } + + void NTCP2Session::PostI2NPMessages (std::vector > msgs) + { + if (m_IsTerminated) return; + for (auto it: msgs) + m_SendQueue.push_back (it); + if (!m_IsSending) + SendQueue (); + } + + void NTCP2Session::SendLocalRouterInfo () + { + if (!IsOutgoing ()) // we send it in SessionConfirmed + m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ())); + } + + NTCP2Server::NTCP2Server (): + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), + m_TerminationTimer (m_Service) + { + } + + NTCP2Server::~NTCP2Server () + { + Stop (); + } + + void NTCP2Server::Start () + { + if (!m_IsRunning) + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&NTCP2Server::Run, this)); + auto& addresses = context.GetRouterInfo ().GetAddresses (); + for (const auto& address: addresses) + { + if (!address) continue; + if (address->IsPublishedNTCP2 ()) + { + if (address->host.is_v4()) + { + try + { + 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; + } + + 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 + { + m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); + m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (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, "NTCP: failed to bind to ip6 port ", address->port); + continue; + } + } + } + } + ScheduleTermination (); + } + } + + void NTCP2Server::Stop () + { + { + // we have to copy it because Terminate changes m_NTCP2Sessions + auto ntcpSessions = m_NTCP2Sessions; + for (auto& it: ntcpSessions) + it.second->Terminate (); + for (auto& it: m_PendingIncomingSessions) + it->Terminate (); + } + m_NTCP2Sessions.clear (); + + if (m_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 ()); + } + } + } + + bool NTCP2Server::AddNTCP2Session (std::shared_ptr session) + { + if (!session || !session->GetRemoteIdentity ()) return false; + auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); + auto it = m_NTCP2Sessions.find (ident); + if (it != m_NTCP2Sessions.end ()) + { + LogPrint (eLogWarning, "NTCP2: session to ", ident.ToBase64 (), " already exists"); + session->Terminate(); + return false; + } + m_NTCP2Sessions.insert (std::make_pair (ident, session)); + return true; + } + + void NTCP2Server::RemoveNTCP2Session (std::shared_ptr session) + { + if (session && session->GetRemoteIdentity ()) + m_NTCP2Sessions.erase (session->GetRemoteIdentity ()->GetIdentHash ()); + } + + std::shared_ptr NTCP2Server::FindNTCP2Session (const i2p::data::IdentHash& ident) + { + auto it = m_NTCP2Sessions.find (ident); + if (it != m_NTCP2Sessions.end ()) + return it->second; + return nullptr; + } + + 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]() + { + if (this->AddNTCP2Session (conn)) + { + auto timer = std::make_shared(m_Service); + 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) + { + 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 (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer)); + } + }); + } + + void NTCP2Server::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer) + { + timer->cancel (); + if (ecode) + { + LogPrint (eLogInfo, "NTCP2: Connect error ", ecode.message ()); + conn->Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetSocket ().remote_endpoint ()); + conn->ClientLogin (); + } + } + + void NTCP2Server::HandleAccept (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + boost::system::error_code ec; + auto ep = conn->GetSocket ().remote_endpoint(ec); + if (!ec) + { + LogPrint (eLogDebug, "NTCP2: Connected from ", ep); + if (conn) + { + conn->ServerLogin (); + m_PendingIncomingSessions.push_back (conn); + } + } + else + LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); + } + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (*this); + m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, + conn, std::placeholders::_1)); + } + } + + void NTCP2Server::HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + boost::system::error_code ec; + auto ep = conn->GetSocket ().remote_endpoint(ec); + if (!ec) + { + LogPrint (eLogDebug, "NTCP2: Connected from ", ep); + if (conn) + { + conn->ServerLogin (); + m_PendingIncomingSessions.push_back (conn); + } + } + else + LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); + } + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, + conn, std::placeholders::_1)); + } + } + + void NTCP2Server::ScheduleTermination () + { + m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP2_TERMINATION_CHECK_TIMEOUT)); + m_TerminationTimer.async_wait (std::bind (&NTCP2Server::HandleTerminationTimer, + this, std::placeholders::_1)); + } + + void NTCP2Server::HandleTerminationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + // established + for (auto& it: m_NTCP2Sessions) + if (it.second->IsTerminationTimeoutExpired (ts)) + { + auto session = it.second; + LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds"); + session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way + } + // pending + for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) + { + if ((*it)->IsEstablished () || (*it)->IsTerminated ()) + it = m_PendingIncomingSessions.erase (it); // established or terminated + else if ((*it)->IsTerminationTimeoutExpired (ts)) + { + (*it)->Terminate (); + it = m_PendingIncomingSessions.erase (it); // expired + } + else + it++; + } + + ScheduleTermination (); + } + } +} +} + diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h new file mode 100644 index 00000000..2b6748ab --- /dev/null +++ b/libi2pd/NTCP2.h @@ -0,0 +1,259 @@ +/* +* Copyright (c) 2013-2018, 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__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "RouterInfo.h" +#include "TransportSession.h" + +namespace i2p +{ +namespace transport +{ + + 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 + const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds + 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 + + enum NTCP2BlockType + { + eNTCP2BlkDateTime = 0, + eNTCP2BlkOptions, // 1 + eNTCP2BlkRouterInfo, // 2 + eNTCP2BlkI2NPMessage, // 3 + eNTCP2BlkTermination, // 4 + eNTCP2BlkPadding = 254 + }; + + enum NTCP2TerminationReason + { + eNTCP2NormalClose = 0, + eNTCP2TerminationReceived, // 1 + eNTCP2IdleTimeout, // 2 + eNTCP2RouterShutdown, // 3 + eNTCP2DataPhaseAEADFailure, // 4 + eNTCP2IncompatibleOptions, // 5 + eNTCP2IncompatibleSignatureType, // 6 + eNTCP2ClockSkew, // 7 + eNTCP2PaddingViolation, // 8 + eNTCP2AEADFramingError, // 9 + eNTCP2PayloadFormatError, // 10 + eNTCP2Message1Error, // 11 + eNTCP2Message2Error, // 12 + eNTCP2Message3Error, // 13 + eNTCP2IntraFrameReadTimeout, // 14 + eNTCP2RouterInfoSignatureVerificationFail, // 15 + eNTCP2IncorrectSParameter, // 16 + eNTCP2Banned, // 17 + }; + + + typedef std::array NTCP2FrameBuffer; + struct NTCP2Establisher + { + NTCP2Establisher (); + ~NTCP2Establisher (); + + const uint8_t * GetPub () const { return m_EphemeralPublicKey; }; + const uint8_t * GetPriv () const { return m_EphemeralPrivateKey; }; + 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 * GetCK () const { return m_CK; }; + const uint8_t * GetH () const { return m_H; }; + + void KDF1Alice (); + void KDF1Bob (); + void KDF2Alice (); + void KDF2Bob (); + void KDF3Alice (); // for SessionConfirmed part 2 + void KDF3Bob (); + + void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived); + void KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH + void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate + void CreateEphemeralKey (); + + void CreateSessionRequestMessage (); + void CreateSessionCreatedMessage (); + void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce); + void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce); + + bool ProcessSessionRequestMessage (uint16_t& paddingLen); + bool ProcessSessionCreatedMessage (uint16_t& paddingLen); + bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce); + bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf); + + BN_CTX * m_Ctx; + uint8_t m_EphemeralPrivateKey[32], m_EphemeralPublicKey[32], m_RemoteEphemeralPublicKey[32]; // x25519 + uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/; + i2p::data::IdentHash m_RemoteIdentHash; + 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 (); + void Terminate (); + void TerminateByTimeout (); + void Done (); + + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + + bool IsEstablished () const { return m_IsEstablished; }; + bool IsTerminated () const { return m_IsTerminated; }; + + void ClientLogin (); // Alice + void ServerLogin (); // Bob + + void SendLocalRouterInfo (); // after handshake + void SendI2NPMessages (const std::vector >& msgs); + + private: + + void Established (); + + void CreateNonce (uint64_t seqn, uint8_t * nonce); + void KeyDerivationFunctionDataPhase (); + + // establish + void SendSessionRequest (); + void SendSessionCreated (); + void SendSessionConfirmed (); + + void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + + // data + void ReceiveLength (); + void HandleReceivedLength (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void ProcessNextFrame (const uint8_t * frame, size_t len); + + void SendNextFrame (const uint8_t * payload, size_t len); + void HandleNextFrameSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendQueue (); + void SendRouterInfo (); + void SendTermination (NTCP2TerminationReason reason); + void SendTerminationAndTerminate (NTCP2TerminationReason reason); + void PostI2NPMessages (std::vector > msgs); + + private: + + NTCP2Server& m_Server; + boost::asio::ip::tcp::socket m_Socket; + bool m_IsEstablished, m_IsTerminated; + + std::unique_ptr m_Establisher; + // data phase + uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], m_Sipkeysba[32]; + const uint8_t * m_SendKey, * m_ReceiveKey, * m_SendSipKey, * m_ReceiveSipKey; + uint16_t m_NextReceivedLen; + uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; + union + { + uint8_t buf[8]; + uint16_t key; + } m_ReceiveIV, m_SendIV; + uint64_t m_ReceiveSequenceNumber, m_SendSequenceNumber; + + i2p::I2NPMessagesHandler m_Handler; + + bool m_IsSending; + std::list > m_SendQueue; + }; + + class NTCP2Server + { + public: + + NTCP2Server (); + ~NTCP2Server (); + + void Start (); + void Stop (); + + bool AddNTCP2Session (std::shared_ptr session); + void RemoveNTCP2Session (std::shared_ptr session); + std::shared_ptr FindNTCP2Session (const i2p::data::IdentHash& ident); + + boost::asio::io_service& GetService () { return m_Service; }; + + void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn); + + NTCP2FrameBuffer * NewNTCP2FrameBuffer () { return m_NTCP2FrameBuffersPool.Acquire(); } + void DeleteNTCP2FrameBuffer (NTCP2FrameBuffer * buf) { return m_NTCP2FrameBuffersPool.Release(buf); } + + 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); + + // timer + void ScheduleTermination (); + void HandleTerminationTimer (const boost::system::error_code& ecode); + + 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::list > m_PendingIncomingSessions; + + i2p::util::MemoryPool m_NTCP2FrameBuffersPool; + + public: + + // for HTTP/I2PControl + const decltype(m_NTCP2Sessions)& GetNTCP2Sessions () const { return m_NTCP2Sessions; }; + }; +} +} + +#endif diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index a297ad6a..e9de08e2 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -402,7 +402,7 @@ namespace transport uint32_t tsA1 = be32toh (tsA); if (tsA1 < ts - NTCP_CLOCK_SKEW || tsA1 > ts + NTCP_CLOCK_SKEW) { - LogPrint (eLogError, "NTCP: Phase3 time difference ", ts - tsA1, " exceeds clock skew"); + LogPrint (eLogError, "NTCP: Phase3 time difference ", (int)(ts - tsA1), " exceeds clock skew"); Terminate (); return; } @@ -485,7 +485,7 @@ namespace transport auto ts = i2p::util::GetSecondsSinceEpoch (); if (tsB < ts - NTCP_CLOCK_SKEW || tsB > ts + NTCP_CLOCK_SKEW) { - LogPrint (eLogError, "NTCP: Phase4 time difference ", ts - tsB, " exceeds clock skew"); + LogPrint (eLogError, "NTCP: Phase4 time difference ", (int)(ts - tsB), " exceeds clock skew"); Terminate (); return; } @@ -819,7 +819,7 @@ namespace transport for (const auto& address: addresses) { if (!address) continue; - if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP) + if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !address->IsNTCP2 ()) { if (address->host.is_v4()) { diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index b136dfd5..410b71e6 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -734,7 +734,7 @@ namespace data m_Requests.RequestComplete (ident, nullptr); } else - // no more requests for detination possible. delete it + // no more requests for destination possible. delete it m_Requests.RequestComplete (ident, nullptr); } else if(!m_FloodfillBootstrap) diff --git a/libi2pd/Poly1305.cpp b/libi2pd/Poly1305.cpp new file mode 100644 index 00000000..9ee46640 --- /dev/null +++ b/libi2pd/Poly1305.cpp @@ -0,0 +1,251 @@ +#include "Poly1305.h" +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +namespace i2p +{ +namespace crypto +{ + namespace poly1305 + { + + struct LongBlock + { + unsigned long data[17]; + operator unsigned long * () + { + return data; + } + }; + + struct Block + { + unsigned char data[17]; + + operator uint8_t * () + { + return data; + } + + Block & operator += (const Block & other) + { + unsigned short u; + unsigned int i; + for(u = 0, i = 0; i < 17; i++) + { + u += (unsigned short) data[i] + (unsigned short) other.data[i]; + data[i] = (unsigned char) u & 0xff; + u >>= 8; + } + return *this; + } + + Block & operator %=(const LongBlock & other) + { + unsigned long u; + unsigned int i; + u = 0; + for (i = 0; i < 16; i++) { + u += other.data[i]; + data[i] = (unsigned char)u & 0xff; + u >>= 8; + } + u += other.data[16]; + data[16] = (unsigned char)u & 0x03; + u >>= 2; + u += (u << 2); + for (i = 0; i < 16; i++) { + u += data[i]; + data[i] = (unsigned char)u & 0xff; + u >>= 8; + } + data[16] += (unsigned char)u; + return *this; + } + + Block & operator = (const Block & other) + { + memcpy(data, other.data, sizeof(data)); + return *this; + } + + Block & operator ~ () + { + static const Block minusp = { + 0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfc + }; + Block orig; + unsigned char neg; + unsigned int i; + orig = *this; + *this += minusp; + neg = -(data[16] >> 7); + for(i = 0; i < 17; i++) + data[i] ^= neg & (orig.data[i] ^ data[i]); + + return *this; + } + + void PutKey(const uint8_t * key) + { + data[0] = key[0] & 0xff; + data[1] = key[1] & 0xff; + data[2] = key[2] & 0xff; + data[3] = key[3] & 0x0f; + data[4] = key[4] & 0xfc; + data[5] = key[5] & 0xff; + data[6] = key[6] & 0xff; + data[7] = key[7] & 0x0f; + data[8] = key[8] & 0xfc; + data[9] = key[9] & 0xff; + data[10] = key[10] & 0xff; + data[11] = key[11] & 0x0f; + data[12] = key[12] & 0xfc; + data[13] = key[13] & 0xff; + data[14] = key[14] & 0xff; + data[15] = key[15] & 0x0f; + data[16] = 0; + } + + void Put(const uint8_t * d, uint8_t last=0) + { + memcpy(data, d, 17); + data[16] = last; + } + }; + + struct Buffer + { + uint8_t data[POLY1305_BLOCK_BYTES]; + + operator uint8_t * () + { + return data; + } + }; + } + + struct Poly1305 + { +#if (__GNUC__ == 4) && (__GNUC_MINOR__ < 8) // older than gcc 4.8 + Poly1305(const uint8_t * key) : m_Leftover(0), m_Final(0) + { + memset (&m_H, 0, sizeof (m_H)); +#else + Poly1305(const uint8_t * key) : m_Leftover(0), m_H{0}, m_Final(0) + { +#endif + m_R.PutKey(key); + m_Pad.Put(key + 16); + } + + void Update(const uint8_t * buf, size_t sz) + { + // process leftover + if(m_Leftover) + { + size_t want = POLY1305_BLOCK_BYTES - m_Leftover; + if(want > sz) want = sz; + memcpy(m_Buffer + m_Leftover, buf, want); + sz -= want; + buf += want; + m_Leftover += want; + if(m_Leftover < POLY1305_BLOCK_BYTES) return; + Blocks(m_Buffer, POLY1305_BLOCK_BYTES); + m_Leftover = 0; + } + // process blocks + if(sz >= POLY1305_BLOCK_BYTES) + { + size_t want = (sz & ~(POLY1305_BLOCK_BYTES - 1)); + Blocks(buf, want); + buf += want; + sz -= want; + } + // leftover + if(sz) + { + memcpy(m_Buffer+m_Leftover, buf, sz); + m_Leftover += sz; + } + } + + void Blocks(const uint8_t * buf, size_t sz) + { + const unsigned char hi = m_Final ^ 1; + while (sz >= POLY1305_BLOCK_BYTES) { + + unsigned long u; + + unsigned int i, j; + m_Msg.Put(buf, hi); + /* h += m */ + m_H += m_Msg; + + /* h *= r */ + for (i = 0; i < 17; i++) { + u = 0; + for (j = 0; j <= i ; j++) { + u += (unsigned short)m_H.data[j] * m_R.data[i - j]; + } + for (j = i + 1; j < 17; j++) { + unsigned long v = (unsigned short)m_H.data[j] * m_R.data[i + 17 - j]; + v = ((v << 8) + (v << 6)); /* v *= (5 << 6); */ + u += v; + } + m_HR[i] = u; + } + /* (partial) h %= p */ + m_H %= m_HR; + buf += POLY1305_BLOCK_BYTES; + sz -= POLY1305_BLOCK_BYTES; + } + } + + void Finish(uint32_t *& out) + { + // process leftovers + if(m_Leftover) + { + size_t idx = m_Leftover; + m_Buffer[idx++] = 1; + for(; idx < POLY1305_BLOCK_BYTES; idx++) + m_Buffer[idx] = 0; + m_Final = 1; + Blocks(m_Buffer, POLY1305_BLOCK_BYTES); + } + + // freeze H + ~m_H; + // add pad + m_H += m_Pad; + // copy digest + memcpy(out, m_H, 16); + } + + size_t m_Leftover; + poly1305::Buffer m_Buffer; + poly1305::Block m_H; + poly1305::Block m_R; + poly1305::Block m_Pad; + poly1305::Block m_Msg; + poly1305::LongBlock m_HR; + uint8_t m_Final; + + }; + + void Poly1305HMAC(uint32_t * out, const uint32_t * key, const uint8_t * buf, std::size_t sz) + { + const uint8_t * k = (const uint8_t *) key; + Poly1305 p(k); + p.Update(buf, sz); + p.Finish(out); + } +} +} diff --git a/libi2pd/Poly1305.h b/libi2pd/Poly1305.h new file mode 100644 index 00000000..2c62c5aa --- /dev/null +++ b/libi2pd/Poly1305.h @@ -0,0 +1,28 @@ +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +#ifndef LIBI2PD_POLY1305_H +#define LIBI2PD_POLY1305_H +#include +#include + +namespace i2p +{ +namespace crypto +{ + const std::size_t POLY1305_DIGEST_BYTES = 16; + const std::size_t POLY1305_DIGEST_DWORDS = 4; + const std::size_t POLY1305_KEY_BYTES = 32; + const std::size_t POLY1305_KEY_DWORDS = 8; + const std::size_t POLY1305_BLOCK_BYTES = 16; + + void Poly1305HMAC(uint32_t * out, const uint32_t * key, const uint8_t * buf, std::size_t sz); + +} +} + +#endif diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 4a2f8055..eebe9b38 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -300,7 +300,7 @@ namespace data s.read (localFileName, fileNameLength); localFileName[fileNameLength] = 0; s.seekg (extraFieldLength, std::ios::cur); - // take care about data desriptor if presented + // take care about data descriptor if presented if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) { size_t pos = s.tellg (); diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index a82ace85..a3d33113 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -1,6 +1,8 @@ #include +#include #include "Config.h" #include "Crypto.h" +#include "Ed25519.h" #include "Timestamp.h" #include "I2NPProtocol.h" #include "NetDb.hpp" @@ -35,7 +37,7 @@ namespace i2p void RouterContext::CreateNewRouter () { m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - SaveKeys (); + SaveKeys (); NewRouterInfo (); } @@ -48,7 +50,10 @@ namespace i2p port = rand () % (30777 - 9111) + 9111; // I2P network ports range bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool nat; i2p::config::GetOption("nat", nat); + bool ssu; i2p::config::GetOption("ssu", ssu); + bool ntcp; i2p::config::GetOption("ntcp", ntcp); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool nat; i2p::config::GetOption("nat", nat); std::string ifname; i2p::config::GetOption("ifname", ifname); std::string ifname4; i2p::config::GetOption("ifname4", ifname4); std::string ifname6; i2p::config::GetOption("ifname6", ifname6); @@ -64,8 +69,10 @@ namespace i2p if(ifname4.size()) host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string(); - routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); - routerInfo.AddNTCPAddress (host.c_str(), port); + if (ssu) + routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); + if (ntcp) + routerInfo.AddNTCPAddress (host.c_str(), port); } if (ipv6) { @@ -78,8 +85,10 @@ namespace i2p if(ifname6.size()) host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string(); - routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); - routerInfo.AddNTCPAddress (host.c_str(), port); + if (ssu) + routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); + if (ntcp) + routerInfo.AddNTCPAddress (host.c_str(), port); } routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | @@ -89,6 +98,13 @@ namespace i2p routerInfo.CreateBuffer (m_Keys); m_RouterInfo.SetRouterIdentity (GetIdentity ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); + + if (ntcp2) // we don't store iv in the address if non published so we must update it from keys + { + if (!m_NTCP2Keys) NewNTCP2Keys (); + UpdateNTCP2Address (true); + } + } void RouterContext::UpdateRouterInfo () @@ -98,6 +114,19 @@ namespace i2p m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); } + void RouterContext::NewNTCP2Keys () + { + m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); + RAND_bytes (m_NTCP2Keys->staticPrivateKey, 32); + RAND_bytes (m_NTCP2Keys->iv, 16); + BN_CTX * ctx = BN_CTX_new (); + i2p::crypto::GetEd25519 ()->ScalarMulB (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey, ctx); + BN_CTX_free (ctx); + // save + std::ofstream fk (i2p::fs::DataDirPath (NTCP2_KEYS), std::ofstream::binary | std::ofstream::out); + fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); + } + void RouterContext::SetStatus (RouterStatus status) { if (status != m_Status) @@ -123,7 +152,7 @@ namespace i2p bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { - if (address->port != port) + if (!address->IsNTCP2 () && address->port != port) { address->port = port; updated = true; @@ -133,6 +162,52 @@ namespace i2p UpdateRouterInfo (); } + void RouterContext::PublishNTCP2Address (int port, bool publish) + { + if (!m_NTCP2Keys) return; + if (!port) + port = rand () % (30777 - 9111) + 9111; // I2P network ports range + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish)) + { + address->port = port; + address->ntcp2->isPublished = publish; + address->ntcp2->iv = m_NTCP2Keys->iv; + updated = true; + } + } + if (updated) + UpdateRouterInfo (); + } + + void RouterContext::UpdateNTCP2Address (bool enable) + { + auto& addresses = m_RouterInfo.GetAddresses (); + bool found = false, updated = false; + for (auto it = addresses.begin (); it != addresses.end (); ++it) + { + if ((*it)->IsNTCP2 ()) + { + found = true; + if (!enable) + { + addresses.erase (it); + updated= true; + } + break; + } + } + if (enable && !found) + { + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + updated = true; + } + if (updated) + UpdateRouterInfo (); + } + void RouterContext::UpdateAddress (const boost::asio::ip::address& host) { bool updated = false; @@ -269,7 +344,7 @@ namespace i2p auto& addresses = m_RouterInfo.GetAddresses (); for (auto it = addresses.begin (); it != addresses.end (); ++it) { - if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && + if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () && (*it)->host.is_v4 ()) { addresses.erase (it); @@ -296,16 +371,19 @@ namespace i2p caps |= i2p::data::RouterInfo::eFloodfill; m_RouterInfo.SetCaps (caps); - // insert NTCP back auto& addresses = m_RouterInfo.GetAddresses (); - for (const auto& addr : addresses) - { - if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU && - addr->host.is_v4 ()) + // insert NTCP back + bool ntcp; i2p::config::GetOption("ntcp", ntcp); + if (ntcp) { + for (const auto& addr : addresses) { - // insert NTCP address with host/port from SSU - m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port); - break; + if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU && + addr->host.is_v4 ()) + { + // insert NTCP address with host/port from SSU + m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port); + break; + } } } // delete previous introducers @@ -407,7 +485,21 @@ namespace i2p m_Keys.FromBuffer (buf, len); delete[] buf; } - + // read NTCP2 keys if available + std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); + if (n2k) + { + n2k.seekg (0, std::ios::end); + len = n2k.tellg(); + n2k.seekg (0, std::ios::beg); + if (len == sizeof (NTCP2PrivateKeys)) + { + m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); + n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); + } + n2k.close (); + } + // read RouterInfo m_RouterInfo.SetRouterIdentity (GetIdentity ()); i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); if (!routerInfo.IsUnreachable ()) // router.info looks good @@ -429,6 +521,16 @@ namespace i2p if (IsUnreachable ()) SetReachable (); // we assume reachable until we discover firewall through peer tests + // read NTCP2 + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) + { + if (!m_NTCP2Keys) NewNTCP2Keys (); + UpdateNTCP2Address (true); // enable NTCP2 + } + else + UpdateNTCP2Address (false); // disable NTCP2 + return true; } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 4bd324f5..f1a62c5a 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -14,6 +14,7 @@ namespace i2p { const char ROUTER_INFO[] = "router.info"; const char ROUTER_KEYS[] = "router.keys"; + const char NTCP2_KEYS[] = "ntcp2.keys"; const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes enum RouterStatus @@ -32,6 +33,15 @@ namespace i2p class RouterContext: public i2p::garlic::GarlicDestination { + private: + + struct NTCP2PrivateKeys + { + uint8_t staticPublicKey[32]; + uint8_t staticPrivateKey[32]; + uint8_t iv[16]; + }; + public: RouterContext (); @@ -49,6 +59,9 @@ namespace i2p return std::shared_ptr (this, [](i2p::garlic::GarlicDestination *) {}); } + const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; + const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; }; + const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; uint32_t GetUptime () const; uint32_t GetStartupTime () const { return m_StartupTime; }; @@ -63,8 +76,10 @@ namespace i2p void SetNetID (int netID) { m_NetID = netID; }; bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; - void UpdatePort (int port); // called from Daemon + void UpdatePort (int port); // called from Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon + void PublishNTCP2Address (int port, bool publish = true); + void UpdateNTCP2Address (bool enable); bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); bool IsUnreachable () const; @@ -108,6 +123,7 @@ namespace i2p void CreateNewRouter (); void NewRouterInfo (); void UpdateRouterInfo (); + void NewNTCP2Keys (); bool Load (); void SaveKeys (); @@ -125,6 +141,7 @@ namespace i2p RouterError m_Error; int m_NetID; std::mutex m_GarlicMutex; + std::unique_ptr m_NTCP2Keys; }; extern RouterContext context; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index e3f4d2d4..79a70462 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -176,10 +176,14 @@ namespace data auto address = std::make_shared

(); s.read ((char *)&address->cost, sizeof (address->cost)); s.read ((char *)&address->date, sizeof (address->date)); - char transportStyle[5]; - ReadString (transportStyle, 5, s); - if (!strcmp (transportStyle, "NTCP")) + bool isNTCP2Only = false; + char transportStyle[6]; + auto transportStyleLen = ReadString (transportStyle, 6, s) - 1; + if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 + { address->transportStyle = eTransportNTCP; + if (transportStyleLen > 4 && transportStyle[4] == '2') isNTCP2Only= true; + } else if (!strcmp (transportStyle, "SSU")) { address->transportStyle = eTransportSSU; @@ -244,6 +248,19 @@ namespace data } else if (!strcmp (key, "caps")) ExtractCaps (value); + else if (!strcmp (key, "s")) // ntcp2 static key + { + if (!address->ntcp2) address->ntcp2.reset (new NTCP2Ext ()); + supportedTransports |= (address->host.is_v4 ()) ? eNTCP2V4 : eNTCP2V6; + Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32); + } + else if (!strcmp (key, "i")) // ntcp2 iv + { + if (!address->ntcp2) address->ntcp2.reset (new NTCP2Ext ()); + supportedTransports |= (address->host.is_v4 ()) ? eNTCP2V4 : eNTCP2V6; + Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16); + address->ntcp2->isPublished = true; // presence if "i" means "published" + } else if (key[0] == 'i') { // introducers @@ -276,7 +293,8 @@ namespace data if (!s) return; } if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented - if (supportedTransports) + if (isNTCP2Only && address->ntcp2) address->ntcp2->isNTCP2Only = true; + if (supportedTransports) { addresses->push_back(address); m_SupportedTransports |= supportedTransports; @@ -423,7 +441,7 @@ namespace data s.write ((const char *)&address.date, sizeof (address.date)); std::stringstream properties; if (address.transportStyle == eTransportNTCP) - WriteString ("NTCP", s); + WriteString (address.IsNTCP2 () ? "NTCP2" : "NTCP", s); else if (address.transportStyle == eTransportSSU) { WriteString ("SSU", s); @@ -439,10 +457,13 @@ namespace data else WriteString ("", s); - WriteString ("host", properties); - properties << '='; - WriteString (address.host.to_string (), properties); - properties << ';'; + if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ()) + { + WriteString ("host", properties); + properties << '='; + WriteString (address.host.to_string (), properties); + properties << ';'; + } if (address.transportStyle == eTransportSSU) { // write introducers if any @@ -517,10 +538,29 @@ namespace data properties << ';'; } } - WriteString ("port", properties); - properties << '='; - WriteString (boost::lexical_cast(address.port), properties); - properties << ';'; + + if (address.IsPublishedNTCP2 ()) + { + // publish i for NTCP2 + WriteString ("i", properties); properties << '='; + WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';'; + } + + if (!address.IsNTCP2 () || address.IsPublishedNTCP2 ()) + { + WriteString ("port", properties); + properties << '='; + WriteString (boost::lexical_cast(address.port), properties); + properties << ';'; + } + if (address.IsNTCP2 ()) + { + // publish s and v for NTCP2 + WriteString ("s", properties); properties << '='; + WriteString (address.ntcp2->staticKey.ToBase64 (), properties); properties << ';'; + WriteString ("v", properties); properties << '='; + WriteString ("2", properties); properties << ';'; + } uint16_t size = htobe16 (properties.str ().size ()); s.write ((char *)&size, sizeof (size)); @@ -628,12 +668,12 @@ namespace data addr->host = boost::asio::ip::address::from_string (host); addr->port = port; addr->transportStyle = eTransportNTCP; - addr->cost = 2; + addr->cost = 6; addr->date = 0; for (const auto& it: *m_Addresses) // don't insert same address twice if (*it == *addr) return; m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4; - m_Addresses->push_back(std::move(addr)); + m_Addresses->push_front(std::move(addr)); // always make NTCP first } void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) @@ -656,6 +696,22 @@ namespace data m_Caps |= eSSUIntroducer; } + void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv) + { + for (const auto& it: *m_Addresses) // don't insert one more NTCP2 + if (it->ntcp2) return; + auto addr = std::make_shared
(); + addr->port = 0; + addr->transportStyle = eTransportNTCP; + addr->cost = 3; + addr->date = 0; + addr->ntcp2.reset (new NTCP2Ext ()); + addr->ntcp2->isNTCP2Only = true; // NTCP2 only address + memcpy (addr->ntcp2->staticKey, staticKey, 32); + memcpy (addr->ntcp2->iv, iv, 16); + m_Addresses->push_back(std::move(addr)); + } + bool RouterInfo::AddIntroducer (const Introducer& introducer) { for (auto& addr : *m_Addresses) @@ -735,6 +791,14 @@ namespace data return m_SupportedTransports & (eSSUV4 | eSSUV6); } + bool RouterInfo::IsNTCP2 (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eNTCP2V4; + else + return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); + } + bool RouterInfo::IsV6 () const { return m_SupportedTransports & (eNTCPV6 | eSSUV6); @@ -742,19 +806,19 @@ namespace data bool RouterInfo::IsV4 () const { - return m_SupportedTransports & (eNTCPV4 | eSSUV4); + return m_SupportedTransports & (eNTCPV4 | eSSUV4 | eNTCP2V4); } void RouterInfo::EnableV6 () { if (!IsV6 ()) - m_SupportedTransports |= eNTCPV6 | eSSUV6; + m_SupportedTransports |= eNTCPV6 | eSSUV6 | eNTCP2V6; } void RouterInfo::EnableV4 () { if (!IsV4 ()) - m_SupportedTransports |= eNTCPV4 | eSSUV4; + m_SupportedTransports |= eNTCPV4 | eSSUV4 | eNTCP2V4; } @@ -762,7 +826,7 @@ namespace data { if (IsV6 ()) { - m_SupportedTransports &= ~(eNTCPV6 | eSSUV6); + m_SupportedTransports &= ~(eNTCPV6 | eSSUV6 | eNTCP2V6); for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; @@ -778,7 +842,7 @@ namespace data { if (IsV4 ()) { - m_SupportedTransports &= ~(eNTCPV4 | eSSUV4); + m_SupportedTransports &= ~(eNTCPV4 | eSSUV4 | eNTCP2V4); for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; @@ -798,37 +862,55 @@ namespace data std::shared_ptr RouterInfo::GetNTCPAddress (bool v4only) const { - return GetAddress (eTransportNTCP, v4only); + return GetAddress ( + [v4only](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportNTCP) && !address->IsNTCP2Only () && (!v4only || address->host.is_v4 ()); + }); } std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const { - return GetAddress (eTransportSSU, v4only); + return GetAddress ( + [v4only](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && (!v4only || address->host.is_v4 ()); + }); } std::shared_ptr RouterInfo::GetSSUV6Address () const { - return GetAddress (eTransportSSU, false, true); + return GetAddress ( + [](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && address->host.is_v6 (); + }); } - std::shared_ptr RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const + template + std::shared_ptr RouterInfo::GetAddress (Filter filter) const { + // TODO: make it more gereric using comparator #if (BOOST_VERSION >= 105300) auto addresses = boost::atomic_load (&m_Addresses); #else auto addresses = m_Addresses; #endif for (const auto& address : *addresses) - { - if (address->transportStyle == s) - { - if ((!v4only || address->host.is_v4 ()) && (!v6only || address->host.is_v6 ())) - return address; - } - } + if (filter (address)) return address; + return nullptr; } + std::shared_ptr RouterInfo::GetNTCP2Address (bool publishedOnly, bool v4only) const + { + return GetAddress ( + [publishedOnly, v4only](std::shared_ptr address)->bool + { + return address->IsNTCP2 () && (!publishedOnly || address->IsPublishedNTCP2 ()) && (!v4only || address->host.is_v4 ()); + }); + } + std::shared_ptr RouterInfo::GetProfile () const { if (!m_Profile) diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 09e2c015..47a5d680 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -48,7 +48,9 @@ namespace data eNTCPV4 = 0x01, eNTCPV6 = 0x02, eSSUV4 = 0x04, - eSSUV6 = 0x08 + eSSUV6 = 0x08, + eNTCP2V4 = 0x10, + eNTCP2V6 = 0x20 }; enum Caps @@ -88,6 +90,14 @@ namespace data std::vector introducers; }; + struct NTCP2Ext + { + Tag<32> staticKey; + Tag<16> iv; + bool isPublished = false; + bool isNTCP2Only = false; + }; + struct Address { TransportStyle transportStyle; @@ -97,6 +107,7 @@ namespace data uint64_t date; uint8_t cost; std::unique_ptr ssu; // not null for SSU + std::unique_ptr ntcp2; // not null for NTCP2 bool IsCompatible (const boost::asio::ip::address& other) const { @@ -113,6 +124,10 @@ namespace data { return !(*this == other); } + + bool IsNTCP2 () const { return (bool)ntcp2; }; + bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; }; + bool IsNTCP2Only () const { return ntcp2 && ntcp2->isNTCP2Only; }; }; typedef std::list > Addresses; @@ -129,11 +144,13 @@ namespace data uint64_t GetTimestamp () const { return m_Timestamp; }; Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr std::shared_ptr GetNTCPAddress (bool v4only = true) const; + std::shared_ptr GetNTCP2Address (bool publishedOnly, bool v4only = true) const; std::shared_ptr GetSSUAddress (bool v4only = true) const; std::shared_ptr GetSSUV6Address () const; void AddNTCPAddress (const char * host, int port); void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); + void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv); bool AddIntroducer (const Introducer& introducer); bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only @@ -144,6 +161,7 @@ namespace data bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsNTCP (bool v4only = true) const; bool IsSSU (bool v4only = true) const; + bool IsNTCP2 (bool v4only = true) const; bool IsV6 () const; bool IsV4 () const; void EnableV6 (); @@ -200,7 +218,8 @@ namespace data size_t ReadString (char* str, size_t len, std::istream& s) const; void WriteString (const std::string& str, std::ostream& s) const; void ExtractCaps (const char * value); - std::shared_ptr GetAddress (TransportStyle s, bool v4only, bool v6only = false) const; + template + std::shared_ptr GetAddress (Filter filter) const; void UpdateCapsProperty (); private: diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index aded9bc8..baa265bc 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -6,442 +6,6 @@ namespace i2p { namespace crypto { - class Ed25519 - { - public: - - Ed25519 () - { - BN_CTX * ctx = BN_CTX_new (); - BIGNUM * tmp = BN_new (); - - q = BN_new (); - // 2^255-19 - BN_set_bit (q, 255); // 2^255 - BN_sub_word (q, 19); - - l = BN_new (); - // 2^252 + 27742317777372353535851937790883648493 - BN_set_bit (l, 252); - two_252_2 = BN_dup (l); - BN_dec2bn (&tmp, "27742317777372353535851937790883648493"); - BN_add (l, l, tmp); - BN_sub_word (two_252_2, 2); // 2^252 - 2 - - // -121665*inv(121666) - d = BN_new (); - BN_set_word (tmp, 121666); - BN_mod_inverse (tmp, tmp, q, ctx); - BN_set_word (d, 121665); - BN_set_negative (d, 1); - BN_mul (d, d, tmp, ctx); - - // 2^((q-1)/4) - I = BN_new (); - BN_free (tmp); - tmp = BN_dup (q); - BN_sub_word (tmp, 1); - BN_div_word (tmp, 4); - BN_set_word (I, 2); - BN_mod_exp (I, I, tmp, q, ctx); - BN_free (tmp); - - // 4*inv(5) - BIGNUM * By = BN_new (); - BN_set_word (By, 5); - BN_mod_inverse (By, By, q, ctx); - BN_mul_word (By, 4); - BIGNUM * Bx = RecoverX (By, ctx); - BN_mod (Bx, Bx, q, ctx); // % q - BN_mod (By, By, q, ctx); // % q - - // precalculate Bi256 table - Bi256Carry = { Bx, By }; // B - for (int i = 0; i < 32; i++) - { - Bi256[i][0] = Bi256Carry; // first point - for (int j = 1; j < 128; j++) - Bi256[i][j] = Sum (Bi256[i][j-1], Bi256[i][0], ctx); // (256+j+1)^i*B - Bi256Carry = Bi256[i][127]; - for (int j = 0; j < 128; j++) // add first point 128 more times - Bi256Carry = Sum (Bi256Carry, Bi256[i][0], ctx); - } - - BN_CTX_free (ctx); - } - - Ed25519 (const Ed25519& other): q (BN_dup (other.q)), l (BN_dup (other.l)), - d (BN_dup (other.d)), I (BN_dup (other.I)), two_252_2 (BN_dup (other.two_252_2)), - Bi256Carry (other.Bi256Carry) - { - for (int i = 0; i < 32; i++) - for (int j = 0; j < 128; j++) - Bi256[i][j] = other.Bi256[i][j]; - } - - ~Ed25519 () - { - BN_free (q); - BN_free (l); - BN_free (d); - BN_free (I); - BN_free (two_252_2); - } - - - EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const - { - return MulB (expandedPrivateKey, ctx); // left half of expanded key, considered as Little Endian - } - - EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const - { - return DecodePoint (buf, ctx); - } - - void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const - { - EncodePoint (Normalize (publicKey, ctx), buf); - } - - bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const - { - BN_CTX * ctx = BN_CTX_new (); - BIGNUM * h = DecodeBN<64> (digest); - // signature 0..31 - R, 32..63 - S - // B*S = R + PK*h => R = B*S - PK*h - // we don't decode R, but encode (B*S - PK*h) - auto Bs = MulB (signature + EDDSA25519_SIGNATURE_LENGTH/2, ctx); // B*S; - BN_mod (h, h, l, ctx); // public key is multiple of B, but B%l = 0 - auto PKh = Mul (publicKey, h, ctx); // PK*h - uint8_t diff[32]; - EncodePoint (Normalize (Sum (Bs, -PKh, ctx), ctx), diff); // Bs - PKh encoded - bool passed = !memcmp (signature, diff, 32); // R - BN_free (h); - BN_CTX_free (ctx); - if (!passed) - LogPrint (eLogError, "25519 signature verification failed"); - return passed; - } - - void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, - uint8_t * signature) const - { - BN_CTX * bnCtx = BN_CTX_new (); - // calculate r - SHA512_CTX ctx; - SHA512_Init (&ctx); - SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key - SHA512_Update (&ctx, buf, len); // data - uint8_t digest[64]; - SHA512_Final (digest, &ctx); - BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors - // calculate R - uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf - EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors - // calculate S - SHA512_Init (&ctx); - SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R - SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key - SHA512_Update (&ctx, buf, len); // data - SHA512_Final (digest, &ctx); - BIGNUM * h = DecodeBN<64> (digest); - // S = (r + h*a) % l - BIGNUM * a = DecodeBN (expandedPrivateKey); // left half of expanded key - BN_mod_mul (h, h, a, l, bnCtx); // %l - BN_mod_add (h, h, r, l, bnCtx); // %l - memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2); - EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S - BN_free (r); BN_free (h); BN_free (a); - BN_CTX_free (bnCtx); - } - - private: - - EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const - { - // x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2) - // y3 = (y1*y2+x1*x2)*(z1*z2+d*t1*t2) - // z3 = (z1*z2-d*t1*t2)*(z1*z2+d*t1*t2) - // t3 = (y1*y2+x1*x2)*(x1*y2+y1*x2) - BIGNUM * x3 = BN_new (), * y3 = BN_new (), * z3 = BN_new (), * t3 = BN_new (); - - BN_mul (x3, p1.x, p2.x, ctx); // A = x1*x2 - BN_mul (y3, p1.y, p2.y, ctx); // B = y1*y2 - - BN_CTX_start (ctx); - BIGNUM * t1 = p1.t, * t2 = p2.t; - if (!t1) { t1 = BN_CTX_get (ctx); BN_mul (t1, p1.x, p1.y, ctx); } - if (!t2) { t2 = BN_CTX_get (ctx); BN_mul (t2, p2.x, p2.y, ctx); } - BN_mul (t3, t1, t2, ctx); - BN_mul (t3, t3, d, ctx); // C = d*t1*t2 - - if (p1.z) - { - if (p2.z) - BN_mul (z3, p1.z, p2.z, ctx); // D = z1*z2 - else - BN_copy (z3, p1.z); // D = z1 - } - else - { - if (p2.z) - BN_copy (z3, p2.z); // D = z2 - else - BN_one (z3); // D = 1 - } - - BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx); - BN_add (E, p1.x, p1.y); - BN_add (F, p2.x, p2.y); - BN_mul (E, E, F, ctx); // (x1 + y1)*(x2 + y2) - BN_sub (E, E, x3); - BN_sub (E, E, y3); // E = (x1 + y1)*(x2 + y2) - A - B - BN_sub (F, z3, t3); // F = D - C - BN_add (G, z3, t3); // G = D + C - BN_add (H, y3, x3); // H = B + A - - BN_mod_mul (x3, E, F, q, ctx); // x3 = E*F - BN_mod_mul (y3, G, H, q, ctx); // y3 = G*H - BN_mod_mul (z3, F, G, q, ctx); // z3 = F*G - BN_mod_mul (t3, E, H, q, ctx); // t3 = E*H - - BN_CTX_end (ctx); - - return EDDSAPoint {x3, y3, z3, t3}; - } - - void Double (EDDSAPoint& p, BN_CTX * ctx) const - { - BN_CTX_start (ctx); - BIGNUM * x2 = BN_CTX_get (ctx), * y2 = BN_CTX_get (ctx), * z2 = BN_CTX_get (ctx), * t2 = BN_CTX_get (ctx); - - BN_sqr (x2, p.x, ctx); // x2 = A = x^2 - BN_sqr (y2, p.y, ctx); // y2 = B = y^2 - if (p.t) - BN_sqr (t2, p.t, ctx); // t2 = t^2 - else - { - BN_mul (t2, p.x, p.y, ctx); // t = x*y - BN_sqr (t2, t2, ctx); // t2 = t^2 - } - BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2 - if (p.z) - BN_sqr (z2, p.z, ctx); // z2 = D = z^2 - else - BN_one (z2); // z2 = 1 - - BIGNUM * E = BN_CTX_get (ctx), * F = BN_CTX_get (ctx), * G = BN_CTX_get (ctx), * H = BN_CTX_get (ctx); - // E = (x+y)*(x+y)-A-B = x^2+y^2+2xy-A-B = 2xy - BN_mul (E, p.x, p.y, ctx); - BN_lshift1 (E, E); // E =2*x*y - BN_sub (F, z2, t2); // F = D - C - BN_add (G, z2, t2); // G = D + C - BN_add (H, y2, x2); // H = B + A - - BN_mod_mul (p.x, E, F, q, ctx); // x2 = E*F - BN_mod_mul (p.y, G, H, q, ctx); // y2 = G*H - if (!p.z) p.z = BN_new (); - BN_mod_mul (p.z, F, G, q, ctx); // z2 = F*G - if (!p.t) p.t = BN_new (); - BN_mod_mul (p.t, E, H, q, ctx); // t2 = E*H - - BN_CTX_end (ctx); - } - - EDDSAPoint Mul (const EDDSAPoint& p, const BIGNUM * e, BN_CTX * ctx) const - { - BIGNUM * zero = BN_new (), * one = BN_new (); - BN_zero (zero); BN_one (one); - EDDSAPoint res {zero, one}; - if (!BN_is_zero (e)) - { - int bitCount = BN_num_bits (e); - for (int i = bitCount - 1; i >= 0; i--) - { - Double (res, ctx); - if (BN_is_bit_set (e, i)) res = Sum (res, p, ctx); - } - } - return res; - } - - EDDSAPoint MulB (const uint8_t * e, BN_CTX * ctx) const // B*e, e is 32 bytes Little Endian - { - BIGNUM * zero = BN_new (), * one = BN_new (); - BN_zero (zero); BN_one (one); - EDDSAPoint res {zero, one}; - bool carry = false; - for (int i = 0; i < 32; i++) - { - uint8_t x = e[i]; - if (carry) - { - if (x < 255) - { - x++; - carry = false; - } - else - x = 0; - } - if (x > 0) - { - if (x <= 128) - res = Sum (res, Bi256[i][x-1], ctx); - else - { - res = Sum (res, -Bi256[i][255-x], ctx); // -Bi[256-x] - carry = true; - } - } - } - if (carry) res = Sum (res, Bi256Carry, ctx); - return res; - } - - EDDSAPoint Normalize (const EDDSAPoint& p, BN_CTX * ctx) const - { - if (p.z) - { - BIGNUM * x = BN_new (), * y = BN_new (); - BN_mod_inverse (y, p.z, q, ctx); - BN_mod_mul (x, p.x, y, q, ctx); // x = x/z - BN_mod_mul (y, p.y, y, q, ctx); // y = y/z - return EDDSAPoint{x, y}; - } - else - return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)}; - } - - bool IsOnCurve (const EDDSAPoint& p, BN_CTX * ctx) const - { - BN_CTX_start (ctx); - BIGNUM * x2 = BN_CTX_get (ctx), * y2 = BN_CTX_get (ctx), * tmp = BN_CTX_get (ctx); - BN_sqr (x2, p.x, ctx); // x^2 - BN_sqr (y2, p.y, ctx); // y^2 - // y^2 - x^2 - 1 - d*x^2*y^2 - BN_mul (tmp, d, x2, ctx); - BN_mul (tmp, tmp, y2, ctx); - BN_sub (tmp, y2, tmp); - BN_sub (tmp, tmp, x2); - BN_sub_word (tmp, 1); - BN_mod (tmp, tmp, q, ctx); // % q - bool ret = BN_is_zero (tmp); - BN_CTX_end (ctx); - return ret; - } - - BIGNUM * RecoverX (const BIGNUM * y, BN_CTX * ctx) const - { - BN_CTX_start (ctx); - BIGNUM * y2 = BN_CTX_get (ctx), * xx = BN_CTX_get (ctx); - BN_sqr (y2, y, ctx); // y^2 - // xx = (y^2 -1)*inv(d*y^2 +1) - BN_mul (xx, d, y2, ctx); - BN_add_word (xx, 1); - BN_mod_inverse (xx, xx, q, ctx); - BN_sub_word (y2, 1); - BN_mul (xx, y2, xx, ctx); - // x = srqt(xx) = xx^(2^252-2) - BIGNUM * x = BN_new (); - BN_mod_exp (x, xx, two_252_2, q, ctx); - // check (x^2 -xx) % q - BN_sqr (y2, x, ctx); - BN_mod_sub (y2, y2, xx, q, ctx); - if (!BN_is_zero (y2)) - BN_mod_mul (x, x, I, q, ctx); - if (BN_is_odd (x)) - BN_sub (x, q, x); - BN_CTX_end (ctx); - return x; - } - - EDDSAPoint DecodePoint (const uint8_t * buf, BN_CTX * ctx) const - { - // buf is 32 bytes Little Endian, convert it to Big Endian - uint8_t buf1[EDDSA25519_PUBLIC_KEY_LENGTH]; - for (size_t i = 0; i < EDDSA25519_PUBLIC_KEY_LENGTH/2; i++) // invert bytes - { - buf1[i] = buf[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i]; - buf1[EDDSA25519_PUBLIC_KEY_LENGTH -1 - i] = buf[i]; - } - bool isHighestBitSet = buf1[0] & 0x80; - if (isHighestBitSet) - buf1[0] &= 0x7f; // clear highest bit - BIGNUM * y = BN_new (); - BN_bin2bn (buf1, EDDSA25519_PUBLIC_KEY_LENGTH, y); - BIGNUM * x = RecoverX (y, ctx); - if (BN_is_bit_set (x, 0) != isHighestBitSet) - BN_sub (x, q, x); // x = q - x - BIGNUM * z = BN_new (), * t = BN_new (); - BN_one (z); BN_mod_mul (t, x, y, q, ctx); // pre-calculate t - EDDSAPoint p {x, y, z, t}; - if (!IsOnCurve (p, ctx)) - LogPrint (eLogError, "Decoded point is not on 25519"); - return p; - } - - void EncodePoint (const EDDSAPoint& p, uint8_t * buf) const - { - EncodeBN (p.y, buf,EDDSA25519_PUBLIC_KEY_LENGTH); - if (BN_is_bit_set (p.x, 0)) // highest bit - buf[EDDSA25519_PUBLIC_KEY_LENGTH - 1] |= 0x80; // set highest bit - } - - template - BIGNUM * DecodeBN (const uint8_t * buf) const - { - // buf is Little Endian convert it to Big Endian - uint8_t buf1[len]; - for (size_t i = 0; i < len/2; i++) // invert bytes - { - buf1[i] = buf[len -1 - i]; - buf1[len -1 - i] = buf[i]; - } - BIGNUM * res = BN_new (); - BN_bin2bn (buf1, len, res); - return res; - } - - void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const - { - bn2buf (bn, buf, len); - // To Little Endian - for (size_t i = 0; i < len/2; i++) // invert bytes - { - uint8_t tmp = buf[i]; - buf[i] = buf[len -1 - i]; - buf[len -1 - i] = tmp; - } - } - - private: - - BIGNUM * q, * l, * d, * I; - // transient values - BIGNUM * two_252_2; // 2^252-2 - EDDSAPoint Bi256[32][128]; // per byte, Bi256[i][j] = (256+j+1)^i*B, we don't store zeroes - // if j > 128 we use 256 - j and carry 1 to next byte - // Bi256[0][0] = B, base point - EDDSAPoint Bi256Carry; // Bi256[32][0] - }; - - static std::unique_ptr g_Ed25519; - std::unique_ptr& GetEd25519 () - { - if (!g_Ed25519) - { - auto c = new Ed25519(); - if (!g_Ed25519) // make sure it was not created already - g_Ed25519.reset (c); - else - delete c; - } - return g_Ed25519; - } - - EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey) { memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH); @@ -466,11 +30,7 @@ namespace crypto EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) { // expand key - SHA512 (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH, m_ExpandedPrivateKey); - m_ExpandedPrivateKey[0] &= 0xF8; // drop last 3 bits - m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits - m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit - + Ed25519::ExpandPrivateKey (signingPrivateKey, m_ExpandedPrivateKey); // generate and encode public key BN_CTX * ctx = BN_CTX_new (); auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); diff --git a/libi2pd/Signature.h b/libi2pd/Signature.h index 1e7db9f7..8b30a8e8 100644 --- a/libi2pd/Signature.h +++ b/libi2pd/Signature.h @@ -9,6 +9,7 @@ #include #include #include "Crypto.h" +#include "Ed25519.h" #include "Gost.h" namespace i2p @@ -361,62 +362,6 @@ namespace crypto typedef RSASigner RSASHA5124096Signer; // EdDSA - struct EDDSAPoint - { - BIGNUM * x {nullptr}; - BIGNUM * y {nullptr}; - BIGNUM * z {nullptr}; - BIGNUM * t {nullptr}; // projective coordinates - - EDDSAPoint () {} - EDDSAPoint (const EDDSAPoint& other) { *this = other; } - EDDSAPoint (EDDSAPoint&& other) { *this = std::move (other); } - EDDSAPoint (BIGNUM * x1, BIGNUM * y1, BIGNUM * z1 = nullptr, BIGNUM * t1 = nullptr) - : x(x1) - , y(y1) - , z(z1) - , t(t1) - {} - ~EDDSAPoint () { BN_free (x); BN_free (y); BN_free(z); BN_free(t); } - - EDDSAPoint& operator=(EDDSAPoint&& other) - { - if (this != &other) - { - BN_free (x); x = other.x; other.x = nullptr; - BN_free (y); y = other.y; other.y = nullptr; - BN_free (z); z = other.z; other.z = nullptr; - BN_free (t); t = other.t; other.t = nullptr; - } - return *this; - } - - EDDSAPoint& operator=(const EDDSAPoint& other) - { - if (this != &other) - { - BN_free (x); x = other.x ? BN_dup (other.x) : nullptr; - BN_free (y); y = other.y ? BN_dup (other.y) : nullptr; - BN_free (z); z = other.z ? BN_dup (other.z) : nullptr; - BN_free (t); t = other.t ? BN_dup (other.t) : nullptr; - } - return *this; - } - - EDDSAPoint operator-() const - { - BIGNUM * x1 = NULL, * y1 = NULL, * z1 = NULL, * t1 = NULL; - if (x) { x1 = BN_dup (x); BN_set_negative (x1, !BN_is_negative (x)); }; - if (y) y1 = BN_dup (y); - if (z) z1 = BN_dup (z); - if (t) { t1 = BN_dup (t); BN_set_negative (t1, !BN_is_negative (t)); }; - return EDDSAPoint {x1, y1, z1, t1}; - } - }; - - const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32; - const size_t EDDSA25519_SIGNATURE_LENGTH = 64; - const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32; class EDDSA25519Verifier: public Verifier { public: diff --git a/libi2pd/Siphash.h b/libi2pd/Siphash.h new file mode 100644 index 00000000..aa8b8631 --- /dev/null +++ b/libi2pd/Siphash.h @@ -0,0 +1,152 @@ +/** + This code is licensed under the MCGSI Public License + Copyright 2018 Jeff Becker + + Kovri go write your own code + + */ +#ifndef SIPHASH_H +#define SIPHASH_H + +#include + +namespace i2p +{ +namespace crypto +{ + namespace siphash + { + constexpr int crounds = 2; + constexpr int drounds = 4; + + inline uint64_t rotl(const uint64_t & x, int b) + { + uint64_t ret = x << b; + ret |= x >> (64 - b); + return ret; + } + + inline void u32to8le(const uint32_t & v, uint8_t * p) + { + p[0] = (uint8_t) v; + p[1] = (uint8_t) (v >> 8); + p[2] = (uint8_t) (v >> 16); + p[3] = (uint8_t) (v >> 24); + } + + inline void u64to8le(const uint64_t & v, uint8_t * p) + { + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; + p[4] = (v >> 32) & 0xff; + p[5] = (v >> 40) & 0xff; + p[6] = (v >> 48) & 0xff; + p[7] = (v >> 56) & 0xff; + } + + inline uint64_t u8to64le(const uint8_t * p) + { + uint64_t i = 0; + int idx = 0; + while(idx < 8) + { + i |= ((uint64_t) p[idx]) << (idx * 8); + ++idx; + } + return i; + } + + inline void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3) + { + _v0 += _v1; + _v1 = rotl(_v1, 13); + _v1 ^= _v0; + _v0 = rotl(_v0, 32); + _v2 += _v3; + _v3 = rotl(_v3, 16); + _v3 ^= _v2; + _v0 += _v3; + _v3 = rotl(_v3, 21); + _v3 ^= _v0; + _v2 += _v1; + _v1 = rotl(_v1, 17); + _v1 ^= _v2; + _v2 = rotl(_v2, 32); + } + } + + /** hashsz must be 8 or 16 */ + template + inline void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key) + { + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + const uint64_t k0 = siphash::u8to64le(key); + const uint64_t k1 = siphash::u8to64le(key + 8); + uint64_t msg; + int i; + const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t)); + auto left = bufsz & 7; + uint64_t b = ((uint64_t)bufsz) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + if(hashsz == 16) v1 ^= 0xee; + + while(buf != end) + { + msg = siphash::u8to64le(buf); + v3 ^= msg; + for(i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); + + v0 ^= msg; + buf += 8; + } + + while(left) + { + --left; + b |= ((uint64_t)(buf[left])) << (left * 8); + } + + v3 ^= b; + + for(i = 0; i < siphash::crounds; ++i) + siphash::round(v0, v1, v2, v3); + + v0 ^= b; + + + if(hashsz == 16) + v2 ^= 0xee; + else + v2 ^= 0xff; + + for(i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); + + b = v0 ^ v1 ^ v2 ^ v3; + + siphash::u64to8le(b, h); + + if(hashsz == 8) return; + + v1 ^= 0xdd; + + for (i = 0; i < siphash::drounds; ++i) + siphash::round(v0, v1, v2, v3); + + b = v0 ^ v1 ^ v2 ^ v3; + siphash::u64to8le(b, h + 8); + } +} +} + +#endif diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 91acc9d0..e8386b61 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -578,9 +578,7 @@ namespace stream if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ()) // nothing to send { m_Status = eStreamStatusClosed; - // close could be called from another thread so do SendClose from the destination thread - // this is so m_LocalDestination.NewPacket () does not trigger a race condition - m_Service.post(std::bind(&Stream::SendClose, shared_from_this())); + SendClose(); } break; case eStreamStatusClosed: @@ -903,11 +901,7 @@ namespace stream StreamingDestination::StreamingDestination (std::shared_ptr owner, uint16_t localPort, bool gzip): m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_LastIncomingReceiveStreamID (0), - m_PendingIncomingTimer (m_Owner->GetService ()), - m_ConnTrackTimer(m_Owner->GetService()), - m_ConnsPerMinute(DEFAULT_MAX_CONNS_PER_MIN), - m_LastBanClear(i2p::util::GetMillisecondsSinceEpoch()), - m_EnableDrop(false) + m_PendingIncomingTimer (m_Owner->GetService ()) { } @@ -923,7 +917,6 @@ namespace stream void StreamingDestination::Start () { - ScheduleConnTrack(); } void StreamingDestination::Stop () @@ -931,15 +924,10 @@ namespace stream ResetAcceptor (); m_PendingIncomingTimer.cancel (); m_PendingIncomingStreams.clear (); - m_ConnTrackTimer.cancel(); { std::unique_lock l(m_StreamsMutex); m_Streams.clear (); } - { - std::unique_lock l(m_ConnsMutex); - m_Conns.clear (); - } } void StreamingDestination::HandleNextPacket (Packet * packet) @@ -971,17 +959,7 @@ namespace stream auto incomingStream = CreateNewIncomingStream (); incomingStream->HandleNextPacket (packet); // SYN auto ident = incomingStream->GetRemoteIdentity(); - if(ident && m_EnableDrop) - { - auto ih = ident->GetIdentHash(); - if(DropNewStream(ih)) - { - // drop - LogPrint(eLogWarning, "Streaming: Dropping connection, too many inbound streams from ", ih.ToBase32()); - incomingStream->Terminate(); - return; - } - } + m_LastIncomingReceiveStreamID = receiveStreamID; // handle saved packets if any @@ -1176,63 +1154,5 @@ namespace stream return msg; } - void StreamingDestination::SetMaxConnsPerMinute(const uint32_t conns) - { - m_EnableDrop = conns > 0; - m_ConnsPerMinute = conns; - LogPrint(eLogDebug, "Streaming: Set max conns per minute per destination to ", conns); - } - - bool StreamingDestination::DropNewStream(const i2p::data::IdentHash & ih) - { - std::lock_guard lock(m_ConnsMutex); - if (m_Banned.size() > MAX_BANNED_CONNS) return true; // overload - auto end = std::end(m_Banned); - if ( std::find(std::begin(m_Banned), end, ih) != end) return true; // already banned - auto itr = m_Conns.find(ih); - if (itr == m_Conns.end()) - m_Conns[ih] = 0; - - m_Conns[ih] += 1; - - bool ban = m_Conns[ih] >= m_ConnsPerMinute; - if (ban) - { - m_Banned.push_back(ih); - m_Conns.erase(ih); - LogPrint(eLogWarning, "Streaming: ban ", ih.ToBase32()); - } - return ban; - } - - void StreamingDestination::HandleConnTrack(const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - { // acquire lock - std::lock_guard lock(m_ConnsMutex); - // clear conn tracking - m_Conns.clear(); - // check for ban clear - auto ts = i2p::util::GetMillisecondsSinceEpoch(); - if (ts - m_LastBanClear >= DEFAULT_BAN_INTERVAL) - { - // clear bans - m_Banned.clear(); - m_LastBanClear = ts; - } - } - // reschedule timer - ScheduleConnTrack(); - } - } - - void StreamingDestination::ScheduleConnTrack() - { - m_ConnTrackTimer.expires_from_now (boost::posix_time::seconds(60)); - m_ConnTrackTimer.async_wait ( - std::bind (&StreamingDestination::HandleConnTrack, - shared_from_this (), std::placeholders::_1)); - } } } diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index a114844d..3db8d760 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -53,22 +53,6 @@ namespace stream const int PENDING_INCOMING_TIMEOUT = 10; // in seconds const int MAX_RECEIVE_TIMEOUT = 30; // in seconds - /** i2cp option for limiting inbound stremaing connections */ - const char I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN[] = "maxconns"; - /** default maximum connections attempts per minute per destination */ - const uint32_t DEFAULT_MAX_CONNS_PER_MIN = 600; - - /** - * max banned destinations per local destination - * TODO: make configurable - */ - const uint16_t MAX_BANNED_CONNS = 9999; - /** - * length of a ban in ms - * TODO: make configurable - */ - const uint64_t DEFAULT_BAN_INTERVAL = 60 * 60 * 1000; - struct Packet { size_t len, offset; @@ -181,6 +165,9 @@ namespace stream void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; + void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; + + /** only call close from destination thread, use Stream::AsyncClose for other threads */ void Close (); void Cancel () { m_ReceiveTimer.cancel (); }; @@ -273,9 +260,6 @@ namespace stream void HandleDataMessagePayload (const uint8_t * buf, size_t len); std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort); - /** set max connections per minute per destination */ - void SetMaxConnsPerMinute(const uint32_t conns); - Packet * NewPacket () { return m_PacketsPool.Acquire(); } void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } @@ -286,13 +270,6 @@ namespace stream std::shared_ptr CreateNewIncomingStream (); void HandlePendingIncomingTimer (const boost::system::error_code& ecode); - /** handle cleaning up connection tracking for ratelimits */ - void HandleConnTrack(const boost::system::error_code& ecode); - - bool DropNewStream(const i2p::data::IdentHash & ident); - - void ScheduleConnTrack(); - private: std::shared_ptr m_Owner; @@ -306,17 +283,7 @@ namespace stream boost::asio::deadline_timer m_PendingIncomingTimer; std::map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN - std::mutex m_ConnsMutex; - /** how many connections per minute did each identity have */ - std::map m_Conns; - boost::asio::deadline_timer m_ConnTrackTimer; - uint32_t m_ConnsPerMinute; - /** banned identities */ - std::vector m_Banned; - uint64_t m_LastBanClear; - i2p::util::MemoryPool m_PacketsPool; - bool m_EnableDrop; public: diff --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h index 62bed352..49067ce2 100644 --- a/libi2pd/TransportSession.h +++ b/libi2pd/TransportSession.h @@ -79,6 +79,7 @@ namespace transport bool IsTerminationTimeoutExpired (uint64_t ts) const { return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); }; + virtual void SendLocalRouterInfo () { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; virtual void SendI2NPMessages (const std::vector >& msgs) = 0; protected: diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 9914c75e..6a833ae0 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -117,7 +117,8 @@ namespace transport Transports::Transports (): m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), - m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys + m_NTCPServer (nullptr), m_SSUServer (nullptr), m_NTCP2Server (nullptr), + m_DHKeysPairSupplier (5), // 5 pre-generated keys m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), @@ -191,6 +192,13 @@ namespace transport LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy); return; } + // create NTCP2. TODO: move to acceptor + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + if (ntcp2) + { + m_NTCP2Server = new NTCP2Server (); + m_NTCP2Server->Start (); + } // create acceptors auto& addresses = context.GetRouterInfo ().GetAddresses (); @@ -262,6 +270,13 @@ namespace transport m_NTCPServer = nullptr; } + if (m_NTCP2Server) + { + m_NTCP2Server->Stop (); + delete m_NTCP2Server; + m_NTCP2Server = nullptr; + } + m_DHKeysPairSupplier.Stop (); m_IsRunning = false; if (m_Service) m_Service->stop (); @@ -386,9 +401,24 @@ namespace transport { if (peer.router) // we have RI already { - if (!peer.numAttempts) // NTCP + 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 == 1) // NTCP1 + { + peer.numAttempts++; auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); if (address && m_NTCPServer) { @@ -446,7 +476,7 @@ namespace transport else LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU"); } - if (peer.numAttempts == 1)// SSU + if (peer.numAttempts == 2)// SSU { peer.numAttempts++; if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ())) @@ -709,7 +739,7 @@ namespace transport sendDatabaseStore = false; // we have it in the list already } if (sendDatabaseStore) - session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); + session->SendLocalRouterInfo (); else session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds it->second.sessions.push_back (session); diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index d4410cb3..cb523545 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -15,6 +15,7 @@ #include "TransportSession.h" #include "NTCPSession.h" #include "SSU.h" +#include "NTCP2.h" #include "RouterInfo.h" #include "I2NPProtocol.h" #include "Identity.h" @@ -80,6 +81,7 @@ namespace transport bool IsBoundNTCP() const { return m_NTCPServer != nullptr; } bool IsBoundSSU() const { return m_SSUServer != nullptr; } + bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } bool IsOnline() const { return m_IsOnline; }; void SetOnline (bool online) { m_IsOnline = online; }; @@ -154,6 +156,7 @@ namespace transport NTCPServer * m_NTCPServer; SSUServer * m_SSUServer; + NTCP2Server * m_NTCP2Server; mutable std::mutex m_PeersMutex; std::map m_Peers; @@ -179,6 +182,7 @@ namespace transport // for HTTP only const NTCPServer * GetNTCPServer () const { return m_NTCPServer; }; const SSUServer * GetSSUServer () const { return m_SSUServer; }; + const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; const decltype(m_Peers)& GetPeers () const { return m_Peers; }; }; diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index c7e1b1b4..66620717 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -511,7 +511,7 @@ namespace tunnel HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); break; default: - LogPrint (eLogWarning, "Tunnel: unexpected messsage type ", (int) typeID); + LogPrint (eLogWarning, "Tunnel: unexpected message type ", (int) typeID); } msg = m_Queue.Get (); diff --git a/libi2pd/version.h b/libi2pd/version.h index 36e7eb6e..7c015e84 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 18 +#define I2PD_VERSION_MINOR 20 #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 33 +#define I2P_VERSION_MICRO 36 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index f29ecee3..e745ceaf 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -377,7 +377,7 @@ namespace client } numAddresses++; auto it = m_Addresses.find (name); - if (it != m_Addresses.end ()) // aleady exists ? + if (it != m_Addresses.end ()) // already exists ? { if (it->second != ident->GetIdentHash ()) // address changed? { @@ -755,7 +755,8 @@ namespace client }, SUBSCRIPTION_REQUEST_TIMEOUT); std::unique_lock l(newDataReceivedMutex); - if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) + // wait 1 more second + if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT + 1)) == std::cv_status::timeout) { LogPrint (eLogError, "Addressbook: subscriptions request timeout expired"); numAttempts++; diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index e5acb97b..b58677c8 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -362,8 +362,6 @@ namespace client { m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA m_SharedLocalDestination->Acquire (); - m_Destinations[m_SharedLocalDestination->GetIdentity ()->GetIdentHash ()] = m_SharedLocalDestination; - m_SharedLocalDestination->Start (); } std::shared_ptr ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const @@ -488,8 +486,8 @@ namespace client { localDestination = m_SharedLocalDestination; } - auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort); - if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr(clientTunnel))).second) + auto clientTunnel = std::make_shared(name, dest, end, localDestination, destinationPort); + if(m_ClientForwards.insert(std::make_pair(end, clientTunnel)).second) { clientTunnel->Start(); } @@ -498,32 +496,36 @@ namespace client } else { boost::asio::ip::tcp::endpoint clientEndpoint; - I2PService * clientTunnel = nullptr; + std::shared_ptr clientTunnel; if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS) { // socks proxy std::string outproxy = section.second.get("outproxy", ""); - clientTunnel = new i2p::proxy::SOCKSProxy(name, address, port, !outproxy.emtpy(), outproxy, destinationPort, localDestination); - clientEndpoint = ((i2p::proxy::SOCKSProxy*)clientTunnel)->GetLocalEndpoint (); + auto tun = std::make_shared(name, address, port, !outproxy.empty(), outproxy, destinationPort, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint (); } else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY) { // http proxy std::string outproxy = section.second.get("outproxy", ""); - clientTunnel = new i2p::proxy::HTTPProxy(name, address, port, outproxy, localDestination); - clientEndpoint = ((i2p::proxy::HTTPProxy*)clientTunnel)->GetLocalEndpoint (); + auto tun = std::make_shared(name, address, port, outproxy, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint (); } else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) { // websocks proxy - clientTunnel = new WebSocks(address, port, localDestination);; - clientEndpoint = ((WebSocks*)clientTunnel)->GetLocalEndpoint(); + auto tun = std::make_shared(address, port, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint(); } else { // tcp client - clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort); - clientEndpoint = ((I2PClientTunnel*)clientTunnel)->GetLocalEndpoint (); + auto tun = std::make_shared (name, dest, address, port, localDestination, destinationPort); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint (); } uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); if(timeout) @@ -532,7 +534,7 @@ namespace client LogPrint(eLogInfo, "Clients: I2P Client tunnel connect timeout set to ", timeout); } - auto ins = m_ClientTunnels.insert (std::make_pair (clientEndpoint, std::unique_ptr(clientTunnel))); + auto ins = m_ClientTunnels.insert (std::make_pair (clientEndpoint, clientTunnel)); if (ins.second) { clientTunnel->Start (); @@ -568,7 +570,7 @@ namespace client bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN); + std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1"); bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); @@ -589,7 +591,7 @@ namespace client // TODO: hostnames auto localAddress = boost::asio::ip::address::from_string(address); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); - I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port); + auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port); if(!isUniqueLocal) { LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); @@ -600,7 +602,7 @@ namespace client std::make_pair( std::make_pair( localDestination->GetIdentHash(), port), - std::unique_ptr(serverTunnel))).second) + serverTunnel)).second) { serverTunnel->Start(); LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32()); @@ -611,16 +613,14 @@ namespace client continue; } - I2PServerTunnel * serverTunnel; + std::shared_ptr serverTunnel; if (type == I2P_TUNNELS_SECTION_TYPE_HTTP) - serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip); + serverTunnel = std::make_shared (name, host, port, localDestination, hostOverride, inPort, gzip); else if (type == I2P_TUNNELS_SECTION_TYPE_IRC) - serverTunnel = new I2PServerTunnelIRC (name, host, port, localDestination, webircpass, inPort, gzip); + serverTunnel = std::make_shared (name, host, port, localDestination, webircpass, inPort, gzip); else // regular server tunnel by default - serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip); + serverTunnel = std::make_shared (name, host, port, localDestination, inPort, gzip); - LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns); - serverTunnel->SetMaxConnsPerMinute(maxConns); if(!isUniqueLocal) { LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); @@ -643,8 +643,8 @@ namespace client serverTunnel->SetAccessList (idents); } auto ins = m_ServerTunnels.insert (std::make_pair ( - std::make_pair (localDestination->GetIdentHash (), inPort), - std::unique_ptr(serverTunnel))); + std::make_pair (localDestination->GetIdentHash (), inPort), + serverTunnel)); if (ins.second) { serverTunnel->Start (); diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 922d7acc..06aab3b1 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -113,12 +113,12 @@ namespace client i2p::proxy::HTTPProxy * m_HttpProxy; i2p::proxy::SOCKSProxy * m_SocksProxy; - std::map > m_ClientTunnels; // local endpoint->tunnel - std::map, std::unique_ptr > m_ServerTunnels; // ->tunnel + std::map > m_ClientTunnels; // local endpoint->tunnel + std::map, std::shared_ptr > m_ServerTunnels; // ->tunnel std::mutex m_ForwardsMutex; - std::map > m_ClientForwards; // local endpoint -> udp tunnel - std::map, std::unique_ptr > m_ServerForwards; // -> udp tunnel + std::map > m_ClientForwards; // local endpoint -> udp tunnel + std::map, std::shared_ptr > m_ServerForwards; // -> udp tunnel SAMBridge * m_SamBridge; BOBCommandChannel * m_BOBCommandChannel; diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index 47f756d3..f0530ac1 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -219,7 +219,7 @@ namespace proxy { /* replace headers */ req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); /* add headers */ - req.AddHeader("Connection", "close"); /* keep-alive conns not supported yet */ + req.UpdateHeader("Connection", "close"); /* keep-alive conns not supported yet */ } /** @@ -387,18 +387,12 @@ namespace proxy { LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); m_ClientRequestURL.schema = ""; m_ClientRequestURL.host = ""; + std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? m_ClientRequest.uri = m_ClientRequestURL.to_string(); - if (m_ProxyURL.schema == "http" && (!m_ProxyURL.user.empty () || !m_ProxyURL.pass.empty ())) - { - // http proxy authorization - std::string s = "basic " + i2p::data::ToBase64Standard (m_ProxyURL.user + ":" + m_ProxyURL.pass); - m_ClientRequest.AddHeader("Proxy-Authorization", s); - } - m_ClientRequest.write(m_ClientRequestBuffer); m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); - + // assume http if empty schema if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") { @@ -406,7 +400,18 @@ namespace proxy { if (!m_ProxyURL.port) m_ProxyURL.port = 80; if (m_ProxyURL.is_i2p()) { - m_send_buf = m_recv_buf; + m_ClientRequest.uri = origURI; + if (!m_ProxyURL.user.empty () || !m_ProxyURL.pass.empty ()) + { + // remove existing authorization if any + m_ClientRequest.RemoveHeader("Proxy-"); + // add own http proxy authorization + std::string s = "Basic " + i2p::data::ToBase64Standard (m_ProxyURL.user + ":" + m_ProxyURL.pass); + m_ClientRequest.AddHeader("Proxy-Authorization", s); + } + m_send_buf = m_ClientRequest.to_string(); + m_recv_buf.erase(0, m_req_len); + m_send_buf.append(m_recv_buf); GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), m_ProxyURL.host, m_ProxyURL.port); } diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index b08fded1..7d7ce558 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -250,7 +250,7 @@ namespace client if (handler) (this->*handler)(m_Payload, m_PayloadLen); else - LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]); + LogPrint (eLogError, "I2CP: Unknown I2CP message ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]); } void I2CPSession::Terminate () @@ -398,7 +398,7 @@ namespace client } else { - LogPrint (eLogError, "I2CP: create session signature verification falied"); + LogPrint (eLogError, "I2CP: create session signature verification failed"); SendSessionStatusMessage (3); // invalid } } @@ -455,16 +455,16 @@ namespace client LogPrint(eLogError, "I2CP: invalid reconfigure message signature"); } else - LogPrint(eLogError, "I2CP: mapping size missmatch"); + LogPrint(eLogError, "I2CP: mapping size mismatch"); } else - LogPrint(eLogError, "I2CP: destination missmatch"); + LogPrint(eLogError, "I2CP: destination mismatch"); } else LogPrint(eLogError, "I2CP: malfromed destination"); } else - LogPrint(eLogError, "I2CP: session missmatch"); + LogPrint(eLogError, "I2CP: session mismatch"); } else LogPrint(eLogError, "I2CP: short message"); diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 21e1fdfa..da8dd65a 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -14,6 +14,7 @@ namespace client m_LocalDestination (localDestination ? localDestination : i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)), m_ReadyTimer(m_LocalDestination->GetService()), + m_ReadyTimerTriggered(false), m_ConnectTimeout(0), isUpdated (true) { @@ -47,29 +48,25 @@ namespace client void I2PService::SetConnectTimeout(uint32_t timeout) { - if(timeout && !m_ConnectTimeout) - { - TriggerReadyCheckTimer(); - } - else if (m_ConnectTimeout && !timeout) - { - m_ReadyTimer.cancel(); - } m_ConnectTimeout = timeout; } void I2PService::AddReadyCallback(ReadyCallback cb) { uint32_t now = i2p::util::GetSecondsSinceEpoch(); - uint32_t tm = now + m_ConnectTimeout; + uint32_t tm = (m_ConnectTimeout) ? now + m_ConnectTimeout : NEVER_TIMES_OUT; + LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now); m_ReadyCallbacks.push_back({cb, tm}); + if (!m_ReadyTimerTriggered) TriggerReadyCheckTimer(); } void I2PService::TriggerReadyCheckTimer() { m_ReadyTimer.expires_from_now(boost::posix_time::seconds (1)); m_ReadyTimer.async_wait(std::bind(&I2PService::HandleReadyCheckTimer, this, std::placeholders::_1)); + m_ReadyTimerTriggered = true; + } void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec) @@ -87,7 +84,7 @@ namespace client auto itr = m_ReadyCallbacks.begin(); while(itr != m_ReadyCallbacks.end()) { - if(itr->second >= now) + if(itr->second != NEVER_TIMES_OUT && now >= itr->second) { itr->first(boost::asio::error::timed_out); itr = m_ReadyCallbacks.erase(itr); @@ -96,8 +93,10 @@ namespace client ++itr; } } - if(!ec) - TriggerReadyCheckTimer(); + if(!ec && m_ReadyCallbacks.size()) + TriggerReadyCheckTimer(); + else + m_ReadyTimerTriggered = false; } void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h index ecfd5bc5..55921898 100644 --- a/libi2pd_client/I2PService.h +++ b/libi2pd_client/I2PService.h @@ -67,8 +67,11 @@ namespace client std::mutex m_HandlersMutex; std::vector > m_ReadyCallbacks; boost::asio::deadline_timer m_ReadyTimer; + bool m_ReadyTimerTriggered; uint32_t m_ConnectTimeout; + const size_t NEVER_TIMES_OUT = 0; + public: bool isUpdated; // transient, used during reload only }; diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index 1bdf8bb5..0cff9ad5 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -280,8 +280,6 @@ namespace client const char* GetName() { return m_Name.c_str (); } - void SetMaxConnsPerMinute(const uint32_t conns) { m_PortDestination->SetMaxConnsPerMinute(conns); } - private: void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it, std::shared_ptr resolver); diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 05943981..ac2dd853 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -15,8 +15,8 @@ namespace i2p { namespace client { - SAMSocket::SAMSocket (SAMBridge& owner, std::shared_ptr socket): - m_Owner (owner), m_Socket(socket), m_Timer (m_Owner.GetService ()), + SAMSocket::SAMSocket (SAMBridge& owner): + m_Owner (owner), m_Socket(owner.GetService()), m_Timer (m_Owner.GetService ()), m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_IsAccepting (false), m_Stream (nullptr) @@ -25,51 +25,17 @@ namespace client SAMSocket::~SAMSocket () { - if(m_Stream) - { - m_Stream->Close (); - m_Stream.reset (); - } - auto Session = m_Owner.FindSession(m_ID); - - switch (m_SocketType) - { - case eSAMSocketTypeSession: - m_Owner.CloseSession (m_ID); - break; - case eSAMSocketTypeStream: - { - if (Session) - Session->DelSocket (this); - break; - } - case eSAMSocketTypeAcceptor: - { - if (Session) - { - Session->DelSocket (this); - if (m_IsAccepting && Session->localDestination) - Session->localDestination->StopAcceptingStreams (); - } - break; - } - default: - ; - } - m_SocketType = eSAMSocketTypeTerminated; - if (m_Socket && m_Socket->is_open()) m_Socket->close (); - m_Socket.reset (); + m_Stream = nullptr; } void SAMSocket::Terminate (const char* reason) { if(m_Stream) { - m_Stream->Close (); - m_Stream.reset (); + m_Stream->AsyncClose (); + m_Stream = nullptr; } auto Session = m_Owner.FindSession(m_ID); - switch (m_SocketType) { case eSAMSocketTypeSession: @@ -77,15 +43,12 @@ namespace client break; case eSAMSocketTypeStream: { - if (Session) - Session->DelSocket (this); break; } case eSAMSocketTypeAcceptor: { if (Session) { - Session->DelSocket (this); if (m_IsAccepting && Session->localDestination) Session->localDestination->StopAcceptingStreams (); } @@ -95,16 +58,20 @@ namespace client ; } m_SocketType = eSAMSocketTypeTerminated; - if (m_Socket && m_Socket->is_open()) m_Socket->close (); - m_Socket.reset (); + if (m_Socket.is_open ()) + { + boost::system::error_code ec; + m_Socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); + m_Socket.close (); + } + m_Owner.RemoveSocket(shared_from_this()); } void SAMSocket::ReceiveHandshake () - { - if(m_Socket) - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); + { + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); } static bool SAMVersionAcceptable(const std::string & ver) @@ -125,7 +92,7 @@ namespace client void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) - { + { LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: handshake read error"); @@ -184,7 +151,7 @@ namespace client #else size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); #endif - boost::asio::async_write (*m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -199,17 +166,22 @@ namespace client } } + bool SAMSocket::IsSession(const std::string & id) const + { + return id == m_ID; + } + void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) - { + { LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: handshake reply send error"); } - else if(m_Socket) + else { - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), std::bind(&SAMSocket::HandleMessage, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -220,7 +192,7 @@ namespace client LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg); if (!m_IsSilent) - boost::asio::async_write (*m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, close)); else @@ -235,7 +207,7 @@ namespace client void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) { if (ecode) - { + { LogPrint (eLogError, "SAM: reply send error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: reply send error"); @@ -252,7 +224,7 @@ namespace client void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) - { + { LogPrint (eLogError, "SAM: read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: read error"); @@ -501,7 +473,6 @@ namespace client if(session) { m_SocketType = eSAMSocketTypeStream; - session->AddSocket (shared_from_this ()); m_Stream = session->localDestination->CreateStream (remote); m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send m_BufferOffset = 0; @@ -534,7 +505,6 @@ namespace client if (session) { m_SocketType = eSAMSocketTypeAcceptor; - session->AddSocket (shared_from_this ()); if (!session->localDestination->IsAcceptingStreams ()) { m_IsAccepting = true; @@ -599,7 +569,7 @@ namespace client keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); #else size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); + keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); #endif SendMessageReply (m_Buffer, l, false); } @@ -704,17 +674,9 @@ namespace client void SAMSocket::Receive () { - if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE) - { - LogPrint (eLogError, "SAM: Buffer is full, terminate"); - Terminate ("Buffer is full"); - return; - } else if (m_Socket) - m_Socket->async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), - std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - else - LogPrint(eLogError, "SAM: receive with no native socket"); + m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), + std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage, + shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -731,15 +693,12 @@ namespace client { bytes_transferred += m_BufferOffset; m_BufferOffset = 0; - auto s = shared_from_this (); m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred, - [s](const boost::system::error_code& ecode) - { - if (!ecode) - s->m_Owner.GetService ().post ([s] { s->Receive (); }); - else - s->m_Owner.GetService ().post ([s] { s->Terminate ("AsyncSend failed"); }); - }); + std::bind(&SAMSocket::HandleStreamSend, shared_from_this(), std::placeholders::_1)); + } + else + { + Terminate("No Stream Remaining"); } } } @@ -766,21 +725,21 @@ namespace client WriteI2PDataImmediate(buff, len); } else // no more data + { + delete [] buff; Terminate ("no more data"); + } } } } void SAMSocket::WriteI2PDataImmediate(uint8_t * buff, size_t sz) { - if(m_Socket) - boost::asio::async_write ( - *m_Socket, - boost::asio::buffer (buff, sz), - boost::asio::transfer_all(), - std::bind (&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this (), std::placeholders::_1, buff)); // postpone termination - else - LogPrint(eLogError, "SAM: no native socket"); + boost::asio::async_write ( + m_Socket, + boost::asio::buffer (buff, sz), + boost::asio::transfer_all(), + std::bind (&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this (), std::placeholders::_1, buff)); // postpone termination } void SAMSocket::HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff) @@ -790,9 +749,11 @@ namespace client void SAMSocket::WriteI2PData(size_t sz) { - uint8_t * sendbuff = new uint8_t[sz]; - memcpy(sendbuff, m_StreamBuffer, sz); - WriteI2PDataImmediate(sendbuff, sz); + boost::asio::async_write ( + m_Socket, + boost::asio::buffer (m_StreamBuffer, sz), + boost::asio::transfer_all(), + std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -826,7 +787,8 @@ namespace client { WriteI2PData(bytes_transferred); } - I2PReceive(); + else + I2PReceive(); } } } @@ -858,7 +820,7 @@ namespace client if (session) { // find more pending acceptors - for (auto it: session->ListSockets ()) + for (auto & it: m_Owner.ListSockets (m_ID)) if (it->m_SocketType == eSAMSocketTypeAcceptor) { it->m_IsAccepting = true; @@ -930,29 +892,30 @@ namespace client } } - SAMSession::SAMSession (std::shared_ptr dest): + void SAMSocket::HandleStreamSend(const boost::system::error_code & ec) + { + m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); + } + + SAMSession::SAMSession (SAMBridge & parent, const std::string & id, std::shared_ptr dest): + m_Bridge(parent), localDestination (dest), - UDPEndpoint(nullptr) + UDPEndpoint(nullptr), + Name(id) { } - + SAMSession::~SAMSession () { - CloseStreams(); i2p::client::context.DeleteLocalDestination (localDestination); } void SAMSession::CloseStreams () { - std::vector > socks; + for(const auto & itr : m_Bridge.ListSockets(Name)) { - std::lock_guard lock(m_SocketsMutex); - for (const auto& sock : m_Sockets) { - socks.push_back(sock); - } + itr->Terminate(nullptr); } - for (auto & sock : socks ) sock->Terminate("SAMSession::CloseStreams()"); - m_Sockets.clear(); } SAMBridge::SAMBridge (const std::string& address, int port): @@ -1009,12 +972,17 @@ namespace client void SAMBridge::Accept () { - auto native = std::make_shared(m_Service); - auto newSocket = std::make_shared (*this, native); - m_Acceptor.async_accept (*native, std::bind (&SAMBridge::HandleAccept, this, + auto newSocket = std::make_shared(*this); + m_Acceptor.async_accept (newSocket->GetSocket(), std::bind (&SAMBridge::HandleAccept, this, std::placeholders::_1, newSocket)); } + void SAMBridge::RemoveSocket(const std::shared_ptr & socket) + { + std::unique_lock lock(m_OpenSocketsMutex); + m_OpenSockets.remove_if([socket](const std::shared_ptr & item) -> bool { return item == socket; }); + } + void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) { if (!ecode) @@ -1024,6 +992,10 @@ namespace client if (!ec) { LogPrint (eLogDebug, "SAM: new connection from ", ep); + { + std::unique_lock l(m_OpenSocketsMutex); + m_OpenSockets.push_back(socket); + } socket->ReceiveHandshake (); } else @@ -1066,7 +1038,7 @@ namespace client if (localDestination) { localDestination->Acquire (); - auto session = std::make_shared(localDestination); + auto session = std::make_shared(*this, id, localDestination); std::unique_lock l(m_SessionsMutex); auto ret = m_Sessions.insert (std::make_pair(id, session)); if (!ret.second) @@ -1105,6 +1077,18 @@ namespace client return nullptr; } + std::list > SAMBridge::ListSockets(const std::string & id) const + { + std::list > list; + { + std::unique_lock l(m_OpenSocketsMutex); + for (const auto & itr : m_OpenSockets) + if (itr->IsSession(id)) + list.push_back(itr); + } + return list; + } + void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote) { if(remote) @@ -1127,33 +1111,38 @@ namespace client { m_DatagramReceiveBuffer[bytes_transferred] = 0; char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n'); - *eol = 0; eol++; - size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); - LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); - char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); - if (sessionID) + if(eol) { - sessionID++; - char * destination = strchr (sessionID, ' '); - if (destination) + *eol = 0; eol++; + size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); + LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); + char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); + if (sessionID) { - *destination = 0; destination++; - auto session = FindSession (sessionID); - if (session) + sessionID++; + char * destination = strchr (sessionID, ' '); + if (destination) { - i2p::data::IdentityEx dest; - dest.FromBase64 (destination); - session->localDestination->GetDatagramDestination ()-> - SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); + *destination = 0; destination++; + auto session = FindSession (sessionID); + if (session) + { + i2p::data::IdentityEx dest; + dest.FromBase64 (destination); + session->localDestination->GetDatagramDestination ()-> + SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); + } + else + LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); } else - LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); + LogPrint (eLogError, "SAM: Missing destination key"); } else - LogPrint (eLogError, "SAM: Missing destination key"); + LogPrint (eLogError, "SAM: Missing sessionID"); } else - LogPrint (eLogError, "SAM: Missing sessionID"); + LogPrint(eLogError, "SAM: invalid datagram"); ReceiveDatagram (); } else diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 6ecd14a4..953af1cd 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -82,18 +82,21 @@ namespace client public: typedef boost::asio::ip::tcp::socket Socket_t; - SAMSocket (SAMBridge& owner, std::shared_ptr socket); + SAMSocket (SAMBridge& owner); ~SAMSocket (); - boost::asio::ip::tcp::socket& GetSocket () { return *m_Socket; }; + Socket_t& GetSocket () { return m_Socket; }; void ReceiveHandshake (); void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; SAMSocketType GetSocketType () const { return m_SocketType; }; void Terminate (const char* reason); + bool IsSession(const std::string & id) const; + private: - + void TerminateClose() { Terminate(nullptr); } + void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -128,10 +131,12 @@ namespace client void WriteI2PDataImmediate(uint8_t * ptr, size_t sz); void HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff); + void HandleStreamSend(const boost::system::error_code & ec); + private: SAMBridge& m_Owner; - std::shared_ptr m_Socket; + Socket_t m_Socket; boost::asio::deadline_timer m_Timer; char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; size_t m_BufferOffset; @@ -145,34 +150,12 @@ namespace client struct SAMSession { + SAMBridge & m_Bridge; std::shared_ptr localDestination; - std::list > m_Sockets; std::shared_ptr UDPEndpoint; - std::mutex m_SocketsMutex; + std::string Name; - /** safely add a socket to this session */ - void AddSocket(std::shared_ptr sock) { - std::lock_guard lock(m_SocketsMutex); - m_Sockets.push_back(sock); - } - - /** safely remove a socket from this session */ - void DelSocket(SAMSocket * sock) { - std::lock_guard lock(m_SocketsMutex); - m_Sockets.remove_if([sock](const std::shared_ptr s) -> bool { return s.get() == sock; }); - } - - /** get a list holding a copy of all sam sockets from this session */ - std::list > ListSockets() { - std::list > l; - { - std::lock_guard lock(m_SocketsMutex); - for(const auto& sock : m_Sockets ) l.push_back(sock); - } - return l; - } - - SAMSession (std::shared_ptr dest); + SAMSession (SAMBridge & parent, const std::string & name, std::shared_ptr dest); ~SAMSession (); void CloseStreams (); @@ -189,14 +172,18 @@ namespace client void Stop (); boost::asio::io_service& GetService () { return m_Service; }; - std::shared_ptr CreateSession (const std::string& id, const std::string& destination, // empty string means transient + std::shared_ptr CreateSession (const std::string& id, const std::string& destination, // empty string means transient const std::map * params); void CloseSession (const std::string& id); std::shared_ptr FindSession (const std::string& id) const; + std::list > ListSockets(const std::string & id) const; + /** send raw data to remote endpoint from our UDP Socket */ void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); + void RemoveSocket(const std::shared_ptr & socket); + private: void Run (); @@ -217,6 +204,8 @@ namespace client boost::asio::ip::udp::socket m_DatagramSocket; mutable std::mutex m_SessionsMutex; std::map > m_Sessions; + mutable std::mutex m_OpenSocketsMutex; + std::list > m_OpenSockets; uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; public: diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index ddf4bbe9..8a67901c 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -84,8 +84,8 @@ namespace proxy SOCKS5_HOST_UNREACH = 4, // Host unreachable SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer SOCKS5_TTL_EXPIRED = 6, // TTL Expired - SOCKS5_CMD_UNSUP = 7, // Command unsuported - SOCKS5_ADDR_UNSUP = 8, // Address type unsuported + SOCKS5_CMD_UNSUP = 7, // Command unsupported + SOCKS5_ADDR_UNSUP = 8, // Address type unsupported SOCKS4_OK = 90, // No error for SOCKS4 SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server diff --git a/qt/i2pd_qt/DaemonQT.cpp b/qt/i2pd_qt/DaemonQT.cpp index dd7c892d..f5e6d62b 100644 --- a/qt/i2pd_qt/DaemonQT.cpp +++ b/qt/i2pd_qt/DaemonQT.cpp @@ -1,6 +1,11 @@ +#include + #include "DaemonQT.h" #include "Daemon.h" #include "mainwindow.h" + +#include "Log.h" + #include #include #include @@ -90,12 +95,12 @@ namespace qt delete mutex; } - bool DaemonQTImpl::init(int argc, char* argv[]) + bool DaemonQTImpl::init(int argc, char* argv[], std::shared_ptr logstream) { mutex=new QMutex(QMutex::Recursive); setRunningCallback(0); m_IsRunning=false; - return Daemon.init(argc,argv); + return Daemon.init(argc,argv,logstream); } void DaemonQTImpl::start() @@ -146,33 +151,35 @@ namespace qt int result; { + std::shared_ptr logstreamptr=std::make_shared(); + //TODO move daemon init deinit to a bg thread DaemonQTImpl daemon; - qDebug("Initialising the daemon..."); - bool daemonInitSuccess = daemon.init(argc, argv); + (*logstreamptr) << "Initialising the daemon..." << std::endl; + bool daemonInitSuccess = daemon.init(argc, argv, logstreamptr); if(!daemonInitSuccess) { QMessageBox::critical(0, "Error", "Daemon init failed"); return 1; } - qDebug("Initialised, creating the main window..."); - MainWindow w; - qDebug("Before main window.show()..."); + LogPrint(eLogDebug, "Initialised, creating the main window..."); + MainWindow w(logstreamptr); + LogPrint(eLogDebug, "Before main window.show()..."); w.show (); { i2p::qt::Controller daemonQtController(daemon); w.setI2PController(&daemonQtController); - qDebug("Starting the daemon..."); + LogPrint(eLogDebug, "Starting the daemon..."); emit daemonQtController.startDaemon(); //daemon.start (); - qDebug("Starting GUI event loop..."); + LogPrint(eLogDebug, "Starting GUI event loop..."); result = app.exec(); //daemon.stop (); } } //QMessageBox::information(&w, "Debug", "demon stopped"); - qDebug("Exiting the application"); + LogPrint(eLogDebug, "Exiting the application"); return result; } } diff --git a/qt/i2pd_qt/DaemonQT.h b/qt/i2pd_qt/DaemonQT.h index d0add0e3..aa329f56 100644 --- a/qt/i2pd_qt/DaemonQT.h +++ b/qt/i2pd_qt/DaemonQT.h @@ -1,6 +1,7 @@ #ifndef DAEMONQT_H #define DAEMONQT_H +#include #include #include #include @@ -25,7 +26,7 @@ namespace qt * @param argv * @return success */ - bool init(int argc, char* argv[]); + bool init(int argc, char* argv[], std::shared_ptr logstream); void start(); void stop(); void restart(); diff --git a/qt/i2pd_qt/ServerTunnelPane.cpp b/qt/i2pd_qt/ServerTunnelPane.cpp index 029a3ea2..bc6389a9 100644 --- a/qt/i2pd_qt/ServerTunnelPane.cpp +++ b/qt/i2pd_qt/ServerTunnelPane.cpp @@ -204,24 +204,6 @@ int ServerTunnelPane::appendServerTunnelForm( horizontalLayout_2->addItem(horizontalSpacer); tunnelGridLayout->addLayout(horizontalLayout_2); } - { - uint32_t maxConns = tunnelConfig->getmaxConns(); - QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); - horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2")); - ui.maxConnsLabel = new QLabel(gridLayoutWidget_2); - maxConnsLabel->setObjectName(QStringLiteral("maxConnsLabel")); - horizontalLayout_2->addWidget(maxConnsLabel); - ui.maxConnsLineEdit = new QLineEdit(gridLayoutWidget_2); - maxConnsLineEdit->setObjectName(QStringLiteral("maxConnsLineEdit")); - maxConnsLineEdit->setText(QString::number(maxConns)); - maxConnsLineEdit->setMaximumWidth(80); - QObject::connect(maxConnsLineEdit, SIGNAL(textChanged(const QString &)), - this, SLOT(updated())); - horizontalLayout_2->addWidget(maxConnsLineEdit); - QSpacerItem * horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - horizontalLayout_2->addItem(horizontalSpacer); - tunnelGridLayout->addLayout(horizontalLayout_2); - } { std::string address = tunnelConfig->getaddress(); QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); diff --git a/qt/i2pd_qt/ServerTunnelPane.h b/qt/i2pd_qt/ServerTunnelPane.h index 556c8473..0a07267b 100644 --- a/qt/i2pd_qt/ServerTunnelPane.h +++ b/qt/i2pd_qt/ServerTunnelPane.h @@ -81,10 +81,6 @@ private: QLabel * addressLabel; QLineEdit * addressLineEdit; - //maxConns - QLabel * maxConnsLabel; - QLineEdit * maxConnsLineEdit; - //gzip QCheckBox * gzipCheckBox; @@ -109,7 +105,6 @@ private: hostOverrideLabel->setText(QApplication::translate("srvTunForm", "Host override:", 0)); webIRCPassLabel->setText(QApplication::translate("srvTunForm", "WebIRC password:", 0)); addressLabel->setText(QApplication::translate("srvTunForm", "Address:", 0)); - maxConnsLabel->setText(QApplication::translate("srvTunForm", "Max connections:", 0)); gzipCheckBox->setText(QApplication::translate("srvTunForm", "GZip", 0)); isUniqueLocalCheckBox->setText(QApplication::translate("srvTunForm", "Is unique local", 0)); @@ -152,14 +147,6 @@ protected: stc->setaddress(addressLineEdit->text().toStdString()); - auto mcStr=maxConnsLineEdit->text(); - uint32_t mcInt=(uint32_t)mcStr.toInt(&ok); - if(!ok){ - highlightWrongInput(QApplication::tr("Bad maxConns, must be int.")+" "+cannotSaveSettings,maxConnsLineEdit); - return false; - } - stc->setmaxConns(mcInt); - stc->setgzip(gzipCheckBox->isChecked()); stc->setisUniqueLocal(isUniqueLocalCheckBox->isChecked()); diff --git a/qt/i2pd_qt/SignatureTypeComboboxFactory.h b/qt/i2pd_qt/SignatureTypeComboboxFactory.h index 41245dac..f7cac658 100644 --- a/qt/i2pd_qt/SignatureTypeComboboxFactory.h +++ b/qt/i2pd_qt/SignatureTypeComboboxFactory.h @@ -18,7 +18,7 @@ class SignatureTypeComboBoxFactory } public: - static const uint16_t getSigType(const QVariant& var) { + static uint16_t getSigType(const QVariant& var) { return (uint16_t)var.toInt(); } diff --git a/qt/i2pd_qt/TunnelConfig.cpp b/qt/i2pd_qt/TunnelConfig.cpp index 81216c0b..e4354b62 100644 --- a/qt/i2pd_qt/TunnelConfig.cpp +++ b/qt/i2pd_qt/TunnelConfig.cpp @@ -48,7 +48,6 @@ void ServerTunnelConfig::saveToStringStream(std::stringstream& out) { << "enableuniquelocal=" << (isUniqueLocal?"true":"false") << "\n" << "address=" << address << "\n" << "hostoverride=" << hostOverride << "\n" - << "webircpassword=" << webircpass << "\n" - << "maxconns=" << maxConns << "\n"; + << "webircpassword=" << webircpass << "\n"; } diff --git a/qt/i2pd_qt/TunnelConfig.h b/qt/i2pd_qt/TunnelConfig.h index c714a4f5..58a1fa0b 100644 --- a/qt/i2pd_qt/TunnelConfig.h +++ b/qt/i2pd_qt/TunnelConfig.h @@ -148,7 +148,6 @@ public: std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); - uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN); std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1"); bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); # * inport -- optional, i2p service port, if unset - the same as 'port' @@ -170,7 +169,6 @@ class ServerTunnelConfig : public TunnelConfig { std::string webircpass; bool gzip; i2p::data::SigningKeyType sigType; - uint32_t maxConns; std::string address; bool isUniqueLocal; public: @@ -184,7 +182,6 @@ public: std::string webircpass_, bool gzip_, i2p::data::SigningKeyType sigType_, - uint32_t maxConns_, std::string address_, bool isUniqueLocal_): TunnelConfig(name_, type_, i2cpParameters_), host(host_), @@ -196,7 +193,6 @@ public: webircpass(webircpass_), gzip(gzip_), sigType(sigType_), - maxConns(maxConns_), address(address_), isUniqueLocal(isUniqueLocal_) {} std::string& gethost(){return host;} @@ -208,7 +204,6 @@ public: std::string& getwebircpass(){return webircpass;} bool getgzip(){return gzip;} i2p::data::SigningKeyType getsigType(){return sigType;} - uint32_t getmaxConns(){return maxConns;} std::string& getaddress(){return address;} bool getisUniqueLocal(){return isUniqueLocal;} void sethost(const std::string& host_){host=host_;} @@ -220,7 +215,6 @@ public: void setwebircpass(const std::string& webircpass_){webircpass=webircpass_;} void setgzip(bool gzip_){gzip=gzip_;} void setsigType(i2p::data::SigningKeyType sigType_){sigType=sigType_;} - void setmaxConns(uint32_t maxConns_){maxConns=maxConns_;} void setaddress(const std::string& address_){address=address_;} void setisUniqueLocal(bool isUniqueLocal_){isUniqueLocal=isUniqueLocal_;} virtual void saveToStringStream(std::stringstream& out); diff --git a/qt/i2pd_qt/android/AndroidManifest.xml b/qt/i2pd_qt/android/AndroidManifest.xml index 56b37e6a..c62e118e 100644 --- a/qt/i2pd_qt/android/AndroidManifest.xml +++ b/qt/i2pd_qt/android/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl index bbd8116d..e23cb699 100644 --- a/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl +++ b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl @@ -51,7 +51,7 @@ interface IMinistro * "sources" StringArray Sources list from where Ministro will download the libs. Make sure you are using ONLY secure locations. * "repository" String Overwrites the default Ministro repository. Possible values: default, stable, testing and unstable * "required.modules" StringArray Required modules by your application -* "application.title" String Application name, used to show more informations to user +* "application.title" String Application name, used to show more information to user * "qt.provider" String Qt libs provider, currently only "necessitas" is supported. * "minimum.ministro.api" Integer Minimum Ministro API level, used to check if Ministro service compatible with your application. Current API Level is 3 ! * "minimum.qt.version" Integer Minimim Qt version (e.g. 0x040800, which means Qt 4.8.0, check http://qt-project.org/doc/qt-4.8/qtglobal.html#QT_VERSION)! diff --git a/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java index 677e8f46..9c607109 100644 --- a/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -97,7 +97,7 @@ import android.view.ActionMode.Callback; public class QtActivity extends Activity { - private final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro instalation is finished + private final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro installation is finished private static final int MINISTRO_API_LEVEL = 5; // Ministro api level (check IMinistro.aidl file) private static final int NECESSITAS_API_LEVEL = 2; // Necessitas api level used by platform plugin private static final int QT_VERSION = 0x050100; // This app requires at least Qt version 5.1.0 diff --git a/qt/i2pd_qt/i2pd.qrc b/qt/i2pd_qt/i2pd.qrc index 2abdeb05..4e5523e9 100644 --- a/qt/i2pd_qt/i2pd.qrc +++ b/qt/i2pd_qt/i2pd.qrc @@ -1,5 +1,6 @@ - - images/icon.png - + + resources/icons/mask.ico + resources/images/icon.png + diff --git a/qt/i2pd_qt/i2pd.rc b/qt/i2pd_qt/i2pd.rc new file mode 100644 index 00000000..bebdf1d6 --- /dev/null +++ b/qt/i2pd_qt/i2pd.rc @@ -0,0 +1,32 @@ +IDI_ICON1 ICON DISCARDABLE "resources/icons/mask.ico" + +#include // needed for VERSIONINFO +#include "../../libi2pd/version.h" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION I2PD_VERSION_MAJOR,I2PD_VERSION_MINOR,I2PD_VERSION_MICRO,I2PD_VERSION_PATCH +PRODUCTVERSION I2P_VERSION_MAJOR,I2P_VERSION_MINOR,I2P_VERSION_MICRO,I2P_VERSION_PATCH +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "PurpleI2P" + VALUE "FileDescription", "I2Pd Qt" + VALUE "FileVersion", I2PD_VERSION + VALUE "InternalName", "i2pd-qt" + VALUE "LegalCopyright", "Copyright (C) 2013-2018, The PurpleI2P Project" + VALUE "LegalTrademarks1", "Distributed under the BSD 3-Clause software license, see the accompanying file COPYING or https://opensource.org/licenses/BSD-3-Clause." + VALUE "OriginalFilename", "i2pd_qt.exe" + VALUE "ProductName", "i2pd-qt" + VALUE "ProductVersion", I2P_VERSION + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 941dfff3..0488f289 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -1,289 +1,318 @@ -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = i2pd_qt -TEMPLATE = app -QMAKE_CXXFLAGS *= -std=c++11 -DEFINES += USE_UPNP - -# change to your own path, where you will store all needed libraries with 'git clone' commands below. -MAIN_PATH = /path/to/libraries - -# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -# 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 -BOOST_PATH = $$MAIN_PATH/Boost-for-Android-Prebuilt -OPENSSL_PATH = $$MAIN_PATH/OpenSSL-for-Android-Prebuilt -MINIUPNP_PATH = $$MAIN_PATH/MiniUPnP-for-Android-Prebuilt -IFADDRS_PATH = $$MAIN_PATH/android-ifaddrs - -# Steps in Android SDK manager: -# 1) Check Extras/Google Support Library https://developer.android.com/topic/libraries/support-library/setup.html -# 2) Check API 11 -# Finally, click Install. - -SOURCES += DaemonQT.cpp mainwindow.cpp \ - ../../libi2pd/api.cpp \ - ../../libi2pd/Base.cpp \ - ../../libi2pd/BloomFilter.cpp \ - ../../libi2pd/Config.cpp \ - ../../libi2pd/CPU.cpp \ - ../../libi2pd/Crypto.cpp \ - ../../libi2pd/CryptoKey.cpp \ - ../../libi2pd/Datagram.cpp \ - ../../libi2pd/Destination.cpp \ - ../../libi2pd/Event.cpp \ - ../../libi2pd/Family.cpp \ - ../../libi2pd/FS.cpp \ - ../../libi2pd/Garlic.cpp \ - ../../libi2pd/Gost.cpp \ - ../../libi2pd/Gzip.cpp \ - ../../libi2pd/HTTP.cpp \ - ../../libi2pd/I2NPProtocol.cpp \ - ../../libi2pd/I2PEndian.cpp \ - ../../libi2pd/Identity.cpp \ - ../../libi2pd/LeaseSet.cpp \ - ../../libi2pd/Log.cpp \ - ../../libi2pd/NetDb.cpp \ - ../../libi2pd/NetDbRequests.cpp \ - ../../libi2pd/NTCPSession.cpp \ - ../../libi2pd/Profiling.cpp \ - ../../libi2pd/Reseed.cpp \ - ../../libi2pd/RouterContext.cpp \ - ../../libi2pd/RouterInfo.cpp \ - ../../libi2pd/Signature.cpp \ - ../../libi2pd/SSU.cpp \ - ../../libi2pd/SSUData.cpp \ - ../../libi2pd/SSUSession.cpp \ - ../../libi2pd/Streaming.cpp \ - ../../libi2pd/Timestamp.cpp \ - ../../libi2pd/TransitTunnel.cpp \ - ../../libi2pd/Transports.cpp \ - ../../libi2pd/Tunnel.cpp \ - ../../libi2pd/TunnelEndpoint.cpp \ - ../../libi2pd/TunnelGateway.cpp \ - ../../libi2pd/TunnelPool.cpp \ - ../../libi2pd/util.cpp \ - ../../libi2pd_client/AddressBook.cpp \ - ../../libi2pd_client/BOB.cpp \ - ../../libi2pd_client/ClientContext.cpp \ - ../../libi2pd_client/HTTPProxy.cpp \ - ../../libi2pd_client/I2CP.cpp \ - ../../libi2pd_client/I2PService.cpp \ - ../../libi2pd_client/I2PTunnel.cpp \ - ../../libi2pd_client/MatchedDestination.cpp \ - ../../libi2pd_client/SAM.cpp \ - ../../libi2pd_client/SOCKS.cpp \ - ../../libi2pd_client/Websocket.cpp \ - ../../libi2pd_client/WebSocks.cpp \ - ClientTunnelPane.cpp \ - MainWindowItems.cpp \ - ServerTunnelPane.cpp \ - SignatureTypeComboboxFactory.cpp \ - TunnelConfig.cpp \ - TunnelPane.cpp \ - ../../daemon/Daemon.cpp \ - ../../daemon/HTTPServer.cpp \ - ../../daemon/i2pd.cpp \ - ../../daemon/I2PControl.cpp \ - ../../daemon/UnixDaemon.cpp \ - ../../daemon/UPnP.cpp \ - textbrowsertweaked1.cpp \ - pagewithbackbutton.cpp \ - widgetlock.cpp \ - widgetlockregistry.cpp - -#qt creator does not handle this well -#SOURCES += $$files(../../libi2pd/*.cpp) -#SOURCES += $$files(../../libi2pd_client/*.cpp) -#SOURCES += $$files(../../daemon/*.cpp) -#SOURCES += $$files(./*.cpp) - -SOURCES -= ../../daemon/UnixDaemon.cpp - -HEADERS += DaemonQT.h mainwindow.h \ - ../../libi2pd/api.h \ - ../../libi2pd/Base.h \ - ../../libi2pd/BloomFilter.h \ - ../../libi2pd/Config.h \ - ../../libi2pd/Crypto.h \ - ../../libi2pd/CryptoKey.h \ - ../../libi2pd/Datagram.h \ - ../../libi2pd/Destination.h \ - ../../libi2pd/Event.h \ - ../../libi2pd/Family.h \ - ../../libi2pd/FS.h \ - ../../libi2pd/Garlic.h \ - ../../libi2pd/Gost.h \ - ../../libi2pd/Gzip.h \ - ../../libi2pd/HTTP.h \ - ../../libi2pd/I2NPProtocol.h \ - ../../libi2pd/I2PEndian.h \ - ../../libi2pd/Identity.h \ - ../../libi2pd/LeaseSet.h \ - ../../libi2pd/LittleBigEndian.h \ - ../../libi2pd/Log.h \ - ../../libi2pd/NetDb.hpp \ - ../../libi2pd/NetDbRequests.h \ - ../../libi2pd/NTCPSession.h \ - ../../libi2pd/Profiling.h \ - ../../libi2pd/Queue.h \ - ../../libi2pd/Reseed.h \ - ../../libi2pd/RouterContext.h \ - ../../libi2pd/RouterInfo.h \ - ../../libi2pd/Signature.h \ - ../../libi2pd/SSU.h \ - ../../libi2pd/SSUData.h \ - ../../libi2pd/SSUSession.h \ - ../../libi2pd/Streaming.h \ - ../../libi2pd/Tag.h \ - ../../libi2pd/Timestamp.h \ - ../../libi2pd/TransitTunnel.h \ - ../../libi2pd/Transports.h \ - ../../libi2pd/TransportSession.h \ - ../../libi2pd/Tunnel.h \ - ../../libi2pd/TunnelBase.h \ - ../../libi2pd/TunnelConfig.h \ - ../../libi2pd/TunnelEndpoint.h \ - ../../libi2pd/TunnelGateway.h \ - ../../libi2pd/TunnelPool.h \ - ../../libi2pd/util.h \ - ../../libi2pd/version.h \ - ../../libi2pd_client/AddressBook.h \ - ../../libi2pd_client/BOB.h \ - ../../libi2pd_client/ClientContext.h \ - ../../libi2pd_client/HTTPProxy.h \ - ../../libi2pd_client/I2CP.h \ - ../../libi2pd_client/I2PService.h \ - ../../libi2pd_client/I2PTunnel.h \ - ../../libi2pd_client/MatchedDestination.h \ - ../../libi2pd_client/SAM.h \ - ../../libi2pd_client/SOCKS.h \ - ../../libi2pd_client/Websocket.h \ - ../../libi2pd_client/WebSocks.h \ - ClientTunnelPane.h \ - MainWindowItems.h \ - ServerTunnelPane.h \ - SignatureTypeComboboxFactory.h \ - TunnelConfig.h \ - TunnelPane.h \ - TunnelsPageUpdateListener.h \ - ../../daemon/Daemon.h \ - ../../daemon/HTTPServer.h \ - ../../daemon/I2PControl.h \ - ../../daemon/UPnP.h \ - textbrowsertweaked1.h \ - pagewithbackbutton.h \ - widgetlock.h \ - widgetlockregistry.h - -INCLUDEPATH += ../../libi2pd -INCLUDEPATH += ../../libi2pd_client -INCLUDEPATH += ../../daemon -INCLUDEPATH += . - -FORMS += mainwindow.ui \ - tunnelform.ui \ - statusbuttons.ui \ - routercommandswidget.ui \ - generalsettingswidget.ui - -LIBS += -lz - -macx { - message("using mac os x target") - BREWROOT=/usr/local - BOOSTROOT=$$BREWROOT/opt/boost - SSLROOT=$$BREWROOT/opt/libressl - UPNPROOT=$$BREWROOT/opt/miniupnpc - INCLUDEPATH += $$BOOSTROOT/include - INCLUDEPATH += $$SSLROOT/include - INCLUDEPATH += $$UPNPROOT/include - LIBS += $$SSLROOT/lib/libcrypto.a - LIBS += $$SSLROOT/lib/libssl.a - LIBS += $$BOOSTROOT/lib/libboost_system.a - LIBS += $$BOOSTROOT/lib/libboost_date_time.a - LIBS += $$BOOSTROOT/lib/libboost_filesystem.a - LIBS += $$BOOSTROOT/lib/libboost_program_options.a - LIBS += $$UPNPROOT/lib/libminiupnpc.a -} - -android { - message("Using Android settings") - DEFINES += ANDROID=1 - DEFINES += __ANDROID__ - - CONFIG += mobility - - MOBILITY = - - INCLUDEPATH += $$BOOST_PATH/boost_1_53_0/include \ - $$OPENSSL_PATH/openssl-1.0.2/include \ - $$MINIUPNP_PATH/miniupnp-2.0/include \ - $$IFADDRS_PATH - DISTFILES += android/AndroidManifest.xml - - ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android - - SOURCES += $$IFADDRS_PATH/ifaddrs.c - HEADERS += $$IFADDRS_PATH/ifaddrs.h - - equals(ANDROID_TARGET_ARCH, armeabi-v7a){ - DEFINES += ANDROID_ARM7A - # http://stackoverflow.com/a/30235934/529442 - LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \ - -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ - -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ - -L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl \ - -L$$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/ -lminiupnpc - - PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \ - $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a - DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include - - ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \ - $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so \ - $$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/libminiupnpc.so - } - - equals(ANDROID_TARGET_ARCH, x86){ - # http://stackoverflow.com/a/30235934/529442 - LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \ - -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ - -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ - -L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl \ - -L$$MINIUPNP_PATH/miniupnp-2.0/x86/lib/ -lminiupnpc - - PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \ - $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a - - DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include - - ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \ - $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so \ - $$MINIUPNP_PATH/miniupnp-2.0/x86/lib/libminiupnpc.so - } -} - -linux:!android { - message("Using Linux settings") - LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc -} - -windows:!android { - message("Using Windows settings") - DEFINES += BOOST_USE_WINDOWS_H WINDOWS - LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc -} - -!android:!symbian:!maemo5:!simulator { - message("Build with a system tray icon") - # see also http://doc.qt.io/qt-4.8/qt-desktop-systray-systray-pro.html for example on wince* - #sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS i2pd_qt.pro resources images - RESOURCES = i2pd.qrc - QT += xml - #INSTALLS += sources -} - +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = i2pd_qt +TEMPLATE = app +QMAKE_CXXFLAGS *= -std=c++11 -ggdb +DEFINES += USE_UPNP + +# change to your own path, where you will store all needed libraries with 'git clone' commands below. +MAIN_PATH = /path/to/libraries + +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# 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 +BOOST_PATH = $$MAIN_PATH/Boost-for-Android-Prebuilt +OPENSSL_PATH = $$MAIN_PATH/OpenSSL-for-Android-Prebuilt +MINIUPNP_PATH = $$MAIN_PATH/MiniUPnP-for-Android-Prebuilt +IFADDRS_PATH = $$MAIN_PATH/android-ifaddrs + +# Steps in Android SDK manager: +# 1) Check Extras/Google Support Library https://developer.android.com/topic/libraries/support-library/setup.html +# 2) Check API 11 +# Finally, click Install. + +SOURCES += DaemonQT.cpp mainwindow.cpp \ + ../../libi2pd/api.cpp \ + ../../libi2pd/Base.cpp \ + ../../libi2pd/BloomFilter.cpp \ + ../../libi2pd/Config.cpp \ + ../../libi2pd/CPU.cpp \ + ../../libi2pd/Crypto.cpp \ + ../../libi2pd/CryptoKey.cpp \ + ../../libi2pd/Datagram.cpp \ + ../../libi2pd/Destination.cpp \ + ../../libi2pd/Event.cpp \ + ../../libi2pd/Family.cpp \ + ../../libi2pd/FS.cpp \ + ../../libi2pd/Garlic.cpp \ + ../../libi2pd/Gost.cpp \ + ../../libi2pd/Gzip.cpp \ + ../../libi2pd/HTTP.cpp \ + ../../libi2pd/I2NPProtocol.cpp \ + ../../libi2pd/I2PEndian.cpp \ + ../../libi2pd/Identity.cpp \ + ../../libi2pd/LeaseSet.cpp \ + ../../libi2pd/Log.cpp \ + ../../libi2pd/NetDb.cpp \ + ../../libi2pd/NetDbRequests.cpp \ + ../../libi2pd/NTCPSession.cpp \ + ../../libi2pd/Profiling.cpp \ + ../../libi2pd/Reseed.cpp \ + ../../libi2pd/RouterContext.cpp \ + ../../libi2pd/RouterInfo.cpp \ + ../../libi2pd/Signature.cpp \ + ../../libi2pd/SSU.cpp \ + ../../libi2pd/SSUData.cpp \ + ../../libi2pd/SSUSession.cpp \ + ../../libi2pd/Streaming.cpp \ + ../../libi2pd/Timestamp.cpp \ + ../../libi2pd/TransitTunnel.cpp \ + ../../libi2pd/Transports.cpp \ + ../../libi2pd/Tunnel.cpp \ + ../../libi2pd/TunnelEndpoint.cpp \ + ../../libi2pd/TunnelGateway.cpp \ + ../../libi2pd/TunnelPool.cpp \ + ../../libi2pd/util.cpp \ + ../../libi2pd/Ed25519.cpp \ + ../../libi2pd/Chacha20.cpp \ + ../../libi2pd/Poly1305.cpp \ + ../../libi2pd_client/AddressBook.cpp \ + ../../libi2pd_client/BOB.cpp \ + ../../libi2pd_client/ClientContext.cpp \ + ../../libi2pd_client/HTTPProxy.cpp \ + ../../libi2pd_client/I2CP.cpp \ + ../../libi2pd_client/I2PService.cpp \ + ../../libi2pd_client/I2PTunnel.cpp \ + ../../libi2pd_client/MatchedDestination.cpp \ + ../../libi2pd_client/SAM.cpp \ + ../../libi2pd_client/SOCKS.cpp \ + ../../libi2pd_client/Websocket.cpp \ + ../../libi2pd_client/WebSocks.cpp \ + ClientTunnelPane.cpp \ + MainWindowItems.cpp \ + ServerTunnelPane.cpp \ + SignatureTypeComboboxFactory.cpp \ + TunnelConfig.cpp \ + TunnelPane.cpp \ + ../../daemon/Daemon.cpp \ + ../../daemon/HTTPServer.cpp \ + ../../daemon/i2pd.cpp \ + ../../daemon/I2PControl.cpp \ + ../../daemon/UnixDaemon.cpp \ + ../../daemon/UPnP.cpp \ + textbrowsertweaked1.cpp \ + pagewithbackbutton.cpp \ + widgetlock.cpp \ + widgetlockregistry.cpp \ + logviewermanager.cpp + +#qt creator does not handle this well +#SOURCES += $$files(../../libi2pd/*.cpp) +#SOURCES += $$files(../../libi2pd_client/*.cpp) +#SOURCES += $$files(../../daemon/*.cpp) +#SOURCES += $$files(./*.cpp) + +SOURCES -= ../../daemon/UnixDaemon.cpp + +HEADERS += DaemonQT.h mainwindow.h \ + ../../libi2pd/api.h \ + ../../libi2pd/Base.h \ + ../../libi2pd/BloomFilter.h \ + ../../libi2pd/Config.h \ + ../../libi2pd/Crypto.h \ + ../../libi2pd/CryptoKey.h \ + ../../libi2pd/Datagram.h \ + ../../libi2pd/Destination.h \ + ../../libi2pd/Event.h \ + ../../libi2pd/Family.h \ + ../../libi2pd/FS.h \ + ../../libi2pd/Garlic.h \ + ../../libi2pd/Gost.h \ + ../../libi2pd/Gzip.h \ + ../../libi2pd/HTTP.h \ + ../../libi2pd/I2NPProtocol.h \ + ../../libi2pd/I2PEndian.h \ + ../../libi2pd/Identity.h \ + ../../libi2pd/LeaseSet.h \ + ../../libi2pd/LittleBigEndian.h \ + ../../libi2pd/Log.h \ + ../../libi2pd/NetDb.hpp \ + ../../libi2pd/NetDbRequests.h \ + ../../libi2pd/NTCPSession.h \ + ../../libi2pd/Profiling.h \ + ../../libi2pd/Queue.h \ + ../../libi2pd/Reseed.h \ + ../../libi2pd/RouterContext.h \ + ../../libi2pd/RouterInfo.h \ + ../../libi2pd/Signature.h \ + ../../libi2pd/SSU.h \ + ../../libi2pd/SSUData.h \ + ../../libi2pd/SSUSession.h \ + ../../libi2pd/Streaming.h \ + ../../libi2pd/Tag.h \ + ../../libi2pd/Timestamp.h \ + ../../libi2pd/TransitTunnel.h \ + ../../libi2pd/Transports.h \ + ../../libi2pd/TransportSession.h \ + ../../libi2pd/Tunnel.h \ + ../../libi2pd/TunnelBase.h \ + ../../libi2pd/TunnelConfig.h \ + ../../libi2pd/TunnelEndpoint.h \ + ../../libi2pd/TunnelGateway.h \ + ../../libi2pd/TunnelPool.h \ + ../../libi2pd/util.h \ + ../../libi2pd/version.h \ + ../../libi2pd_client/AddressBook.h \ + ../../libi2pd_client/BOB.h \ + ../../libi2pd_client/ClientContext.h \ + ../../libi2pd_client/HTTPProxy.h \ + ../../libi2pd_client/I2CP.h \ + ../../libi2pd_client/I2PService.h \ + ../../libi2pd_client/I2PTunnel.h \ + ../../libi2pd_client/MatchedDestination.h \ + ../../libi2pd_client/SAM.h \ + ../../libi2pd_client/SOCKS.h \ + ../../libi2pd_client/Websocket.h \ + ../../libi2pd_client/WebSocks.h \ + ClientTunnelPane.h \ + MainWindowItems.h \ + ServerTunnelPane.h \ + SignatureTypeComboboxFactory.h \ + TunnelConfig.h \ + TunnelPane.h \ + TunnelsPageUpdateListener.h \ + ../../daemon/Daemon.h \ + ../../daemon/HTTPServer.h \ + ../../daemon/I2PControl.h \ + ../../daemon/UPnP.h \ + textbrowsertweaked1.h \ + pagewithbackbutton.h \ + widgetlock.h \ + widgetlockregistry.h \ + i2pd.rc \ + i2pd.rc \ + logviewermanager.h + +INCLUDEPATH += ../../libi2pd +INCLUDEPATH += ../../libi2pd_client +INCLUDEPATH += ../../daemon +INCLUDEPATH += . + +FORMS += mainwindow.ui \ + tunnelform.ui \ + statusbuttons.ui \ + routercommandswidget.ui \ + generalsettingswidget.ui + +LIBS += -lz + +macx { + message("using mac os x target") + BREWROOT=/usr/local + BOOSTROOT=$$BREWROOT/opt/boost + SSLROOT=$$BREWROOT/opt/libressl + UPNPROOT=$$BREWROOT/opt/miniupnpc + INCLUDEPATH += $$BOOSTROOT/include + INCLUDEPATH += $$SSLROOT/include + INCLUDEPATH += $$UPNPROOT/include + LIBS += $$SSLROOT/lib/libcrypto.a + LIBS += $$SSLROOT/lib/libssl.a + LIBS += $$BOOSTROOT/lib/libboost_system.a + LIBS += $$BOOSTROOT/lib/libboost_date_time.a + LIBS += $$BOOSTROOT/lib/libboost_filesystem.a + LIBS += $$BOOSTROOT/lib/libboost_program_options.a + LIBS += $$UPNPROOT/lib/libminiupnpc.a +} + +android { + message("Using Android settings") + DEFINES += ANDROID=1 + DEFINES += __ANDROID__ + + CONFIG += mobility + + MOBILITY = + + INCLUDEPATH += $$BOOST_PATH/boost_1_53_0/include \ + $$OPENSSL_PATH/openssl-1.0.2/include \ + $$MINIUPNP_PATH/miniupnp-2.0/include \ + $$IFADDRS_PATH + DISTFILES += android/AndroidManifest.xml + + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + + SOURCES += $$IFADDRS_PATH/ifaddrs.c + HEADERS += $$IFADDRS_PATH/ifaddrs.h + + equals(ANDROID_TARGET_ARCH, armeabi-v7a){ + DEFINES += ANDROID_ARM7A + # http://stackoverflow.com/a/30235934/529442 + LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \ + -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ + -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ + -L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl \ + -L$$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/ -lminiupnpc + + PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a + DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + + ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so \ + $$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/libminiupnpc.so + } + + equals(ANDROID_TARGET_ARCH, x86){ + # http://stackoverflow.com/a/30235934/529442 + LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \ + -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ + -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ + -L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl \ + -L$$MINIUPNP_PATH/miniupnp-2.0/x86/lib/ -lminiupnpc + + PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a + + DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + + ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so \ + $$MINIUPNP_PATH/miniupnp-2.0/x86/lib/libminiupnpc.so + } +} + +linux:!android { + message("Using Linux settings") + LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc +} + +windows { + message("Using Windows settings") + RC_FILE = i2pd.rc + DEFINES += BOOST_USE_WINDOWS_H WINDOWS _WINDOWS WIN32_LEAN_AND_MEAN MINIUPNP_STATICLIB + DEFINES -= UNICODE _UNICODE + BOOST_SUFFIX = -mt + QMAKE_CXXFLAGS_RELEASE = -Os + QMAKE_LFLAGS = -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows + + #linker's -s means "strip" + QMAKE_LFLAGS_RELEASE += -s + + LIBS = -lminiupnpc \ + -lboost_system$$BOOST_SUFFIX \ + -lboost_date_time$$BOOST_SUFFIX \ + -lboost_filesystem$$BOOST_SUFFIX \ + -lboost_program_options$$BOOST_SUFFIX \ + -lssl \ + -lcrypto \ + -lz \ + -lwsock32 \ + -lws2_32 \ + -lgdi32 \ + -liphlpapi \ + -lstdc++ \ + -lpthread +} + +!android:!symbian:!maemo5:!simulator { + message("Build with a system tray icon") + # see also http://doc.qt.io/qt-4.8/qt-desktop-systray-systray-pro.html for example on wince* + #sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS i2pd_qt.pro resources images + RESOURCES = i2pd.qrc + QT += xml + #INSTALLS += sources +} + diff --git a/qt/i2pd_qt/logviewermanager.cpp b/qt/i2pd_qt/logviewermanager.cpp new file mode 100644 index 00000000..30fc904a --- /dev/null +++ b/qt/i2pd_qt/logviewermanager.cpp @@ -0,0 +1,45 @@ +#include "logviewermanager.h" + +LogViewerManager::LogViewerManager(std::shared_ptr logStream_, + QPlainTextEdit* logTextEdit_, + QObject *parent) : + QObject(parent), + logStream(logStream_), + logTextEdit(logTextEdit_), + controllerForBgThread(nullptr) +{ + assert(logTextEdit!=nullptr); + controllerForBgThread=new i2pd::qt::logviewer::Controller(*this); +} + +namespace i2pd { +namespace qt { +namespace logviewer { + +QString Worker::pollAndShootATimerForInfiniteRetries() { + std::shared_ptr logStream=logViewerManager.getLogStream(); + assert(logStream!=nullptr); + std::streamsize MAX_SZ=64*1024; + char*buf=(char*)malloc(MAX_SZ*sizeof(char)); + if(buf==nullptr)return ""; + std::streamsize read=logStream->readsome(buf, MAX_SZ); + if(read<0)read=0; + QString ret=QString::fromUtf8(buf, read); + free(buf); + return ret; +} + +Controller::Controller(LogViewerManager ¶meter1):logViewerManager(parameter1) { + Worker *worker = new Worker(parameter1); + worker->moveToThread(&workerThread); + connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(this, &Controller::operate1, worker, &Worker::doWork1); + connect(worker, &Worker::resultReady, + ¶meter1, &LogViewerManager::appendPlainText_atGuiThread); + workerThread.start(); + timerId=startTimer(100/*millis*/); +} + +} +} +} diff --git a/qt/i2pd_qt/logviewermanager.h b/qt/i2pd_qt/logviewermanager.h new file mode 100644 index 00000000..e9ede79f --- /dev/null +++ b/qt/i2pd_qt/logviewermanager.h @@ -0,0 +1,130 @@ +#ifndef LOGVIEWERMANAGER_H +#define LOGVIEWERMANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "FS.h" +#include "Log.h" + +class LogViewerManager; + +namespace i2pd { +namespace qt { +namespace logviewer { + +class Worker : public QObject +{ + Q_OBJECT +private: + LogViewerManager &logViewerManager; +public: + Worker(LogViewerManager ¶meter1):logViewerManager(parameter1){} +private: + QString pollAndShootATimerForInfiniteRetries(); + +public slots: + void doWork1() { + /* ... here is the expensive or blocking operation ... */ + QString read=pollAndShootATimerForInfiniteRetries(); + emit resultReady(read); + } + +signals: + void resultReady(QString read); +}; + +class Controller : public QObject +{ + Q_OBJECT + QThread workerThread; + LogViewerManager& logViewerManager; + int timerId; +public: + Controller(LogViewerManager ¶meter1); + ~Controller() { + if(timerId!=0)killTimer(timerId); + workerThread.quit(); + workerThread.wait(); + } +signals: + void operate1(); +protected: + void timerEvent(QTimerEvent */*event*/) { + emit operate1(); + } +}; + +} +} +} + +class LogViewerManager : public QObject +{ + Q_OBJECT +private: + std::shared_ptr logStream; + QPlainTextEdit* logTextEdit; + i2pd::qt::logviewer::Controller* controllerForBgThread; +public: + //also starts a bg thread (QTimer) polling logStream->readsome(buf, n) + explicit LogViewerManager(std::shared_ptr logStream_, + QPlainTextEdit* logTextEdit_, + QObject *parent); + //also deallocs the bg thread (QTimer) + virtual ~LogViewerManager(){} + const i2pd::qt::logviewer::Controller& getControllerForBgThread() { + assert(controllerForBgThread!=nullptr); + return *controllerForBgThread; + } + const QPlainTextEdit* getLogTextEdit(){ return logTextEdit; } + const std::shared_ptr getLogStream(){ return logStream; } +signals: + +public slots: + //void appendFromNonGuiThread(std::string read) { + //} +public slots: + void appendPlainText_atGuiThread(QString plainText) { + if(plainText.length()==0)return; + assert(logTextEdit!=nullptr); + int scrollPosVert =logTextEdit->verticalScrollBar()->value(); + int scrollPosHoriz=logTextEdit->horizontalScrollBar()->value(); + int scrollPosVertMax =logTextEdit->verticalScrollBar()->maximum(); + const int MAX_LINES=10*1024; + logTextEdit->setMaximumBlockCount(MAX_LINES); + //logTextEdit->appendPlainText(plainText); + //navigate the window to the end + //QTextCursor cursor = logTextEdit->textCursor(); + //cursor.movePosition(QTextCursor::MoveOperation::End); + //logTextEdit->setTextCursor(cursor); + //QTextCursor prev_cursor = logTextEdit->textCursor(); + logTextEdit->moveCursor(QTextCursor::End); + logTextEdit->insertPlainText(plainText); + if(/*prev_cursor.atEnd()*/scrollPosVert==scrollPosVertMax){ + //logTextEdit->moveCursor(QTextCursor::End); + scrollPosVert =logTextEdit->verticalScrollBar()->maximum(); + scrollPosHoriz=logTextEdit->horizontalScrollBar()->minimum(); + } + //else + // logTextEdit->setTextCursor(prev_cursor); + logTextEdit->verticalScrollBar()->setValue(scrollPosVert); + logTextEdit->horizontalScrollBar()->setValue(scrollPosHoriz); + } + /* + void replaceText_atGuiThread() { + assert(logTextEdit!=nullptr); + logTextEdit->setText(QString::fromStdString(nav.getContent())); + } + */ +}; + +#endif // LOGVIEWERMANAGER_H diff --git a/qt/i2pd_qt/mainwindow.cpp b/qt/i2pd_qt/mainwindow.cpp index fc1e5985..e4c9f2a7 100644 --- a/qt/i2pd_qt/mainwindow.cpp +++ b/qt/i2pd_qt/mainwindow.cpp @@ -27,15 +27,19 @@ #include "DaemonQT.h" #include "SignatureTypeComboboxFactory.h" +#include "logviewermanager.h" + std::string programOptionsWriterCurrentSection; -MainWindow::MainWindow(QWidget *parent) : +MainWindow::MainWindow(std::shared_ptr logStream_, QWidget *parent) : QMainWindow(parent) + ,logStream(logStream_) #ifndef ANDROID ,quitting(false) #endif ,wasSelectingAtStatusMainPage(false) ,showHiddenInfoStatusMainPage(false) + ,logViewerManagerPtr(nullptr) ,ui(new Ui::MainWindow) ,statusButtonsUI(new Ui::StatusButtonsForm) ,routerCommandsUI(new Ui::routerCommandsWidget) @@ -132,6 +136,8 @@ MainWindow::MainWindow(QWidget *parent) : QObject::connect(routerCommandsUI->acceptTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(enableTransit())); QObject::connect(routerCommandsUI->declineTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(disableTransit())); + QObject::connect(ui->logViewerPushButton, SIGNAL(released()), this, SLOT(showLogViewerPage())); + QObject::connect(ui->settingsPagePushButton, SIGNAL(released()), this, SLOT(showSettingsPage())); QObject::connect(ui->tunnelsPagePushButton, SIGNAL(released()), this, SLOT(showTunnelsPage())); @@ -299,6 +305,9 @@ MainWindow::MainWindow(QWidget *parent) : trayIcon->show(); #endif + logViewerManagerPtr=new LogViewerManager(logStream_,ui->logViewerTextEdit,this); + assert(logViewerManagerPtr!=nullptr); + onLoggingOptionsChange(); //QMetaObject::connectSlotsByName(this); } @@ -333,10 +342,11 @@ void MainWindow::showStatusPage(StatusPage newStatusPage){ } wasSelectingAtStatusMainPage=false; } -void MainWindow::showSettingsPage(){ui->stackedWidget->setCurrentIndex(1);setStatusButtonsVisible(false);} -void MainWindow::showTunnelsPage(){ui->stackedWidget->setCurrentIndex(2);setStatusButtonsVisible(false);} -void MainWindow::showRestartPage(){ui->stackedWidget->setCurrentIndex(3);setStatusButtonsVisible(false);} -void MainWindow::showQuitPage(){ui->stackedWidget->setCurrentIndex(4);setStatusButtonsVisible(false);} +void MainWindow::showLogViewerPage(){ui->stackedWidget->setCurrentIndex(1);setStatusButtonsVisible(false);} +void MainWindow::showSettingsPage(){ui->stackedWidget->setCurrentIndex(2);setStatusButtonsVisible(false);} +void MainWindow::showTunnelsPage(){ui->stackedWidget->setCurrentIndex(3);setStatusButtonsVisible(false);} +void MainWindow::showRestartPage(){ui->stackedWidget->setCurrentIndex(4);setStatusButtonsVisible(false);} +void MainWindow::showQuitPage(){ui->stackedWidget->setCurrentIndex(5);setStatusButtonsVisible(false);} void MainWindow::setStatusButtonsVisible(bool visible) { ui->statusButtonsPane->setVisible(visible); @@ -349,7 +359,9 @@ QString MainWindow::getStatusPageHtml(bool showHiddenInfo) { s << ""; switch (statusPage) { - case main_page: i2p::http::ShowStatus(s, showHiddenInfo);break; + case main_page: + i2p::http::ShowStatus(s, showHiddenInfo, i2p::http::OutputFormatEnum::forQtUi); + break; case commands: break; case local_destinations: i2p::http::ShowLocalDestinations(s);break; case leasesets: i2p::http::ShowLeasesSets(s); break; @@ -449,7 +461,7 @@ void MainWindow::createTrayIcon() { } void MainWindow::setIcon() { - QIcon icon(":/images/icon.png"); + QIcon icon(":icons/mask"); trayIcon->setIcon(icon); setWindowIcon(icon); @@ -629,6 +641,8 @@ void MainWindow::loadAllConfigs(){ } ReadTunnelsConfig(); + + onLoggingOptionsChange(); } /** returns false iff not valid items present and save was aborted */ bool MainWindow::saveAllConfigs(){ @@ -666,6 +680,8 @@ bool MainWindow::saveAllConfigs(){ SaveTunnelsConfig(); + onLoggingOptionsChange(); + return true; } diff --git a/qt/i2pd_qt/mainwindow.h b/qt/i2pd_qt/mainwindow.h index cac97a1f..51384f51 100644 --- a/qt/i2pd_qt/mainwindow.h +++ b/qt/i2pd_qt/mainwindow.h @@ -62,6 +62,8 @@ #include "widgetlockregistry.h" #include "widgetlock.h" +class LogViewerManager; + template bool isType(boost::any& a) { return @@ -215,7 +217,8 @@ public: }; class LogDestinationComboBoxItem : public ComboBoxItem { public: - LogDestinationComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}; + LogDestinationComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : + ComboBoxItem(option_, comboBox_) {} virtual ~LogDestinationComboBoxItem(){} virtual void loadFromConfigOption(){ MainWindowItem::loadFromConfigOption(); @@ -228,6 +231,8 @@ public: MainWindowItem::saveToStringStream(out); } virtual bool isValid() { return true; } + + Q_OBJECT }; class LogLevelComboBoxItem : public ComboBoxItem { public: @@ -370,9 +375,10 @@ class Controller; class MainWindow : public QMainWindow { Q_OBJECT - +private: + std::shared_ptr logStream; public: - explicit MainWindow(QWidget *parent=0); + explicit MainWindow(std::shared_ptr logStream_, QWidget *parent=nullptr); ~MainWindow(); void setI2PController(i2p::qt::Controller* controller_); @@ -419,6 +425,7 @@ public slots: void showStatus_i2p_tunnels_Page(); void showStatus_sam_sessions_Page(); + void showLogViewerPage(); void showSettingsPage(); void showTunnelsPage(); void showRestartPage(); @@ -430,6 +437,8 @@ private: bool wasSelectingAtStatusMainPage; bool showHiddenInfoStatusMainPage; + LogViewerManager *logViewerManagerPtr; + void showStatusPage(StatusPage newStatusPage); #ifndef ANDROID void createActions(); @@ -522,13 +531,6 @@ private: void appendTunnelForms(std::string tunnelNameToFocus); void deleteTunnelForms(); - - /* - - TODO signaturetype - - */ - template std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const { @@ -628,7 +630,6 @@ private: std::string webircpass = ""; bool gzip = true; i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256; - uint32_t maxConns = i2p::stream::DEFAULT_MAX_CONNS_PER_MIN; std::string address = "127.0.0.1"; bool isUniqueLocal = true; @@ -646,7 +647,6 @@ private: webircpass, gzip, sigType, - maxConns, address, isUniqueLocal); @@ -734,7 +734,6 @@ private: std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); - uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN); std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1"); bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); @@ -769,7 +768,6 @@ private: webircpass, gzip, sigType, - maxConns, address, isUniqueLocal); } @@ -794,6 +792,8 @@ private: }; TunnelsPageUpdateListenerMainWindowImpl tunnelsPageUpdateListener; + + void onLoggingOptionsChange() {} }; #endif // MAINWINDOW_H diff --git a/qt/i2pd_qt/mainwindow.ui b/qt/i2pd_qt/mainwindow.ui index 9b463f44..dcdf88bd 100644 --- a/qt/i2pd_qt/mainwindow.ui +++ b/qt/i2pd_qt/mainwindow.ui @@ -58,7 +58,7 @@ QLayout::SetMaximumSize - + QLayout::SetMinimumSize @@ -96,6 +96,13 @@ + + + + Log + + + @@ -596,7 +603,7 @@ - TextLabel + wrongInputMessageLabel true @@ -627,7 +634,7 @@ - 2 + 1 @@ -671,6 +678,69 @@ + + + + 0 + 0 + + + + + + 0 + 0 + 711 + 531 + + + + + QLayout::SetMinAndMaxSize + + + + + + 15 + + + + Log + + + + + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + 10000 + + + false + + + true + + + + + + @@ -728,8 +798,8 @@ 0 0 - 689 - 496 + 81 + 28 diff --git a/qt/i2pd_qt/resources/icons/mask.ico b/qt/i2pd_qt/resources/icons/mask.ico new file mode 100644 index 00000000..f5807de5 Binary files /dev/null and b/qt/i2pd_qt/resources/icons/mask.ico differ diff --git a/qt/i2pd_qt/images/icon.png b/qt/i2pd_qt/resources/images/icon.png similarity index 100% rename from qt/i2pd_qt/images/icon.png rename to qt/i2pd_qt/resources/images/icon.png diff --git a/tests/Makefile b/tests/Makefile index f769ad35..498cff17 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,6 +1,6 @@ -CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -I../libi2pd/ -pthread +CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -I../libi2pd/ -pthread -Wl,--unresolved-symbols=ignore-in-object-files -TESTS = test-gost test-gost-sig test-base-64 +TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 all: $(TESTS) run @@ -13,9 +13,15 @@ test-base-%: ../libi2pd/Base.cpp test-base-%.cpp test-gost: ../libi2pd/Gost.cpp ../libi2pd/I2PEndian.cpp test-gost.cpp $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -test-gost-sig: ../libi2pd/Gost.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Signature.cpp ../libi2pd/Crypto.cpp ../libi2pd/Log.cpp test-gost-sig.cpp +test-gost-sig: ../libi2pd/Gost.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Crypto.cpp ../libi2pd/Log.cpp test-gost-sig.cpp $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system +test-x25519: ../libi2pd/Ed25519.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Log.cpp ../libi2pd/Crypto.cpp test-x25519.cpp + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system + +test-aeadchacha20poly1305: ../libi2pd/Crypto.cpp ../libi2pd/ChaCha20.cpp ../libi2pd/Poly1305.cpp test-aeadchacha20poly1305.cpp + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system + run: $(TESTS) @for TEST in $(TESTS); do ./$$TEST ; done diff --git a/tests/test-aeadchacha20poly1305.cpp b/tests/test-aeadchacha20poly1305.cpp new file mode 100644 index 00000000..dcd4b4d6 --- /dev/null +++ b/tests/test-aeadchacha20poly1305.cpp @@ -0,0 +1,54 @@ +#include +#include +#include + +#include "Crypto.h" + +char text[] = "Ladies and Gentlemen of the class of '99: If I could offer you " +"only one tip for the future, sunscreen would be it."; // 114 bytes + +uint8_t key[32] = +{ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f +}; + +uint8_t ad[12] = +{ + 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 +}; + +uint8_t nonce[12] = +{ + 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 +}; + +uint8_t tag[16] = +{ + 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91 +}; + +uint8_t encrypted[114] = +{ + 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, + 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, + 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, + 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, + 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, + 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, + 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, + 0x61, 0x16 +}; + +int main () +{ + uint8_t buf[114+16]; + // test encryption + i2p::crypto::AEADChaCha20Poly1305 ((uint8_t *)text, 114, ad, 12, key, nonce, buf, 114 + 16, true); + assert (memcmp (buf, encrypted, 114) == 0); + assert (memcmp (buf + 114, tag, 16) == 0); + // test decryption + uint8_t buf1[114]; + assert (i2p::crypto::AEADChaCha20Poly1305 (buf, 114, ad, 12, key, nonce, buf1, 114, false)); + assert (memcmp (buf1, text, 114) == 0); +} diff --git a/tests/test-x25519.cpp b/tests/test-x25519.cpp new file mode 100644 index 00000000..9f249dbd --- /dev/null +++ b/tests/test-x25519.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +#include "Ed25519.h" + +const uint8_t k[32] = +{ + 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, + 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, + 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4 +}; + +const uint8_t u[32] = +{ + 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, + 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, + 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c +}; + +uint8_t p[32] = +{ + 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, + 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, + 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52 +}; + +int main () +{ + 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); +} +