diff --git a/ChangeLog b/ChangeLog index d2bfe7bf..54989d44 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,31 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [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 diff --git a/Makefile.linux b/Makefile.linux index 4a82591a..cf045eb4 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -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 -DAESNI + endif endif endif diff --git a/Win32/installer.iss b/Win32/installer.iss index 15ce4372..c4e1fffd 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.19.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index cfc9d55b..ca66c17d 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,7 +3,7 @@ package="org.purplei2p.i2pd" android:installLocation="auto" android:versionCode="1" - android:versionName="2.18.0"> + android:versionName="2.19.0"> - \ No newline at end of file + diff --git a/android/build.gradle b/android/build.gradle index 4fe17d88..c5834e19 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -19,13 +19,13 @@ repositories { android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "25.0.3" defaultConfig { applicationId "org.purplei2p.i2pd" targetSdkVersion 25 minSdkVersion 14 versionCode 1 - versionName "2.18.0" + versionName "2.19.0" ndk { abiFilters 'armeabi-v7a' //abiFilters 'x86' @@ -49,7 +49,7 @@ android { } buildTypes { release { - minifyEnabled false + minifyEnabled true signingConfig signingConfigs.orignal proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' } diff --git a/android/jni/Application.mk b/android/jni/Application.mk index 0fa116c2..b5b920fa 100755 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -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_binary_only/jni/Android.mk b/android_binary_only/jni/Android.mk new file mode 100755 index 00000000..ae56110c --- /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)/miniupnp-2.0/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a +LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnp-2.0/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..27f563ef 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.18.{build} +version: 2.19.{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 364c3304..632edc03 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -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) @@ -94,14 +98,17 @@ endif() add_library(libi2pd ${LIBI2PD_SRC}) set_target_properties(libi2pd PROPERTIES PREFIX "") -install(TARGETS libi2pd - EXPORT libi2pd - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - COMPONENT Libraries) + +if (WITH_LIBRARY) + install(TARGETS libi2pd + EXPORT libi2pd + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT Libraries) # TODO Make libi2pd available to 3rd party projects via CMake as imported target # FIXME This pulls stdafx # install(EXPORT libi2pd DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() set (CLIENT_SRC "${LIBI2PD_CLIENT_SRC_DIR}/AddressBook.cpp" @@ -120,13 +127,17 @@ set (CLIENT_SRC if(WITH_WEBSOCKETS) list (APPEND CLIENT_SRC "${LIBI2PD_CLIENT_SRC_DIR}/Websocket.cpp") endif () + add_library(libi2pdclient ${CLIENT_SRC}) set_target_properties(libi2pdclient PROPERTIES PREFIX "") -install(TARGETS libi2pdclient - EXPORT libi2pdclient - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - COMPONENT Libraries) + +if (WITH_LIBRARY) + install(TARGETS libi2pdclient + EXPORT libi2pdclient + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT Libraries) +endif() set(DAEMON_SRC_DIR ../daemon) diff --git a/build/build_mingw.cmd b/build/build_mingw.cmd index cc6a15fa..e7811b0b 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" 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/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 9af96c37..debd49b0 100644 --- a/contrib/i2pd.service +++ b/contrib/i2pd.service @@ -10,7 +10,7 @@ RuntimeDirectory=i2pd RuntimeDirectoryMode=0700 LogsDirectory=i2pd LogsDirectoryMode=0700 -Type=simple +Type=forking ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --pidfile=/var/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service ExecReload=/bin/kill -HUP $MAINPID PIDFile=/var/run/i2pd/i2pd.pid @@ -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..02986475 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.19.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..db480d35 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.19.0 +Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -96,6 +96,9 @@ getent passwd i2pd >/dev/null || \ %changelog +* 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..ee56a1c7 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); 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..faf386d8 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -198,7 +198,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 +227,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,9 +248,12 @@ namespace http { ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n"; - s << "
\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" ; @@ -272,9 +278,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 +294,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) @@ -493,7 +504,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 @@ -649,7 +660,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 ()) { @@ -863,7 +874,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 +964,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/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..74c366d1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +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..91e3b60f 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 +Switchs, 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..8e537049 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 +## uncoment this if you have "missing info" problem when building package +#override_dh_shlibdeps: +# dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info diff --git a/libi2pd/Base.cpp b/libi2pd/Base.cpp index 09f04c05..f80f2751 100644 --- a/libi2pd/Base.cpp +++ b/libi2pd/Base.cpp @@ -210,6 +210,21 @@ namespace data return 4*d.quot; } + std::string ToBase64Standard (const std::string& in) + { + auto len = Base64EncodingBufferSize (in.length ()); + char * str = new char[len+1]; + auto l = ByteStreamToBase64 ((const uint8_t *)in.c_str (), in.length (), str, len); + str[l] = 0; + // replace '-' by '+' and '~' by '/' + for (size_t i = 0; i < l; i++) + if (str[i] == '-') str[i] = '+'; + else if (str[i] == '~') str[i] = '/'; + std::string s(str); + delete[] str; + return s; + } + /* * * iT64 diff --git a/libi2pd/Base.h b/libi2pd/Base.h index bc92376f..a273f468 100644 --- a/libi2pd/Base.h +++ b/libi2pd/Base.h @@ -15,10 +15,13 @@ namespace data { size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen); - /** + /** Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes - */ - size_t Base64EncodingBufferSize(const size_t input_size); + */ + size_t Base64EncodingBufferSize(const size_t input_size); + + std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization + } // data } // i2p 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..1ff55dd6 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -37,32 +37,33 @@ 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") + ("ntcp2", value()->default_value(false), "Enable NTCP2 (experimental, default: disabled)") #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 +79,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 +192,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/," @@ -330,4 +331,3 @@ namespace config { } // namespace config } // namespace i2p - diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 5ba3334d..6d859342 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 { @@ -594,6 +601,13 @@ namespace crypto // AES #ifdef AESNI + #ifdef ARM64AES + void init_aesenc(void){ + // TODO: Implementation + } + + #endif + #define KeyExpansion256(round0,round1) \ "pshufd $0xff, %%xmm2, %%xmm2 \n" \ "movaps %%xmm1, %%xmm4 \n" \ @@ -1050,6 +1064,91 @@ 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); + // encrypt data + memcpy (buf, msg, msgLen); + chacha20 (buf, msgLen, nonce, key, 1); + + // 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; + } + } + memcpy (polyMsg.data () + offset, encrypt ? buf : msg, msgLen); 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); + ret = EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen) > 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..25646dbb 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -124,6 +124,9 @@ namespace crypto #ifdef AESNI + #ifdef ARM64AES + void init_aesenc(void) __attribute__((constructor)); + #endif class ECBCryptoAESNI { public: @@ -178,6 +181,7 @@ 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); @@ -200,6 +204,7 @@ 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); @@ -249,6 +254,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 +265,8 @@ namespace crypto // take care about openssl version #include -#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL +#define LEGACY_OPENSSL ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL +#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/CryptoKey.cpp b/libi2pd/CryptoKey.cpp index cd6952a0..711d4ce6 100644 --- a/libi2pd/CryptoKey.cpp +++ b/libi2pd/CryptoKey.cpp @@ -12,9 +12,9 @@ namespace crypto memcpy (m_PublicKey, pub, 256); } - void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) + void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { - ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, true); + ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding); } ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv) @@ -22,9 +22,9 @@ namespace crypto memcpy (m_PrivateKey, priv, 256); } - bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) + bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { - return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, true); + return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding); } ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub) @@ -44,10 +44,10 @@ namespace crypto if (m_PublicKey) EC_POINT_free (m_PublicKey); } - void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) + void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { if (m_Curve && m_PublicKey) - ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted, ctx, true); + ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted, ctx, zeroPadding); } ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv) @@ -62,10 +62,10 @@ namespace crypto if (m_PrivateKey) BN_free (m_PrivateKey); } - bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) + bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { if (m_Curve && m_PrivateKey) - return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data, ctx, true); + return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data, ctx, zeroPadding); return false; } @@ -104,10 +104,10 @@ namespace crypto if (m_PublicKey) EC_POINT_free (m_PublicKey); } - void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) + void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { if (m_PublicKey) - ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted, ctx, true); + ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted, ctx, zeroPadding); } ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv) @@ -120,10 +120,10 @@ namespace crypto if (m_PrivateKey) BN_free (m_PrivateKey); } - bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) + bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { if (m_PrivateKey) - return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data, ctx, true); + return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data, ctx, zeroPadding); return false; } diff --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index ece86eb0..0dff7584 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -13,7 +13,7 @@ namespace crypto public: virtual ~CryptoKeyEncryptor () {}; - virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) = 0; // 222 bytes data, 512 bytes encrypted + virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) = 0; // 222 bytes data, 512/514 bytes encrypted }; class CryptoKeyDecryptor @@ -21,7 +21,7 @@ namespace crypto public: virtual ~CryptoKeyDecryptor () {}; - virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) = 0; // 512 bytes encrypted, 222 bytes data + virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) = 0; // 512/514 bytes encrypted, 222 bytes data }; // ElGamal @@ -30,7 +30,7 @@ namespace crypto public: ElGamalEncryptor (const uint8_t * pub); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx); + void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); private: @@ -42,7 +42,7 @@ namespace crypto public: ElGamalDecryptor (const uint8_t * priv); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); private: @@ -57,7 +57,7 @@ namespace crypto ECIESP256Encryptor (const uint8_t * pub); ~ECIESP256Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx); + void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); private: @@ -72,7 +72,7 @@ namespace crypto ECIESP256Decryptor (const uint8_t * priv); ~ECIESP256Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); private: @@ -90,7 +90,7 @@ namespace crypto ECIESGOSTR3410Encryptor (const uint8_t * pub); ~ECIESGOSTR3410Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx); + void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); private: @@ -104,7 +104,7 @@ namespace crypto ECIESGOSTR3410Decryptor (const uint8_t * priv); ~ECIESGOSTR3410Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); private: diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index b191cbf1..d3632881 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -281,8 +281,12 @@ namespace client i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); if (m_IsPublic) { - m_PublishVerificationTimer.cancel (); - Publish (); + auto s = shared_from_this (); + m_Service.post ([s](void) + { + s->m_PublishVerificationTimer.cancel (); + s->Publish (); + }); } } @@ -325,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)); @@ -855,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 @@ -1024,7 +1033,7 @@ namespace client bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const { if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data, ctx); + return m_Decryptor->Decrypt (encrypted, data, ctx, true); else LogPrint (eLogError, "Destinations: decryptor is not set"); return false; 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.cpp b/libi2pd/I2NPProtocol.cpp index 9bb7dfd1..c91bfdb3 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -327,7 +327,7 @@ namespace i2p { LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::ElGamalDecrypt (i2p::context.GetPrivateKeys ().GetPrivateKey () , record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx); + i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx); BN_CTX_free (ctx); // replace record to reply if (i2p::context.AcceptsTunnels () && 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/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index f2355930..8b6063fc 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -212,7 +212,7 @@ namespace data { auto encryptor = m_Identity->CreateEncryptor (m_EncryptionKey); if (encryptor) - encryptor->Encrypt (data, encrypted, ctx); + encryptor->Encrypt (data, encrypted, ctx, true); } LocalLeaseSet::LocalLeaseSet (std::shared_ptr identity, const uint8_t * encryptionPublicKey, std::vector > tunnels): 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..594f4a93 --- /dev/null +++ b/libi2pd/NTCP2.cpp @@ -0,0 +1,623 @@ +#include +#include +#include +#include +#include +#include "Log.h" +#include "I2PEndian.h" +#include "Crypto.h" +#include "Ed25519.h" +#include "Siphash.h" +#include "RouterContext.h" +#include "NTCP2.h" + +namespace i2p +{ +namespace transport +{ + NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter): + TransportSession (in_RemoteRouter, 30), + m_Server (server), m_Socket (m_Server.GetService ()), + m_IsEstablished (false), m_IsTerminated (false), + m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr), + m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), + m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0) + { + auto addr = in_RemoteRouter->GetNTCPAddress (); + if (addr->ntcp2) + { + memcpy (m_RemoteStaticKey, addr->ntcp2->staticKey, 32); + memcpy (m_IV, addr->ntcp2->iv, 16); + } + else + LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); + } + + NTCP2Session::~NTCP2Session () + { + delete[] m_SessionRequestBuffer; + delete[] m_SessionCreatedBuffer; + delete[] m_SessionConfirmedBuffer; + delete[] m_NextReceivedBuffer; + delete[] m_NextSendBuffer; + } + + void NTCP2Session::Terminate () + { + if (!m_IsTerminated) + { + m_IsTerminated = true; + m_IsEstablished = false; + m_Socket.close (); + LogPrint (eLogDebug, "NTCP2: session terminated"); + } + } + + void NTCP2Session::Done () + { + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); + } + + void NTCP2Session::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 NTCP2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + + void NTCP2Session::KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub, uint8_t * derived) + { + 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 uint8_t h[64] = + { + 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(h || rs) + memcpy (h + 32, rs, 32); + SHA256 (h, 64, h); + // h = SHA256(h || pub) + memcpy (h + 32, pub, 32); + SHA256 (h, 64, m_H); + // x25519 between rs and priv + uint8_t inputKeyMaterial[32]; + BN_CTX * ctx = BN_CTX_new (); + i2p::crypto::GetEd25519 ()->ScalarMul (rs, priv, inputKeyMaterial, ctx); // rs*priv + BN_CTX_free (ctx); + MixKey (inputKeyMaterial, derived); + } + + void NTCP2Session::KeyDerivationFunction2 (const uint8_t * priv, const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived) + { + uint8_t h[64]; + memcpy (h, m_H, 32); + memcpy (h + 32, sessionRequest + 32, 32); // encrypted payload + SHA256 (h, 64, h); + int paddingLength = sessionRequestLen - 64; + if (paddingLength > 0) + { + std::vector h1(paddingLength + 32); + memcpy (h1.data (), h, 32); + memcpy (h1.data () + 32, sessionRequest + 64, paddingLength); + SHA256 (h1.data (), paddingLength + 32, h); + } + memcpy (h + 32, pub, 32); + SHA256 (h, 64, m_H); + + // x25519 between remote pub and priv + uint8_t inputKeyMaterial[32]; + BN_CTX * ctx = BN_CTX_new (); + i2p::crypto::GetEd25519 ()->ScalarMul (pub, priv, inputKeyMaterial, ctx); + BN_CTX_free (ctx); + MixKey (inputKeyMaterial, derived); + } + + void NTCP2Session::KeyDerivationFunction3 (const uint8_t * staticPrivKey, uint8_t * derived) + { + uint8_t inputKeyMaterial[32]; + BN_CTX * ctx = BN_CTX_new (); + i2p::crypto::GetEd25519 ()->ScalarMul (m_Y, staticPrivKey, inputKeyMaterial, ctx); + BN_CTX_free (ctx); + MixKey (inputKeyMaterial, derived); + } + + void NTCP2Session::KeyDerivationFunctionDataPhase () + { + uint8_t tempKey[32]; unsigned int len; + HMAC(EVP_sha256(), m_CK, 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_H, 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::CreateEphemeralKey (uint8_t * pub) + { + RAND_bytes (m_EphemeralPrivateKey, 32); + BN_CTX * ctx = BN_CTX_new (); + i2p::crypto::GetEd25519 ()->ScalarMulB (m_EphemeralPrivateKey, pub, ctx); + BN_CTX_free (ctx); + } + + void NTCP2Session::SendSessionRequest () + { + // 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); + // generate key pair (X) + uint8_t x[32]; + CreateEphemeralKey (x); + // encrypt X + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (GetRemoteIdentity ()->GetIdentHash ()); + encryption.SetIV (m_IV); + encryption.Encrypt (x, 32, m_SessionRequestBuffer); + encryption.GetIV (m_IV); // save IV for SessionCreated + // encryption key for next block + uint8_t key[32]; + KeyDerivationFunction1 (m_RemoteStaticKey, m_EphemeralPrivateKey, x, key); + // 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 + htobe16buf (options + 4, i2p::context.GetRouterInfo ().GetBufferLen () + 20); // m3p2Len (RI header + RI + MAC for now) TODO: implement options + // 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, key, nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt + // send message + boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionRequestBuffer, 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_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_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 + { + // decrypt X + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (i2p::context.GetIdentHash ()); + decryption.SetIV (i2p::context.GetNTCP2IV ()); + decryption.Decrypt (m_SessionRequestBuffer, 32, m_Y); + decryption.GetIV (m_IV); // save IV for SessionCreated + // decryption key for next block + uint8_t key[32]; + KeyDerivationFunction1 (m_Y, i2p::context.GetNTCP2StaticPrivateKey (), m_Y, key); + // 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, key, nonce, options, 16, false)) // decrypt + { + if (options[1] == 2) + { + uint16_t paddingLen = bufbe16toh (options + 2); + m_SessionRequestBufferLen = paddingLen + 64; + // TODO: check tsA + if (paddingLen > 0) + boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + else + SendSessionCreated (); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", (int)options[1]); + Terminate (); + } + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); + 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_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size + // generate key pair (y) + uint8_t y[32]; + CreateEphemeralKey (y); + // encrypt Y + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (i2p::context.GetIdentHash ()); + encryption.SetIV (m_IV); + encryption.Encrypt (y, 32, m_SessionCreatedBuffer); + // encryption key for next block (m_K) + KeyDerivationFunction2 (m_EphemeralPrivateKey, m_Y, m_SessionRequestBuffer, m_SessionRequestBufferLen, m_K); + auto paddingLen = rand () % (287 - 64); + 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 + // fill padding + RAND_bytes (m_SessionCreatedBuffer + 56, paddingLen); + // send message + boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionCreatedBuffer, paddingLen + 64), 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); + m_SessionCreatedBufferLen = 64; + // decrypt Y + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (GetRemoteIdentity ()->GetIdentHash ()); + decryption.SetIV (m_IV); + decryption.Decrypt (m_SessionCreatedBuffer, 32, m_Y); + // decryption key for next block (m_K) + KeyDerivationFunction2 (m_EphemeralPrivateKey, m_Y, m_SessionRequestBuffer, m_SessionRequestBufferLen, m_K); + // 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 + { + uint16_t paddingLen = bufbe16toh(payload + 2); + LogPrint (eLogDebug, "NTCP2: padding length ", paddingLen); + // TODO: check tsB + if (paddingLen > 0) + { + boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + else + SendSessionConfirmed (); + } + else + { + LogPrint (eLogWarning, "NTCP2: SessionCreated MAC verification failed "); + 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_SessionCreatedBufferLen += bytes_transferred; + SendSessionConfirmed (); + } + } + + + void NTCP2Session::SendSessionConfirmed () + { + // update AD + uint8_t h[80]; + memcpy (h, m_H, 32); + memcpy (h + 32, m_SessionCreatedBuffer + 32, 32); // encrypted payload + SHA256 (h, 64, h); + int paddingLength = m_SessionCreatedBufferLen - 64; + if (paddingLength > 0) + { + std::vector h1(paddingLength + 32); + memcpy (h1.data (), h, 32); + memcpy (h1.data () + 32, m_SessionCreatedBuffer + 64, paddingLength); + SHA256 (h1.data (), paddingLength + 32, h); + } + // part1 48 bytes + m_SessionConfirmedBuffer = new uint8_t[2048]; // TODO: actual size + uint8_t nonce[12]; + CreateNonce (1, nonce); + i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, h, 32, m_K, nonce, m_SessionConfirmedBuffer, 48, true); // encrypt + // part 2 + // update AD again + memcpy (h + 32, m_SessionConfirmedBuffer, 48); + SHA256 (h, 80, m_H); + + size_t m3p2Len = i2p::context.GetRouterInfo ().GetBufferLen () + 20; + std::vector buf(m3p2Len - 16); + buf[0] = 2; // block + htobe16buf (buf.data () + 1, i2p::context.GetRouterInfo ().GetBufferLen () + 1); // flag + RI + buf[3] = 0; // flag + memcpy (buf.data () + 4, i2p::context.GetRouterInfo ().GetBuffer (), i2p::context.GetRouterInfo ().GetBufferLen ()); + uint8_t key[32]; + KeyDerivationFunction3 (i2p::context.GetNTCP2StaticPrivateKey (), key); + memset (nonce, 0, 12); // set nonce to 0 again + i2p::crypto::AEADChaCha20Poly1305 (buf.data (), m3p2Len - 16, m_H, 32, key, nonce, m_SessionConfirmedBuffer + 48, m3p2Len, true); // encrypt + uint8_t tmp[48]; + memcpy (tmp, m_SessionConfirmedBuffer, 48); + memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext + SHA256 (m_SessionConfirmedBuffer + 16, m3p2Len + 32, m_H); //h = SHA256(h || ciphertext); + memcpy (m_SessionConfirmedBuffer, tmp, 48); + + // send message + boost::asio::async_write (m_Socket, boost::asio::buffer (m_SessionConfirmedBuffer, 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 (); + memcpy (m_ReceiveIV, m_Sipkeysba + 16, 8); //Alice + memcpy (m_SendIV, m_Sipkeysab + 16, 8); //Alice + ReceiveLength (); + + // TODO: remove + uint8_t pad[1024]; + auto paddingLength = rand () % 1000; + RAND_bytes (pad + 3, paddingLength); + pad[0] = 254; + htobe16buf (pad + 1, paddingLength); + SendNextFrame (pad, paddingLength + 3); + } + + void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); + Terminate (); // TODO + } + + void NTCP2Session::ClientLogin () + { + SendSessionRequest (); + } + + void NTCP2Session::ServerLogin () + { + m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now + boost::asio::async_read (m_Socket, boost::asio::buffer(m_SessionRequestBuffer, 64), boost::asio::transfer_all (), + std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void NTCP2Session::ReceiveLength () + { + 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) + { + LogPrint (eLogWarning, "NTCP2: receive length read error: ", ecode.message ()); + Terminate (); + } + else + { + i2p::crypto::Siphash<8> (m_ReceiveIV, m_ReceiveIV, 8, m_Sipkeysba); // assume Alice TODO: + m_NextReceivedLen = be16toh (m_NextReceivedLen ^ bufbe16toh(m_ReceiveIV)); + LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); + delete[] m_NextReceivedBuffer; + m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen]; + Receive (); + } + } + + void NTCP2Session::Receive () + { + 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) + { + LogPrint (eLogWarning, "NTCP2: receive read error: ", ecode.message ()); + Terminate (); + } + else + { + uint8_t nonce[12]; + CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; + uint8_t * decrypted = new uint8_t[m_NextReceivedLen]; + if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_Kba, nonce, decrypted, m_NextReceivedLen, false)) // decrypt. assume Alice TODO: + { + LogPrint (eLogInfo, "NTCP2: received message decrypted"); + ProcessNextFrame (decrypted, m_NextReceivedLen-16); + ReceiveLength (); + } + else + { + LogPrint (eLogWarning, "NTCP2: Received MAC verification failed "); + Terminate (); + } + delete[] decrypted; + } + } + + 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; + } + offset += size; + } + } + + void NTCP2Session::SendNextFrame (const uint8_t * payload, size_t len) + { + 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_Kab, nonce, m_NextSendBuffer + 2, len + 16, true); // encrypt. assume Alice TODO: + i2p::crypto::Siphash<8> (m_SendIV, m_SendIV, 8, m_Sipkeysab); // assume Alice TODO: + htobuf16 (m_NextSendBuffer, bufbe16toh (m_SendIV) ^ htobe16(len + 16)); + LogPrint (eLogDebug, "NTCP2: sent length ", len + 16); + + // send message + 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) + { + delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; + LogPrint (eLogDebug, "NTCP2: Next frame sent"); + } + + NTCP2Server::NTCP2Server (): + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service) + { + } + + NTCP2Server::~NTCP2Server () + { + Stop (); + } + + void NTCP2Server::Start () + { + if (!m_IsRunning) + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&NTCP2Server::Run, this)); + } + } + + void NTCP2Server::Stop () + { + if (m_IsRunning) + { + m_IsRunning = false; + 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 ()); + } + } + } + + 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]() + { + conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn)); + }); + } + + void NTCP2Server::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn) + { + if (ecode) + { + LogPrint (eLogInfo, "NTCP2: Connect error ", ecode.message ()); + conn->Terminate (); + } + else + { + LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetSocket ().remote_endpoint ()); + conn->ClientLogin (); + } + } +} +} + diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h new file mode 100644 index 00000000..6ee76aad --- /dev/null +++ b/libi2pd/NTCP2.h @@ -0,0 +1,111 @@ +#ifndef NTCP2_H__ +#define NTCP2_H__ + +#include +#include +#include +#include +#include "RouterInfo.h" +#include "TransportSession.h" + +namespace i2p +{ +namespace transport +{ + 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 Done (); + + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + + void ClientLogin (); // Alice + void ServerLogin (); // Bob + void SendI2NPMessages (const std::vector >& msgs) {}; // TODO + + private: + + void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived); + void CreateNonce (uint64_t seqn, uint8_t * nonce); + void KeyDerivationFunction1 (const uint8_t * rs, const uint8_t * priv, const uint8_t * pub, uint8_t * derived); // for SessionRequest + void KeyDerivationFunction2 (const uint8_t * priv, const uint8_t * pub, const uint8_t * sessionRequest, size_t sessionRequestLen, uint8_t * derived); // for SessionCreate + void KeyDerivationFunction3 (const uint8_t * staticPrivKey, uint8_t * derived); // for SessionConfirmed part 2 + void KeyDerivationFunctionDataPhase (); + + // establish + void CreateEphemeralKey (uint8_t * pub); + 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); + + // 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); + + private: + + NTCP2Server& m_Server; + boost::asio::ip::tcp::socket m_Socket; + bool m_IsEstablished, m_IsTerminated; + + uint8_t m_EphemeralPrivateKey[32]; // x25519 + uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /* derived after SessionCreated */, m_Y[32] /* or X for Bob */; + uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; + size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; + // data phase + uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], m_Sipkeysba[32]; + uint16_t m_NextReceivedLen; + uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; + uint8_t m_ReceiveIV[8], m_SendIV[8]; + uint64_t m_ReceiveSequenceNumber, m_SendSequenceNumber; + }; + + class NTCP2Server + { + public: + + NTCP2Server (); + ~NTCP2Server (); + + void Start (); + void Stop (); + + boost::asio::io_service& GetService () { return m_Service; }; + + void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn); + + private: + + void Run (); + void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn); + + private: + + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + }; +} +} + +#endif diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index e3d6c004..a297ad6a 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -24,6 +24,12 @@ namespace i2p { namespace transport { + + struct NTCPWork + { + std::shared_ptr session; + }; + NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter): TransportSession (in_RemoteRouter, NTCP_ESTABLISH_TIMEOUT), m_Server (server), m_Socket (m_Server.GetService ()), @@ -177,18 +183,20 @@ namespace transport } } // TODO: check for number of pending keys - auto s = shared_from_this (); - m_Server.Work(s, [s]() -> std::function { - if (!s->m_DHKeysPair) - s->m_DHKeysPair = transports.GetNextDHKeysPair (); - s->CreateAESKey (s->m_Establisher->phase1.pubKey); - return std::bind(&NTCPSession::SendPhase2, s); + auto work = new NTCPWork{shared_from_this()}; + m_Server.Work(work->session, [work, this]() -> std::function { + if (!work->session->m_DHKeysPair) + work->session->m_DHKeysPair = transports.GetNextDHKeysPair (); + work->session->CreateAESKey (work->session->m_Establisher->phase1.pubKey); + return std::bind(&NTCPSession::SendPhase2, work->session, work); }); } } - void NTCPSession::SendPhase2 () + void NTCPSession::SendPhase2 (NTCPWork * work) { + if(work) + delete work; const uint8_t * y = m_DHKeysPair->GetPublicKey (); memcpy (m_Establisher->phase2.pubKey, y, 256); uint8_t xy[512]; @@ -241,16 +249,17 @@ namespace transport } else { - auto s = shared_from_this (); - m_Server.Work(s, [s]() -> std::function { - s->CreateAESKey (s->m_Establisher->phase2.pubKey); - return std::bind(&NTCPSession::HandlePhase2, s); + auto work = new NTCPWork{shared_from_this()}; + m_Server.Work(work->session, [work, this]() -> std::function { + work->session->CreateAESKey (work->session->m_Establisher->phase2.pubKey); + return std::bind(&NTCPSession::HandlePhase2, work->session, work); }); } } - void NTCPSession::HandlePhase2 () + void NTCPSession::HandlePhase2 (NTCPWork * work) { + if(work) delete work; m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); diff --git a/libi2pd/NTCPSession.h b/libi2pd/NTCPSession.h index 5335fbdd..e2207eb7 100644 --- a/libi2pd/NTCPSession.h +++ b/libi2pd/NTCPSession.h @@ -35,6 +35,8 @@ namespace transport } encrypted; }; + struct NTCPWork; + const size_t NTCP_MAX_MESSAGE_SIZE = 16384; const size_t NTCP_BUFFER_SIZE = 1028; // fits 1 tunnel data message const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds @@ -77,12 +79,12 @@ namespace transport void SendPhase3 (); void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase2 (); + void HandlePhase2 (NTCPWork * work=nullptr); void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); //server - void SendPhase2 (); + void SendPhase2 (NTCPWork * work=nullptr); void SendPhase4 (uint32_t tsA, uint32_t tsB); void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB); diff --git a/libi2pd/Poly1305.cpp b/libi2pd/Poly1305.cpp new file mode 100644 index 00000000..5378d3cb --- /dev/null +++ b/libi2pd/Poly1305.cpp @@ -0,0 +1,246 @@ +#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 + { + + Poly1305(const uint8_t * key) : m_Leftover(0), m_H{0}, m_Final(0) + { + 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/RouterContext.cpp b/libi2pd/RouterContext.cpp index 38fe3224..9bd6da9f 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" @@ -34,12 +36,8 @@ namespace i2p void RouterContext::CreateNewRouter () { -#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER) m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); -#else - m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1); -#endif - SaveKeys (); + SaveKeys (); NewRouterInfo (); } @@ -52,7 +50,8 @@ 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 ntcp2; i2p::config::GetOption("ntcp2", 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); @@ -93,6 +92,12 @@ namespace i2p routerInfo.CreateBuffer (m_Keys); m_RouterInfo.SetRouterIdentity (GetIdentity ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); + + if (ntcp2) + { + NewNTCP2Keys (); + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + } } void RouterContext::UpdateRouterInfo () @@ -102,6 +107,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) @@ -433,6 +451,30 @@ namespace i2p if (IsUnreachable ()) SetReachable (); // we assume reachable until we discover firewall through peer tests + // read NTCP2 + bool ntcp2; i2p::config::GetOption("ntcp2", ntcp2); + if (ntcp2) + { + std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); + if (n2k) + { + n2k.seekg (0, std::ios::end); + len = fk.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 (); + } + if (!m_NTCP2Keys) + { + NewNTCP2Keys (); + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + } + } + return true; } @@ -482,6 +524,11 @@ namespace i2p bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const { - return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx) : false; + return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false; + } + + bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const + { + return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, false) : false; } } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index ef23af25..ae4aa17e 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; }; @@ -61,6 +74,7 @@ namespace i2p void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; }; int GetNetID () const { return m_NetID; }; 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 UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon @@ -107,6 +121,7 @@ namespace i2p void CreateNewRouter (); void NewRouterInfo (); void UpdateRouterInfo (); + void NewNTCP2Keys (); bool Load (); void SaveKeys (); @@ -124,6 +139,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 e9c2a384..a0b818fb 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 isNtcp2 = 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') isNtcp2= true; + } else if (!strcmp (transportStyle, "SSU")) { address->transportStyle = eTransportSSU; @@ -244,6 +248,18 @@ 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); + } else if (key[0] == 'i') { // introducers @@ -276,7 +292,7 @@ namespace data if (!s) return; } if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented - if (supportedTransports) + if (supportedTransports && !isNtcp2) // we ignore NTCP2 addresses for now. TODO: { addresses->push_back(address); m_SupportedTransports |= supportedTransports; @@ -423,7 +439,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 +455,13 @@ namespace data else WriteString ("", s); - WriteString ("host", properties); - properties << '='; - WriteString (address.host.to_string (), properties); - properties << ';'; + if (!address.IsNTCP2 ()) // we don't publish NTCP2 address fow now. TODO: implement + { + WriteString ("host", properties); + properties << '='; + WriteString (address.host.to_string (), properties); + properties << ';'; + } if (address.transportStyle == eTransportSSU) { // write introducers if any @@ -517,10 +536,23 @@ namespace data properties << ';'; } } - WriteString ("port", properties); - properties << '='; - WriteString (boost::lexical_cast(address.port), properties); - properties << ';'; + + if (!address.IsNTCP2 ()) // we don't publish NTCP2 address fow now. TODO: implement + { + 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 << ';'; + // TODO: publish "i" + } uint16_t size = htobe16 (properties.str ().size ()); s.write ((char *)&size, sizeof (size)); @@ -656,6 +688,21 @@ 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 = 14; + addr->date = 0; + addr->ntcp2.reset (new NTCP2Ext ()); + 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 +782,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 +797,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 +817,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 +833,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; @@ -840,7 +895,7 @@ namespace data { auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); if (encryptor) - encryptor->Encrypt (data, encrypted, ctx); + encryptor->Encrypt (data, encrypted, ctx, true); } } } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 09e2c015..a12b23e3 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,12 @@ namespace data std::vector introducers; }; + struct NTCP2Ext + { + Tag<32> staticKey; + Tag<16> iv; + }; + struct Address { TransportStyle transportStyle; @@ -97,6 +105,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 +122,8 @@ namespace data { return !(*this == other); } + + bool IsNTCP2 () const { return (bool)ntcp2; }; }; typedef std::list > Addresses; @@ -134,6 +145,7 @@ namespace data 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 +156,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 (); 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/TunnelConfig.h b/libi2pd/TunnelConfig.h index 9707fa17..48e66f2e 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -5,7 +5,6 @@ #include #include #include -#include "Crypto.h" #include "Identity.h" #include "RouterContext.h" #include "Timestamp.h" @@ -103,7 +102,9 @@ namespace tunnel htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET); - i2p::crypto::ElGamalEncrypt (ident->GetEncryptionPublicKey (), clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx); + auto encryptor = ident->CreateEncryptor (nullptr); + if (encryptor) + encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false); memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); } }; diff --git a/libi2pd/version.h b/libi2pd/version.h index 36e7eb6e..129035ad 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 19 #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 35 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index fb8fff97..b40c2832 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,31 +496,35 @@ 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 - clientTunnel = new i2p::proxy::SOCKSProxy(name, address, port, false, "", destinationPort, localDestination); - clientEndpoint = ((i2p::proxy::SOCKSProxy*)clientTunnel)->GetLocalEndpoint (); + auto tun = std::make_shared(name, address, port, false, "", 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) @@ -531,7 +533,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 (); @@ -567,7 +569,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); @@ -588,7 +590,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"); @@ -599,7 +601,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()); @@ -610,16 +612,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"); @@ -642,8 +642,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 ac5d907d..ea95a6bd 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,31 @@ 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 chage uri for? m_ClientRequest.uri = m_ClientRequestURL.to_string(); 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") { + if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") + { // handle upstream http 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); } @@ -409,14 +422,18 @@ namespace proxy { m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); })); } - } else if (m_ProxyURL.schema == "socks") { + } + else if (m_ProxyURL.schema == "socks") + { // handle upstream socks proxy if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); })); - } else { + } + else + { // unknown type, complain GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str()); } diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 361d9f94..b08fded1 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -37,7 +37,7 @@ namespace client bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const { if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data, ctx); + return m_Decryptor->Decrypt (encrypted, data, ctx, true); else LogPrint (eLogError, "I2CP: decryptor is not set"); return false; 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/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..d98e24d4 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/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 a2ba4261..0488f289 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -1,283 +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 -} - -!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); +} +