mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-04-20 07:56:36 +02:00
Compare commits
No commits in common. "openssl" and "2.54.0" have entirely different histories.
154 changed files with 4434 additions and 6856 deletions
4
.github/workflows/build-windows.yml
vendored
4
.github/workflows/build-windows.yml
vendored
|
@ -133,8 +133,6 @@ jobs:
|
||||||
git clone https://github.com/msys2/MINGW-packages
|
git clone https://github.com/msys2/MINGW-packages
|
||||||
cd MINGW-packages
|
cd MINGW-packages
|
||||||
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
|
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
|
||||||
cd mingw-w64-boost
|
|
||||||
sed -i 's/boostorg.jfrog.io\/artifactory\/main/archives.boost.io/' PKGBUILD
|
|
||||||
|
|
||||||
# headers
|
# headers
|
||||||
- name: Get headers package version
|
- name: Get headers package version
|
||||||
|
@ -232,8 +230,6 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
cd MINGW-packages/mingw-w64-boost
|
cd MINGW-packages/mingw-w64-boost
|
||||||
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||||
- name: Remove boost packages
|
|
||||||
run: pacman --noconfirm -R mingw-w64-i686-boost mingw-w64-i686-boost-libs
|
|
||||||
- name: Install boost package
|
- name: Install boost package
|
||||||
run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst
|
run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst
|
||||||
|
|
||||||
|
|
8
.github/workflows/docker.yml
vendored
8
.github/workflows/docker.yml
vendored
|
@ -108,7 +108,7 @@ jobs:
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@master
|
||||||
with:
|
with:
|
||||||
inputs: purplei2p/i2pd:latest
|
inputs: purplei2p/i2pd:latest
|
||||||
tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||||
push: true
|
push: true
|
||||||
|
|
||||||
- name: Create and push latest manifest image to GHCR
|
- name: Create and push latest manifest image to GHCR
|
||||||
|
@ -116,7 +116,7 @@ jobs:
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@master
|
||||||
with:
|
with:
|
||||||
inputs: ghcr.io/purplei2p/i2pd:latest
|
inputs: ghcr.io/purplei2p/i2pd:latest
|
||||||
tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||||
push: true
|
push: true
|
||||||
|
|
||||||
- name: Store release version to env
|
- name: Store release version to env
|
||||||
|
@ -128,7 +128,7 @@ jobs:
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@master
|
||||||
with:
|
with:
|
||||||
inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||||
push: true
|
push: true
|
||||||
|
|
||||||
- name: Create and push release manifest to GHCR
|
- name: Create and push release manifest to GHCR
|
||||||
|
@ -136,5 +136,5 @@ jobs:
|
||||||
uses: Noelware/docker-manifest-action@master
|
uses: Noelware/docker-manifest-action@master
|
||||||
with:
|
with:
|
||||||
inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||||
push: true
|
push: true
|
||||||
|
|
64
ChangeLog
64
ChangeLog
|
@ -1,70 +1,6 @@
|
||||||
# for this file format description,
|
# for this file format description,
|
||||||
# see https://github.com/olivierlacan/keep-a-changelog
|
# see https://github.com/olivierlacan/keep-a-changelog
|
||||||
|
|
||||||
## [2.56.0] - 2025-02-11
|
|
||||||
### Added
|
|
||||||
- Config params for shared local destination
|
|
||||||
- AddressBook full addresses cache
|
|
||||||
- Decline transit tunnel to duplicated router
|
|
||||||
- Recreate tunnels in random order
|
|
||||||
### Changed
|
|
||||||
- Exclude disk operations from SSU2 and NTCP2 threads
|
|
||||||
- Set minimal version for peer test to 0.9.62
|
|
||||||
- Send ack requested flag after second SSU2 resend attempt
|
|
||||||
- Shorter ECIESx25519 ack request interval for datagram and I2CP sessions
|
|
||||||
- Don't change datagram routing path too often if unidirectional data stream
|
|
||||||
- Reduce LeaseSet and local RouterInfo publishing confirmation intervals
|
|
||||||
- Don't delete buffer of connected routers or if an update received
|
|
||||||
- Smaller RouterInfo request timeout if sent directly
|
|
||||||
- Persist local RouterInfo in separate thread
|
|
||||||
- Don't recalculate and process ranges for every SSU2 Ack block
|
|
||||||
- Reseeds list
|
|
||||||
### Fixed
|
|
||||||
- Termination deadlock if SAM session is active
|
|
||||||
- Race condition at tunnel endpoint
|
|
||||||
- Inbound tunnel build encryption
|
|
||||||
|
|
||||||
## [2.55.0] - 2024-12-30
|
|
||||||
### Added
|
|
||||||
- Support boost 1.87
|
|
||||||
- "i2p.streaming.maxConcurrentStreams" tunnel's param to limit number of simultaneous streams
|
|
||||||
- Separate thread for tunnel build requests
|
|
||||||
- Show next peer and connectivity on "Transit tunnels" page
|
|
||||||
- Tunnel name for local destination thread
|
|
||||||
- Throttle incoming ECIESx25519 sessions
|
|
||||||
- Send tunnel data to transport session directly if possible
|
|
||||||
- Publish 'R' cap for yggdrasil-only routers, and 'U' cap for routers through proxy
|
|
||||||
- Random tunnel rejection when medium congestion
|
|
||||||
- Save unreachable router's endpoint to use it next time without introducers
|
|
||||||
- Recognize symmetric NAT from peer test message 7
|
|
||||||
- Resend HolePunch and RelayResponse messages
|
|
||||||
### Changed
|
|
||||||
- Removed own implementation of AESNI and always use one from openssl
|
|
||||||
- Renamed main thread to i2pd-daemon
|
|
||||||
- Set i2p.streaming.profile=2 for shared local destination
|
|
||||||
- Reduced LeaseSet and RouterInfo lookup timeouts
|
|
||||||
- Cleanup ECIES sessions and tags more often
|
|
||||||
- Check LeaseSet expiration time
|
|
||||||
- Handle NTCP2 session handshakes in separate thread
|
|
||||||
- Limit last decline time by 1.5 hours in router's profile
|
|
||||||
- Don't handle RelayRequest and RelayIntro with same nonce twice
|
|
||||||
- Increased hole punch expiration interval
|
|
||||||
- Send peer test message 6 with delay if message 4 was received before message 5
|
|
||||||
- Pre-calculate more x25519 keys for transports in runtime
|
|
||||||
- Don't request LeaseSet for incoming stream
|
|
||||||
- Terminate incoming stream right away if no remote LeaseSet
|
|
||||||
- Handle choked, new RTO and window size calculation and resetting algorithm for streams
|
|
||||||
### Fixed
|
|
||||||
- Empty string in addressbook subscriptions
|
|
||||||
- ECIESx25519 sessions without destination
|
|
||||||
- Missing RouterInfo buffer in NetDb
|
|
||||||
- Invalid I2PControl certificate
|
|
||||||
- Routers disappear from NetDb when offline
|
|
||||||
- Peer test message 6 sent to unknown endpoint
|
|
||||||
- Race condition with LeaseSet update
|
|
||||||
- Excessive CPU usage by streams
|
|
||||||
- Crash on shutdown
|
|
||||||
|
|
||||||
## [2.54.0] - 2024-10-06
|
## [2.54.0] - 2024-10-06
|
||||||
### Added
|
### Added
|
||||||
- Maintain recently connected routers list to avoid false-positive peer test
|
- Maintain recently connected routers list to avoid false-positive peer test
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2013-2025, The PurpleI2P Project
|
Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -29,6 +29,7 @@ DAEMON_SRC_DIR := daemon
|
||||||
# import source files lists
|
# import source files lists
|
||||||
include filelist.mk
|
include filelist.mk
|
||||||
|
|
||||||
|
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||||
USE_STATIC := $(or $(USE_STATIC),no)
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
USE_UPNP := $(or $(USE_UPNP),no)
|
USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
DEBUG := $(or $(DEBUG),yes)
|
DEBUG := $(or $(DEBUG),yes)
|
||||||
|
@ -69,9 +70,6 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||||
else ifneq (, $(findstring haiku, $(SYS)))
|
else ifneq (, $(findstring haiku, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
include Makefile.haiku
|
include Makefile.haiku
|
||||||
else ifneq (, $(findstring solaris, $(SYS)))
|
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
|
||||||
include Makefile.solaris
|
|
||||||
else # not supported
|
else # not supported
|
||||||
$(error Not supported platform)
|
$(error Not supported platform)
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -3,7 +3,7 @@ CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misl
|
||||||
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
|
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
|
||||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||||
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||||
LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_system -lboost_program_options
|
LDLIBS = -lcrypto -lssl -lz -lpthread -lboost_system -lboost_program_options
|
||||||
|
|
||||||
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
||||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
ifeq ($(shell $(CXX) -dumpmachine | cut -c 1-4), i586)
|
|
||||||
CXX = g++-x86
|
|
||||||
else
|
|
||||||
CXX = g++
|
CXX = g++
|
||||||
endif
|
CXXFLAGS := -Wall -std=c++17
|
||||||
CXXFLAGS := -Wall -std=c++20
|
|
||||||
INCFLAGS = -I/system/develop/headers
|
INCFLAGS = -I/system/develop/headers
|
||||||
DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||||
LDLIBS = -lbe -lbsd -lnetwork -lz -lssl -lcrypto -lboost_program_options -lpthread
|
LDLIBS = -lbe -lbsd -lnetwork -lz -lcrypto -lssl -lboost_system -lboost_program_options -lpthread
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),yes)
|
ifeq ($(USE_UPNP),yes)
|
||||||
DEFINES += -DUSE_UPNP
|
DEFINES += -DUSE_UPNP
|
||||||
|
|
|
@ -18,7 +18,7 @@ endif
|
||||||
LDLIBS += -lpthread -ldl
|
LDLIBS += -lpthread -ldl
|
||||||
else
|
else
|
||||||
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
||||||
LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread
|
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread
|
||||||
ifeq ($(USE_UPNP),yes)
|
ifeq ($(USE_UPNP),yes)
|
||||||
LDFLAGS += -L${UPNPROOT}/lib
|
LDFLAGS += -L${UPNPROOT}/lib
|
||||||
LDLIBS += -lminiupnpc
|
LDLIBS += -lminiupnpc
|
||||||
|
@ -30,6 +30,13 @@ ifeq ($(USE_UPNP),yes)
|
||||||
INCFLAGS += -I${UPNPROOT}/include
|
INCFLAGS += -I${UPNPROOT}/include
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_AESNI),yes)
|
||||||
|
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
|
||||||
|
NEEDED_CXXFLAGS += -maes
|
||||||
|
DEFINES += -D__AES__
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
install -d ${PREFIX}/bin
|
install -d ${PREFIX}/bin
|
||||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||||
|
|
|
@ -13,9 +13,12 @@ LDFLAGS ?= ${LD_DEBUG}
|
||||||
CXXVER := $(shell $(CXX) -dumpversion)
|
CXXVER := $(shell $(CXX) -dumpversion)
|
||||||
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||||
NEEDED_CXXFLAGS += -std=c++17
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
|
else ifeq ($(shell expr match ${CXXVER} "7"),1) # gcc 7
|
||||||
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
|
LDLIBS = -lboost_filesystem
|
||||||
else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
|
else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
|
||||||
NEEDED_CXXFLAGS += -std=c++17
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
LDLIBS = -lboost_system -lstdc++fs
|
LDLIBS = -lstdc++fs
|
||||||
else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12
|
else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12
|
||||||
NEEDED_CXXFLAGS += -std=c++17
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+
|
else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+
|
||||||
|
@ -31,6 +34,7 @@ ifeq ($(USE_STATIC),yes)
|
||||||
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||||
# the shared libraries from the glibc version used for linking
|
# the shared libraries from the glibc version used for linking
|
||||||
LIBDIR := /usr/lib/$(SYS)
|
LIBDIR := /usr/lib/$(SYS)
|
||||||
|
LDLIBS += $(LIBDIR)/libboost_system.a
|
||||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||||
LDLIBS += $(LIBDIR)/libssl.a
|
LDLIBS += $(LIBDIR)/libssl.a
|
||||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||||
|
@ -40,7 +44,7 @@ ifeq ($(USE_UPNP),yes)
|
||||||
endif
|
endif
|
||||||
LDLIBS += -lpthread -ldl
|
LDLIBS += -lpthread -ldl
|
||||||
else
|
else
|
||||||
LDLIBS += -lssl -lcrypto -lz -lboost_program_options -lpthread -latomic
|
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_program_options -lpthread -latomic
|
||||||
ifeq ($(USE_UPNP),yes)
|
ifeq ($(USE_UPNP),yes)
|
||||||
LDLIBS += -lminiupnpc
|
LDLIBS += -lminiupnpc
|
||||||
endif
|
endif
|
||||||
|
@ -51,6 +55,13 @@ ifeq ($(USE_UPNP),yes)
|
||||||
DEFINES += -DUSE_UPNP
|
DEFINES += -DUSE_UPNP
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_AESNI),yes)
|
||||||
|
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
|
||||||
|
NEEDED_CXXFLAGS += -maes
|
||||||
|
DEFINES += -D__AES__
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
install -d ${PREFIX}/bin
|
install -d ${PREFIX}/bin
|
||||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||||
|
|
|
@ -42,6 +42,12 @@ ifeq ($(USE_WIN32_APP), yes)
|
||||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_AESNI),yes)
|
||||||
|
NEEDED_CXXFLAGS += -maes
|
||||||
|
LDFLAGS += -maes
|
||||||
|
DEFINES += -D__AES__
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_ASLR),yes)
|
ifeq ($(USE_ASLR),yes)
|
||||||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -7,9 +7,9 @@ LDFLAGS += -Wl,-dead_strip
|
||||||
LDFLAGS += -Wl,-dead_strip_dylibs
|
LDFLAGS += -Wl,-dead_strip_dylibs
|
||||||
|
|
||||||
ifeq ($(USE_STATIC),yes)
|
ifeq ($(USE_STATIC),yes)
|
||||||
LDLIBS = -lz /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||||
else
|
else
|
||||||
LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread
|
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_filesystem -lboost_program_options -lpthread
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),yes)
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
@ -25,5 +25,9 @@ endif
|
||||||
OSARCH = $(shell uname -p)
|
OSARCH = $(shell uname -p)
|
||||||
|
|
||||||
ifneq ($(OSARCH),powerpc)
|
ifneq ($(OSARCH),powerpc)
|
||||||
|
ifeq ($(USE_AESNI),yes)
|
||||||
|
CXXFLAGS += -D__AES__ -maes
|
||||||
|
else
|
||||||
CXXFLAGS += -msse
|
CXXFLAGS += -msse
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
CXX = g++
|
|
||||||
INCFLAGS = -I/usr/openssl/3/include
|
|
||||||
CXXFLAGS := -Wall -std=c++20
|
|
||||||
LDLIBS = -L/usr/openssl/3/lib/64 -lssl -lcrypto -lboost_program_options -lz -lpthread -lsocket
|
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),yes)
|
|
||||||
DEFINES += -DUSE_UPNP
|
|
||||||
LDLIBS += -lminiupnpc
|
|
||||||
endif
|
|
|
@ -29,6 +29,7 @@ project(
|
||||||
)
|
)
|
||||||
|
|
||||||
# configurable options
|
# configurable options
|
||||||
|
option(WITH_AESNI "Use AES-NI instructions set" ON)
|
||||||
option(WITH_HARDENING "Use hardening compiler flags" OFF)
|
option(WITH_HARDENING "Use hardening compiler flags" OFF)
|
||||||
option(WITH_LIBRARY "Build library" ON)
|
option(WITH_LIBRARY "Build library" ON)
|
||||||
option(WITH_BINARY "Build binary" ON)
|
option(WITH_BINARY "Build binary" ON)
|
||||||
|
@ -184,6 +185,16 @@ if(UNIX)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Note: AES-NI and AVX is available on x86-based CPU's.
|
||||||
|
# Here also ARM64 implementation, but currently we don't support it.
|
||||||
|
# MSVC is not supported due to different ASM processing, so we hope OpenSSL has its own checks to run optimized code.
|
||||||
|
if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386"))
|
||||||
|
if(NOT MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
|
||||||
|
endif()
|
||||||
|
add_definitions(-D__AES__)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WITH_ADDRSANITIZER)
|
if(WITH_ADDRSANITIZER)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||||
|
@ -324,6 +335,7 @@ message(STATUS "Architecture : ${ARCHITECTURE}")
|
||||||
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
|
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
|
||||||
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
|
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
|
||||||
message(STATUS "Options:")
|
message(STATUS "Options:")
|
||||||
|
message(STATUS " AESNI : ${WITH_AESNI}")
|
||||||
message(STATUS " HARDENING : ${WITH_HARDENING}")
|
message(STATUS " HARDENING : ${WITH_HARDENING}")
|
||||||
message(STATUS " LIBRARY : ${WITH_LIBRARY}")
|
message(STATUS " LIBRARY : ${WITH_LIBRARY}")
|
||||||
message(STATUS " BINARY : ${WITH_BINARY}")
|
message(STATUS " BINARY : ${WITH_BINARY}")
|
||||||
|
|
|
@ -59,7 +59,7 @@ get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||||
# function returns an empty string via _git_dir_var.
|
# function returns an empty string via _git_dir_var.
|
||||||
#
|
#
|
||||||
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||||
# neither foo nor bar contain a file/directory .git. This will return
|
# neither foo nor bar contain a file/directory .git. This wil return
|
||||||
# C:/bla/.git
|
# C:/bla/.git
|
||||||
#
|
#
|
||||||
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||||
|
|
32
contrib/certificates/reseed/hiduser0_at_mail.i2p.crt
Normal file
32
contrib/certificates/reseed/hiduser0_at_mail.i2p.crt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFgTCCA2mgAwIBAgIETWAY1DANBgkqhkiG9w0BAQ0FADBxMQswCQYDVQQGEwJY
|
||||||
|
WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt
|
||||||
|
b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBAbWFp
|
||||||
|
bC5pMnAwHhcNMjExMjEzMTU0MDI3WhcNMzExMjExMTU0MDI3WjBxMQswCQYDVQQG
|
||||||
|
EwJYWDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5v
|
||||||
|
bnltb3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBA
|
||||||
|
bWFpbC5pMnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXnjJ8UQ0f
|
||||||
|
lHHpfPMiHofBPSuL4sbOJY6fOXwPhSg/h6THh9DS/ZWmJXQ3qRD0glDVtv4/Dr/9
|
||||||
|
ldGQ5eltF9iCFXCQlMEy2HjQrBKq0nsl7RpYK12cyMaod0kkzCUk9ITLi9CmHM3Z
|
||||||
|
gQZcmG8TWjFEpDR+idx/QkQt2pcO4vzWlDit3Vh4ivnbX5jGQHbsVjQEMQWxr+pX
|
||||||
|
dsS+YQpjZ6RBmrooGTPO8QDOOeYLAn0lCjmffc/kzIH9E/p4/O0rOpyhVYbdxUD1
|
||||||
|
5wkqN9l4yrtxmORG/PudnRQQ0r4TUq8vsxfGY0Euo9IbhgXF2Parel1ZhDxB1WZV
|
||||||
|
VwWtgLIh9jGA1UMa8SYKnEfp8LWNZ3b3mUUnZb3kMrLk6jGYRWNsHmamhd4mC7AZ
|
||||||
|
qf/8lOkEIw3bPd3YguCDRVcLui5BwIEZmqXg8uoESxfO/sW3pBrN/8M7MkTex9kN
|
||||||
|
vjitGDDXvenK27qmNgZxbBlX72yTSfys7XTYTLnxZC8AwdAo2Wz9Z6HhGiPonf2h
|
||||||
|
vZkc9ZxuE0jFIrsbJra4X7iyjXgi4vV4ARNg/9Ft6F4/OIbECgeDcBQqq4TlT2bZ
|
||||||
|
EfWVrBbqXoj5vNsLigIkd+AyUNwPYEcB5IFSiiOh98pC7BH3pg0m8U5YBjxe1i+9
|
||||||
|
EQOOG0Qtx+JigXZHu6bGE0Twy9zy+UzoKQIDAQABoyEwHzAdBgNVHQ4EFgQUGK1b
|
||||||
|
0DkL6aLalcfBc/Uj/SF08C0wDQYJKoZIhvcNAQENBQADggIBAMpXM82bJDpH1TlH
|
||||||
|
TvhU3Z7nfZdvEhOQfujaFUYiuNripuEKcFGn948+DvAG0FUN+uNlJoqOVs8D7InD
|
||||||
|
gWlA9zpqw5Cl5Hij/Wns9QbXuAHJeA23fVUoaM2A6v9ifcIQ1A+rDuRQAo6/64KW
|
||||||
|
ChTg2e99RBpfGOyqgeh7tLLe0lPPekVpKHFuXabokaKRDuBcVHcUL4tWXe3dcyqa
|
||||||
|
Ej/PJrrS+nWL0EGZ4q80CEd2LPuDzPxNGCJt/R7ZfadENWajcgcXGceh1QBzozrB
|
||||||
|
SL/Ya6wF9SrsB7V/r5wX0LM4ZdDaLWbtmUe5Op0h/ZMH25Sa8xAXVz+O9L6sWSoO
|
||||||
|
FaiYTOvAiyyPz+nsxKa3xYryDHno7eKSt+hGOcaurhxbdZaEFY/CegEc73tCt9xK
|
||||||
|
e9qF8O/WkDLmixuErw3f5en4IfzGR7p3lJAwW/8WD8C6HS39h/eE7dVZNaWgtQnZ
|
||||||
|
SgGjgZMTJqTcQ3aZmfuCZefxGFok8w6AIkdbnd1pdMBRjYu8aXgl2hQSB9ZADDE9
|
||||||
|
R5d3rXi0PkSFLIvsNjVa5KXrZk/tB0Hpfmepq7CufBqjP/LG9TieRoXzLYUKFF74
|
||||||
|
QRwjP+y7AJ+VDUTpY1NV1P+k+2raubU2bOnLF3zL5DtyoyieGPhyeMMvp0fRIxdg
|
||||||
|
bSl5VHgPXHNM8mcnndMAuzvl7jEK
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
||||||
Author: r4sas <r4sas@i2pmail.org>
|
Author: r4sas <r4sas@i2pmail.org>
|
||||||
|
|
||||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||||
Last-Update: 2024-12-30
|
Last-Update: 2022-03-23
|
||||||
|
|
||||||
--- i2pd.orig/Makefile
|
--- i2pd.orig/Makefile
|
||||||
+++ i2pd/Makefile
|
+++ i2pd/Makefile
|
||||||
@@ -31,7 +31,7 @@ # import source files lists
|
@@ -31,7 +31,7 @@ include filelist.mk
|
||||||
include filelist.mk
|
|
||||||
|
|
||||||
|
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||||
USE_STATIC := $(or $(USE_STATIC),no)
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
+USE_UPNP := $(or $(USE_UPNP),yes)
|
+USE_UPNP := $(or $(USE_UPNP),yes)
|
||||||
|
|
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
||||||
Author: r4sas <r4sas@i2pmail.org>
|
Author: r4sas <r4sas@i2pmail.org>
|
||||||
|
|
||||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||||
Last-Update: 2024-12-30
|
Last-Update: 2022-03-23
|
||||||
|
|
||||||
--- i2pd.orig/Makefile
|
--- i2pd.orig/Makefile
|
||||||
+++ i2pd/Makefile
|
+++ i2pd/Makefile
|
||||||
@@ -31,7 +31,7 @@ # import source files lists
|
@@ -31,7 +31,7 @@ include filelist.mk
|
||||||
include filelist.mk
|
|
||||||
|
|
||||||
|
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||||
USE_STATIC := $(or $(USE_STATIC),no)
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
+USE_UPNP := $(or $(USE_UPNP),yes)
|
+USE_UPNP := $(or $(USE_UPNP),yes)
|
||||||
|
|
|
@ -243,7 +243,7 @@ verify = true
|
||||||
## Default: reg.i2p at "mainline" I2P Network
|
## Default: reg.i2p at "mainline" I2P Network
|
||||||
# defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt
|
# defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt
|
||||||
## Optional subscriptions URLs, separated by comma
|
## Optional subscriptions URLs, separated by comma
|
||||||
# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt
|
# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt,http://rus.i2p/hosts.txt
|
||||||
|
|
||||||
[limits]
|
[limits]
|
||||||
## Maximum active transit sessions (default: 5000)
|
## Maximum active transit sessions (default: 5000)
|
||||||
|
@ -277,3 +277,9 @@ verify = true
|
||||||
## Save full addresses on disk (default: true)
|
## Save full addresses on disk (default: true)
|
||||||
# addressbook = true
|
# addressbook = true
|
||||||
|
|
||||||
|
[cpuext]
|
||||||
|
## Use CPU AES-NI instructions set when work with cryptography when available (default: true)
|
||||||
|
# aesni = true
|
||||||
|
## Force usage of CPU instructions set, even if they not found (default: false)
|
||||||
|
## DO NOT TOUCH that option if you really don't know what are you doing!
|
||||||
|
# force = false
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=I2P Router written in C++
|
Description=I2P Router written in C++
|
||||||
Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/
|
Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/
|
||||||
Wants=network.target
|
After=network.target
|
||||||
After=network.target network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=i2pd
|
User=i2pd
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||||
|
|
||||||
Name: i2pd-git
|
Name: i2pd-git
|
||||||
Version: 2.56.0
|
Version: 2.54.0
|
||||||
Release: git%{git_hash}%{?dist}
|
Release: git%{git_hash}%{?dist}
|
||||||
Summary: I2P router written in C++
|
Summary: I2P router written in C++
|
||||||
Conflicts: i2pd
|
Conflicts: i2pd
|
||||||
|
@ -148,12 +148,6 @@ getent passwd i2pd >/dev/null || \
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue Feb 11 2025 orignal <orignal@i2pmail.org> - 2.56.0
|
|
||||||
- update to 2.56.0
|
|
||||||
|
|
||||||
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0
|
|
||||||
- update to 2.55.0
|
|
||||||
|
|
||||||
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
|
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
|
||||||
- update to 2.54.0
|
- update to 2.54.0
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Name: i2pd
|
Name: i2pd
|
||||||
Version: 2.56.0
|
Version: 2.54.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: I2P router written in C++
|
Summary: I2P router written in C++
|
||||||
Conflicts: i2pd-git
|
Conflicts: i2pd-git
|
||||||
|
@ -146,12 +146,6 @@ getent passwd i2pd >/dev/null || \
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue Feb 11 2025 orignal <orignal@i2pmail.org> - 2.56.0
|
|
||||||
- update to 2.56.0
|
|
||||||
|
|
||||||
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0
|
|
||||||
- update to 2.55.0
|
|
||||||
|
|
||||||
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
|
* Sun Oct 6 2024 orignal <orignal@i2pmail.org> - 2.54.0
|
||||||
- update to 2.54.0
|
- update to 2.54.0
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ port = 6668
|
||||||
destination = irc.ilita.i2p
|
destination = irc.ilita.i2p
|
||||||
destinationport = 6667
|
destinationport = 6667
|
||||||
keys = irc-keys.dat
|
keys = irc-keys.dat
|
||||||
i2p.streaming.profile=2
|
|
||||||
|
|
||||||
#[IRC-IRC2P]
|
#[IRC-IRC2P]
|
||||||
#type = client
|
#type = client
|
||||||
|
|
|
@ -149,10 +149,12 @@ namespace util
|
||||||
LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir);
|
LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir);
|
||||||
|
|
||||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||||
|
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
|
||||||
|
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
|
||||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||||
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
|
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
|
||||||
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
|
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
|
||||||
i2p::crypto::InitCrypto (precomputation);
|
i2p::crypto::InitCrypto (precomputation, aesni, forceCpuExt);
|
||||||
|
|
||||||
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
|
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -132,8 +132,7 @@ namespace http {
|
||||||
|
|
||||||
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
|
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
|
||||||
{
|
{
|
||||||
std::string state;
|
std::string state, stateText;
|
||||||
std::string_view stateText;
|
|
||||||
switch (eState)
|
switch (eState)
|
||||||
{
|
{
|
||||||
case i2p::tunnel::eTunnelStateBuildReplyReceived :
|
case i2p::tunnel::eTunnelStateBuildReplyReceived :
|
||||||
|
@ -147,7 +146,7 @@ namespace http {
|
||||||
}
|
}
|
||||||
if (stateText.empty ()) stateText = tr(state);
|
if (stateText.empty ()) stateText = tr(state);
|
||||||
|
|
||||||
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + std::string(tr("exploratory")) + ")" : "") << "</span>, "; // TODO:
|
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
|
||||||
ShowTraffic(s, bytes);
|
ShowTraffic(s, bytes);
|
||||||
s << "\r\n";
|
s << "\r\n";
|
||||||
}
|
}
|
||||||
|
@ -214,7 +213,7 @@ namespace http {
|
||||||
"</html>\r\n";
|
"</html>\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ShowError(std::stringstream& s, std::string_view string)
|
static void ShowError(std::stringstream& s, const std::string& string)
|
||||||
{
|
{
|
||||||
s << "<b>" << tr("ERROR") << ":</b> " << string << "<br>\r\n";
|
s << "<b>" << tr("ERROR") << ":</b> " << string << "<br>\r\n";
|
||||||
}
|
}
|
||||||
|
@ -702,7 +701,6 @@ namespace http {
|
||||||
{
|
{
|
||||||
s << "<b>" << tr("Tunnels") << ":</b><br>\r\n";
|
s << "<b>" << tr("Tunnels") << ":</b><br>\r\n";
|
||||||
s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
|
s << "<b>" << tr("Queue size") << ":</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n<br>\r\n";
|
||||||
s << "<b>" << tr("TBM Queue size") << ":</b> " << i2p::tunnel::tunnels.GetTBMQueueSize () << "<br>\r\n<br>\r\n";
|
|
||||||
|
|
||||||
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||||
|
|
||||||
|
@ -827,7 +825,7 @@ namespace http {
|
||||||
if (i2p::tunnel::tunnels.CountTransitTunnels())
|
if (i2p::tunnel::tunnels.CountTransitTunnels())
|
||||||
{
|
{
|
||||||
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n";
|
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n";
|
||||||
s << "<table><thead><th>⇒</th><th>ID</th><th>⇒</th><th>" << tr("Amount") << "</th><th>" << tr("Next") << "</th></thead><tbody class=\"tableitem\">";
|
s << "<table><thead><th>⇒</th><th>ID</th><th>⇒</th><th>" << tr("Amount") << "</th></thead><tbody class=\"tableitem\">";
|
||||||
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
||||||
{
|
{
|
||||||
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
|
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
|
||||||
|
@ -837,7 +835,7 @@ namespace http {
|
||||||
else
|
else
|
||||||
s << "<tr><td>⇒</td><td>" << it->GetTunnelID () << "</td><td>⇒</td><td>";
|
s << "<tr><td>⇒</td><td>" << it->GetTunnelID () << "</td><td>⇒</td><td>";
|
||||||
ShowTraffic(s, it->GetNumTransmittedBytes ());
|
ShowTraffic(s, it->GetNumTransmittedBytes ());
|
||||||
s << "</td><td>" << it->GetNextPeerName () << "</td></tr>\r\n";
|
s << "</td></tr>\r\n";
|
||||||
}
|
}
|
||||||
s << "</tbody></table>\r\n";
|
s << "</tbody></table>\r\n";
|
||||||
}
|
}
|
||||||
|
@ -1263,7 +1261,7 @@ namespace http {
|
||||||
ShowLeasesSets(s);
|
ShowLeasesSets(s);
|
||||||
else {
|
else {
|
||||||
res.code = 400;
|
res.code = 400;
|
||||||
ShowError(s, std::string (tr("Unknown page")) + ": " + page); // TODO
|
ShowError(s, tr("Unknown page") + ": " + page);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1419,11 +1417,13 @@ namespace http {
|
||||||
{
|
{
|
||||||
auto signatureLen = dest->GetIdentity ()->GetSignatureLen ();
|
auto signatureLen = dest->GetIdentity ()->GetSignatureLen ();
|
||||||
uint8_t * signature = new uint8_t[signatureLen];
|
uint8_t * signature = new uint8_t[signatureLen];
|
||||||
|
char * sig = new char[signatureLen*2];
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
|
|
||||||
out << name << "=" << dest->GetIdentity ()->ToBase64 ();
|
out << name << "=" << dest->GetIdentity ()->ToBase64 ();
|
||||||
dest->Sign ((uint8_t *)out.str ().c_str (), out.str ().length (), signature);
|
dest->Sign ((uint8_t *)out.str ().c_str (), out.str ().length (), signature);
|
||||||
auto sig = i2p::data::ByteStreamToBase64 (signature, signatureLen);
|
auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2);
|
||||||
|
sig[len] = 0;
|
||||||
out << "#!sig=" << sig;
|
out << "#!sig=" << sig;
|
||||||
s << "<b>" << tr("SUCCESS") << "</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
|
s << "<b>" << tr("SUCCESS") << "</b>:<br>\r\n<form action=\"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/add\" method=\"post\" rel=\"noreferrer\" target=\"_blank\">\r\n"
|
||||||
"<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
|
"<textarea readonly name=\"record\" cols=\"80\" rows=\"10\">" << out.str () << "</textarea>\r\n<br>\r\n<br>\r\n"
|
||||||
|
@ -1432,6 +1432,7 @@ namespace http {
|
||||||
"<input type=\"submit\" value=\"" << tr("Submit") << "\">\r\n"
|
"<input type=\"submit\" value=\"" << tr("Submit") << "\">\r\n"
|
||||||
"</form>\r\n<br>\r\n";
|
"</form>\r\n<br>\r\n";
|
||||||
delete[] signature;
|
delete[] signature;
|
||||||
|
delete[] sig;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("Domain can't end with .b32.i2p") << "\r\n<br>\r\n<br>\r\n";
|
s << "<b>" << tr("ERROR") << "</b>: " << tr("Domain can't end with .b32.i2p") << "\r\n<br>\r\n<br>\r\n";
|
||||||
|
@ -1460,7 +1461,7 @@ namespace http {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res.code = 400;
|
res.code = 400;
|
||||||
ShowError(s, std::string (tr("Unknown command")) + ": " + cmd); // TODO
|
ShowError(s, tr("Unknown command") + ": " + cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1484,8 +1485,8 @@ namespace http {
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPServer::HTTPServer (const std::string& address, int port):
|
HTTPServer::HTTPServer (const std::string& address, int port):
|
||||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service.get_executor ()),
|
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port)),
|
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
|
||||||
m_Hostname(address)
|
m_Hostname(address)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,8 @@ namespace http
|
||||||
|
|
||||||
bool m_IsRunning;
|
bool m_IsRunning;
|
||||||
std::unique_ptr<std::thread> m_Thread;
|
std::unique_ptr<std::thread> m_Thread;
|
||||||
boost::asio::io_context m_Service;
|
boost::asio::io_service m_Service;
|
||||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_Work;
|
boost::asio::io_service::work m_Work;
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||||
std::string m_Hostname;
|
std::string m_Hostname;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
||||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
@ -29,24 +30,11 @@ namespace i2p
|
||||||
namespace client
|
namespace client
|
||||||
{
|
{
|
||||||
I2PControlService::I2PControlService (const std::string& address, int port):
|
I2PControlService::I2PControlService (const std::string& address, int port):
|
||||||
m_IsRunning (false),
|
m_IsRunning (false), m_Thread (nullptr),
|
||||||
|
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
|
||||||
m_SSLContext (boost::asio::ssl::context::sslv23),
|
m_SSLContext (boost::asio::ssl::context::sslv23),
|
||||||
m_ShutdownTimer (m_Service)
|
m_ShutdownTimer (m_Service)
|
||||||
{
|
{
|
||||||
if (port)
|
|
||||||
m_Acceptor = std::make_unique<boost::asio::ip::tcp::acceptor>(m_Service,
|
|
||||||
boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port));
|
|
||||||
else
|
|
||||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
|
||||||
{
|
|
||||||
std::remove (address.c_str ()); // just in case
|
|
||||||
m_LocalAcceptor = std::make_unique<boost::asio::local::stream_protocol::acceptor>(m_Service,
|
|
||||||
boost::asio::local::stream_protocol::endpoint(address));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
LogPrint(eLogError, "I2PControl: Local sockets are not supported");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
i2p::config::GetOption("i2pcontrol.password", m_Password);
|
i2p::config::GetOption("i2pcontrol.password", m_Password);
|
||||||
|
|
||||||
// certificate / keys
|
// certificate / keys
|
||||||
|
@ -57,29 +45,15 @@ namespace client
|
||||||
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
|
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
|
||||||
if (i2pcp_key.at(0) != '/')
|
if (i2pcp_key.at(0) != '/')
|
||||||
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
|
i2pcp_key = i2p::fs::DataDirPath(i2pcp_key);
|
||||||
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key))
|
if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) {
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection");
|
LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection");
|
||||||
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt);
|
LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt);
|
||||||
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
|
|
||||||
boost::system::error_code ec;
|
|
||||||
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec);
|
|
||||||
if (!ec)
|
|
||||||
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec);
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "I2PControl: Failed to load ceritifcate: ", ec.message (), ". Recreating");
|
|
||||||
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
|
||||||
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec);
|
|
||||||
if (!ec)
|
|
||||||
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec);
|
|
||||||
if (ec)
|
|
||||||
// give up
|
|
||||||
LogPrint (eLogError, "I2PControl: Can't load certificates");
|
|
||||||
}
|
}
|
||||||
|
m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
|
||||||
|
m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem);
|
||||||
|
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem);
|
||||||
|
|
||||||
// handlers
|
// handlers
|
||||||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
||||||
|
@ -110,7 +84,7 @@ namespace client
|
||||||
{
|
{
|
||||||
Accept ();
|
Accept ();
|
||||||
m_IsRunning = true;
|
m_IsRunning = true;
|
||||||
m_Thread = std::make_unique<std::thread>(std::bind (&I2PControlService::Run, this));
|
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,19 +93,12 @@ namespace client
|
||||||
if (m_IsRunning)
|
if (m_IsRunning)
|
||||||
{
|
{
|
||||||
m_IsRunning = false;
|
m_IsRunning = false;
|
||||||
if (m_Acceptor) m_Acceptor->cancel ();
|
m_Acceptor.cancel ();
|
||||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
|
||||||
if (m_LocalAcceptor)
|
|
||||||
{
|
|
||||||
auto path = m_LocalAcceptor->local_endpoint().path();
|
|
||||||
m_LocalAcceptor->cancel ();
|
|
||||||
std::remove (path.c_str ());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
m_Service.stop ();
|
m_Service.stop ();
|
||||||
if (m_Thread)
|
if (m_Thread)
|
||||||
{
|
{
|
||||||
m_Thread->join ();
|
m_Thread->join ();
|
||||||
|
delete m_Thread;
|
||||||
m_Thread = nullptr;
|
m_Thread = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,60 +120,40 @@ namespace client
|
||||||
|
|
||||||
void I2PControlService::Accept ()
|
void I2PControlService::Accept ()
|
||||||
{
|
{
|
||||||
if (m_Acceptor)
|
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
|
||||||
{
|
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
|
||||||
auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> > (m_Service, m_SSLContext);
|
std::placeholders::_1, newSocket));
|
||||||
m_Acceptor->async_accept (newSocket->lowest_layer(),
|
|
||||||
[this, newSocket](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
HandleAccepted (ecode, newSocket);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
|
||||||
else if (m_LocalAcceptor)
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::local::stream_protocol::socket> > (m_Service, m_SSLContext);
|
|
||||||
m_LocalAcceptor->async_accept (newSocket->lowest_layer(),
|
|
||||||
[this, newSocket](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
HandleAccepted (ecode, newSocket);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ssl_socket>
|
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||||
void I2PControlService::HandleAccepted (const boost::system::error_code& ecode,
|
|
||||||
std::shared_ptr<ssl_socket> newSocket)
|
|
||||||
{
|
{
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
Accept ();
|
Accept ();
|
||||||
|
|
||||||
if (ecode)
|
if (ecode) {
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ());
|
LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LogPrint (eLogDebug, "I2PControl: New request from ", newSocket->lowest_layer ().remote_endpoint ());
|
LogPrint (eLogDebug, "I2PControl: New request from ", socket->lowest_layer ().remote_endpoint ());
|
||||||
Handshake (newSocket);
|
Handshake (socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ssl_socket>
|
|
||||||
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
|
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
|
||||||
{
|
{
|
||||||
socket->async_handshake(boost::asio::ssl::stream_base::server,
|
socket->async_handshake(boost::asio::ssl::stream_base::server,
|
||||||
[this, socket](const boost::system::error_code& ecode)
|
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
|
||||||
{
|
}
|
||||||
if (ecode)
|
|
||||||
|
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||||
{
|
{
|
||||||
|
if (ecode) {
|
||||||
LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ());
|
LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
|
||||||
ReadRequest (socket);
|
ReadRequest (socket);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ssl_socket>
|
|
||||||
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
|
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
|
||||||
{
|
{
|
||||||
auto request = std::make_shared<I2PControlBuffer>();
|
auto request = std::make_shared<I2PControlBuffer>();
|
||||||
|
@ -216,13 +163,10 @@ namespace client
|
||||||
#else
|
#else
|
||||||
boost::asio::buffer (request->data (), request->size ()),
|
boost::asio::buffer (request->data (), request->size ()),
|
||||||
#endif
|
#endif
|
||||||
[this, socket, request](const boost::system::error_code& ecode, size_t bytes_transferred)
|
std::bind(&I2PControlService::HandleRequestReceived, this,
|
||||||
{
|
std::placeholders::_1, std::placeholders::_2, socket, request));
|
||||||
HandleRequestReceived (ecode, bytes_transferred, socket, request);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ssl_socket>
|
|
||||||
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
||||||
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
||||||
std::shared_ptr<I2PControlBuffer> buf)
|
std::shared_ptr<I2PControlBuffer> buf)
|
||||||
|
@ -300,7 +244,6 @@ namespace client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ssl_socket>
|
|
||||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||||
{
|
{
|
||||||
|
@ -310,7 +253,7 @@ namespace client
|
||||||
std::ostringstream header;
|
std::ostringstream header;
|
||||||
header << "HTTP/1.1 200 OK\r\n";
|
header << "HTTP/1.1 200 OK\r\n";
|
||||||
header << "Connection: close\r\n";
|
header << "Connection: close\r\n";
|
||||||
header << "Content-Length: " << std::to_string(len) << "\r\n";
|
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
|
||||||
header << "Content-Type: application/json\r\n";
|
header << "Content-Type: application/json\r\n";
|
||||||
header << "Date: ";
|
header << "Date: ";
|
||||||
std::time_t t = std::time (nullptr);
|
std::time_t t = std::time (nullptr);
|
||||||
|
@ -323,11 +266,16 @@ namespace client
|
||||||
memcpy (buf->data () + offset, response.str ().c_str (), len);
|
memcpy (buf->data () + offset, response.str ().c_str (), len);
|
||||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
|
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
|
||||||
boost::asio::transfer_all (),
|
boost::asio::transfer_all (),
|
||||||
[socket, buf](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
std::bind(&I2PControlService::HandleResponseSent, this,
|
||||||
|
std::placeholders::_1, std::placeholders::_2, socket, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||||
|
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
|
||||||
{
|
{
|
||||||
if (ecode)
|
if (ecode) {
|
||||||
LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ());
|
LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ());
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlers
|
// handlers
|
||||||
|
@ -455,7 +403,7 @@ namespace client
|
||||||
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
|
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
|
||||||
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
|
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
|
||||||
X509_set_issuer_name (x509, name); // set issuer to ourselves
|
X509_set_issuer_name (x509, name); // set issuer to ourselves
|
||||||
X509_sign (x509, pkey, EVP_sha1 ()); // sign, last param must be NULL for EdDSA
|
X509_sign (x509, pkey, EVP_sha1 ()); // sign
|
||||||
|
|
||||||
// save cert
|
// save cert
|
||||||
if ((f = fopen (crt_path, "wb")) != NULL) {
|
if ((f = fopen (crt_path, "wb")) != NULL) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -35,6 +35,8 @@ namespace client
|
||||||
|
|
||||||
class I2PControlService: public I2PControlHandlers
|
class I2PControlService: public I2PControlHandlers
|
||||||
{
|
{
|
||||||
|
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
I2PControlService (const std::string& address, int port);
|
I2PControlService (const std::string& address, int port);
|
||||||
|
@ -47,18 +49,16 @@ namespace client
|
||||||
|
|
||||||
void Run ();
|
void Run ();
|
||||||
void Accept ();
|
void Accept ();
|
||||||
template<typename ssl_socket>
|
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||||
void HandleAccepted (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> newSocket);
|
|
||||||
template<typename ssl_socket>
|
|
||||||
void Handshake (std::shared_ptr<ssl_socket> socket);
|
void Handshake (std::shared_ptr<ssl_socket> socket);
|
||||||
template<typename ssl_socket>
|
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket);
|
||||||
void ReadRequest (std::shared_ptr<ssl_socket> socket);
|
void ReadRequest (std::shared_ptr<ssl_socket> socket);
|
||||||
template<typename ssl_socket>
|
|
||||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||||
template<typename ssl_socket>
|
|
||||||
void SendResponse (std::shared_ptr<ssl_socket> socket,
|
void SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
|
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
|
||||||
|
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
||||||
|
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||||
|
|
||||||
void CreateCertificate (const char *crt_path, const char *key_path);
|
void CreateCertificate (const char *crt_path, const char *key_path);
|
||||||
|
|
||||||
|
@ -86,13 +86,10 @@ namespace client
|
||||||
|
|
||||||
std::string m_Password;
|
std::string m_Password;
|
||||||
bool m_IsRunning;
|
bool m_IsRunning;
|
||||||
std::unique_ptr<std::thread> m_Thread;
|
std::thread * m_Thread;
|
||||||
|
|
||||||
boost::asio::io_context m_Service;
|
boost::asio::io_service m_Service;
|
||||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_Acceptor;
|
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||||
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
|
|
||||||
std::unique_ptr<boost::asio::local::stream_protocol::acceptor> m_LocalAcceptor;
|
|
||||||
#endif
|
|
||||||
boost::asio::ssl::context m_SSLContext;
|
boost::asio::ssl::context m_SSLContext;
|
||||||
boost::asio::deadline_timer m_ShutdownTimer;
|
boost::asio::deadline_timer m_ShutdownTimer;
|
||||||
std::set<std::string> m_Tokens;
|
std::set<std::string> m_Tokens;
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace transport
|
||||||
{
|
{
|
||||||
m_IsRunning = true;
|
m_IsRunning = true;
|
||||||
LogPrint(eLogInfo, "UPnP: Starting");
|
LogPrint(eLogInfo, "UPnP: Starting");
|
||||||
boost::asio::post (m_Service, std::bind (&UPnP::Discover, this));
|
m_Service.post (std::bind (&UPnP::Discover, this));
|
||||||
std::unique_lock<std::mutex> l(m_StartedMutex);
|
std::unique_lock<std::mutex> l(m_StartedMutex);
|
||||||
m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this)));
|
m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this)));
|
||||||
m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum
|
m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum
|
||||||
|
@ -150,7 +150,7 @@ namespace transport
|
||||||
|
|
||||||
// UPnP discovered
|
// UPnP discovered
|
||||||
LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress);
|
LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress);
|
||||||
i2p::context.UpdateAddress (boost::asio::ip::make_address (m_externalIPAddress));
|
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress));
|
||||||
// port mapping
|
// port mapping
|
||||||
PortMapping ();
|
PortMapping ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ namespace transport
|
||||||
std::unique_ptr<std::thread> m_Thread;
|
std::unique_ptr<std::thread> m_Thread;
|
||||||
std::condition_variable m_Started;
|
std::condition_variable m_Started;
|
||||||
std::mutex m_StartedMutex;
|
std::mutex m_StartedMutex;
|
||||||
boost::asio::io_context m_Service;
|
boost::asio::io_service m_Service;
|
||||||
boost::asio::deadline_timer m_Timer;
|
boost::asio::deadline_timer m_Timer;
|
||||||
bool m_upnpUrlsInitialized = false;
|
bool m_upnpUrlsInitialized = false;
|
||||||
struct UPNPUrls m_upnpUrls;
|
struct UPNPUrls m_upnpUrls;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -25,7 +25,6 @@
|
||||||
#include "RouterContext.h"
|
#include "RouterContext.h"
|
||||||
#include "ClientContext.h"
|
#include "ClientContext.h"
|
||||||
#include "Transports.h"
|
#include "Transports.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
void handle_signal(int sig)
|
void handle_signal(int sig)
|
||||||
{
|
{
|
||||||
|
@ -221,7 +220,6 @@ namespace i2p
|
||||||
|
|
||||||
void DaemonLinux::run ()
|
void DaemonLinux::run ()
|
||||||
{
|
{
|
||||||
i2p::util::SetThreadName ("i2pd-daemon");
|
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||||
|
|
12
debian/changelog
vendored
12
debian/changelog
vendored
|
@ -1,15 +1,3 @@
|
||||||
i2pd (2.56.0-1) unstable; urgency=medium
|
|
||||||
|
|
||||||
* updated to version 2.56.0/0.9.65
|
|
||||||
|
|
||||||
-- orignal <orignal@i2pmail.org> Tue, 11 Feb 2025 16:00:00 +0000
|
|
||||||
|
|
||||||
i2pd (2.55.0-1) unstable; urgency=medium
|
|
||||||
|
|
||||||
* updated to version 2.55.0
|
|
||||||
|
|
||||||
-- orignal <orignal@i2pmail.org> Mon, 30 Dec 2024 16:00:00 +0000
|
|
||||||
|
|
||||||
i2pd (2.54.0-1) unstable; urgency=medium
|
i2pd (2.54.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* updated to version 2.54.0/0.9.64
|
* updated to version 2.54.0/0.9.64
|
||||||
|
|
6
debian/patches/01-upnp.patch
vendored
6
debian/patches/01-upnp.patch
vendored
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
||||||
Author: r4sas <r4sas@i2pmail.org>
|
Author: r4sas <r4sas@i2pmail.org>
|
||||||
|
|
||||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||||
Last-Update: 2024-12-30
|
Last-Update: 2022-03-23
|
||||||
|
|
||||||
--- i2pd.orig/Makefile
|
--- i2pd.orig/Makefile
|
||||||
+++ i2pd/Makefile
|
+++ i2pd/Makefile
|
||||||
@@ -31,7 +31,7 @@ # import source files lists
|
@@ -31,7 +31,7 @@ include filelist.mk
|
||||||
include filelist.mk
|
|
||||||
|
|
||||||
|
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||||
USE_STATIC := $(or $(USE_STATIC),no)
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
+USE_UPNP := $(or $(USE_UPNP),yes)
|
+USE_UPNP := $(or $(USE_UPNP),yes)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace afrikaans // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"failed", "Het misluk"},
|
{"failed", "Het misluk"},
|
||||||
{"unknown", "onbekend"},
|
{"unknown", "onbekend"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace armenian // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f ԿիԲ"},
|
{"%.2f KiB", "%.2f ԿիԲ"},
|
||||||
{"%.2f MiB", "%.2f ՄիԲ"},
|
{"%.2f MiB", "%.2f ՄիԲ"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
* Copyright (c) 2022-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace chinese // language namespace
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
* Copyright (c) 2022-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace czech // language namespace
|
||||||
return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
|
return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -30,7 +30,7 @@ namespace english // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"", ""},
|
{"", ""},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
* Copyright (c) 2022-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace french // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f Kio"},
|
{"%.2f KiB", "%.2f Kio"},
|
||||||
{"%.2f MiB", "%.2f Mio"},
|
{"%.2f MiB", "%.2f Mio"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace german // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -30,12 +30,12 @@ namespace i18n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view translate (std::string_view arg)
|
std::string translate (const std::string& arg)
|
||||||
{
|
{
|
||||||
return i2p::client::context.GetLanguage ()->GetString (arg);
|
return i2p::client::context.GetLanguage ()->GetString (arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string translate (const std::string& arg, const std::string& arg2, const int n)
|
std::string translate (const std::string& arg, const std::string& arg2, const int& n)
|
||||||
{
|
{
|
||||||
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n);
|
||||||
}
|
}
|
||||||
|
|
24
i18n/I18N.h
24
i18n/I18N.h
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -10,7 +10,6 @@
|
||||||
#define __I18N_H__
|
#define __I18N_H__
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -19,13 +18,12 @@ namespace i2p
|
||||||
{
|
{
|
||||||
namespace i18n
|
namespace i18n
|
||||||
{
|
{
|
||||||
typedef std::map<std::string_view, std::string_view> LocaleStrings;
|
|
||||||
class Locale
|
class Locale
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Locale (
|
Locale (
|
||||||
const std::string& language,
|
const std::string& language,
|
||||||
const LocaleStrings& strings,
|
const std::map<std::string, std::string>& strings,
|
||||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||||
std::function<int(int)> formula
|
std::function<int(int)> formula
|
||||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||||
|
@ -36,7 +34,7 @@ namespace i18n
|
||||||
return m_Language;
|
return m_Language;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view GetString (std::string_view arg) const
|
std::string GetString (const std::string& arg) const
|
||||||
{
|
{
|
||||||
const auto it = m_Strings.find(arg);
|
const auto it = m_Strings.find(arg);
|
||||||
if (it == m_Strings.end())
|
if (it == m_Strings.end())
|
||||||
|
@ -49,7 +47,7 @@ namespace i18n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetPlural (const std::string& arg, const std::string& arg2, int n) const
|
std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const
|
||||||
{
|
{
|
||||||
const auto it = m_Plurals.find(arg2);
|
const auto it = m_Plurals.find(arg2);
|
||||||
if (it == m_Plurals.end()) // not found, fallback to english
|
if (it == m_Plurals.end()) // not found, fallback to english
|
||||||
|
@ -65,14 +63,14 @@ namespace i18n
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string m_Language;
|
const std::string m_Language;
|
||||||
const LocaleStrings m_Strings;
|
const std::map<std::string, std::string> m_Strings;
|
||||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||||
std::function<int(int)> m_Formula;
|
std::function<int(int)> m_Formula;
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetLanguage(const std::string &lang);
|
void SetLanguage(const std::string &lang);
|
||||||
std::string_view translate (std::string_view arg);
|
std::string translate (const std::string& arg);
|
||||||
std::string translate (const std::string& arg, const std::string& arg2, int n);
|
std::string translate (const std::string& arg, const std::string& arg2, const int& n);
|
||||||
} // i18n
|
} // i18n
|
||||||
} // i2p
|
} // i2p
|
||||||
|
|
||||||
|
@ -81,7 +79,7 @@ namespace i18n
|
||||||
* @param arg String with message
|
* @param arg String with message
|
||||||
*/
|
*/
|
||||||
template<typename TValue>
|
template<typename TValue>
|
||||||
std::string_view tr (TValue&& arg)
|
std::string tr (TValue&& arg)
|
||||||
{
|
{
|
||||||
return i2p::i18n::translate(std::forward<TValue>(arg));
|
return i2p::i18n::translate(std::forward<TValue>(arg));
|
||||||
}
|
}
|
||||||
|
@ -94,7 +92,7 @@ std::string_view tr (TValue&& arg)
|
||||||
template<typename TValue, typename... TArgs>
|
template<typename TValue, typename... TArgs>
|
||||||
std::string tr (TValue&& arg, TArgs&&... args)
|
std::string tr (TValue&& arg, TArgs&&... args)
|
||||||
{
|
{
|
||||||
std::string tr_str = std::string (i2p::i18n::translate(std::forward<TValue>(arg))); // TODO:
|
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg));
|
||||||
|
|
||||||
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
|
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
|
||||||
std::string str(size, 0);
|
std::string str(size, 0);
|
||||||
|
@ -110,7 +108,7 @@ std::string tr (TValue&& arg, TArgs&&... args)
|
||||||
* @param n Integer, used for selection of form
|
* @param n Integer, used for selection of form
|
||||||
*/
|
*/
|
||||||
template<typename TValue, typename TValue2>
|
template<typename TValue, typename TValue2>
|
||||||
std::string ntr (TValue&& arg, TValue2&& arg2, int n)
|
std::string ntr (TValue&& arg, TValue2&& arg2, int& n)
|
||||||
{
|
{
|
||||||
return i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
return i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||||
}
|
}
|
||||||
|
@ -123,7 +121,7 @@ std::string ntr (TValue&& arg, TValue2&& arg2, int n)
|
||||||
* @param args Array of arguments for string formatting
|
* @param args Array of arguments for string formatting
|
||||||
*/
|
*/
|
||||||
template<typename TValue, typename TValue2, typename... TArgs>
|
template<typename TValue, typename TValue2, typename... TArgs>
|
||||||
std::string ntr (TValue&& arg, TValue2&& arg2, int n, TArgs&&... args)
|
std::string ntr (TValue&& arg, TValue2&& arg2, int& n, TArgs&&... args)
|
||||||
{
|
{
|
||||||
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace italian // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
* Copyright (c) 2023-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace polish // language namespace
|
||||||
return (n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
return (n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
* Copyright (c) 2023-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace portuguese // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace russian // language namespace
|
||||||
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
|
return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f КиБ"},
|
{"%.2f KiB", "%.2f КиБ"},
|
||||||
{"%.2f MiB", "%.2f МиБ"},
|
{"%.2f MiB", "%.2f МиБ"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
* Copyright (c) 2022-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace spanish // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
* Copyright (c) 2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace swedish // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023-2025, The PurpleI2P Project
|
* Copyright (c) 2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace turkish // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace turkmen // language namespace
|
||||||
return n != 1 ? 1 : 0;
|
return n != 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace ukrainian // language namespace
|
||||||
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
|
return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f КіБ"},
|
{"%.2f KiB", "%.2f КіБ"},
|
||||||
{"%.2f MiB", "%.2f МіБ"},
|
{"%.2f MiB", "%.2f МіБ"},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2025, The PurpleI2P Project
|
* Copyright (c) 2021-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ namespace uzbek // language namespace
|
||||||
return n > 1 ? 1 : 0;
|
return n > 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const LocaleStrings strings
|
static std::map<std::string, std::string> strings
|
||||||
{
|
{
|
||||||
{"%.2f KiB", "%.2f KiB"},
|
{"%.2f KiB", "%.2f KiB"},
|
||||||
{"%.2f MiB", "%.2f MiB"},
|
{"%.2f MiB", "%.2f MiB"},
|
||||||
|
|
187
libi2pd/Base.cpp
187
libi2pd/Base.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -15,7 +15,7 @@ namespace i2p
|
||||||
{
|
{
|
||||||
namespace data
|
namespace data
|
||||||
{
|
{
|
||||||
static constexpr char T32[32] =
|
static const char T32[32] =
|
||||||
{
|
{
|
||||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||||
|
@ -28,6 +28,11 @@ namespace data
|
||||||
return T32;
|
return T32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBase32 (char ch)
|
||||||
|
{
|
||||||
|
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
|
||||||
|
}
|
||||||
|
|
||||||
static void iT64Build(void);
|
static void iT64Build(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -38,7 +43,7 @@ namespace data
|
||||||
* Direct Substitution Table
|
* Direct Substitution Table
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static constexpr char T64[64] =
|
static const char T64[64] =
|
||||||
{
|
{
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||||
|
@ -55,16 +60,23 @@ namespace data
|
||||||
return T64;
|
return T64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBase64 (char ch)
|
||||||
|
{
|
||||||
|
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '~';
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reverse Substitution Table (built in run time)
|
* Reverse Substitution Table (built in run time)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static char iT64[256];
|
static char iT64[256];
|
||||||
static int isFirstTime = 1;
|
static int isFirstTime = 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Padding
|
* Padding
|
||||||
*/
|
*/
|
||||||
static constexpr char P64 = '=';
|
|
||||||
|
static char P64 = '=';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
@ -74,69 +86,78 @@ namespace data
|
||||||
* Converts binary encoded data to BASE64 format.
|
* Converts binary encoded data to BASE64 format.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
std::string ByteStreamToBase64 (// base64 encoded string
|
|
||||||
const uint8_t * InBuffer, // Input buffer, binary data
|
size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */
|
||||||
size_t InCount // Number of bytes in the input buffer
|
const uint8_t * InBuffer, /* Input buffer, binary data */
|
||||||
|
size_t InCount, /* Number of bytes in the input buffer */
|
||||||
|
char * OutBuffer, /* output buffer */
|
||||||
|
size_t len /* length of output buffer */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
unsigned char * ps;
|
unsigned char * ps;
|
||||||
|
unsigned char * pd;
|
||||||
unsigned char acc_1;
|
unsigned char acc_1;
|
||||||
unsigned char acc_2;
|
unsigned char acc_2;
|
||||||
int i;
|
int i;
|
||||||
int n;
|
int n;
|
||||||
int m;
|
int m;
|
||||||
|
size_t outCount;
|
||||||
|
|
||||||
ps = (unsigned char *)InBuffer;
|
ps = (unsigned char *)InBuffer;
|
||||||
n = InCount / 3;
|
n = InCount / 3;
|
||||||
m = InCount % 3;
|
m = InCount % 3;
|
||||||
size_t outCount = m ? (4 * (n + 1)) : (4 * n);
|
if (!m)
|
||||||
|
outCount = 4 * n;
|
||||||
|
else
|
||||||
|
outCount = 4 * (n + 1);
|
||||||
|
|
||||||
std::string out;
|
if (outCount > len) return 0;
|
||||||
out.reserve (outCount);
|
|
||||||
|
pd = (unsigned char *)OutBuffer;
|
||||||
for ( i = 0; i < n; i++ )
|
for ( i = 0; i < n; i++ )
|
||||||
{
|
{
|
||||||
acc_1 = *ps++;
|
acc_1 = *ps++;
|
||||||
acc_2 = (acc_1 << 4) & 0x30;
|
acc_2 = (acc_1 << 4) & 0x30;
|
||||||
acc_1 >>= 2; // base64 digit #1
|
acc_1 >>= 2; /* base64 digit #1 */
|
||||||
out.push_back (T64[acc_1]);
|
*pd++ = T64[acc_1];
|
||||||
acc_1 = *ps++;
|
acc_1 = *ps++;
|
||||||
acc_2 |= acc_1 >> 4; // base64 digit #2
|
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
||||||
out.push_back (T64[acc_2]);
|
*pd++ = T64[acc_2];
|
||||||
acc_1 &= 0x0f;
|
acc_1 &= 0x0f;
|
||||||
acc_1 <<= 2;
|
acc_1 <<= 2;
|
||||||
acc_2 = *ps++;
|
acc_2 = *ps++;
|
||||||
acc_1 |= acc_2 >> 6; // base64 digit #3
|
acc_1 |= acc_2 >> 6; /* base64 digit #3 */
|
||||||
out.push_back (T64[acc_1]);
|
*pd++ = T64[acc_1];
|
||||||
acc_2 &= 0x3f; // base64 digit #4
|
acc_2 &= 0x3f; /* base64 digit #4 */
|
||||||
out.push_back (T64[acc_2]);
|
*pd++ = T64[acc_2];
|
||||||
}
|
}
|
||||||
if ( m == 1 )
|
if ( m == 1 )
|
||||||
{
|
{
|
||||||
acc_1 = *ps++;
|
acc_1 = *ps++;
|
||||||
acc_2 = (acc_1 << 4) & 0x3f; // base64 digit #2
|
acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */
|
||||||
acc_1 >>= 2; // base64 digit #1
|
acc_1 >>= 2; /* base64 digit #1 */
|
||||||
out.push_back (T64[acc_1]);
|
*pd++ = T64[acc_1];
|
||||||
out.push_back (T64[acc_2]);
|
*pd++ = T64[acc_2];
|
||||||
out.push_back (P64);
|
*pd++ = P64;
|
||||||
out.push_back (P64);
|
*pd++ = P64;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if ( m == 2 )
|
else if ( m == 2 )
|
||||||
{
|
{
|
||||||
acc_1 = *ps++;
|
acc_1 = *ps++;
|
||||||
acc_2 = (acc_1 << 4) & 0x3f;
|
acc_2 = (acc_1 << 4) & 0x3f;
|
||||||
acc_1 >>= 2; // base64 digit #1
|
acc_1 >>= 2; /* base64 digit #1 */
|
||||||
out.push_back (T64[acc_1]);
|
*pd++ = T64[acc_1];
|
||||||
acc_1 = *ps++;
|
acc_1 = *ps++;
|
||||||
acc_2 |= acc_1 >> 4; // base64 digit #2
|
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
||||||
out.push_back (T64[acc_2]);
|
*pd++ = T64[acc_2];
|
||||||
acc_1 &= 0x0f;
|
acc_1 &= 0x0f;
|
||||||
acc_1 <<= 2; // base64 digit #3
|
acc_1 <<= 2; /* base64 digit #3 */
|
||||||
out.push_back (T64[acc_1]);
|
*pd++ = T64[acc_1];
|
||||||
out.push_back (P64);
|
*pd++ = P64;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return outCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -144,42 +165,55 @@ namespace data
|
||||||
* Base64ToByteStream
|
* Base64ToByteStream
|
||||||
* ------------------
|
* ------------------
|
||||||
*
|
*
|
||||||
* Converts BASE64 encoded string to binary format. If input buffer is
|
* Converts BASE64 encoded data to binary format. If input buffer is
|
||||||
* not properly padded, buffer of negative length is returned
|
* not properly padded, buffer of negative length is returned
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
size_t Base64ToByteStream ( // Number of output bytes
|
|
||||||
std::string_view base64Str, // BASE64 encoded string
|
size_t Base64ToByteStream ( /* Number of output bytes */
|
||||||
uint8_t * OutBuffer, // output buffer length
|
const char * InBuffer, /* BASE64 encoded buffer */
|
||||||
size_t len // length of output buffer
|
size_t InCount, /* Number of input bytes */
|
||||||
|
uint8_t * OutBuffer, /* output buffer length */
|
||||||
|
size_t len /* length of output buffer */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
unsigned char * ps;
|
||||||
unsigned char * pd;
|
unsigned char * pd;
|
||||||
unsigned char acc_1;
|
unsigned char acc_1;
|
||||||
unsigned char acc_2;
|
unsigned char acc_2;
|
||||||
|
int i;
|
||||||
|
int n;
|
||||||
|
int m;
|
||||||
size_t outCount;
|
size_t outCount;
|
||||||
|
|
||||||
if (base64Str.empty () || base64Str[0] == P64) return 0;
|
if (isFirstTime)
|
||||||
auto d = std::div (base64Str.length (), 4);
|
iT64Build();
|
||||||
if (!d.rem)
|
|
||||||
outCount = 3 * d.quot;
|
n = InCount / 4;
|
||||||
|
m = InCount % 4;
|
||||||
|
|
||||||
|
if (InCount && !m)
|
||||||
|
outCount = 3 * n;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (isFirstTime) iT64Build();
|
if(*InBuffer == P64)
|
||||||
|
return 0;
|
||||||
|
|
||||||
auto pos = base64Str.find_last_not_of (P64);
|
ps = (unsigned char *)(InBuffer + InCount - 1);
|
||||||
if (pos == base64Str.npos) return 0;
|
while ( *ps-- == P64 )
|
||||||
outCount -= (base64Str.length () - pos - 1);
|
outCount--;
|
||||||
if (outCount > len) return 0;
|
ps = (unsigned char *)InBuffer;
|
||||||
|
|
||||||
|
if (outCount > len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
auto ps = base64Str.begin ();
|
|
||||||
pd = OutBuffer;
|
pd = OutBuffer;
|
||||||
auto endOfOutBuffer = OutBuffer + outCount;
|
auto endOfOutBuffer = OutBuffer + outCount;
|
||||||
for (int i = 0; i < d.quot; i++)
|
for ( i = 0; i < n; i++ )
|
||||||
{
|
{
|
||||||
acc_1 = iT64[int(*ps++)];
|
acc_1 = iT64[*ps++];
|
||||||
acc_2 = iT64[int(*ps++)];
|
acc_2 = iT64[*ps++];
|
||||||
acc_1 <<= 2;
|
acc_1 <<= 2;
|
||||||
acc_1 |= acc_2 >> 4;
|
acc_1 |= acc_2 >> 4;
|
||||||
*pd++ = acc_1;
|
*pd++ = acc_1;
|
||||||
|
@ -187,13 +221,13 @@ namespace data
|
||||||
break;
|
break;
|
||||||
|
|
||||||
acc_2 <<= 4;
|
acc_2 <<= 4;
|
||||||
acc_1 = iT64[int(*ps++)];
|
acc_1 = iT64[*ps++];
|
||||||
acc_2 |= acc_1 >> 2;
|
acc_2 |= acc_1 >> 2;
|
||||||
*pd++ = acc_2;
|
*pd++ = acc_2;
|
||||||
if (pd >= endOfOutBuffer)
|
if (pd >= endOfOutBuffer)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
acc_2 = iT64[int(*ps++)];
|
acc_2 = iT64[*ps++];
|
||||||
acc_2 |= acc_1 << 6;
|
acc_2 |= acc_1 << 6;
|
||||||
*pd++ = acc_2;
|
*pd++ = acc_2;
|
||||||
}
|
}
|
||||||
|
@ -201,16 +235,31 @@ namespace data
|
||||||
return outCount;
|
return outCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ToBase64Standard (std::string_view in)
|
size_t Base64EncodingBufferSize (const size_t input_size)
|
||||||
{
|
{
|
||||||
auto str = ByteStreamToBase64 ((const uint8_t *)in.data (), in.length ());
|
auto d = div (input_size, 3);
|
||||||
|
if (d.rem)
|
||||||
|
d.quot++;
|
||||||
|
|
||||||
|
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 '/'
|
// replace '-' by '+' and '~' by '/'
|
||||||
for (auto& ch: str)
|
for (size_t i = 0; i < l; i++)
|
||||||
if (ch == '-')
|
if (str[i] == '-')
|
||||||
ch = '+';
|
str[i] = '+';
|
||||||
else if (ch == '~')
|
else if (str[i] == '~')
|
||||||
ch = '/';
|
str[i] = '/';
|
||||||
return str;
|
|
||||||
|
std::string s(str);
|
||||||
|
delete[] str;
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -231,12 +280,13 @@ namespace data
|
||||||
iT64[(int)P64] = 0;
|
iT64[(int)P64] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen)
|
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen)
|
||||||
{
|
{
|
||||||
unsigned int tmp = 0, bits = 0;
|
unsigned int tmp = 0, bits = 0;
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
for (auto ch: base32Str)
|
for (size_t i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
|
char ch = inBuf[i];
|
||||||
if (ch >= '2' && ch <= '7') // digit
|
if (ch >= '2' && ch <= '7') // digit
|
||||||
ch = (ch - '2') + 26; // 26 means a-z
|
ch = (ch - '2') + 26; // 26 means a-z
|
||||||
else if (ch >= 'a' && ch <= 'z')
|
else if (ch >= 'a' && ch <= 'z')
|
||||||
|
@ -258,13 +308,11 @@ namespace data
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len)
|
size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen)
|
||||||
{
|
{
|
||||||
std::string out;
|
size_t ret = 0, pos = 1;
|
||||||
out.reserve ((len * 8 + 4) / 5);
|
|
||||||
size_t pos = 1;
|
|
||||||
unsigned int bits = 8, tmp = inBuf[0];
|
unsigned int bits = 8, tmp = inBuf[0];
|
||||||
while (bits > 0 || pos < len)
|
while (ret < outLen && (bits > 0 || pos < len))
|
||||||
{
|
{
|
||||||
if (bits < 5)
|
if (bits < 5)
|
||||||
{
|
{
|
||||||
|
@ -284,9 +332,10 @@ namespace data
|
||||||
|
|
||||||
bits -= 5;
|
bits -= 5;
|
||||||
int ind = (tmp >> bits) & 0x1F;
|
int ind = (tmp >> bits) & 0x1F;
|
||||||
out.push_back ((ind < 26) ? (ind + 'a') : ((ind - 26) + '2'));
|
outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2');
|
||||||
|
ret++;
|
||||||
}
|
}
|
||||||
return out;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,41 +11,26 @@
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <iostream>
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
std::string ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount);
|
|
||||||
size_t Base64ToByteStream (std::string_view base64Str, uint8_t * OutBuffer, size_t len);
|
|
||||||
|
|
||||||
|
namespace i2p {
|
||||||
|
namespace data {
|
||||||
|
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len);
|
||||||
|
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len );
|
||||||
const char * GetBase32SubstitutionTable ();
|
const char * GetBase32SubstitutionTable ();
|
||||||
const char * GetBase64SubstitutionTable ();
|
const char * GetBase64SubstitutionTable ();
|
||||||
constexpr bool IsBase64 (char ch)
|
bool IsBase64 (char ch);
|
||||||
{
|
|
||||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '~';
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen);
|
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
|
||||||
std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len);
|
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
||||||
constexpr bool IsBase32 (char ch)
|
bool IsBase32 (char ch);
|
||||||
{
|
|
||||||
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
* Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
||||||
*/
|
*/
|
||||||
inline size_t Base64EncodingBufferSize(size_t input_size)
|
size_t Base64EncodingBufferSize(const size_t input_size);
|
||||||
{
|
|
||||||
auto d = std::div (input_size, 3);
|
|
||||||
if (d.rem) d.quot++;
|
|
||||||
return 4 * d.quot;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ToBase64Standard (std::string_view in); // using standard table, for Proxy-Authorization
|
std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization
|
||||||
|
|
||||||
} // data
|
} // data
|
||||||
} // i2p
|
} // i2p
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -152,11 +152,11 @@ namespace data
|
||||||
m_BlindedSigType = m_SigType;
|
m_BlindedSigType = m_SigType;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlindedPublicKey::BlindedPublicKey (std::string_view b33):
|
BlindedPublicKey::BlindedPublicKey (const std::string& b33):
|
||||||
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later
|
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later
|
||||||
{
|
{
|
||||||
uint8_t addr[40]; // TODO: define length from b33
|
uint8_t addr[40]; // TODO: define length from b33
|
||||||
size_t l = i2p::data::Base32ToByteStream (b33, addr, 40);
|
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40);
|
||||||
if (l < 32)
|
if (l < 32)
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "Blinding: Malformed b33 ", b33);
|
LogPrint (eLogError, "Blinding: Malformed b33 ", b33);
|
||||||
|
@ -198,7 +198,7 @@ namespace data
|
||||||
std::string BlindedPublicKey::ToB33 () const
|
std::string BlindedPublicKey::ToB33 () const
|
||||||
{
|
{
|
||||||
if (m_PublicKey.size () > 32) return ""; // assume 25519
|
if (m_PublicKey.size () > 32) return ""; // assume 25519
|
||||||
uint8_t addr[35];
|
uint8_t addr[35]; char str[60]; // TODO: define actual length
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
|
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
|
||||||
addr[0] = flags; // flags
|
addr[0] = flags; // flags
|
||||||
|
@ -208,7 +208,8 @@ namespace data
|
||||||
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ());
|
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ());
|
||||||
// checksum is Little Endian
|
// checksum is Little Endian
|
||||||
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
|
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
|
||||||
return ByteStreamToBase32 (addr, m_PublicKey.size () + 3);
|
auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60);
|
||||||
|
return std::string (str, str + l);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlindedPublicKey::GetCredential (uint8_t * credential) const
|
void BlindedPublicKey::GetCredential (uint8_t * credential) const
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ namespace data
|
||||||
public:
|
public:
|
||||||
|
|
||||||
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
|
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
|
||||||
BlindedPublicKey (std::string_view b33); // from b33 without .b32.i2p
|
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
|
||||||
std::string ToB33 () const;
|
std::string ToB33 () const;
|
||||||
|
|
||||||
const uint8_t * GetPublicKey () const { return m_PublicKey.data (); };
|
const uint8_t * GetPublicKey () const { return m_PublicKey.data (); };
|
||||||
|
|
68
libi2pd/CPU.cpp
Normal file
68
libi2pd/CPU.cpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CPU.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
#ifndef bit_AES
|
||||||
|
#define bit_AES (1 << 25)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && __GNUC__ < 6 && IS_X86
|
||||||
|
#include <cpuid.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace i2p
|
||||||
|
{
|
||||||
|
namespace cpu
|
||||||
|
{
|
||||||
|
bool aesni = false;
|
||||||
|
|
||||||
|
inline bool cpu_support_aes()
|
||||||
|
{
|
||||||
|
#if IS_X86
|
||||||
|
#if defined(__clang__)
|
||||||
|
# if (__clang_major__ >= 6)
|
||||||
|
__builtin_cpu_init();
|
||||||
|
# endif
|
||||||
|
return __builtin_cpu_supports("aes");
|
||||||
|
#elif (defined(__GNUC__) && __GNUC__ >= 6)
|
||||||
|
__builtin_cpu_init();
|
||||||
|
return __builtin_cpu_supports("aes");
|
||||||
|
#elif (defined(__GNUC__) && __GNUC__ < 6)
|
||||||
|
int cpu_info[4];
|
||||||
|
bool flag = false;
|
||||||
|
__cpuid(0, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
|
||||||
|
if (cpu_info[0] >= 0x00000001) {
|
||||||
|
__cpuid(0x00000001, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]);
|
||||||
|
flag = ((cpu_info[2] & bit_AES) != 0);
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
int cpu_info[4];
|
||||||
|
__cpuid(cpu_info, 1);
|
||||||
|
return ((cpu_info[2] & bit_AES) != 0);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Detect(bool AesSwitch, bool force)
|
||||||
|
{
|
||||||
|
if ((cpu_support_aes() && AesSwitch) || (AesSwitch && force)) {
|
||||||
|
aesni = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -21,4 +21,20 @@
|
||||||
# define IS_X86_64 0
|
# define IS_X86_64 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__AES__) && !defined(_MSC_VER) && IS_X86
|
||||||
|
# define SUPPORTS_AES 1
|
||||||
|
#else
|
||||||
|
# define SUPPORTS_AES 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace i2p
|
||||||
|
{
|
||||||
|
namespace cpu
|
||||||
|
{
|
||||||
|
extern bool aesni;
|
||||||
|
|
||||||
|
void Detect(bool AesSwitch, bool force);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -117,7 +117,7 @@ namespace config {
|
||||||
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
||||||
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
|
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
|
||||||
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
|
("httpproxy.addresshelper", value<bool>()->default_value(true), "Enable or disable addresshelper")
|
||||||
("httpproxy.senduseragent", value<bool>()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by default")
|
("httpproxy.senduseragent", value<bool>()->default_value(false), "Pass through user's User-Agent if enabled. Disabled by deafult")
|
||||||
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
|
("httpproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
|
||||||
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
|
("httpproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
|
||||||
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
|
("httpproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
|
||||||
|
@ -154,17 +154,6 @@ namespace config {
|
||||||
("socksproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "SOCKS Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
|
("socksproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "SOCKS Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
|
||||||
;
|
;
|
||||||
|
|
||||||
options_description shareddest("Shared local destination options");
|
|
||||||
shareddest.add_options()
|
|
||||||
("shareddest.inbound.length", value<std::string>()->default_value("3"), "Shared local destination inbound tunnel length")
|
|
||||||
("shareddest.outbound.length", value<std::string>()->default_value("3"), "Shared local destination outbound tunnel length")
|
|
||||||
("shareddest.inbound.quantity", value<std::string>()->default_value("3"), "Shared local destination inbound tunnels quantity")
|
|
||||||
("shareddest.outbound.quantity", value<std::string>()->default_value("3"), "Shared local destination outbound tunnels quantity")
|
|
||||||
("shareddest.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Shared local destination's LeaseSet type")
|
|
||||||
("shareddest.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Shared local destination's LeaseSet encryption type")
|
|
||||||
("shareddest.i2p.streaming.profile", value<std::string>()->default_value("2"), "Shared local destination bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description sam("SAM bridge options");
|
options_description sam("SAM bridge options");
|
||||||
sam.add_options()
|
sam.add_options()
|
||||||
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
|
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
|
||||||
|
@ -238,7 +227,7 @@ namespace config {
|
||||||
"https://reseed.onion.im/,"
|
"https://reseed.onion.im/,"
|
||||||
"https://i2pseed.creativecowpat.net:8443/,"
|
"https://i2pseed.creativecowpat.net:8443/,"
|
||||||
"https://reseed.i2pgit.org/,"
|
"https://reseed.i2pgit.org/,"
|
||||||
"https://coconut.incognet.io/,"
|
"https://banana.incognet.io/,"
|
||||||
"https://reseed-pl.i2pd.xyz/,"
|
"https://reseed-pl.i2pd.xyz/,"
|
||||||
"https://www2.mk16.de/,"
|
"https://www2.mk16.de/,"
|
||||||
"https://i2p.ghativega.in/,"
|
"https://i2p.ghativega.in/,"
|
||||||
|
@ -249,6 +238,7 @@ namespace config {
|
||||||
"http://[324:71e:281a:9ed3::ace]:7070/,"
|
"http://[324:71e:281a:9ed3::ace]:7070/,"
|
||||||
"http://[301:65b9:c7cd:9a36::1]:18801/,"
|
"http://[301:65b9:c7cd:9a36::1]:18801/,"
|
||||||
"http://[320:8936:ec1a:31f1::216]/,"
|
"http://[320:8936:ec1a:31f1::216]/,"
|
||||||
|
"http://[306:3834:97b9:a00a::1]/,"
|
||||||
"http://[316:f9e0:f22e:a74f::216]/"
|
"http://[316:f9e0:f22e:a74f::216]/"
|
||||||
), "Reseed URLs through the Yggdrasil, separated by comma")
|
), "Reseed URLs through the Yggdrasil, separated by comma")
|
||||||
;
|
;
|
||||||
|
@ -326,11 +316,11 @@ namespace config {
|
||||||
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
|
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
|
||||||
;
|
;
|
||||||
|
|
||||||
options_description cpuext("CPU encryption extensions options. Deprecated");
|
options_description cpuext("CPU encryption extensions options");
|
||||||
cpuext.add_options()
|
cpuext.add_options()
|
||||||
("cpuext.aesni", bool_switch()->default_value(true), "Deprecated option")
|
("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used")
|
||||||
("cpuext.avx", bool_switch()->default_value(false), "Deprecated option")
|
("cpuext.avx", bool_switch()->default_value(false), "Deprecated option")
|
||||||
("cpuext.force", bool_switch()->default_value(false), "Deprecated option")
|
("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines")
|
||||||
;
|
;
|
||||||
|
|
||||||
options_description meshnets("Meshnet transports options");
|
options_description meshnets("Meshnet transports options");
|
||||||
|
@ -352,7 +342,6 @@ namespace config {
|
||||||
.add(httpserver)
|
.add(httpserver)
|
||||||
.add(httpproxy)
|
.add(httpproxy)
|
||||||
.add(socksproxy)
|
.add(socksproxy)
|
||||||
.add(shareddest)
|
|
||||||
.add(sam)
|
.add(sam)
|
||||||
.add(bob)
|
.add(bob)
|
||||||
.add(i2cp)
|
.add(i2cp)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -19,11 +19,6 @@
|
||||||
#if OPENSSL_HKDF
|
#if OPENSSL_HKDF
|
||||||
#include <openssl/kdf.h>
|
#include <openssl/kdf.h>
|
||||||
#endif
|
#endif
|
||||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
|
||||||
#include <openssl/param_build.h>
|
|
||||||
#include <openssl/core_names.h>
|
|
||||||
#endif
|
|
||||||
#include "CPU.h"
|
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "Ed25519.h"
|
#include "Ed25519.h"
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
|
@ -33,7 +28,7 @@ namespace i2p
|
||||||
{
|
{
|
||||||
namespace crypto
|
namespace crypto
|
||||||
{
|
{
|
||||||
constexpr uint8_t elgp_[256]=
|
const uint8_t elgp_[256]=
|
||||||
{
|
{
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
||||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
||||||
|
@ -53,9 +48,9 @@ namespace crypto
|
||||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int elgg_ = 2;
|
const int elgg_ = 2;
|
||||||
|
|
||||||
constexpr uint8_t dsap_[128]=
|
const uint8_t dsap_[128]=
|
||||||
{
|
{
|
||||||
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
||||||
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
||||||
|
@ -67,13 +62,13 @@ namespace crypto
|
||||||
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint8_t dsaq_[20]=
|
const uint8_t dsaq_[20]=
|
||||||
{
|
{
|
||||||
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
||||||
0x68, 0x40, 0x46, 0xb7
|
0x68, 0x40, 0x46, 0xb7
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint8_t dsag_[128]=
|
const uint8_t dsag_[128]=
|
||||||
{
|
{
|
||||||
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
||||||
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
||||||
|
@ -85,7 +80,7 @@ namespace crypto
|
||||||
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int rsae_ = 65537;
|
const int rsae_ = 65537;
|
||||||
|
|
||||||
struct CryptoConstants
|
struct CryptoConstants
|
||||||
{
|
{
|
||||||
|
@ -150,37 +145,6 @@ namespace crypto
|
||||||
#define dsap GetCryptoConstants ().dsap
|
#define dsap GetCryptoConstants ().dsap
|
||||||
#define dsaq GetCryptoConstants ().dsaq
|
#define dsaq GetCryptoConstants ().dsaq
|
||||||
#define dsag GetCryptoConstants ().dsag
|
#define dsag GetCryptoConstants ().dsag
|
||||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
|
||||||
EVP_PKEY * CreateDSA (BIGNUM * pubKey, BIGNUM * privKey)
|
|
||||||
{
|
|
||||||
EVP_PKEY * pkey = nullptr;
|
|
||||||
int selection = EVP_PKEY_KEY_PARAMETERS;
|
|
||||||
auto bld = OSSL_PARAM_BLD_new();
|
|
||||||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, dsap);
|
|
||||||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, dsaq);
|
|
||||||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, dsag);
|
|
||||||
if (pubKey)
|
|
||||||
{
|
|
||||||
OSSL_PARAM_BLD_push_BN (bld, OSSL_PKEY_PARAM_PUB_KEY, pubKey);
|
|
||||||
selection = EVP_PKEY_PUBLIC_KEY;
|
|
||||||
}
|
|
||||||
if (privKey)
|
|
||||||
{
|
|
||||||
OSSL_PARAM_BLD_push_BN (bld, OSSL_PKEY_PARAM_PRIV_KEY, privKey);
|
|
||||||
selection = EVP_PKEY_KEYPAIR;
|
|
||||||
}
|
|
||||||
auto params = OSSL_PARAM_BLD_to_param(bld);
|
|
||||||
|
|
||||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "DSA", NULL);
|
|
||||||
EVP_PKEY_fromdata_init(ctx);
|
|
||||||
EVP_PKEY_fromdata(ctx, &pkey, selection, params);
|
|
||||||
|
|
||||||
EVP_PKEY_CTX_free(ctx);
|
|
||||||
OSSL_PARAM_free(params);
|
|
||||||
OSSL_PARAM_BLD_free(bld);
|
|
||||||
return pkey;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
DSA * CreateDSA ()
|
DSA * CreateDSA ()
|
||||||
{
|
{
|
||||||
DSA * dsa = DSA_new ();
|
DSA * dsa = DSA_new ();
|
||||||
|
@ -188,7 +152,6 @@ namespace crypto
|
||||||
DSA_set0_key (dsa, NULL, NULL);
|
DSA_set0_key (dsa, NULL, NULL);
|
||||||
return dsa;
|
return dsa;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// DH/ElGamal
|
// DH/ElGamal
|
||||||
|
|
||||||
|
@ -277,12 +240,17 @@ namespace crypto
|
||||||
// x25519
|
// x25519
|
||||||
X25519Keys::X25519Keys ()
|
X25519Keys::X25519Keys ()
|
||||||
{
|
{
|
||||||
|
#if OPENSSL_X25519
|
||||||
m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL);
|
m_Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL);
|
||||||
m_Pkey = nullptr;
|
m_Pkey = nullptr;
|
||||||
|
#else
|
||||||
|
m_Ctx = BN_CTX_new ();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub)
|
X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub)
|
||||||
{
|
{
|
||||||
|
#if OPENSSL_X25519
|
||||||
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
||||||
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
|
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
|
||||||
if (pub)
|
if (pub)
|
||||||
|
@ -292,16 +260,29 @@ namespace crypto
|
||||||
size_t len = 32;
|
size_t len = 32;
|
||||||
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
m_Ctx = BN_CTX_new ();
|
||||||
|
memcpy (m_PrivateKey, priv, 32);
|
||||||
|
if (pub)
|
||||||
|
memcpy (m_PublicKey, pub, 32);
|
||||||
|
else
|
||||||
|
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
X25519Keys::~X25519Keys ()
|
X25519Keys::~X25519Keys ()
|
||||||
{
|
{
|
||||||
|
#if OPENSSL_X25519
|
||||||
EVP_PKEY_CTX_free (m_Ctx);
|
EVP_PKEY_CTX_free (m_Ctx);
|
||||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||||
|
#else
|
||||||
|
BN_CTX_free (m_Ctx);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void X25519Keys::GenerateKeys ()
|
void X25519Keys::GenerateKeys ()
|
||||||
{
|
{
|
||||||
|
#if OPENSSL_X25519
|
||||||
if (m_Pkey)
|
if (m_Pkey)
|
||||||
{
|
{
|
||||||
EVP_PKEY_free (m_Pkey);
|
EVP_PKEY_free (m_Pkey);
|
||||||
|
@ -313,11 +294,16 @@ namespace crypto
|
||||||
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx?
|
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx?
|
||||||
size_t len = 32;
|
size_t len = 32;
|
||||||
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
||||||
|
#else
|
||||||
|
RAND_bytes (m_PrivateKey, 32);
|
||||||
|
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
|
bool X25519Keys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||||
{
|
{
|
||||||
if (!pub || (pub[31] & 0x80)) return false; // not x25519 key
|
if (!pub || (pub[31] & 0x80)) return false; // not x25519 key
|
||||||
|
#if OPENSSL_X25519
|
||||||
EVP_PKEY_derive_init (m_Ctx);
|
EVP_PKEY_derive_init (m_Ctx);
|
||||||
auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32);
|
auto pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, pub, 32);
|
||||||
if (!pkey) return false;
|
if (!pkey) return false;
|
||||||
|
@ -325,17 +311,25 @@ namespace crypto
|
||||||
size_t len = 32;
|
size_t len = 32;
|
||||||
EVP_PKEY_derive (m_Ctx, shared, &len);
|
EVP_PKEY_derive (m_Ctx, shared, &len);
|
||||||
EVP_PKEY_free (pkey);
|
EVP_PKEY_free (pkey);
|
||||||
|
#else
|
||||||
|
GetEd25519 ()->ScalarMul (pub, m_PrivateKey, shared, m_Ctx);
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void X25519Keys::GetPrivateKey (uint8_t * priv) const
|
void X25519Keys::GetPrivateKey (uint8_t * priv) const
|
||||||
{
|
{
|
||||||
|
#if OPENSSL_X25519
|
||||||
size_t len = 32;
|
size_t len = 32;
|
||||||
EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len);
|
EVP_PKEY_get_raw_private_key (m_Pkey, priv, &len);
|
||||||
|
#else
|
||||||
|
memcpy (priv, m_PrivateKey, 32);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic)
|
void X25519Keys::SetPrivateKey (const uint8_t * priv, bool calculatePublic)
|
||||||
{
|
{
|
||||||
|
#if OPENSSL_X25519
|
||||||
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
|
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
|
||||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||||
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
||||||
|
@ -345,6 +339,11 @@ namespace crypto
|
||||||
size_t len = 32;
|
size_t len = 32;
|
||||||
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
memcpy (m_PrivateKey, priv, 32);
|
||||||
|
if (calculatePublic)
|
||||||
|
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElGamal
|
// ElGamal
|
||||||
|
@ -478,8 +477,9 @@ namespace crypto
|
||||||
// encrypt
|
// encrypt
|
||||||
CBCEncryption encryption;
|
CBCEncryption encryption;
|
||||||
encryption.SetKey (shared);
|
encryption.SetKey (shared);
|
||||||
|
encryption.SetIV (iv);
|
||||||
encrypted[257] = 0;
|
encrypted[257] = 0;
|
||||||
encryption.Encrypt (m, 256, iv, encrypted + 258);
|
encryption.Encrypt (m, 256, encrypted + 258);
|
||||||
EC_POINT_free (p);
|
EC_POINT_free (p);
|
||||||
BN_CTX_end (ctx);
|
BN_CTX_end (ctx);
|
||||||
BN_CTX_free (ctx);
|
BN_CTX_free (ctx);
|
||||||
|
@ -512,7 +512,8 @@ namespace crypto
|
||||||
uint8_t m[256];
|
uint8_t m[256];
|
||||||
CBCDecryption decryption;
|
CBCDecryption decryption;
|
||||||
decryption.SetKey (shared);
|
decryption.SetKey (shared);
|
||||||
decryption.Decrypt (encrypted + 258, 256, iv, m);
|
decryption.SetIV (iv);
|
||||||
|
decryption.Decrypt (encrypted + 258, 256, m);
|
||||||
// verify and copy
|
// verify and copy
|
||||||
uint8_t hash[32];
|
uint8_t hash[32];
|
||||||
SHA256 (m + 33, 222, hash);
|
SHA256 (m + 33, 222, hash);
|
||||||
|
@ -550,114 +551,440 @@ namespace crypto
|
||||||
}
|
}
|
||||||
|
|
||||||
// AES
|
// AES
|
||||||
ECBEncryption::ECBEncryption ()
|
#if SUPPORTS_AES
|
||||||
|
#define KeyExpansion256(round0,round1) \
|
||||||
|
"pshufd $0xff, %%xmm2, %%xmm2 \n" \
|
||||||
|
"movaps %%xmm1, %%xmm4 \n" \
|
||||||
|
"pslldq $4, %%xmm4 \n" \
|
||||||
|
"pxor %%xmm4, %%xmm1 \n" \
|
||||||
|
"pslldq $4, %%xmm4 \n" \
|
||||||
|
"pxor %%xmm4, %%xmm1 \n" \
|
||||||
|
"pslldq $4, %%xmm4 \n" \
|
||||||
|
"pxor %%xmm4, %%xmm1 \n" \
|
||||||
|
"pxor %%xmm2, %%xmm1 \n" \
|
||||||
|
"movaps %%xmm1, "#round0"(%[sched]) \n" \
|
||||||
|
"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
|
||||||
|
"pshufd $0xaa, %%xmm4, %%xmm2 \n" \
|
||||||
|
"movaps %%xmm3, %%xmm4 \n" \
|
||||||
|
"pslldq $4, %%xmm4 \n" \
|
||||||
|
"pxor %%xmm4, %%xmm3 \n" \
|
||||||
|
"pslldq $4, %%xmm4 \n" \
|
||||||
|
"pxor %%xmm4, %%xmm3 \n" \
|
||||||
|
"pslldq $4, %%xmm4 \n" \
|
||||||
|
"pxor %%xmm4, %%xmm3 \n" \
|
||||||
|
"pxor %%xmm2, %%xmm3 \n" \
|
||||||
|
"movaps %%xmm3, "#round1"(%[sched]) \n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SUPPORTS_AES
|
||||||
|
void ECBCryptoAESNI::ExpandKey (const AESKey& key)
|
||||||
{
|
{
|
||||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
__asm__
|
||||||
|
(
|
||||||
|
"movups (%[key]), %%xmm1 \n"
|
||||||
|
"movups 16(%[key]), %%xmm3 \n"
|
||||||
|
"movaps %%xmm1, (%[sched]) \n"
|
||||||
|
"movaps %%xmm3, 16(%[sched]) \n"
|
||||||
|
"aeskeygenassist $1, %%xmm3, %%xmm2 \n"
|
||||||
|
KeyExpansion256(32,48)
|
||||||
|
"aeskeygenassist $2, %%xmm3, %%xmm2 \n"
|
||||||
|
KeyExpansion256(64,80)
|
||||||
|
"aeskeygenassist $4, %%xmm3, %%xmm2 \n"
|
||||||
|
KeyExpansion256(96,112)
|
||||||
|
"aeskeygenassist $8, %%xmm3, %%xmm2 \n"
|
||||||
|
KeyExpansion256(128,144)
|
||||||
|
"aeskeygenassist $16, %%xmm3, %%xmm2 \n"
|
||||||
|
KeyExpansion256(160,176)
|
||||||
|
"aeskeygenassist $32, %%xmm3, %%xmm2 \n"
|
||||||
|
KeyExpansion256(192,208)
|
||||||
|
"aeskeygenassist $64, %%xmm3, %%xmm2 \n"
|
||||||
|
// key expansion final
|
||||||
|
"pshufd $0xff, %%xmm2, %%xmm2 \n"
|
||||||
|
"movaps %%xmm1, %%xmm4 \n"
|
||||||
|
"pslldq $4, %%xmm4 \n"
|
||||||
|
"pxor %%xmm4, %%xmm1 \n"
|
||||||
|
"pslldq $4, %%xmm4 \n"
|
||||||
|
"pxor %%xmm4, %%xmm1 \n"
|
||||||
|
"pslldq $4, %%xmm4 \n"
|
||||||
|
"pxor %%xmm4, %%xmm1 \n"
|
||||||
|
"pxor %%xmm2, %%xmm1 \n"
|
||||||
|
"movups %%xmm1, 224(%[sched]) \n"
|
||||||
|
: // output
|
||||||
|
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
|
||||||
|
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if SUPPORTS_AES
|
||||||
|
#define EncryptAES256(sched) \
|
||||||
|
"pxor (%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 16(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 32(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 48(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 64(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 80(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 96(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 112(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 128(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 144(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 160(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 176(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 192(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenc 208(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesenclast 224(%["#sched"]), %%xmm0 \n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ECBEncryption::Encrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||||
|
{
|
||||||
|
#if SUPPORTS_AES
|
||||||
|
if(i2p::cpu::aesni)
|
||||||
|
{
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
EncryptAES256(sched)
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
:
|
||||||
|
: [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out)
|
||||||
|
: "%xmm0", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
AES_encrypt (in->buf, out->buf, &m_Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ECBEncryption::~ECBEncryption ()
|
#if SUPPORTS_AES
|
||||||
|
#define DecryptAES256(sched) \
|
||||||
|
"pxor 224(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 208(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 192(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 176(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 160(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 144(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 128(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 112(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 96(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 80(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 64(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 48(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 32(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdec 16(%["#sched"]), %%xmm0 \n" \
|
||||||
|
"aesdeclast (%["#sched"]), %%xmm0 \n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ECBDecryption::Decrypt (const ChipherBlock * in, ChipherBlock * out)
|
||||||
{
|
{
|
||||||
if (m_Ctx)
|
#if SUPPORTS_AES
|
||||||
EVP_CIPHER_CTX_free (m_Ctx);
|
if(i2p::cpu::aesni)
|
||||||
|
{
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
DecryptAES256(sched)
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
:
|
||||||
|
: [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out)
|
||||||
|
: "%xmm0", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
AES_decrypt (in->buf, out->buf, &m_Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ECBEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
#if SUPPORTS_AES
|
||||||
|
#define CallAESIMC(offset) \
|
||||||
|
"movaps "#offset"(%[shed]), %%xmm0 \n" \
|
||||||
|
"aesimc %%xmm0, %%xmm0 \n" \
|
||||||
|
"movaps %%xmm0, "#offset"(%[shed]) \n"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ECBEncryption::SetKey (const AESKey& key)
|
||||||
{
|
{
|
||||||
EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL);
|
#if SUPPORTS_AES
|
||||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
if(i2p::cpu::aesni)
|
||||||
int len;
|
{
|
||||||
EVP_EncryptUpdate (m_Ctx, out, &len, in, 16);
|
ExpandKey (key);
|
||||||
EVP_EncryptFinal_ex (m_Ctx, out + len, &len);
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
AES_set_encrypt_key (key, 256, &m_Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ECBDecryption::ECBDecryption ()
|
void ECBDecryption::SetKey (const AESKey& key)
|
||||||
{
|
{
|
||||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
#if SUPPORTS_AES
|
||||||
|
if(i2p::cpu::aesni)
|
||||||
|
{
|
||||||
|
ExpandKey (key); // expand encryption key first
|
||||||
|
// then invert it using aesimc
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
CallAESIMC(16)
|
||||||
|
CallAESIMC(32)
|
||||||
|
CallAESIMC(48)
|
||||||
|
CallAESIMC(64)
|
||||||
|
CallAESIMC(80)
|
||||||
|
CallAESIMC(96)
|
||||||
|
CallAESIMC(112)
|
||||||
|
CallAESIMC(128)
|
||||||
|
CallAESIMC(144)
|
||||||
|
CallAESIMC(160)
|
||||||
|
CallAESIMC(176)
|
||||||
|
CallAESIMC(192)
|
||||||
|
CallAESIMC(208)
|
||||||
|
:
|
||||||
|
: [shed]"r"(GetKeySchedule ())
|
||||||
|
: "%xmm0", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
AES_set_decrypt_key (key, 256, &m_Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ECBDecryption::~ECBDecryption ()
|
void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
||||||
{
|
{
|
||||||
if (m_Ctx)
|
#if SUPPORTS_AES
|
||||||
EVP_CIPHER_CTX_free (m_Ctx);
|
if(i2p::cpu::aesni)
|
||||||
|
{
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
"movups (%[iv]), %%xmm1 \n"
|
||||||
|
"1: \n"
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
"pxor %%xmm1, %%xmm0 \n"
|
||||||
|
EncryptAES256(sched)
|
||||||
|
"movaps %%xmm0, %%xmm1 \n"
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
"add $16, %[in] \n"
|
||||||
|
"add $16, %[out] \n"
|
||||||
|
"dec %[num] \n"
|
||||||
|
"jnz 1b \n"
|
||||||
|
"movups %%xmm1, (%[iv]) \n"
|
||||||
|
:
|
||||||
|
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||||
|
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||||
|
: "%xmm0", "%xmm1", "cc", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numBlocks; i++)
|
||||||
|
{
|
||||||
|
*m_LastBlock.GetChipherBlock () ^= in[i];
|
||||||
|
m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
|
||||||
|
out[i] = *m_LastBlock.GetChipherBlock ();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ECBDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
||||||
{
|
|
||||||
EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_ecb(), NULL, m_Key, NULL);
|
|
||||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
|
||||||
int len;
|
|
||||||
EVP_DecryptUpdate (m_Ctx, out, &len, in, 16);
|
|
||||||
EVP_DecryptFinal_ex (m_Ctx, out + len, &len);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CBCEncryption::CBCEncryption ()
|
|
||||||
{
|
|
||||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
|
||||||
}
|
|
||||||
|
|
||||||
CBCEncryption::~CBCEncryption ()
|
|
||||||
{
|
|
||||||
if (m_Ctx)
|
|
||||||
EVP_CIPHER_CTX_free (m_Ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBCEncryption::Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out)
|
|
||||||
{
|
{
|
||||||
// len/16
|
// len/16
|
||||||
EVP_EncryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv);
|
int numBlocks = len >> 4;
|
||||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
if (numBlocks > 0)
|
||||||
int l;
|
Encrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||||
EVP_EncryptUpdate (m_Ctx, out, &l, in, len);
|
|
||||||
EVP_EncryptFinal_ex (m_Ctx, out + l, &l);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBCDecryption::CBCDecryption ()
|
void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
||||||
{
|
{
|
||||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
#if SUPPORTS_AES
|
||||||
|
if(i2p::cpu::aesni)
|
||||||
|
{
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
"movups (%[iv]), %%xmm1 \n"
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
"pxor %%xmm1, %%xmm0 \n"
|
||||||
|
EncryptAES256(sched)
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
"movups %%xmm0, (%[iv]) \n"
|
||||||
|
:
|
||||||
|
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||||
|
[in]"r"(in), [out]"r"(out)
|
||||||
|
: "%xmm0", "%xmm1", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||||
}
|
}
|
||||||
|
|
||||||
CBCDecryption::~CBCDecryption ()
|
void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out)
|
||||||
{
|
{
|
||||||
if (m_Ctx)
|
#if SUPPORTS_AES
|
||||||
EVP_CIPHER_CTX_free (m_Ctx);
|
if(i2p::cpu::aesni)
|
||||||
|
{
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
"movups (%[iv]), %%xmm1 \n"
|
||||||
|
"1: \n"
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
"movaps %%xmm0, %%xmm2 \n"
|
||||||
|
DecryptAES256(sched)
|
||||||
|
"pxor %%xmm1, %%xmm0 \n"
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
"movaps %%xmm2, %%xmm1 \n"
|
||||||
|
"add $16, %[in] \n"
|
||||||
|
"add $16, %[out] \n"
|
||||||
|
"dec %[num] \n"
|
||||||
|
"jnz 1b \n"
|
||||||
|
"movups %%xmm1, (%[iv]) \n"
|
||||||
|
:
|
||||||
|
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||||
|
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||||
|
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numBlocks; i++)
|
||||||
|
{
|
||||||
|
ChipherBlock tmp = in[i];
|
||||||
|
m_ECBDecryption.Decrypt (in + i, out + i);
|
||||||
|
out[i] ^= *m_IV.GetChipherBlock ();
|
||||||
|
*m_IV.GetChipherBlock () = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBCDecryption::Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out)
|
void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out)
|
||||||
{
|
{
|
||||||
// len/16
|
int numBlocks = len >> 4;
|
||||||
EVP_DecryptInit_ex (m_Ctx, EVP_aes_256_cbc(), NULL, m_Key, iv);
|
if (numBlocks > 0)
|
||||||
EVP_CIPHER_CTX_set_padding (m_Ctx, 0);
|
Decrypt (numBlocks, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||||
int l;
|
}
|
||||||
EVP_DecryptUpdate (m_Ctx, out, &l, in, len);
|
|
||||||
EVP_DecryptFinal_ex (m_Ctx, out + l, &l);
|
void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
||||||
|
{
|
||||||
|
#if SUPPORTS_AES
|
||||||
|
if(i2p::cpu::aesni)
|
||||||
|
{
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
"movups (%[iv]), %%xmm1 \n"
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
"movups %%xmm0, (%[iv]) \n"
|
||||||
|
DecryptAES256(sched)
|
||||||
|
"pxor %%xmm1, %%xmm0 \n"
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
:
|
||||||
|
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||||
|
[in]"r"(in), [out]"r"(out)
|
||||||
|
: "%xmm0", "%xmm1", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
void TunnelEncryption::Encrypt (const uint8_t * in, uint8_t * out)
|
||||||
{
|
{
|
||||||
uint8_t iv[16];
|
#if SUPPORTS_AES
|
||||||
m_IVEncryption.Encrypt (in, iv); // iv
|
if(i2p::cpu::aesni)
|
||||||
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data
|
{
|
||||||
m_IVEncryption.Encrypt (iv, out); // double iv
|
__asm__
|
||||||
|
(
|
||||||
|
// encrypt IV
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
EncryptAES256(sched_iv)
|
||||||
|
"movaps %%xmm0, %%xmm1 \n"
|
||||||
|
// double IV encryption
|
||||||
|
EncryptAES256(sched_iv)
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
// encrypt data, IV is xmm1
|
||||||
|
"1: \n"
|
||||||
|
"add $16, %[in] \n"
|
||||||
|
"add $16, %[out] \n"
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
"pxor %%xmm1, %%xmm0 \n"
|
||||||
|
EncryptAES256(sched_l)
|
||||||
|
"movaps %%xmm0, %%xmm1 \n"
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
"dec %[num] \n"
|
||||||
|
"jnz 1b \n"
|
||||||
|
:
|
||||||
|
: [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.ECB().GetKeySchedule ()),
|
||||||
|
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
||||||
|
: "%xmm0", "%xmm1", "cc", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
m_IVEncryption.Encrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
||||||
|
m_LayerEncryption.SetIV (out);
|
||||||
|
m_LayerEncryption.Encrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
||||||
|
m_IVEncryption.Encrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
void TunnelDecryption::Decrypt (const uint8_t * in, uint8_t * out)
|
||||||
{
|
{
|
||||||
uint8_t iv[16];
|
#if SUPPORTS_AES
|
||||||
m_IVDecryption.Decrypt (in, iv); // iv
|
if(i2p::cpu::aesni)
|
||||||
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, iv, out + 16); // data
|
{
|
||||||
m_IVDecryption.Decrypt (iv, out); // double iv
|
__asm__
|
||||||
|
(
|
||||||
|
// decrypt IV
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
DecryptAES256(sched_iv)
|
||||||
|
"movaps %%xmm0, %%xmm1 \n"
|
||||||
|
// double IV encryption
|
||||||
|
DecryptAES256(sched_iv)
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
// decrypt data, IV is xmm1
|
||||||
|
"1: \n"
|
||||||
|
"add $16, %[in] \n"
|
||||||
|
"add $16, %[out] \n"
|
||||||
|
"movups (%[in]), %%xmm0 \n"
|
||||||
|
"movaps %%xmm0, %%xmm2 \n"
|
||||||
|
DecryptAES256(sched_l)
|
||||||
|
"pxor %%xmm1, %%xmm0 \n"
|
||||||
|
"movups %%xmm0, (%[out]) \n"
|
||||||
|
"movaps %%xmm2, %%xmm1 \n"
|
||||||
|
"dec %[num] \n"
|
||||||
|
"jnz 1b \n"
|
||||||
|
:
|
||||||
|
: [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.ECB().GetKeySchedule ()),
|
||||||
|
[in]"r"(in), [out]"r"(out), [num]"r"(63) // 63 blocks = 1008 bytes
|
||||||
|
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
m_IVDecryption.Decrypt ((const ChipherBlock *)in, (ChipherBlock *)out); // iv
|
||||||
|
m_LayerDecryption.SetIV (out);
|
||||||
|
m_LayerDecryption.Decrypt (in + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, out + 16); // data
|
||||||
|
m_IVDecryption.Decrypt ((ChipherBlock *)out, (ChipherBlock *)out); // double iv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AEAD/ChaCha20/Poly1305
|
// AEAD/ChaCha20/Poly1305
|
||||||
|
|
||||||
static bool AEADChaCha20Poly1305 (EVP_CIPHER_CTX * ctx, const uint8_t * msg, size_t msgLen,
|
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)
|
||||||
const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt)
|
|
||||||
{
|
{
|
||||||
if (!ctx || len < msgLen) return false;
|
if (len < msgLen) return false;
|
||||||
if (encrypt && len < msgLen + 16) return false;
|
if (encrypt && len < msgLen + 16) return false;
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
int outlen = 0;
|
int outlen = 0;
|
||||||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
|
||||||
if (encrypt)
|
if (encrypt)
|
||||||
{
|
{
|
||||||
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
||||||
|
@ -670,7 +997,7 @@ namespace crypto
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x4000000fL
|
#if defined(LIBRESSL_VERSION_NUMBER)
|
||||||
std::vector<uint8_t> m(msgLen + 16);
|
std::vector<uint8_t> m(msgLen + 16);
|
||||||
if (msg == buf)
|
if (msg == buf)
|
||||||
{
|
{
|
||||||
|
@ -687,100 +1014,38 @@ namespace crypto
|
||||||
EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen);
|
EVP_DecryptUpdate(ctx, buf, &outlen, msg, msgLen);
|
||||||
ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0;
|
ret = EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen) > 0;
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new ();
|
|
||||||
auto ret = AEADChaCha20Poly1305 (ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, encrypt);
|
|
||||||
EVP_CIPHER_CTX_free (ctx);
|
EVP_CIPHER_CTX_free (ctx);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
AEADChaCha20Poly1305Encryptor::AEADChaCha20Poly1305Encryptor ()
|
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
|
||||||
{
|
|
||||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
|
||||||
}
|
|
||||||
|
|
||||||
AEADChaCha20Poly1305Encryptor::~AEADChaCha20Poly1305Encryptor ()
|
|
||||||
{
|
|
||||||
if (m_Ctx)
|
|
||||||
EVP_CIPHER_CTX_free (m_Ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AEADChaCha20Poly1305Encryptor::Encrypt (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)
|
|
||||||
{
|
|
||||||
return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AEADChaCha20Poly1305Encryptor::Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
|
|
||||||
const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
|
|
||||||
{
|
{
|
||||||
if (bufs.empty ()) return;
|
if (bufs.empty ()) return;
|
||||||
int outlen = 0;
|
int outlen = 0;
|
||||||
EVP_EncryptInit_ex(m_Ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
|
||||||
EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
|
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
||||||
EVP_EncryptInit_ex(m_Ctx, NULL, NULL, key, nonce);
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0);
|
||||||
|
EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce);
|
||||||
for (const auto& it: bufs)
|
for (const auto& it: bufs)
|
||||||
EVP_EncryptUpdate(m_Ctx, it.first, &outlen, it.first, it.second);
|
EVP_EncryptUpdate(ctx, it.first, &outlen, it.first, it.second);
|
||||||
EVP_EncryptFinal_ex(m_Ctx, NULL, &outlen);
|
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
|
||||||
EVP_CIPHER_CTX_ctrl(m_Ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac);
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, mac);
|
||||||
|
EVP_CIPHER_CTX_free (ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
AEADChaCha20Poly1305Decryptor::AEADChaCha20Poly1305Decryptor ()
|
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
|
||||||
{
|
|
||||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
|
||||||
}
|
|
||||||
|
|
||||||
AEADChaCha20Poly1305Decryptor::~AEADChaCha20Poly1305Decryptor ()
|
|
||||||
{
|
|
||||||
if (m_Ctx)
|
|
||||||
EVP_CIPHER_CTX_free (m_Ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AEADChaCha20Poly1305Decryptor::Decrypt (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)
|
|
||||||
{
|
|
||||||
return AEADChaCha20Poly1305 (m_Ctx, msg, msgLen, ad, adLen, key, nonce, buf, len, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ChaCha20 (EVP_CIPHER_CTX *ctx, const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
|
|
||||||
{
|
{
|
||||||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
|
||||||
uint32_t iv[4];
|
uint32_t iv[4];
|
||||||
iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce
|
iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce
|
||||||
EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv);
|
EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv);
|
||||||
int outlen = 0;
|
int outlen = 0;
|
||||||
EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen);
|
EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen);
|
||||||
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
|
EVP_EncryptFinal_ex(ctx, NULL, &outlen);
|
||||||
}
|
|
||||||
|
|
||||||
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
|
|
||||||
{
|
|
||||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
|
|
||||||
ChaCha20 (ctx, msg, msgLen, key, nonce, out);
|
|
||||||
EVP_CIPHER_CTX_free (ctx);
|
EVP_CIPHER_CTX_free (ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ChaCha20Context::ChaCha20Context ()
|
|
||||||
{
|
|
||||||
m_Ctx = EVP_CIPHER_CTX_new ();
|
|
||||||
}
|
|
||||||
|
|
||||||
ChaCha20Context::~ChaCha20Context ()
|
|
||||||
{
|
|
||||||
if (m_Ctx)
|
|
||||||
EVP_CIPHER_CTX_free (m_Ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChaCha20Context::operator ()(const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
|
|
||||||
{
|
|
||||||
ChaCha20 (m_Ctx, msg, msgLen, key, nonce, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
|
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
|
||||||
uint8_t * out, size_t outLen)
|
uint8_t * out, size_t outLen)
|
||||||
{
|
{
|
||||||
|
@ -821,18 +1086,6 @@ namespace crypto
|
||||||
|
|
||||||
// Noise
|
// Noise
|
||||||
|
|
||||||
void NoiseSymmetricState::Init (const uint8_t * ck, const uint8_t * hh, const uint8_t * pub)
|
|
||||||
{
|
|
||||||
// pub is Bob's public static key, hh = SHA256(h)
|
|
||||||
memcpy (m_CK, ck, 32);
|
|
||||||
SHA256_CTX ctx;
|
|
||||||
SHA256_Init (&ctx);
|
|
||||||
SHA256_Update (&ctx, hh, 32);
|
|
||||||
SHA256_Update (&ctx, pub, 32);
|
|
||||||
SHA256_Final (m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub)
|
|
||||||
m_N = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len)
|
void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
SHA256_CTX ctx;
|
SHA256_CTX ctx;
|
||||||
|
@ -856,93 +1109,74 @@ namespace crypto
|
||||||
{
|
{
|
||||||
HKDF (m_CK, sharedSecret, 32, "", m_CK);
|
HKDF (m_CK, sharedSecret, 32, "", m_CK);
|
||||||
// new ck is m_CK[0:31], key is m_CK[32:63]
|
// new ck is m_CK[0:31], key is m_CK[32:63]
|
||||||
m_N = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NoiseSymmetricState::Encrypt (const uint8_t * in, uint8_t * out, size_t len)
|
static void InitNoiseState (NoiseSymmetricState& state, const uint8_t * ck,
|
||||||
|
const uint8_t * hh, const uint8_t * pub)
|
||||||
{
|
{
|
||||||
uint8_t nonce[12];
|
// pub is Bob's public static key, hh = SHA256(h)
|
||||||
if (m_N)
|
memcpy (state.m_CK, ck, 32);
|
||||||
{
|
SHA256_CTX ctx;
|
||||||
memset (nonce, 0, 4);
|
SHA256_Init (&ctx);
|
||||||
htole64buf (nonce + 4, m_N);
|
SHA256_Update (&ctx, hh, 32);
|
||||||
}
|
SHA256_Update (&ctx, pub, 32);
|
||||||
else
|
SHA256_Final (state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub)
|
||||||
memset (nonce, 0, 12);
|
|
||||||
auto ret = AEADChaCha20Poly1305 (in, len, m_H, 32, m_CK + 32, nonce, out, len + 16, true);
|
|
||||||
if (ret) m_N++;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NoiseSymmetricState::Decrypt (const uint8_t * in, uint8_t * out, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t nonce[12];
|
|
||||||
if (m_N)
|
|
||||||
{
|
|
||||||
memset (nonce, 0, 4);
|
|
||||||
htole64buf (nonce + 4, m_N);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
memset (nonce, 0, 12);
|
|
||||||
auto ret = AEADChaCha20Poly1305 (in, len, m_H, 32, m_CK + 32, nonce, out, len, false);
|
|
||||||
if (ret) m_N++;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub)
|
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub)
|
||||||
{
|
{
|
||||||
static constexpr char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
|
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
|
||||||
static constexpr uint8_t hh[32] =
|
static const uint8_t hh[32] =
|
||||||
{
|
{
|
||||||
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
|
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
|
||||||
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
|
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
|
||||||
}; // hh = SHA256(protocol_name || 0)
|
}; // hh = SHA256(protocol_name || 0)
|
||||||
state.Init ((const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0
|
InitNoiseState (state, (const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub)
|
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub)
|
||||||
{
|
{
|
||||||
static constexpr uint8_t protocolNameHash[32] =
|
static const uint8_t protocolNameHash[32] =
|
||||||
{
|
{
|
||||||
0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed,
|
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
|
0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71
|
||||||
}; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256")
|
}; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256")
|
||||||
static constexpr uint8_t hh[32] =
|
static const uint8_t hh[32] =
|
||||||
{
|
{
|
||||||
0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5,
|
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
|
0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e
|
||||||
}; // SHA256 (protocolNameHash)
|
}; // SHA256 (protocolNameHash)
|
||||||
state.Init (protocolNameHash, hh, pub);
|
InitNoiseState (state, protocolNameHash, hh, pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub)
|
void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub)
|
||||||
{
|
{
|
||||||
static constexpr uint8_t protocolNameHash[32] =
|
static const uint8_t protocolNameHash[32] =
|
||||||
{
|
{
|
||||||
0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, 0xf4,
|
0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, 0xf4,
|
||||||
0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, 0x32
|
0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, 0x32
|
||||||
}; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256")
|
}; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256")
|
||||||
static constexpr uint8_t hh[32] =
|
static const uint8_t hh[32] =
|
||||||
{
|
{
|
||||||
0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, 0x53,
|
0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, 0x53,
|
||||||
0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, 0x33
|
0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, 0x33
|
||||||
}; // SHA256 (protocolNameHash)
|
}; // SHA256 (protocolNameHash)
|
||||||
state.Init (protocolNameHash, hh, pub);
|
InitNoiseState (state, protocolNameHash, hh, pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub)
|
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub)
|
||||||
{
|
{
|
||||||
static constexpr uint8_t protocolNameHash[32] =
|
static const uint8_t protocolNameHash[32] =
|
||||||
{
|
{
|
||||||
0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba,
|
0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba,
|
||||||
0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c
|
0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c
|
||||||
}; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes
|
}; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes
|
||||||
static constexpr uint8_t hh[32] =
|
static const uint8_t hh[32] =
|
||||||
{
|
{
|
||||||
0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32,
|
0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32,
|
||||||
0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c
|
0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c
|
||||||
}; // SHA256 (protocolNameHash)
|
}; // SHA256 (protocolNameHash)
|
||||||
state.Init (protocolNameHash, hh, pub);
|
InitNoiseState (state, protocolNameHash, hh, pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
// init and terminate
|
// init and terminate
|
||||||
|
@ -959,8 +1193,9 @@ namespace crypto
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
void InitCrypto (bool precomputation)
|
void InitCrypto (bool precomputation, bool aesni, bool force)
|
||||||
{
|
{
|
||||||
|
i2p::cpu::Detect (aesni, force);
|
||||||
/* auto numLocks = CRYPTO_num_locks();
|
/* auto numLocks = CRYPTO_num_locks();
|
||||||
for (int i = 0; i < numLocks; i++)
|
for (int i = 0; i < numLocks; i++)
|
||||||
m_OpenSSLMutexes.emplace_back (new std::mutex);
|
m_OpenSSLMutexes.emplace_back (new std::mutex);
|
||||||
|
|
211
libi2pd/Crypto.h
211
libi2pd/Crypto.h
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -25,17 +25,16 @@
|
||||||
|
|
||||||
#include "Base.h"
|
#include "Base.h"
|
||||||
#include "Tag.h"
|
#include "Tag.h"
|
||||||
|
#include "CPU.h"
|
||||||
|
|
||||||
// recognize openssl version and features
|
// recognize openssl version and features
|
||||||
#if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
|
#if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
|
||||||
# define OPENSSL_HKDF 1
|
# define OPENSSL_HKDF 1
|
||||||
# define OPENSSL_EDDSA 1
|
# define OPENSSL_EDDSA 1
|
||||||
|
# define OPENSSL_X25519 1
|
||||||
# if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
|
# if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
|
||||||
# define OPENSSL_SIPHASH 1
|
# define OPENSSL_SIPHASH 1
|
||||||
# endif
|
# endif
|
||||||
# if (OPENSSL_VERSION_NUMBER >= 0x030500000) // 3.5.0
|
|
||||||
# define OPENSSL_PQ 1
|
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -45,11 +44,7 @@ namespace crypto
|
||||||
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
|
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
|
||||||
|
|
||||||
// DSA
|
// DSA
|
||||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
|
||||||
EVP_PKEY * CreateDSA (BIGNUM * pubKey = nullptr, BIGNUM * privKey = nullptr);
|
|
||||||
#else
|
|
||||||
DSA * CreateDSA ();
|
DSA * CreateDSA ();
|
||||||
#endif
|
|
||||||
|
|
||||||
// RSA
|
// RSA
|
||||||
const BIGNUM * GetRSAE ();
|
const BIGNUM * GetRSAE ();
|
||||||
|
@ -75,8 +70,13 @@ namespace crypto
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint8_t m_PublicKey[32];
|
uint8_t m_PublicKey[32];
|
||||||
|
#if OPENSSL_X25519
|
||||||
EVP_PKEY_CTX * m_Ctx;
|
EVP_PKEY_CTX * m_Ctx;
|
||||||
EVP_PKEY * m_Pkey;
|
EVP_PKEY * m_Pkey;
|
||||||
|
#else
|
||||||
|
BN_CTX * m_Ctx;
|
||||||
|
uint8_t m_PrivateKey[32];
|
||||||
|
#endif
|
||||||
bool m_IsElligatorIneligible = false; // true if definitely ineligible
|
bool m_IsElligatorIneligible = false; // true if definitely ineligible
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,70 +91,142 @@ namespace crypto
|
||||||
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
|
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
|
||||||
|
|
||||||
// AES
|
// AES
|
||||||
typedef i2p::data::Tag<32> AESKey;
|
struct ChipherBlock
|
||||||
|
|
||||||
class ECBEncryption
|
|
||||||
{
|
{
|
||||||
public:
|
uint8_t buf[16];
|
||||||
|
|
||||||
ECBEncryption ();
|
void operator^=(const ChipherBlock& other) // XOR
|
||||||
~ECBEncryption ();
|
{
|
||||||
|
if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ?
|
||||||
void SetKey (const uint8_t * key) { m_Key = key; };
|
{
|
||||||
void Encrypt(const uint8_t * in, uint8_t * out);
|
for (int i = 0; i < 4; i++)
|
||||||
|
reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
|
||||||
private:
|
}
|
||||||
|
else
|
||||||
AESKey m_Key;
|
{
|
||||||
EVP_CIPHER_CTX * m_Ctx;
|
for (int i = 0; i < 16; i++)
|
||||||
|
buf[i] ^= other.buf[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ECBDecryption
|
typedef i2p::data::Tag<32> AESKey;
|
||||||
|
|
||||||
|
template<size_t sz>
|
||||||
|
class AESAlignedBuffer // 16 bytes alignment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ECBDecryption ();
|
AESAlignedBuffer ()
|
||||||
~ECBDecryption ();
|
{
|
||||||
|
m_Buf = m_UnalignedBuffer;
|
||||||
|
uint8_t rem = ((size_t)m_Buf) & 0x0f;
|
||||||
|
if (rem)
|
||||||
|
m_Buf += (16 - rem);
|
||||||
|
}
|
||||||
|
|
||||||
void SetKey (const uint8_t * key) { m_Key = key; };
|
operator uint8_t * () { return m_Buf; };
|
||||||
void Decrypt (const uint8_t * in, uint8_t * out);
|
operator const uint8_t * () const { return m_Buf; };
|
||||||
|
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
|
||||||
|
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
AESKey m_Key;
|
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
|
||||||
EVP_CIPHER_CTX * m_Ctx;
|
uint8_t * m_Buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#if SUPPORTS_AES
|
||||||
|
class ECBCryptoAESNI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
uint8_t * GetKeySchedule () { return m_KeySchedule; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void ExpandKey (const AESKey& key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SUPPORTS_AES
|
||||||
|
class ECBEncryption: public ECBCryptoAESNI
|
||||||
|
#else
|
||||||
|
class ECBEncryption
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void SetKey (const AESKey& key);
|
||||||
|
|
||||||
|
void Encrypt(const ChipherBlock * in, ChipherBlock * out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AES_KEY m_Key;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if SUPPORTS_AES
|
||||||
|
class ECBDecryption: public ECBCryptoAESNI
|
||||||
|
#else
|
||||||
|
class ECBDecryption
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void SetKey (const AESKey& key);
|
||||||
|
void Decrypt (const ChipherBlock * in, ChipherBlock * out);
|
||||||
|
private:
|
||||||
|
AES_KEY m_Key;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CBCEncryption
|
class CBCEncryption
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CBCEncryption ();
|
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
|
||||||
~CBCEncryption ();
|
|
||||||
|
|
||||||
void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
|
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
|
||||||
void Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
|
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
|
||||||
|
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); };
|
||||||
|
|
||||||
|
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||||
|
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||||
|
void Encrypt (const uint8_t * in, uint8_t * out); // one block
|
||||||
|
|
||||||
|
ECBEncryption & ECB() { return m_ECBEncryption; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
AESKey m_Key;
|
AESAlignedBuffer<16> m_LastBlock;
|
||||||
EVP_CIPHER_CTX * m_Ctx;
|
|
||||||
|
ECBEncryption m_ECBEncryption;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CBCDecryption
|
class CBCDecryption
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CBCDecryption ();
|
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
|
||||||
~CBCDecryption ();
|
|
||||||
|
|
||||||
void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
|
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
|
||||||
void Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
|
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
|
||||||
|
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); };
|
||||||
|
|
||||||
|
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||||
|
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||||
|
void Decrypt (const uint8_t * in, uint8_t * out); // one block
|
||||||
|
|
||||||
|
ECBDecryption & ECB() { return m_ECBDecryption; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
AESKey m_Key;
|
AESAlignedBuffer<16> m_IV;
|
||||||
EVP_CIPHER_CTX * m_Ctx;
|
ECBDecryption m_ECBDecryption;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TunnelEncryption // with double IV encryption
|
class TunnelEncryption // with double IV encryption
|
||||||
|
@ -194,58 +266,13 @@ namespace crypto
|
||||||
};
|
};
|
||||||
|
|
||||||
// AEAD/ChaCha20/Poly1305
|
// AEAD/ChaCha20/Poly1305
|
||||||
|
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag
|
||||||
|
|
||||||
class AEADChaCha20Poly1305Encryptor
|
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
AEADChaCha20Poly1305Encryptor ();
|
|
||||||
~AEADChaCha20Poly1305Encryptor ();
|
|
||||||
|
|
||||||
bool Encrypt (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); // msgLen is len without tag
|
|
||||||
|
|
||||||
void Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs, const uint8_t * key, const uint8_t * nonce, uint8_t * mac); // encrypt multiple buffers with zero ad
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX * m_Ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AEADChaCha20Poly1305Decryptor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
AEADChaCha20Poly1305Decryptor ();
|
|
||||||
~AEADChaCha20Poly1305Decryptor ();
|
|
||||||
|
|
||||||
bool Decrypt (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); // msgLen is len without tag
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX * m_Ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
// ChaCha20
|
// ChaCha20
|
||||||
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
|
void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
|
||||||
|
|
||||||
class ChaCha20Context
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ChaCha20Context ();
|
|
||||||
~ChaCha20Context ();
|
|
||||||
void operator ()(const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX * m_Ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
// HKDF
|
// HKDF
|
||||||
|
|
||||||
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
|
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
|
||||||
|
@ -255,16 +282,10 @@ namespace crypto
|
||||||
struct NoiseSymmetricState
|
struct NoiseSymmetricState
|
||||||
{
|
{
|
||||||
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
|
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
|
||||||
uint64_t m_N;
|
|
||||||
|
|
||||||
void Init (const uint8_t * ck, const uint8_t * hh, const uint8_t * pub);
|
|
||||||
|
|
||||||
void MixHash (const uint8_t * buf, size_t len);
|
void MixHash (const uint8_t * buf, size_t len);
|
||||||
void MixHash (const std::vector<std::pair<uint8_t *, size_t> >& bufs);
|
void MixHash (const std::vector<std::pair<uint8_t *, size_t> >& bufs);
|
||||||
void MixKey (const uint8_t * sharedSecret);
|
void MixKey (const uint8_t * sharedSecret);
|
||||||
|
|
||||||
bool Encrypt (const uint8_t * in, uint8_t * out, size_t len); // out length = len + 16
|
|
||||||
bool Decrypt (const uint8_t * in, uint8_t * out, size_t len); // len without 16 bytes tag
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router)
|
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router)
|
||||||
|
@ -273,7 +294,7 @@ namespace crypto
|
||||||
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
|
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
|
||||||
|
|
||||||
// init and terminate
|
// init and terminate
|
||||||
void InitCrypto (bool precomputation);
|
void InitCrypto (bool precomputation, bool aesni, bool force);
|
||||||
void TerminateCrypto ();
|
void TerminateCrypto ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -181,21 +181,5 @@ namespace crypto
|
||||||
k.GetPrivateKey (priv);
|
k.GetPrivateKey (priv);
|
||||||
memcpy (pub, k.GetPublicKey (), 32);
|
memcpy (pub, k.GetPublicKey (), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalEncryptionKey::LocalEncryptionKey (i2p::data::CryptoKeyType t): keyType(t)
|
|
||||||
{
|
|
||||||
pub.resize (GetCryptoPublicKeyLen (keyType));
|
|
||||||
priv.resize (GetCryptoPrivateKeyLen (keyType));
|
|
||||||
}
|
|
||||||
|
|
||||||
void LocalEncryptionKey::GenerateKeys ()
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv.data (), pub.data ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void LocalEncryptionKey::CreateDecryptor ()
|
|
||||||
{
|
|
||||||
decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv.data ());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "Identity.h"
|
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
|
@ -158,50 +157,7 @@ namespace crypto
|
||||||
X25519Keys m_StaticKeys;
|
X25519Keys m_StaticKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); // including hybrid
|
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
|
||||||
|
|
||||||
constexpr size_t GetCryptoPrivateKeyLen (i2p::data::CryptoKeyType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ELGAMAL: return 256;
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: return 32;
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: return 32;
|
|
||||||
// ML-KEM hybrid
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
|
||||||
return 32;
|
|
||||||
};
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t GetCryptoPublicKeyLen (i2p::data::CryptoKeyType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ELGAMAL: return 256;
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: return 32;
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: return 32;
|
|
||||||
// ML-KEM hybrid
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
|
||||||
case i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
|
||||||
return 32;
|
|
||||||
};
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LocalEncryptionKey
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> pub, priv;
|
|
||||||
i2p::data::CryptoKeyType keyType;
|
|
||||||
std::shared_ptr<CryptoKeyDecryptor> decryptor;
|
|
||||||
|
|
||||||
LocalEncryptionKey (i2p::data::CryptoKeyType t);
|
|
||||||
void GenerateKeys ();
|
|
||||||
void CreateDecryptor ();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -104,7 +104,8 @@ namespace datagram
|
||||||
|
|
||||||
if (verified)
|
if (verified)
|
||||||
{
|
{
|
||||||
auto session = ObtainSession (identity.GetIdentHash());
|
auto h = identity.GetIdentHash();
|
||||||
|
auto session = ObtainSession(h);
|
||||||
session->Ack();
|
session->Ack();
|
||||||
auto r = FindReceiver(toPort);
|
auto r = FindReceiver(toPort);
|
||||||
if(r)
|
if(r)
|
||||||
|
@ -380,19 +381,15 @@ namespace datagram
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
||||||
if (m_RoutingSession)
|
|
||||||
{
|
|
||||||
m_RoutingSession->SetAckRequestInterval (DATAGRAM_SESSION_ACK_REQUEST_INTERVAL);
|
|
||||||
if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ())
|
if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ())
|
||||||
m_PendingRoutingSessions.push_back (m_RoutingSession);
|
m_PendingRoutingSessions.push_back (m_RoutingSession);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto path = m_RoutingSession->GetSharedRoutingPath();
|
auto path = m_RoutingSession->GetSharedRoutingPath();
|
||||||
if (path && m_RoutingSession->IsRatchets () && m_RoutingSession->CleanupUnconfirmedTags ())
|
if (path && m_RoutingSession->IsRatchets () && (m_RoutingSession->CleanupUnconfirmedTags () ||
|
||||||
|
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT))
|
||||||
{
|
{
|
||||||
LogPrint (eLogDebug, "Datagram: path reset");
|
|
||||||
m_RoutingSession->SetSharedRoutingPath (nullptr);
|
m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||||
path = nullptr;
|
path = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -31,6 +31,8 @@ namespace datagram
|
||||||
{
|
{
|
||||||
// milliseconds for max session idle time
|
// milliseconds for max session idle time
|
||||||
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
|
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
|
||||||
|
// milliseconds for how long we try sticking to a dead routing path before trying to switch
|
||||||
|
const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000;
|
||||||
// milliseconds interval a routing path is used before switching
|
// milliseconds interval a routing path is used before switching
|
||||||
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
|
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
|
||||||
// milliseconds before lease expire should we try switching leases
|
// milliseconds before lease expire should we try switching leases
|
||||||
|
@ -42,7 +44,6 @@ namespace datagram
|
||||||
// max 64 messages buffered in send queue for each datagram session
|
// max 64 messages buffered in send queue for each datagram session
|
||||||
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
|
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
|
||||||
const uint64_t DATAGRAM_MAX_FLUSH_INTERVAL = 5; // in milliseconds
|
const uint64_t DATAGRAM_MAX_FLUSH_INTERVAL = 5; // in milliseconds
|
||||||
const int DATAGRAM_SESSION_ACK_REQUEST_INTERVAL = 5500; // in milliseconds
|
|
||||||
|
|
||||||
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
|
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -13,7 +13,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "ECIESX25519AEADRatchetSession.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
|
@ -24,7 +23,7 @@ namespace i2p
|
||||||
{
|
{
|
||||||
namespace client
|
namespace client
|
||||||
{
|
{
|
||||||
LeaseSetDestination::LeaseSetDestination (boost::asio::io_context& service,
|
LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service,
|
||||||
bool isPublic, const std::map<std::string, std::string> * params):
|
bool isPublic, const std::map<std::string, std::string> * params):
|
||||||
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||||
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
||||||
|
@ -169,7 +168,7 @@ namespace client
|
||||||
LoadTags ();
|
LoadTags ();
|
||||||
m_Pool->SetLocalDestination (shared_from_this ());
|
m_Pool->SetLocalDestination (shared_from_this ());
|
||||||
m_Pool->SetActive (true);
|
m_Pool->SetActive (true);
|
||||||
m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT));
|
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||||
shared_from_this (), std::placeholders::_1));
|
shared_from_this (), std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
@ -196,7 +195,7 @@ namespace client
|
||||||
m_IsPublic = itr->second != "true";
|
m_IsPublic = itr->second != "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
int inLen = 0, outLen = 0, inQuant = 0, outQuant = 0, numTags = 0, minLatency = 0, maxLatency = 0;
|
int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency;
|
||||||
std::map<std::string, int&> intOpts = {
|
std::map<std::string, int&> intOpts = {
|
||||||
{I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen},
|
{I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen},
|
||||||
{I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen},
|
{I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen},
|
||||||
|
@ -295,7 +294,7 @@ namespace client
|
||||||
if (m_IsPublic)
|
if (m_IsPublic)
|
||||||
{
|
{
|
||||||
auto s = shared_from_this ();
|
auto s = shared_from_this ();
|
||||||
boost::asio::post (m_Service, [s](void)
|
m_Service.post ([s](void)
|
||||||
{
|
{
|
||||||
s->m_PublishVerificationTimer.cancel ();
|
s->m_PublishVerificationTimer.cancel ();
|
||||||
s->Publish ();
|
s->Publish ();
|
||||||
|
@ -323,7 +322,7 @@ namespace client
|
||||||
memcpy (data.k, key, 32);
|
memcpy (data.k, key, 32);
|
||||||
memcpy (data.t, tag, 32);
|
memcpy (data.t, tag, 32);
|
||||||
auto s = shared_from_this ();
|
auto s = shared_from_this ();
|
||||||
boost::asio::post (m_Service, [s,data](void)
|
m_Service.post ([s,data](void)
|
||||||
{
|
{
|
||||||
s->AddSessionKey (data.k, data.t);
|
s->AddSessionKey (data.k, data.t);
|
||||||
});
|
});
|
||||||
|
@ -340,7 +339,7 @@ namespace client
|
||||||
memcpy (data.k, key, 32);
|
memcpy (data.k, key, 32);
|
||||||
data.t = tag;
|
data.t = tag;
|
||||||
auto s = shared_from_this ();
|
auto s = shared_from_this ();
|
||||||
boost::asio::post (m_Service, [s,data](void)
|
m_Service.post ([s,data](void)
|
||||||
{
|
{
|
||||||
s->AddECIESx25519Key (data.k, data.t);
|
s->AddECIESx25519Key (data.k, data.t);
|
||||||
});
|
});
|
||||||
|
@ -348,42 +347,23 @@ namespace client
|
||||||
|
|
||||||
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
if (!msg) return;
|
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
|
||||||
bool empty = false;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_IncomingMsgsQueueMutex);
|
|
||||||
empty = m_IncomingMsgsQueue.empty ();
|
|
||||||
m_IncomingMsgsQueue.push_back (msg);
|
|
||||||
}
|
|
||||||
if (empty)
|
|
||||||
boost::asio::post (m_Service, [s = shared_from_this ()]()
|
|
||||||
{
|
|
||||||
std::list<std::shared_ptr<I2NPMessage> > receivedMsgs;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(s->m_IncomingMsgsQueueMutex);
|
|
||||||
s->m_IncomingMsgsQueue.swap (receivedMsgs);
|
|
||||||
}
|
|
||||||
for (auto& it: receivedMsgs)
|
|
||||||
s->HandleGarlicMessage (it);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
||||||
boost::asio::post (m_Service, std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
|
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
|
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
|
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
|
||||||
uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET);
|
uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET);
|
||||||
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE,
|
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID);
|
||||||
GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
|
||||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
|
||||||
{
|
{
|
||||||
switch (typeID)
|
switch (typeID)
|
||||||
{
|
{
|
||||||
|
@ -398,7 +378,7 @@ namespace client
|
||||||
m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET));
|
m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET));
|
||||||
break;
|
break;
|
||||||
case eI2NPDatabaseStore:
|
case eI2NPDatabaseStore:
|
||||||
HandleDatabaseStoreMessage (payload, len, from);
|
HandleDatabaseStoreMessage (payload, len);
|
||||||
break;
|
break;
|
||||||
case eI2NPDatabaseSearchReply:
|
case eI2NPDatabaseSearchReply:
|
||||||
HandleDatabaseSearchReplyMessage (payload, len);
|
HandleDatabaseSearchReplyMessage (payload, len);
|
||||||
|
@ -413,8 +393,7 @@ namespace client
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len,
|
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
|
||||||
{
|
{
|
||||||
if (len < DATABASE_STORE_HEADER_SIZE)
|
if (len < DATABASE_STORE_HEADER_SIZE)
|
||||||
{
|
{
|
||||||
|
@ -469,8 +448,7 @@ namespace client
|
||||||
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
|
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
|
||||||
else
|
else
|
||||||
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET],
|
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2
|
||||||
buf + offset, len - offset, true, from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType () ); // LeaseSet2
|
|
||||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||||
{
|
{
|
||||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
||||||
|
@ -499,8 +477,7 @@ namespace client
|
||||||
if (request->requestedBlindedKey)
|
if (request->requestedBlindedKey)
|
||||||
{
|
{
|
||||||
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
|
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
|
||||||
request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr,
|
request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ());
|
||||||
from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType ());
|
|
||||||
if (ls2->IsValid () && !ls2->IsExpired ())
|
if (ls2->IsValid () && !ls2->IsExpired ())
|
||||||
{
|
{
|
||||||
leaseSet = ls2;
|
leaseSet = ls2;
|
||||||
|
@ -603,8 +580,7 @@ namespace client
|
||||||
m_ExcludedFloodfills.clear ();
|
m_ExcludedFloodfills.clear ();
|
||||||
m_PublishReplyToken = 0;
|
m_PublishReplyToken = 0;
|
||||||
// schedule verification
|
// schedule verification
|
||||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT +
|
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
|
||||||
(m_Pool ? m_Pool->GetRng ()() % PUBLISH_VERIFICATION_TIMEOUT_VARIANCE : 0)));
|
|
||||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||||
shared_from_this (), std::placeholders::_1));
|
shared_from_this (), std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
@ -612,11 +588,8 @@ namespace client
|
||||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
|
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::SetLeaseSetUpdated (bool post)
|
void LeaseSetDestination::SetLeaseSetUpdated ()
|
||||||
{
|
{
|
||||||
if (post)
|
|
||||||
boost::asio::post (m_Service, [s = shared_from_this ()]() { s->UpdateLeaseSet (); });
|
|
||||||
else
|
|
||||||
UpdateLeaseSet ();
|
UpdateLeaseSet ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,8 +656,8 @@ namespace client
|
||||||
m_ExcludedFloodfills.clear ();
|
m_ExcludedFloodfills.clear ();
|
||||||
m_PublishReplyToken = 1; // dummy non-zero value
|
m_PublishReplyToken = 1; // dummy non-zero value
|
||||||
// try again after a while
|
// try again after a while
|
||||||
LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds");
|
LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds");
|
||||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
||||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
||||||
shared_from_this (), std::placeholders::_1));
|
shared_from_this (), std::placeholders::_1));
|
||||||
return;
|
return;
|
||||||
|
@ -697,13 +670,13 @@ namespace client
|
||||||
auto s = shared_from_this ();
|
auto s = shared_from_this ();
|
||||||
msg->onDrop = [s]()
|
msg->onDrop = [s]()
|
||||||
{
|
{
|
||||||
boost::asio::post (s->GetService (), [s]()
|
s->GetService ().post([s]()
|
||||||
{
|
{
|
||||||
s->m_PublishConfirmationTimer.cancel ();
|
s->m_PublishConfirmationTimer.cancel ();
|
||||||
s->HandlePublishConfirmationTimer (boost::system::error_code());
|
s->HandlePublishConfirmationTimer (boost::system::error_code());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
||||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
||||||
shared_from_this (), std::placeholders::_1));
|
shared_from_this (), std::placeholders::_1));
|
||||||
outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg);
|
outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg);
|
||||||
|
@ -719,15 +692,15 @@ namespace client
|
||||||
m_PublishReplyToken = 0;
|
m_PublishReplyToken = 0;
|
||||||
if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
|
if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds or failed. will try again");
|
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds or failed. will try again");
|
||||||
Publish ();
|
Publish ();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ());
|
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ());
|
||||||
// Java floodfill never sends confirmation back for unknown crypto type
|
// Java floodfill never sends confirmation back for unknown crypto type
|
||||||
// assume it successive and try to verify
|
// assume it successive and try to verify
|
||||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT + PUBLISH_VERIFICATION_TIMEOUT_VARIANCE)); // always max
|
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
|
||||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||||
shared_from_this (), std::placeholders::_1));
|
shared_from_this (), std::placeholders::_1));
|
||||||
|
|
||||||
|
@ -782,10 +755,10 @@ namespace client
|
||||||
if (!m_Pool || !IsReady ())
|
if (!m_Pool || !IsReady ())
|
||||||
{
|
{
|
||||||
if (requestComplete)
|
if (requestComplete)
|
||||||
boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
|
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr));
|
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), dest, requestComplete, nullptr));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,7 +767,7 @@ namespace client
|
||||||
if (!dest || !m_Pool || !IsReady ())
|
if (!dest || !m_Pool || !IsReady ())
|
||||||
{
|
{
|
||||||
if (requestComplete)
|
if (requestComplete)
|
||||||
boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
|
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto storeHash = dest->GetStoreHash ();
|
auto storeHash = dest->GetStoreHash ();
|
||||||
|
@ -802,17 +775,17 @@ namespace client
|
||||||
if (leaseSet)
|
if (leaseSet)
|
||||||
{
|
{
|
||||||
if (requestComplete)
|
if (requestComplete)
|
||||||
boost::asio::post (m_Service, [requestComplete, leaseSet](void){requestComplete (leaseSet);});
|
m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
boost::asio::post (m_Service, std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
|
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
|
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
|
||||||
{
|
{
|
||||||
auto s = shared_from_this ();
|
auto s = shared_from_this ();
|
||||||
boost::asio::post (m_Service, [dest, notify, s](void)
|
m_Service.post ([dest, notify, s](void)
|
||||||
{
|
{
|
||||||
auto it = s->m_LeaseSetRequests.find (dest);
|
auto it = s->m_LeaseSetRequests.find (dest);
|
||||||
if (it != s->m_LeaseSetRequests.end ())
|
if (it != s->m_LeaseSetRequests.end ())
|
||||||
|
@ -840,7 +813,7 @@ namespace client
|
||||||
request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
|
request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
|
||||||
if (requestComplete)
|
if (requestComplete)
|
||||||
request->requestComplete.push_back (requestComplete);
|
request->requestComplete.push_back (requestComplete);
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||||
if (ret.second) // inserted
|
if (ret.second) // inserted
|
||||||
{
|
{
|
||||||
|
@ -910,7 +883,7 @@ namespace client
|
||||||
auto s = shared_from_this ();
|
auto s = shared_from_this ();
|
||||||
msg->onDrop = [s, dest, request]()
|
msg->onDrop = [s, dest, request]()
|
||||||
{
|
{
|
||||||
boost::asio::post (s->GetService (), [s, dest, request]()
|
s->GetService ().post([s, dest, request]()
|
||||||
{
|
{
|
||||||
s->SendNextLeaseSetRequest (dest, request);
|
s->SendNextLeaseSetRequest (dest, request);
|
||||||
});
|
});
|
||||||
|
@ -923,7 +896,7 @@ namespace client
|
||||||
nextFloodfill->GetIdentHash (), 0, msg
|
nextFloodfill->GetIdentHash (), 0, msg
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
request->requestTimeoutTimer.expires_from_now (boost::posix_time::milliseconds(LEASESET_REQUEST_TIMEOUT));
|
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
|
||||||
request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
|
request->requestTimeoutTimer.async_wait (std::bind (&LeaseSetDestination::HandleRequestTimoutTimer,
|
||||||
shared_from_this (), std::placeholders::_1, dest));
|
shared_from_this (), std::placeholders::_1, dest));
|
||||||
}
|
}
|
||||||
|
@ -940,7 +913,7 @@ namespace client
|
||||||
if (it != m_LeaseSetRequests.end ())
|
if (it != m_LeaseSetRequests.end ())
|
||||||
{
|
{
|
||||||
bool done = false;
|
bool done = false;
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||||
{
|
{
|
||||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
||||||
|
@ -977,8 +950,7 @@ namespace client
|
||||||
CleanupExpiredTags ();
|
CleanupExpiredTags ();
|
||||||
CleanupRemoteLeaseSets ();
|
CleanupRemoteLeaseSets ();
|
||||||
CleanupDestination ();
|
CleanupDestination ();
|
||||||
m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT +
|
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||||
(m_Pool ? m_Pool->GetRng ()() % DESTINATION_CLEANUP_TIMEOUT_VARIANCE : 0)));
|
|
||||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||||
shared_from_this (), std::placeholders::_1));
|
shared_from_this (), std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
@ -992,7 +964,7 @@ namespace client
|
||||||
{
|
{
|
||||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
||||||
{
|
{
|
||||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
LogPrint (eLogWarning, "Destination: Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||||
it = m_RemoteLeaseSets.erase (it);
|
it = m_RemoteLeaseSets.erase (it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1000,13 +972,19 @@ namespace client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientDestination::ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
|
i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const
|
||||||
|
{
|
||||||
|
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||||
|
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
|
||||||
|
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
|
||||||
bool isPublic, const std::map<std::string, std::string> * params):
|
bool isPublic, const std::map<std::string, std::string> * params):
|
||||||
LeaseSetDestination (service, isPublic, params),
|
LeaseSetDestination (service, isPublic, params),
|
||||||
m_Keys (keys), m_PreferredCryptoType (0), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
|
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
|
||||||
m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED),
|
m_StreamingOutboundSpeed (DEFAULT_MAX_OUTBOUND_SPEED),
|
||||||
m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
|
m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
|
||||||
m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS),
|
|
||||||
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0),
|
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0),
|
||||||
m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0),
|
m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0),
|
||||||
m_ReadyChecker(service)
|
m_ReadyChecker(service)
|
||||||
|
@ -1028,15 +1006,7 @@ namespace client
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
i2p::data::CryptoKeyType cryptoType = std::stoi(it1);
|
encryptionKeyTypes.insert (std::stoi(it1));
|
||||||
#if !OPENSSL_PQ
|
|
||||||
if (cryptoType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (!m_PreferredCryptoType && cryptoType)
|
|
||||||
m_PreferredCryptoType = cryptoType; // first non-zero in the list
|
|
||||||
encryptionKeyTypes.insert (cryptoType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (std::exception& ex)
|
catch (std::exception& ex)
|
||||||
{
|
{
|
||||||
|
@ -1053,15 +1023,20 @@ namespace client
|
||||||
|
|
||||||
for (auto& it: encryptionKeyTypes)
|
for (auto& it: encryptionKeyTypes)
|
||||||
{
|
{
|
||||||
auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (it);
|
auto encryptionKey = new EncryptionKey (it);
|
||||||
if (IsPublic ())
|
if (IsPublic ())
|
||||||
PersistTemporaryKeys (encryptionKey);
|
PersistTemporaryKeys (encryptionKey);
|
||||||
else
|
else
|
||||||
encryptionKey->GenerateKeys ();
|
encryptionKey->GenerateKeys ();
|
||||||
encryptionKey->CreateDecryptor ();
|
encryptionKey->CreateDecryptor ();
|
||||||
if (it > i2p::data::CRYPTO_KEY_TYPE_ELGAMAL && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||||
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Only DSA can use LeaseSet1
|
{
|
||||||
m_EncryptionKeys.emplace (it, encryptionKey);
|
m_ECIESx25519EncryptionKey.reset (encryptionKey);
|
||||||
|
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||||
|
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_StandardEncryptionKey.reset (encryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPublic ())
|
if (IsPublic ())
|
||||||
|
@ -1081,8 +1056,6 @@ namespace client
|
||||||
it = params->find (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED);
|
it = params->find (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
m_StreamingInboundSpeed = std::stoi(it->second);
|
m_StreamingInboundSpeed = std::stoi(it->second);
|
||||||
if (it != params->end ())
|
|
||||||
m_StreamingMaxConcurrentStreams = std::stoi(it->second);
|
|
||||||
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true
|
m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true
|
||||||
|
@ -1218,7 +1191,7 @@ namespace client
|
||||||
if (leaseSet)
|
if (leaseSet)
|
||||||
{
|
{
|
||||||
auto stream = CreateStream (leaseSet, port);
|
auto stream = CreateStream (leaseSet, port);
|
||||||
boost::asio::post (GetService (), [streamRequestComplete, stream]()
|
GetService ().post ([streamRequestComplete, stream]()
|
||||||
{
|
{
|
||||||
streamRequestComplete(stream);
|
streamRequestComplete(stream);
|
||||||
});
|
});
|
||||||
|
@ -1411,56 +1384,31 @@ namespace client
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientDestination::PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys)
|
void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys)
|
||||||
{
|
{
|
||||||
if (!keys) return;
|
if (!keys) return;
|
||||||
std::string ident = GetIdentHash().ToBase32();
|
std::string ident = GetIdentHash().ToBase32();
|
||||||
std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat");
|
std::string path = i2p::fs::DataDirPath("destinations", ident + "." + std::to_string (keys->keyType) + ".dat");
|
||||||
std::ifstream f(path, std::ifstream::binary);
|
std::ifstream f(path, std::ifstream::binary);
|
||||||
if (f)
|
|
||||||
{
|
|
||||||
size_t len = 0;
|
|
||||||
if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
|
|
||||||
len = 512;
|
|
||||||
else if (keys->keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
|
||||||
{
|
|
||||||
f.seekg (0, std::ios::end);
|
|
||||||
len = f.tellg();
|
|
||||||
f.seekg (0, std::ios::beg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len == 512)
|
if (f) {
|
||||||
{
|
f.read ((char *)keys->pub, 256);
|
||||||
char pub[256], priv[256];
|
f.read ((char *)keys->priv, 256);
|
||||||
f.read (pub, 256);
|
|
||||||
memcpy (keys->pub.data(), pub, keys->pub.size());
|
|
||||||
f.read (priv, 256);
|
|
||||||
memcpy (keys->priv.data (), priv, keys->priv.size ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
f.read ((char *)keys->pub.data(), keys->pub.size());
|
|
||||||
f.read ((char *)keys->priv.data(), keys->priv.size());
|
|
||||||
}
|
|
||||||
if (f)
|
|
||||||
return;
|
return;
|
||||||
else
|
|
||||||
LogPrint(eLogWarning, "Destination: Can't read keys from ", path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type ", keys->keyType, " for address ", ident, ".b32.i2p");
|
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
|
||||||
memset (keys->priv.data (), 0, keys->priv.size ());
|
memset (keys->priv, 0, 256);
|
||||||
memset (keys->pub.data (), 0, keys->pub.size ());
|
memset (keys->pub, 0, 256);
|
||||||
keys->GenerateKeys ();
|
keys->GenerateKeys ();
|
||||||
|
// TODO:: persist crypto key type
|
||||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
||||||
if (f1)
|
if (f1) {
|
||||||
{
|
f1.write ((char *)keys->pub, 256);
|
||||||
f1.write ((char *)keys->pub.data (), keys->pub.size ());
|
f1.write ((char *)keys->priv, 256);
|
||||||
f1.write ((char *)keys->priv.data (), keys->priv.size ());
|
return;
|
||||||
}
|
}
|
||||||
if (!f1)
|
LogPrint(eLogCritical, "Destinations: Can't save keys to ", path);
|
||||||
LogPrint(eLogError, "Destination: Can't save keys to ", path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||||
|
@ -1468,10 +1416,9 @@ namespace client
|
||||||
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
|
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
|
||||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||||
{
|
{
|
||||||
auto it = m_EncryptionKeys.find (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
|
if (m_StandardEncryptionKey)
|
||||||
if (it != m_EncryptionKeys.end ())
|
|
||||||
{
|
{
|
||||||
leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), it->second->pub.data (), tunnels);
|
leaseSet = std::make_shared<i2p::data::LocalLeaseSet> (GetIdentity (), m_StandardEncryptionKey->pub, tunnels);
|
||||||
// sign
|
// sign
|
||||||
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ());
|
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ());
|
||||||
}
|
}
|
||||||
|
@ -1481,26 +1428,11 @@ namespace client
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// standard LS2 (type 3) first
|
// standard LS2 (type 3) first
|
||||||
if (m_EncryptionKeys.empty ())
|
i2p::data::LocalLeaseSet2::KeySections keySections;
|
||||||
{
|
if (m_ECIESx25519EncryptionKey)
|
||||||
LogPrint (eLogError, "Destinations: No encryption keys");
|
keySections.push_back ({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub} );
|
||||||
return;
|
if (m_StandardEncryptionKey)
|
||||||
}
|
keySections.push_back ({m_StandardEncryptionKey->keyType, (uint16_t)m_StandardEncryptionKey->decryptor->GetPublicKeyLen (), m_StandardEncryptionKey->pub} );
|
||||||
|
|
||||||
i2p::data::LocalLeaseSet2::EncryptionKeys keySections;
|
|
||||||
std::shared_ptr<const i2p::crypto::LocalEncryptionKey> preferredSection;
|
|
||||||
if (m_EncryptionKeys.size () == 1)
|
|
||||||
preferredSection = m_EncryptionKeys.begin ()->second; // only key
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const auto& it: m_EncryptionKeys)
|
|
||||||
if (it.first == m_PreferredCryptoType)
|
|
||||||
preferredSection = it.second;
|
|
||||||
else
|
|
||||||
keySections.push_back (it.second);
|
|
||||||
}
|
|
||||||
if (preferredSection)
|
|
||||||
keySections.push_front (preferredSection); // make preferred first
|
|
||||||
|
|
||||||
auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
auto publishedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||||
if (publishedTimestamp <= m_LastPublishedTimestamp)
|
if (publishedTimestamp <= m_LastPublishedTimestamp)
|
||||||
|
@ -1526,22 +1458,11 @@ namespace client
|
||||||
|
|
||||||
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
|
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<i2p::crypto::LocalEncryptionKey> encryptionKey;
|
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||||
if (!m_EncryptionKeys.empty ())
|
if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor)
|
||||||
{
|
return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data);
|
||||||
if (m_EncryptionKeys.rbegin ()->first == preferredCrypto)
|
if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor)
|
||||||
encryptionKey = m_EncryptionKeys.rbegin ()->second;
|
return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data);
|
||||||
else
|
|
||||||
{
|
|
||||||
auto it = m_EncryptionKeys.find (preferredCrypto);
|
|
||||||
if (it != m_EncryptionKeys.end ())
|
|
||||||
encryptionKey = it->second;
|
|
||||||
}
|
|
||||||
if (!encryptionKey)
|
|
||||||
encryptionKey = m_EncryptionKeys.rbegin ()->second;
|
|
||||||
}
|
|
||||||
if (encryptionKey)
|
|
||||||
return encryptionKey->decryptor->Decrypt (encrypted, data);
|
|
||||||
else
|
else
|
||||||
LogPrint (eLogError, "Destinations: Decryptor is not set");
|
LogPrint (eLogError, "Destinations: Decryptor is not set");
|
||||||
return false;
|
return false;
|
||||||
|
@ -1549,26 +1470,14 @@ namespace client
|
||||||
|
|
||||||
bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
|
bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
|
||||||
{
|
{
|
||||||
#if __cplusplus >= 202002L // C++20
|
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey;
|
||||||
return m_EncryptionKeys.contains (keyType);
|
|
||||||
#else
|
|
||||||
return m_EncryptionKeys.count (keyType) > 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
i2p::data::CryptoKeyType ClientDestination::GetRatchetsHighestCryptoType () const
|
|
||||||
{
|
|
||||||
if (m_EncryptionKeys.empty ()) return 0;
|
|
||||||
auto cryptoType = m_EncryptionKeys.rbegin ()->first;
|
|
||||||
return cryptoType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? cryptoType : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
|
const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
|
||||||
{
|
{
|
||||||
auto it = m_EncryptionKeys.find (keyType);
|
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||||
if (it != m_EncryptionKeys.end ())
|
return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr;
|
||||||
return it->second->pub.data ();
|
return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
|
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
|
||||||
|
@ -1602,8 +1511,6 @@ namespace client
|
||||||
RunnableService ("Destination"),
|
RunnableService ("Destination"),
|
||||||
ClientDestination (GetIOService (), keys, isPublic, params)
|
ClientDestination (GetIOService (), keys, isPublic, params)
|
||||||
{
|
{
|
||||||
if (!GetNickname ().empty ())
|
|
||||||
RunnableService::SetName (GetNickname ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RunnableClientDestination::~RunnableClientDestination ()
|
RunnableClientDestination::~RunnableClientDestination ()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -22,7 +22,6 @@
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
#include "TunnelPool.h"
|
#include "TunnelPool.h"
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "LeaseSet.h"
|
#include "LeaseSet.h"
|
||||||
#include "Garlic.h"
|
#include "Garlic.h"
|
||||||
#include "NetDb.hpp"
|
#include "NetDb.hpp"
|
||||||
|
@ -37,15 +36,13 @@ namespace client
|
||||||
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
|
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
|
||||||
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
|
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
|
||||||
const uint8_t PROTOCOL_TYPE_RAW = 18;
|
const uint8_t PROTOCOL_TYPE_RAW = 18;
|
||||||
const int PUBLISH_CONFIRMATION_TIMEOUT = 1800; // in milliseconds
|
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
|
||||||
const int PUBLISH_VERIFICATION_TIMEOUT = 5; // in seconds after successful publish
|
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish
|
||||||
const int PUBLISH_VERIFICATION_TIMEOUT_VARIANCE = 3; // in seconds
|
|
||||||
const int PUBLISH_MIN_INTERVAL = 20; // in seconds
|
const int PUBLISH_MIN_INTERVAL = 20; // in seconds
|
||||||
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
|
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
|
||||||
const int LEASESET_REQUEST_TIMEOUT = 1600; // in milliseconds
|
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
|
||||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 12000; // in milliseconds
|
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
|
||||||
const int DESTINATION_CLEANUP_TIMEOUT = 44; // in seconds
|
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
|
||||||
const int DESTINATION_CLEANUP_TIMEOUT_VARIANCE = 30; // in seconds
|
|
||||||
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
||||||
|
|
||||||
// I2CP
|
// I2CP
|
||||||
|
@ -97,8 +94,6 @@ namespace client
|
||||||
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
|
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
|
||||||
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
|
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
|
||||||
const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK;
|
const int DEFAULT_STREAMING_PROFILE = STREAMING_PROFILE_BULK;
|
||||||
const char I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS[] = "i2p.streaming.maxConcurrentStreams";
|
|
||||||
const int DEFAULT_MAX_CONCURRENT_STREAMS = 2048;
|
|
||||||
|
|
||||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||||
|
|
||||||
|
@ -109,7 +104,7 @@ namespace client
|
||||||
// leaseSet = nullptr means not found
|
// leaseSet = nullptr means not found
|
||||||
struct LeaseSetRequest
|
struct LeaseSetRequest
|
||||||
{
|
{
|
||||||
LeaseSetRequest (boost::asio::io_context& service): requestTime (0), requestTimeoutTimer (service) {};
|
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
|
||||||
std::unordered_set<i2p::data::IdentHash> excluded;
|
std::unordered_set<i2p::data::IdentHash> excluded;
|
||||||
uint64_t requestTime;
|
uint64_t requestTime;
|
||||||
boost::asio::deadline_timer requestTimeoutTimer;
|
boost::asio::deadline_timer requestTimeoutTimer;
|
||||||
|
@ -127,10 +122,10 @@ namespace client
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
LeaseSetDestination (boost::asio::io_context& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||||
~LeaseSetDestination ();
|
~LeaseSetDestination ();
|
||||||
const std::string& GetNickname () const { return m_Nickname; };
|
const std::string& GetNickname () const { return m_Nickname; };
|
||||||
auto& GetService () { return m_Service; };
|
boost::asio::io_service& GetService () { return m_Service; };
|
||||||
|
|
||||||
virtual void Start ();
|
virtual void Start ();
|
||||||
virtual void Stop ();
|
virtual void Stop ();
|
||||||
|
@ -147,15 +142,15 @@ namespace client
|
||||||
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
|
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
|
||||||
|
|
||||||
// implements GarlicDestination
|
// implements GarlicDestination
|
||||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override;
|
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override { return m_Pool; }
|
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
||||||
|
|
||||||
// override GarlicDestination
|
// override GarlicDestination
|
||||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag) override;
|
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
|
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
|
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
|
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void SetLeaseSetUpdated (bool post) override;
|
void SetLeaseSetUpdated ();
|
||||||
|
|
||||||
bool IsPublic () const { return m_IsPublic; };
|
bool IsPublic () const { return m_IsPublic; };
|
||||||
void SetPublic (bool pub) { m_IsPublic = pub; };
|
void SetPublic (bool pub) { m_IsPublic = pub; };
|
||||||
|
@ -163,16 +158,14 @@ namespace client
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// implements GarlicDestination
|
// implements GarlicDestination
|
||||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
|
||||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
|
||||||
|
|
||||||
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
|
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
|
||||||
int GetLeaseSetType () const { return m_LeaseSetType; };
|
int GetLeaseSetType () const { return m_LeaseSetType; };
|
||||||
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
|
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
|
||||||
int GetAuthType () const { return m_AuthType; };
|
int GetAuthType () const { return m_AuthType; };
|
||||||
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
||||||
virtual i2p::data::CryptoKeyType GetPreferredCryptoType () const = 0;
|
|
||||||
// I2CP
|
// I2CP
|
||||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||||
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
|
virtual void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) = 0;
|
||||||
|
@ -185,7 +178,7 @@ namespace client
|
||||||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
||||||
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
|
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
|
||||||
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
|
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
|
||||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
||||||
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
||||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||||
|
|
||||||
|
@ -195,17 +188,15 @@ namespace client
|
||||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||||
void CleanupRemoteLeaseSets ();
|
void CleanupRemoteLeaseSets ();
|
||||||
|
i2p::data::CryptoKeyType GetPreferredCryptoType () const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
boost::asio::io_context& m_Service;
|
boost::asio::io_service& m_Service;
|
||||||
mutable std::mutex m_RemoteLeaseSetsMutex;
|
mutable std::mutex m_RemoteLeaseSetsMutex;
|
||||||
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||||
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
||||||
|
|
||||||
std::list<std::shared_ptr<I2NPMessage> > m_IncomingMsgsQueue;
|
|
||||||
mutable std::mutex m_IncomingMsgsQueueMutex;
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
||||||
std::mutex m_LeaseSetMutex;
|
std::mutex m_LeaseSetMutex;
|
||||||
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
|
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||||
|
@ -231,14 +222,25 @@ namespace client
|
||||||
|
|
||||||
class ClientDestination: public LeaseSetDestination
|
class ClientDestination: public LeaseSetDestination
|
||||||
{
|
{
|
||||||
|
struct EncryptionKey
|
||||||
|
{
|
||||||
|
uint8_t pub[256], priv[256];
|
||||||
|
i2p::data::CryptoKeyType keyType;
|
||||||
|
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> decryptor;
|
||||||
|
|
||||||
|
EncryptionKey (i2p::data::CryptoKeyType t):keyType(t) { memset (pub, 0, 256); memset (priv, 0, 256); };
|
||||||
|
void GenerateKeys () { i2p::data::PrivateKeys::GenerateCryptoKeyPair (keyType, priv, pub); };
|
||||||
|
void CreateDecryptor () { decryptor = i2p::data::PrivateKeys::CreateDecryptor (keyType, priv); };
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ClientDestination (boost::asio::io_context& service, const i2p::data::PrivateKeys& keys,
|
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
|
||||||
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||||
~ClientDestination ();
|
~ClientDestination ();
|
||||||
|
|
||||||
void Start () override;
|
void Start ();
|
||||||
void Stop () override;
|
void Stop ();
|
||||||
|
|
||||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||||
|
@ -267,7 +269,6 @@ namespace client
|
||||||
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
|
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
|
||||||
int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; }
|
int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; }
|
||||||
int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; }
|
int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; }
|
||||||
int GetStreamingMaxConcurrentStreams () const { return m_StreamingMaxConcurrentStreams; }
|
|
||||||
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
|
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
|
||||||
|
|
||||||
// datagram
|
// datagram
|
||||||
|
@ -275,28 +276,24 @@ namespace client
|
||||||
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
|
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
|
||||||
|
|
||||||
// implements LocalDestination
|
// implements LocalDestination
|
||||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override { return m_Keys.GetPublic (); };
|
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override;
|
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
|
||||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const override;
|
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// GarlicDestionation
|
void CleanupDestination ();
|
||||||
i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override;
|
|
||||||
// LeaseSetDestination
|
|
||||||
void CleanupDestination () override;
|
|
||||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const override { return m_PreferredCryptoType; }
|
|
||||||
// I2CP
|
// I2CP
|
||||||
void HandleDataMessage (const uint8_t * buf, size_t len) override;
|
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) override;
|
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> GetSharedFromThis () {
|
std::shared_ptr<ClientDestination> GetSharedFromThis () {
|
||||||
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
|
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
|
||||||
}
|
}
|
||||||
void PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys);
|
void PersistTemporaryKeys (EncryptionKey * keys);
|
||||||
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
|
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
|
||||||
|
|
||||||
template<typename Dest>
|
template<typename Dest>
|
||||||
|
@ -305,10 +302,12 @@ namespace client
|
||||||
private:
|
private:
|
||||||
|
|
||||||
i2p::data::PrivateKeys m_Keys;
|
i2p::data::PrivateKeys m_Keys;
|
||||||
std::map<i2p::data::CryptoKeyType, std::shared_ptr<i2p::crypto::LocalEncryptionKey> > m_EncryptionKeys; // last is most preferable
|
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
|
||||||
i2p::data::CryptoKeyType m_PreferredCryptoType;
|
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
|
||||||
|
|
||||||
int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams;
|
int m_StreamingAckDelay;
|
||||||
|
int m_StreamingOutboundSpeed;
|
||||||
|
int m_StreamingInboundSpeed;
|
||||||
bool m_IsStreamingAnswerPings;
|
bool m_IsStreamingAnswerPings;
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,6 @@
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "PostQuantum.h"
|
|
||||||
#include "Elligator.h"
|
#include "Elligator.h"
|
||||||
#include "Tag.h"
|
#include "Tag.h"
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
|
@ -95,17 +94,6 @@ namespace garlic
|
||||||
m_ItermediateSymmKeys.erase (index);
|
m_ItermediateSymmKeys.erase (index);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReceiveRatchetTagSet::ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS):
|
|
||||||
m_Session (session), m_IsNS (isNS)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ReceiveRatchetTagSet::~ReceiveRatchetTagSet ()
|
|
||||||
{
|
|
||||||
if (m_IsNS && m_Session)
|
|
||||||
m_Session->CleanupReceiveNSRKeys ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReceiveRatchetTagSet::Expire ()
|
void ReceiveRatchetTagSet::Expire ()
|
||||||
{
|
{
|
||||||
if (!m_ExpirationTimestamp)
|
if (!m_ExpirationTimestamp)
|
||||||
|
@ -174,12 +162,12 @@ namespace garlic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (m_Destination)
|
if (m_Destination)
|
||||||
m_Destination->HandleECIESx25519GarlicClove (buf + offset, size, nullptr);
|
m_Destination->HandleECIESx25519GarlicClove (buf + offset, size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS):
|
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS):
|
||||||
GarlicRoutingSession (owner, true), m_RemoteStaticKeyType (0)
|
GarlicRoutingSession (owner, true)
|
||||||
{
|
{
|
||||||
if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
|
if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
|
||||||
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
|
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
|
||||||
|
@ -264,19 +252,12 @@ namespace garlic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ECIESX25519AEADRatchetSession::CleanupReceiveNSRKeys ()
|
|
||||||
{
|
|
||||||
m_EphemeralKeys = nullptr;
|
|
||||||
#if OPENSSL_PQ
|
|
||||||
m_PQKeys = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
|
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
if (!GetOwner ()) return false;
|
if (!GetOwner ()) return false;
|
||||||
// we are Bob
|
// we are Bob
|
||||||
// KDF1
|
// KDF1
|
||||||
|
i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
||||||
|
|
||||||
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
|
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
|
||||||
{
|
{
|
||||||
|
@ -284,61 +265,20 @@ namespace garlic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
buf += 32; len -= 32;
|
buf += 32; len -= 32;
|
||||||
|
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
||||||
|
|
||||||
uint8_t sharedSecret[32];
|
uint8_t sharedSecret[32];
|
||||||
bool decrypted = false;
|
|
||||||
auto cryptoType = GetOwner ()->GetRatchetsHighestCryptoType ();
|
|
||||||
#if OPENSSL_PQ
|
|
||||||
if (cryptoType > i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // we support post quantum
|
|
||||||
{
|
|
||||||
i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), cryptoType, GetOwner ()->GetEncryptionPublicKey (cryptoType)); // bpk
|
|
||||||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
|
||||||
|
|
||||||
if (GetOwner ()->Decrypt (m_Aepk, sharedSecret, cryptoType)) // x25519(bsk, aepk)
|
|
||||||
{
|
|
||||||
MixKey (sharedSecret);
|
|
||||||
|
|
||||||
auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (cryptoType);
|
|
||||||
std::vector<uint8_t> encapsKey(keyLen);
|
|
||||||
if (Decrypt (buf, encapsKey.data (), keyLen))
|
|
||||||
{
|
|
||||||
decrypted = true; // encaps section has right hash
|
|
||||||
MixHash (buf, keyLen + 16);
|
|
||||||
buf += keyLen + 16;
|
|
||||||
len -= keyLen + 16;
|
|
||||||
|
|
||||||
m_PQKeys = i2p::crypto::CreateMLKEMKeys (cryptoType);
|
|
||||||
m_PQKeys->SetPublicKey (encapsKey.data ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!decrypted)
|
|
||||||
{
|
|
||||||
if (cryptoType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
|
||||||
GetOwner ()->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
|
||||||
{
|
|
||||||
cryptoType = i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
|
|
||||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
|
||||||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
|
||||||
|
|
||||||
if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
|
if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
|
LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: No supported encryption type");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt flags/static
|
// decrypt flags/static
|
||||||
uint8_t fs[32];
|
uint8_t nonce[12], fs[32];
|
||||||
if (!Decrypt (buf, fs, 32))
|
CreateNonce (0, nonce);
|
||||||
|
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
|
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
|
||||||
return false;
|
return false;
|
||||||
|
@ -351,18 +291,20 @@ namespace garlic
|
||||||
if (isStatic)
|
if (isStatic)
|
||||||
{
|
{
|
||||||
// static key, fs is apk
|
// static key, fs is apk
|
||||||
SetRemoteStaticKey (cryptoType, fs);
|
memcpy (m_RemoteStaticKey, fs, 32);
|
||||||
if (!GetOwner ()->Decrypt (fs, sharedSecret, m_RemoteStaticKeyType)) // x25519(bsk, apk)
|
if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
}
|
}
|
||||||
|
else // all zeros flags
|
||||||
|
CreateNonce (1, nonce);
|
||||||
|
|
||||||
// decrypt payload
|
// decrypt payload
|
||||||
std::vector<uint8_t> payload (len - 16); // we must save original ciphertext
|
std::vector<uint8_t> payload (len - 16); // we must save original ciphertext
|
||||||
if (!Decrypt (buf, payload.data (), len - 16))
|
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
|
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
|
||||||
return false;
|
return false;
|
||||||
|
@ -398,7 +340,7 @@ namespace garlic
|
||||||
{
|
{
|
||||||
case eECIESx25519BlkGalicClove:
|
case eECIESx25519BlkGalicClove:
|
||||||
if (GetOwner ())
|
if (GetOwner ())
|
||||||
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size, this);
|
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size);
|
||||||
break;
|
break;
|
||||||
case eECIESx25519BlkNextKey:
|
case eECIESx25519BlkNextKey:
|
||||||
LogPrint (eLogDebug, "Garlic: Next key");
|
LogPrint (eLogDebug, "Garlic: Next key");
|
||||||
|
@ -550,15 +492,6 @@ namespace garlic
|
||||||
offset += 32;
|
offset += 32;
|
||||||
|
|
||||||
// KDF1
|
// KDF1
|
||||||
#if OPENSSL_PQ
|
|
||||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
|
||||||
{
|
|
||||||
i2p::crypto::InitNoiseIKStateMLKEM (GetNoiseState (), m_RemoteStaticKeyType, m_RemoteStaticKey); // bpk
|
|
||||||
m_PQKeys = i2p::crypto::CreateMLKEMKeys (m_RemoteStaticKeyType);
|
|
||||||
m_PQKeys->GenerateKeys ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
|
i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk
|
||||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
|
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
|
||||||
uint8_t sharedSecret[32];
|
uint8_t sharedSecret[32];
|
||||||
|
@ -568,32 +501,18 @@ namespace garlic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
#if OPENSSL_PQ
|
|
||||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
|
||||||
{
|
|
||||||
auto keyLen = i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType);
|
|
||||||
std::vector<uint8_t> encapsKey(keyLen);
|
|
||||||
m_PQKeys->GetPublicKey (encapsKey.data ());
|
|
||||||
// encrypt encapsKey
|
|
||||||
if (!Encrypt (encapsKey.data (), out + offset, keyLen))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: ML-KEM encap_key section AEAD encryption failed ");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MixHash (out + offset, keyLen + 16); // h = SHA256(h || ciphertext)
|
|
||||||
offset += keyLen + 16;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// encrypt flags/static key section
|
// encrypt flags/static key section
|
||||||
|
uint8_t nonce[12];
|
||||||
|
CreateNonce (0, nonce);
|
||||||
const uint8_t * fs;
|
const uint8_t * fs;
|
||||||
if (isStatic)
|
if (isStatic)
|
||||||
fs = GetOwner ()->GetEncryptionPublicKey (m_RemoteStaticKeyType);
|
fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memset (out + offset, 0, 32); // all zeros flags section
|
memset (out + offset, 0, 32); // all zeros flags section
|
||||||
fs = out + offset;
|
fs = out + offset;
|
||||||
}
|
}
|
||||||
if (!Encrypt (fs, out + offset, 32))
|
if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed ");
|
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed ");
|
||||||
return false;
|
return false;
|
||||||
|
@ -604,11 +523,13 @@ namespace garlic
|
||||||
// KDF2
|
// KDF2
|
||||||
if (isStatic)
|
if (isStatic)
|
||||||
{
|
{
|
||||||
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bpk)
|
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk)
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
CreateNonce (1, nonce);
|
||||||
// encrypt payload
|
// encrypt payload
|
||||||
if (!Encrypt (payload, out + offset, len))
|
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||||
return false;
|
return false;
|
||||||
|
@ -657,33 +578,16 @@ namespace garlic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
#if OPENSSL_PQ
|
|
||||||
if (m_PQKeys)
|
|
||||||
{
|
|
||||||
size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
|
|
||||||
std::vector<uint8_t> kemCiphertext(cipherTextLen);
|
|
||||||
m_PQKeys->Encaps (kemCiphertext.data (), sharedSecret);
|
|
||||||
|
|
||||||
if (!Encrypt (kemCiphertext.data (), out + offset, cipherTextLen))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: NSR ML-KEM ciphertext section AEAD encryption failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_NSREncodedPQKey = std::make_unique<std::vector<uint8_t> > (cipherTextLen + 16);
|
|
||||||
memcpy (m_NSREncodedPQKey->data (), out + offset, cipherTextLen + 16);
|
|
||||||
MixHash (out + offset, cipherTextLen + 16);
|
|
||||||
MixKey (sharedSecret);
|
|
||||||
offset += cipherTextLen + 16;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk)
|
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
|
uint8_t nonce[12];
|
||||||
|
CreateNonce (0, nonce);
|
||||||
// calculate hash for zero length
|
// calculate hash for zero length
|
||||||
if (!Encrypt (sharedSecret /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
||||||
return false;
|
return false;
|
||||||
|
@ -704,7 +608,6 @@ namespace garlic
|
||||||
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS);
|
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MIN_NUM_GENERATED_TAGS);
|
||||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
||||||
// encrypt payload
|
// encrypt payload
|
||||||
uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0
|
|
||||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt
|
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed");
|
LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed");
|
||||||
|
@ -726,34 +629,16 @@ namespace garlic
|
||||||
memcpy (m_H, m_NSRH, 32);
|
memcpy (m_H, m_NSRH, 32);
|
||||||
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
||||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
|
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
|
||||||
m_N = 0;
|
uint8_t nonce[12];
|
||||||
size_t offset = 40;
|
CreateNonce (0, nonce);
|
||||||
#if OPENSSL_PQ
|
if (!i2p::crypto::AEADChaCha20Poly1305 (nonce /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + 40, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||||
if (m_PQKeys)
|
|
||||||
{
|
|
||||||
if (m_NSREncodedPQKey)
|
|
||||||
{
|
|
||||||
size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
|
|
||||||
memcpy (out + offset, m_NSREncodedPQKey->data (), cipherTextLen + 16);
|
|
||||||
MixHash (out + offset, cipherTextLen + 16);
|
|
||||||
offset += cipherTextLen + 16;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: No stored ML-KEM keys");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!Encrypt (m_NSRH /* can be anything */, out + offset, 0)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
|
MixHash (out + 40, 16); // h = SHA256(h || ciphertext)
|
||||||
// encrypt payload
|
// encrypt payload
|
||||||
uint8_t nonce[12]; memset (nonce, 0, 12);
|
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt
|
||||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset + 16, len + 16, true)) // encrypt
|
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed");
|
LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed");
|
||||||
return false;
|
return false;
|
||||||
|
@ -785,30 +670,13 @@ namespace garlic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
#if OPENSSL_PQ
|
GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
|
||||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
|
||||||
{
|
|
||||||
// decrypt kem_ciphertext section
|
|
||||||
size_t cipherTextLen = i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType);
|
|
||||||
std::vector<uint8_t> kemCiphertext(cipherTextLen);
|
|
||||||
if (!Decrypt (buf, kemCiphertext.data (), cipherTextLen))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Garlic: Reply ML-KEM ciphertext section AEAD decryption failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MixHash (buf, cipherTextLen + 16);
|
|
||||||
buf += cipherTextLen + 16;
|
|
||||||
len -= cipherTextLen + 16;
|
|
||||||
// decaps
|
|
||||||
m_PQKeys->Decaps (kemCiphertext.data (), sharedSecret);
|
|
||||||
MixKey (sharedSecret);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
GetOwner ()->Decrypt (bepk, sharedSecret, m_RemoteStaticKeyType); // x25519 (ask, bepk)
|
|
||||||
MixKey (sharedSecret);
|
MixKey (sharedSecret);
|
||||||
|
|
||||||
|
uint8_t nonce[12];
|
||||||
|
CreateNonce (0, nonce);
|
||||||
// calculate hash for zero length
|
// calculate hash for zero length
|
||||||
if (!Decrypt (buf, sharedSecret/* can be anything */, 0)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
|
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anything */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed");
|
LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed");
|
||||||
return false;
|
return false;
|
||||||
|
@ -833,7 +701,6 @@ namespace garlic
|
||||||
}
|
}
|
||||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
||||||
// decrypt payload
|
// decrypt payload
|
||||||
uint8_t nonce[12]; memset (nonce, 0, 12); // seqn = 0
|
|
||||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt
|
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, buf, len - 16, false)) // decrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
||||||
|
@ -843,8 +710,7 @@ namespace garlic
|
||||||
if (m_State == eSessionStateNewSessionSent)
|
if (m_State == eSessionStateNewSessionSent)
|
||||||
{
|
{
|
||||||
m_State = eSessionStateEstablished;
|
m_State = eSessionStateEstablished;
|
||||||
// don't delete m_EpehemralKey and m_PQKeys because delayd NSR's migth come
|
//m_EphemeralKeys = nullptr; // TODO: delete after a while
|
||||||
// done in CleanupReceiveNSRKeys called from NSR tagset destructor
|
|
||||||
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||||
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
||||||
}
|
}
|
||||||
|
@ -859,8 +725,6 @@ namespace garlic
|
||||||
|
|
||||||
bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
||||||
{
|
{
|
||||||
auto owner = GetOwner ();
|
|
||||||
if (!owner) return false;
|
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
auto index = m_SendTagset->GetNextIndex ();
|
auto index = m_SendTagset->GetNextIndex ();
|
||||||
CreateNonce (index, nonce); // tag's index
|
CreateNonce (index, nonce); // tag's index
|
||||||
|
@ -868,7 +732,8 @@ namespace garlic
|
||||||
if (!tag)
|
if (!tag)
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset");
|
LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset");
|
||||||
owner->RemoveECIESx25519Session (m_RemoteStaticKey);
|
if (GetOwner ())
|
||||||
|
GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy (out, &tag, 8);
|
memcpy (out, &tag, 8);
|
||||||
|
@ -876,7 +741,7 @@ namespace garlic
|
||||||
// ciphertext = ENCRYPT(k, n, payload, ad)
|
// ciphertext = ENCRYPT(k, n, payload, ad)
|
||||||
uint8_t key[32];
|
uint8_t key[32];
|
||||||
m_SendTagset->GetSymmKey (index, key);
|
m_SendTagset->GetSymmKey (index, key);
|
||||||
if (!owner->AEADChaCha20Poly1305Encrypt (payload, len, out, 8, key, nonce, out + 8, outLen - 8))
|
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||||
return false;
|
return false;
|
||||||
|
@ -895,22 +760,20 @@ namespace garlic
|
||||||
uint8_t * payload = buf + 8;
|
uint8_t * payload = buf + 8;
|
||||||
uint8_t key[32];
|
uint8_t key[32];
|
||||||
receiveTagset->GetSymmKey (index, key);
|
receiveTagset->GetSymmKey (index, key);
|
||||||
auto owner = GetOwner ();
|
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 16, buf, 8, key, nonce, payload, len - 16, false)) // decrypt
|
||||||
if (!owner) return true; // drop message
|
|
||||||
|
|
||||||
if (!owner->AEADChaCha20Poly1305Decrypt (payload, len - 16, buf, 8, key, nonce, payload, len - 16))
|
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
HandlePayload (payload, len - 16, receiveTagset, index);
|
HandlePayload (payload, len - 16, receiveTagset, index);
|
||||||
|
if (GetOwner ())
|
||||||
int moreTags = 0;
|
|
||||||
if (owner->GetNumRatchetInboundTags () > 0) // override in settings?
|
|
||||||
{
|
{
|
||||||
if (receiveTagset->GetNextIndex () - index < owner->GetNumRatchetInboundTags ()/2)
|
int moreTags = 0;
|
||||||
moreTags = owner->GetNumRatchetInboundTags ();
|
if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings?
|
||||||
index -= owner->GetNumRatchetInboundTags (); // trim behind
|
{
|
||||||
|
if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2)
|
||||||
|
moreTags = GetOwner ()->GetNumRatchetInboundTags ();
|
||||||
|
index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -924,6 +787,7 @@ namespace garlic
|
||||||
GenerateMoreReceiveTags (receiveTagset, moreTags);
|
GenerateMoreReceiveTags (receiveTagset, moreTags);
|
||||||
if (index > 0)
|
if (index > 0)
|
||||||
receiveTagset->SetTrimBehind (index);
|
receiveTagset->SetTrimBehind (index);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -937,10 +801,6 @@ namespace garlic
|
||||||
m_State = eSessionStateEstablished;
|
m_State = eSessionStateEstablished;
|
||||||
m_NSRSendTagset = nullptr;
|
m_NSRSendTagset = nullptr;
|
||||||
m_EphemeralKeys = nullptr;
|
m_EphemeralKeys = nullptr;
|
||||||
#if OPENSSL_PQ
|
|
||||||
m_PQKeys = nullptr;
|
|
||||||
m_NSREncodedPQKey = nullptr;
|
|
||||||
#endif
|
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case eSessionStateEstablished:
|
case eSessionStateEstablished:
|
||||||
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
|
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
|
||||||
|
@ -971,12 +831,7 @@ namespace garlic
|
||||||
if (!payload) return nullptr;
|
if (!payload) return nullptr;
|
||||||
size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
|
size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
|
||||||
if (!len) return nullptr;
|
if (!len) return nullptr;
|
||||||
#if OPENSSL_PQ
|
|
||||||
auto m = NewI2NPMessage (len + (m_State == eSessionStateEstablished ? 28 :
|
|
||||||
i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 116));
|
|
||||||
#else
|
|
||||||
auto m = NewI2NPMessage (len + 100); // 96 + 4
|
auto m = NewI2NPMessage (len + 100); // 96 + 4
|
||||||
#endif
|
|
||||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
||||||
|
|
||||||
|
@ -991,28 +846,16 @@ namespace garlic
|
||||||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
|
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
len += 96;
|
len += 96;
|
||||||
#if OPENSSL_PQ
|
|
||||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
|
||||||
len += i2p::crypto::GetMLKEMPublicKeyLen (m_RemoteStaticKeyType) + 16;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case eSessionStateNewSessionReceived:
|
case eSessionStateNewSessionReceived:
|
||||||
if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
|
if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
len += 72;
|
len += 72;
|
||||||
#if OPENSSL_PQ
|
|
||||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
|
||||||
len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case eSessionStateNewSessionReplySent:
|
case eSessionStateNewSessionReplySent:
|
||||||
if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
|
if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
len += 72;
|
len += 72;
|
||||||
#if OPENSSL_PQ
|
|
||||||
if (m_RemoteStaticKeyType >= i2p::data::CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD)
|
|
||||||
len += i2p::crypto::GetMLKEMCipherTextLen (m_RemoteStaticKeyType) + 16;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case eSessionStateOneTime:
|
case eSessionStateOneTime:
|
||||||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
|
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
|
||||||
|
@ -1068,7 +911,7 @@ namespace garlic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sendAckRequest && !first &&
|
if (!sendAckRequest && !first &&
|
||||||
((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + m_AckRequestInterval) || // regular request
|
((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + ECIESX25519_ACK_REQUEST_INTERVAL) || // regular request
|
||||||
(m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again
|
(m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again
|
||||||
{
|
{
|
||||||
// not LeaseSet
|
// not LeaseSet
|
||||||
|
@ -1269,8 +1112,6 @@ namespace garlic
|
||||||
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
|
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
|
||||||
{
|
{
|
||||||
CleanupUnconfirmedLeaseSet (ts);
|
CleanupUnconfirmedLeaseSet (ts);
|
||||||
if (!m_Destination && ts > m_LastActivityTimestamp + ECIESX25519_SESSION_CREATE_TIMEOUT) return true; // m_LastActivityTimestamp is NS receive time
|
|
||||||
if (m_State != eSessionStateEstablished && m_SessionCreatedTimestamp && ts > m_SessionCreatedTimestamp + ECIESX25519_SESSION_ESTABLISH_TIMEOUT) return true;
|
|
||||||
return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds
|
return ts > m_LastActivityTimestamp + ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT && // seconds
|
||||||
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
|
ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -14,12 +14,10 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "PostQuantum.h"
|
|
||||||
#include "Garlic.h"
|
#include "Garlic.h"
|
||||||
#include "Tag.h"
|
#include "Tag.h"
|
||||||
|
|
||||||
|
@ -32,10 +30,8 @@ namespace garlic
|
||||||
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
|
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
|
||||||
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
|
const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds
|
||||||
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
|
const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds
|
||||||
const int ECIESX25519_SESSION_CREATE_TIMEOUT = 3; // in seconds, NSR must be send after NS received
|
|
||||||
const int ECIESX25519_SESSION_ESTABLISH_TIMEOUT = 15; // in seconds
|
|
||||||
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // in seconds
|
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // in seconds
|
||||||
const int ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL = 33000; // in milliseconds
|
const int ECIESX25519_ACK_REQUEST_INTERVAL = 33000; // in milliseconds
|
||||||
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
|
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
|
||||||
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
|
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
|
||||||
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
|
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
|
||||||
|
@ -81,8 +77,8 @@ namespace garlic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false);
|
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false):
|
||||||
~ReceiveRatchetTagSet () override;
|
m_Session (session), m_IsNS (isNS) {};
|
||||||
|
|
||||||
bool IsNS () const { return m_IsNS; };
|
bool IsNS () const { return m_IsNS; };
|
||||||
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
|
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
|
||||||
|
@ -166,32 +162,27 @@ namespace garlic
|
||||||
~ECIESX25519AEADRatchetSession ();
|
~ECIESX25519AEADRatchetSession ();
|
||||||
|
|
||||||
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
|
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<ReceiveRatchetTagSet> receiveTagset, int index = 0);
|
||||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) override;
|
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||||
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
|
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||||
|
|
||||||
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
|
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
|
||||||
i2p::data::CryptoKeyType GetRemoteStaticKeyType () const { return m_RemoteStaticKeyType; }
|
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
|
||||||
void SetRemoteStaticKey (i2p::data::CryptoKeyType keyType, const uint8_t * key)
|
|
||||||
{
|
|
||||||
m_RemoteStaticKeyType = keyType;
|
|
||||||
memcpy (m_RemoteStaticKey, key, 32);
|
|
||||||
}
|
|
||||||
void Terminate () { m_IsTerminated = true; }
|
void Terminate () { m_IsTerminated = true; }
|
||||||
void SetDestination (const i2p::data::IdentHash& dest)
|
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
|
||||||
{
|
{
|
||||||
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
|
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckExpired (uint64_t ts); // true is expired
|
bool CheckExpired (uint64_t ts); // true is expired
|
||||||
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; }
|
bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; }
|
||||||
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
|
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
|
||||||
void CleanupReceiveNSRKeys (); // called from ReceiveRatchetTagSet at Alice's side
|
|
||||||
|
|
||||||
bool IsRatchets () const override { return true; };
|
bool IsRatchets () const { return true; };
|
||||||
bool IsReadyToSend () const override { return m_State != eSessionStateNewSessionSent; };
|
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; };
|
||||||
bool IsTerminated () const override { return m_IsTerminated; }
|
bool IsTerminated () const { return m_IsTerminated; }
|
||||||
uint64_t GetLastActivityTimestamp () const override { return m_LastActivityTimestamp; };
|
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
|
||||||
void SetAckRequestInterval (int interval) override { m_AckRequestInterval = interval; };
|
bool CleanupUnconfirmedTags (); // return true if unaswered Ack requests, called from I2CP
|
||||||
bool CleanupUnconfirmedTags () override; // return true if unaswered Ack requests, called from I2CP
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -199,7 +190,7 @@ namespace garlic
|
||||||
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
|
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
|
||||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||||
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
|
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<ReceiveRatchetTagSet>& receiveTagset, int index);
|
||||||
bool MessageConfirmed (uint32_t msgID) override;
|
bool MessageConfirmed (uint32_t msgID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -225,20 +216,15 @@ namespace garlic
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
i2p::data::CryptoKeyType m_RemoteStaticKeyType;
|
|
||||||
uint8_t m_RemoteStaticKey[32];
|
uint8_t m_RemoteStaticKey[32];
|
||||||
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
|
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
|
||||||
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
|
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
|
||||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||||
#if OPENSSL_PQ
|
|
||||||
std::unique_ptr<i2p::crypto::MLKEMKeys> m_PQKeys;
|
|
||||||
std::unique_ptr<std::vector<uint8_t> > m_NSREncodedPQKey;
|
|
||||||
#endif
|
|
||||||
SessionState m_State = eSessionStateNew;
|
SessionState m_State = eSessionStateNew;
|
||||||
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
|
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
|
||||||
m_LastSentTimestamp = 0; // in milliseconds
|
m_LastSentTimestamp = 0; // in milliseconds
|
||||||
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
|
std::shared_ptr<RatchetTagSet> m_SendTagset, m_NSRSendTagset;
|
||||||
std::unique_ptr<i2p::data::IdentHash> m_Destination;// must be set for NS if outgoing and NSR if incoming
|
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
|
||||||
std::list<std::pair<uint16_t, int> > m_AckRequests; // incoming (tagsetid, index)
|
std::list<std::pair<uint16_t, int> > m_AckRequests; // incoming (tagsetid, index)
|
||||||
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
|
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
|
||||||
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
|
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
|
||||||
|
@ -247,7 +233,6 @@ namespace garlic
|
||||||
uint64_t m_LastAckRequestSendTime = 0; // milliseconds
|
uint64_t m_LastAckRequestSendTime = 0; // milliseconds
|
||||||
uint32_t m_AckRequestMsgID = 0;
|
uint32_t m_AckRequestMsgID = 0;
|
||||||
int m_AckRequestNumAttempts = 0;
|
int m_AckRequestNumAttempts = 0;
|
||||||
int m_AckRequestInterval = ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL; // milliseconds
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -457,6 +457,86 @@ namespace crypto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !OPENSSL_X25519
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded)
|
void Ed25519::BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded)
|
||||||
{
|
{
|
||||||
BN_CTX * ctx = BN_CTX_new ();
|
BN_CTX * ctx = BN_CTX_new ();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -84,7 +84,10 @@ namespace crypto
|
||||||
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const;
|
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const;
|
||||||
EDDSAPoint DecodePublicKey (const uint8_t * buf, 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 EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const;
|
||||||
|
#if !OPENSSL_X25519
|
||||||
|
void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519
|
||||||
|
void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const;
|
||||||
|
#endif
|
||||||
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
|
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
|
||||||
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
|
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
|
||||||
|
|
||||||
|
@ -112,6 +115,11 @@ namespace crypto
|
||||||
BIGNUM * DecodeBN (const uint8_t * buf) const;
|
BIGNUM * DecodeBN (const uint8_t * buf) const;
|
||||||
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const;
|
void EncodeBN (const BIGNUM * bn, uint8_t * buf, size_t len) const;
|
||||||
|
|
||||||
|
#if !OPENSSL_X25519
|
||||||
|
// for x25519
|
||||||
|
BIGNUM * ScalarMul (const BIGNUM * p, const BIGNUM * e, BN_CTX * ctx) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
BIGNUM * q, * l, * d, * I;
|
BIGNUM * q, * l, * d, * I;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -15,10 +15,6 @@
|
||||||
#include <TargetConditionals.h>
|
#include <TargetConditionals.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__HAIKU__)
|
|
||||||
#include <FindDirectory.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -173,11 +169,12 @@ namespace fs {
|
||||||
dataDir += "/Library/Application Support/" + appName;
|
dataDir += "/Library/Application Support/" + appName;
|
||||||
return;
|
return;
|
||||||
#elif defined(__HAIKU__)
|
#elif defined(__HAIKU__)
|
||||||
char home[PATH_MAX]; // /boot/home/config/settings
|
char *home = getenv("HOME");
|
||||||
if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, home, PATH_MAX) == B_OK)
|
if (home != NULL && strlen(home) > 0) {
|
||||||
dataDir = std::string(home) + "/" + appName;
|
dataDir = std::string(home) + "/config/settings/" + appName;
|
||||||
else
|
} else {
|
||||||
dataDir = "/tmp/" + appName;
|
dataDir = "/tmp/" + appName;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
#else /* other unix */
|
#else /* other unix */
|
||||||
#if defined(ANDROID)
|
#if defined(ANDROID)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
|
@ -24,8 +25,6 @@ namespace data
|
||||||
|
|
||||||
Families::~Families ()
|
Families::~Families ()
|
||||||
{
|
{
|
||||||
for (auto it : m_SigningKeys)
|
|
||||||
if (it.second.first) EVP_PKEY_free (it.second.first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Families::LoadCertificate (const std::string& filename)
|
void Families::LoadCertificate (const std::string& filename)
|
||||||
|
@ -48,16 +47,48 @@ namespace data
|
||||||
cn += 3;
|
cn += 3;
|
||||||
char * family = strstr (cn, ".family");
|
char * family = strstr (cn, ".family");
|
||||||
if (family) family[0] = 0;
|
if (family) family[0] = 0;
|
||||||
|
}
|
||||||
auto pkey = X509_get_pubkey (cert);
|
auto pkey = X509_get_pubkey (cert);
|
||||||
if (pkey)
|
int keyType = EVP_PKEY_base_id (pkey);
|
||||||
|
switch (keyType)
|
||||||
{
|
{
|
||||||
if (!m_SigningKeys.emplace (cn, std::make_pair(pkey, (int)m_SigningKeys.size () + 1)).second)
|
case EVP_PKEY_DSA:
|
||||||
|
// TODO:
|
||||||
|
break;
|
||||||
|
case EVP_PKEY_EC:
|
||||||
{
|
{
|
||||||
|
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
|
||||||
|
if (ecKey)
|
||||||
|
{
|
||||||
|
auto group = EC_KEY_get0_group (ecKey);
|
||||||
|
if (group)
|
||||||
|
{
|
||||||
|
int curve = EC_GROUP_get_curve_name (group);
|
||||||
|
if (curve == NID_X9_62_prime256v1)
|
||||||
|
{
|
||||||
|
uint8_t signingKey[64];
|
||||||
|
BIGNUM * x = BN_new(), * y = BN_new();
|
||||||
|
EC_POINT_get_affine_coordinates_GFp (group,
|
||||||
|
EC_KEY_get0_public_key (ecKey), x, y, NULL);
|
||||||
|
i2p::crypto::bn2buf (x, signingKey, 32);
|
||||||
|
i2p::crypto::bn2buf (y, signingKey + 32, 32);
|
||||||
|
BN_free (x); BN_free (y);
|
||||||
|
verifier = std::make_shared<i2p::crypto::ECDSAP256Verifier>();
|
||||||
|
verifier->SetPublicKey (signingKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
||||||
|
}
|
||||||
|
EC_KEY_free (ecKey);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported");
|
||||||
|
}
|
||||||
EVP_PKEY_free (pkey);
|
EVP_PKEY_free (pkey);
|
||||||
LogPrint (eLogError, "Family: Duplicated family name ", cn);
|
if (verifier && cn)
|
||||||
}
|
m_SigningKeys.emplace (cn, std::make_pair(verifier, (int)m_SigningKeys.size () + 1));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SSL_free (ssl);
|
SSL_free (ssl);
|
||||||
}
|
}
|
||||||
|
@ -90,31 +121,23 @@ namespace data
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
|
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||||
std::string_view signature, const char * key) const
|
const char * signature, const char * key) const
|
||||||
{
|
{
|
||||||
uint8_t buf[100], signatureBuf[64];
|
uint8_t buf[100], signatureBuf[64];
|
||||||
size_t len = family.length ();
|
size_t len = family.length (), signatureLen = strlen (signature);
|
||||||
if (len + 32 > 100)
|
if (len + 32 > 100)
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "Family: ", family, " is too long");
|
LogPrint (eLogError, "Family: ", family, " is too long");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto it = m_SigningKeys.find (family);
|
|
||||||
if (it != m_SigningKeys.end () && it->second.first)
|
|
||||||
{
|
|
||||||
memcpy (buf, family.c_str (), len);
|
memcpy (buf, family.c_str (), len);
|
||||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||||
len += 32;
|
len += 32;
|
||||||
auto signatureBufLen = Base64ToByteStream (signature, signatureBuf, 64);
|
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||||
if (signatureBufLen)
|
auto it = m_SigningKeys.find (family);
|
||||||
{
|
if (it != m_SigningKeys.end ())
|
||||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
return it->second.first->Verify (buf, len, signatureBuf);
|
||||||
EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, it->second.first);
|
|
||||||
auto ret = EVP_DigestVerify (ctx, signatureBuf, signatureBufLen, buf, len);
|
|
||||||
EVP_MD_CTX_destroy (ctx);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: process key
|
// TODO: process key
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +177,12 @@ namespace data
|
||||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||||
len += 32;
|
len += 32;
|
||||||
signer.Sign (buf, len, signature);
|
signer.Sign (buf, len, signature);
|
||||||
sig = ByteStreamToBase64 (signature, 64);
|
len = Base64EncodingBufferSize (64);
|
||||||
|
char * b64 = new char[len+1];
|
||||||
|
len = ByteStreamToBase64 (signature, 64, b64, len);
|
||||||
|
b64[len] = 0;
|
||||||
|
sig = b64;
|
||||||
|
delete[] b64;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <openssl/evp.h>
|
#include "Signature.h"
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -29,7 +28,7 @@ namespace data
|
||||||
~Families ();
|
~Families ();
|
||||||
void LoadCertificates ();
|
void LoadCertificates ();
|
||||||
bool VerifyFamily (const std::string& family, const IdentHash& ident,
|
bool VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||||
std::string_view signature, const char * key = nullptr) const;
|
const char * signature, const char * key = nullptr) const;
|
||||||
FamilyID GetFamilyID (const std::string& family) const;
|
FamilyID GetFamilyID (const std::string& family) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -38,7 +37,7 @@ namespace data
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::map<std::string, std::pair<EVP_PKEY *, FamilyID> > m_SigningKeys; // family -> (verification pkey, id)
|
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id)
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
|
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -160,7 +160,7 @@ namespace garlic
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
uint8_t iv[32]; // IV is first 16 bytes
|
||||||
SHA256(elGamal.preIV, 32, iv);
|
SHA256(elGamal.preIV, 32, iv);
|
||||||
m_Destination->Encrypt ((uint8_t *)&elGamal, buf);
|
m_Destination->Encrypt ((uint8_t *)&elGamal, buf);
|
||||||
m_IV = iv;
|
m_Encryption.SetIV (iv);
|
||||||
buf += 514;
|
buf += 514;
|
||||||
len += 514;
|
len += 514;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ namespace garlic
|
||||||
memcpy (buf, tag, 32);
|
memcpy (buf, tag, 32);
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
uint8_t iv[32]; // IV is first 16 bytes
|
||||||
SHA256(tag, 32, iv);
|
SHA256(tag, 32, iv);
|
||||||
m_IV = iv;
|
m_Encryption.SetIV (iv);
|
||||||
buf += 32;
|
buf += 32;
|
||||||
len += 32;
|
len += 32;
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ namespace garlic
|
||||||
size_t rem = blockSize % 16;
|
size_t rem = blockSize % 16;
|
||||||
if (rem)
|
if (rem)
|
||||||
blockSize += (16-rem); //padding
|
blockSize += (16-rem); //padding
|
||||||
m_Encryption.Encrypt(buf, blockSize, m_IV, buf);
|
m_Encryption.Encrypt(buf, blockSize, buf);
|
||||||
return blockSize;
|
return blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,8 +426,7 @@ namespace garlic
|
||||||
}
|
}
|
||||||
|
|
||||||
GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
|
GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
|
||||||
m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0),
|
m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard
|
||||||
m_NumRatchetInboundTags (0) // 0 means standard
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,8 +497,7 @@ namespace garlic
|
||||||
buf += 4; // length
|
buf += 4; // length
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
bool supportsRatchets = SupportsRatchets ();
|
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||||
if (supportsRatchets)
|
|
||||||
// try ECIESx25519 tag
|
// try ECIESx25519 tag
|
||||||
found = HandleECIESx25519TagMessage (buf, length);
|
found = HandleECIESx25519TagMessage (buf, length);
|
||||||
if (!found)
|
if (!found)
|
||||||
|
@ -515,7 +513,8 @@ namespace garlic
|
||||||
{
|
{
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
uint8_t iv[32]; // IV is first 16 bytes
|
||||||
SHA256(buf, 32, iv);
|
SHA256(buf, 32, iv);
|
||||||
decryption->Decrypt (buf + 32, length - 32, iv, buf + 32);
|
decryption->SetIV (iv);
|
||||||
|
decryption->Decrypt (buf + 32, length - 32, buf + 32);
|
||||||
HandleAESBlock (buf + 32, length - 32, decryption, msg->from);
|
HandleAESBlock (buf + 32, length - 32, decryption, msg->from);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
@ -533,24 +532,17 @@ namespace garlic
|
||||||
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
|
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
uint8_t iv[32]; // IV is first 16 bytes
|
||||||
SHA256(elGamal.preIV, 32, iv);
|
SHA256(elGamal.preIV, 32, iv);
|
||||||
decryption->Decrypt(buf + 514, length - 514, iv, buf + 514);
|
decryption->SetIV (iv);
|
||||||
|
decryption->Decrypt(buf + 514, length - 514, buf + 514);
|
||||||
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
|
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
|
||||||
}
|
}
|
||||||
else if (supportsRatchets)
|
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||||
{
|
{
|
||||||
// otherwise ECIESx25519
|
// otherwise ECIESx25519
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL)
|
|
||||||
{
|
|
||||||
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
|
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
|
||||||
if (session->HandleNextMessage (buf, length, nullptr, 0))
|
if (!session->HandleNextMessage (buf, length, nullptr, 0))
|
||||||
m_LastIncomingSessionTimestamp = ts;
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Garlic: Incoming sessions come too often");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
LogPrint (eLogError, "Garlic: Failed to decrypt message");
|
LogPrint (eLogError, "Garlic: Failed to decrypt message");
|
||||||
}
|
}
|
||||||
|
@ -745,12 +737,10 @@ namespace garlic
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
||||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet,
|
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
||||||
bool requestNewIfNotFound)
|
|
||||||
{
|
{
|
||||||
if (destination->GetEncryptionType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
|
||||||
{
|
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||||
if (SupportsEncryptionType (destination->GetEncryptionType ()))
|
|
||||||
{
|
{
|
||||||
ECIESX25519AEADRatchetSessionPtr session;
|
ECIESX25519AEADRatchetSessionPtr session;
|
||||||
uint8_t staticKey[32];
|
uint8_t staticKey[32];
|
||||||
|
@ -762,22 +752,18 @@ namespace garlic
|
||||||
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
|
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
|
||||||
{
|
{
|
||||||
LogPrint (eLogDebug, "Garlic: Session restarted");
|
LogPrint (eLogDebug, "Garlic: Session restarted");
|
||||||
requestNewIfNotFound = true; // it's not a new session
|
|
||||||
session = nullptr;
|
session = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!session && requestNewIfNotFound)
|
if (!session)
|
||||||
{
|
{
|
||||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
|
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
|
||||||
session->SetRemoteStaticKey (destination->GetEncryptionType (), staticKey);
|
session->SetRemoteStaticKey (staticKey);
|
||||||
}
|
}
|
||||||
if (session && destination->IsDestination ())
|
if (destination->IsDestination ())
|
||||||
session->SetDestination (destination->GetIdentHash ()); // NS or NSR
|
session->SetDestination (destination->GetIdentHash ()); // TODO: remove
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Garlic: Non-supported encryption type ", destination->GetEncryptionType ());
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ElGamalAESSessionPtr session;
|
ElGamalAESSessionPtr session;
|
||||||
|
@ -911,7 +897,7 @@ namespace garlic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GarlicDestination::SetLeaseSetUpdated (bool post)
|
void GarlicDestination::SetLeaseSetUpdated ()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||||
|
@ -1004,8 +990,7 @@ namespace garlic
|
||||||
i2p::fs::Remove (it);
|
i2p::fs::Remove (it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len,
|
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len)
|
||||||
ECIESX25519AEADRatchetSession * from)
|
|
||||||
{
|
{
|
||||||
const uint8_t * buf1 = buf;
|
const uint8_t * buf1 = buf;
|
||||||
uint8_t flag = buf[0]; buf++; // flag
|
uint8_t flag = buf[0]; buf++; // flag
|
||||||
|
@ -1025,7 +1010,7 @@ namespace garlic
|
||||||
buf += 4; // expiration
|
buf += 4; // expiration
|
||||||
ptrdiff_t offset = buf - buf1;
|
ptrdiff_t offset = buf - buf1;
|
||||||
if (offset <= (int)len)
|
if (offset <= (int)len)
|
||||||
HandleCloveI2NPMessage (typeID, buf, len - offset, msgID, from);
|
HandleCloveI2NPMessage (typeID, buf, len - offset, msgID);
|
||||||
else
|
else
|
||||||
LogPrint (eLogError, "Garlic: Clove is too long");
|
LogPrint (eLogError, "Garlic: Clove is too long");
|
||||||
break;
|
break;
|
||||||
|
@ -1109,17 +1094,5 @@ namespace garlic
|
||||||
m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE];
|
m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE];
|
||||||
return m_PayloadBuffer;
|
return m_PayloadBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GarlicDestination::AEADChaCha20Poly1305Encrypt (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)
|
|
||||||
{
|
|
||||||
return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GarlicDestination::AEADChaCha20Poly1305Decrypt (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)
|
|
||||||
{
|
|
||||||
return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -52,7 +52,6 @@ namespace garlic
|
||||||
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
|
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
|
||||||
const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
|
const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
|
||||||
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 120; // in seconds
|
const int ROUTING_PATH_EXPIRATION_TIMEOUT = 120; // in seconds
|
||||||
const int INCOMING_SESSIONS_MINIMAL_INTERVAL = 200; // in milliseconds
|
|
||||||
|
|
||||||
struct SessionTag: public i2p::data::Tag<32>
|
struct SessionTag: public i2p::data::Tag<32>
|
||||||
{
|
{
|
||||||
|
@ -116,7 +115,6 @@ namespace garlic
|
||||||
virtual bool IsReadyToSend () const { return true; };
|
virtual bool IsReadyToSend () const { return true; };
|
||||||
virtual bool IsTerminated () const { return !GetOwner (); };
|
virtual bool IsTerminated () const { return !GetOwner (); };
|
||||||
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
|
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
|
||||||
virtual void SetAckRequestInterval (int interval) {}; // in milliseconds, override in ECIESX25519AEADRatchetSession
|
|
||||||
|
|
||||||
void SetLeaseSetUpdated ()
|
void SetLeaseSetUpdated ()
|
||||||
{
|
{
|
||||||
|
@ -206,7 +204,6 @@ namespace garlic
|
||||||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
||||||
|
|
||||||
i2p::crypto::CBCEncryption m_Encryption;
|
i2p::crypto::CBCEncryption m_Encryption;
|
||||||
i2p::data::Tag<16> m_IV;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -237,18 +234,12 @@ namespace garlic
|
||||||
int GetNumTags () const { return m_NumTags; };
|
int GetNumTags () const { return m_NumTags; };
|
||||||
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
|
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
|
||||||
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
|
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
|
||||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||||
bool attachLeaseSet, bool requestNewIfNotFound = true);
|
|
||||||
void CleanupExpiredTags ();
|
void CleanupExpiredTags ();
|
||||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||||
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
std::shared_ptr<I2NPMessage> msg);
|
std::shared_ptr<I2NPMessage> msg);
|
||||||
|
|
||||||
bool AEADChaCha20Poly1305Encrypt (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 AEADChaCha20Poly1305Decrypt (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);
|
|
||||||
|
|
||||||
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
||||||
void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
|
void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
|
||||||
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
||||||
|
@ -257,27 +248,22 @@ namespace garlic
|
||||||
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
|
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
|
||||||
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
||||||
void RemoveECIESx25519Session (const uint8_t * staticKey);
|
void RemoveECIESx25519Session (const uint8_t * staticKey);
|
||||||
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len, ECIESX25519AEADRatchetSession * from);
|
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
|
||||||
uint8_t * GetPayloadBuffer ();
|
uint8_t * GetPayloadBuffer ();
|
||||||
|
|
||||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
virtual void SetLeaseSetUpdated (bool post = false);
|
virtual void SetLeaseSetUpdated ();
|
||||||
|
|
||||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
||||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
||||||
virtual i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const
|
|
||||||
{
|
|
||||||
return GetIdentity ()->GetCryptoKeyType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? GetIdentity ()->GetCryptoKeyType () : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
||||||
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found
|
bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found
|
||||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
|
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
|
||||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0;
|
||||||
size_t len, uint32_t msgID, ECIESX25519AEADRatchetSession * from) = 0;
|
|
||||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||||
|
|
||||||
|
@ -286,7 +272,6 @@ namespace garlic
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool SupportsRatchets () const { return GetRatchetsHighestCryptoType () > 0; }
|
|
||||||
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
|
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
|
||||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||||
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||||
|
@ -299,7 +284,6 @@ namespace garlic
|
||||||
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
|
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
|
||||||
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
|
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
|
||||||
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
|
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
|
||||||
uint64_t m_LastIncomingSessionTimestamp; // in milliseconds
|
|
||||||
// incoming
|
// incoming
|
||||||
int m_NumRatchetInboundTags;
|
int m_NumRatchetInboundTags;
|
||||||
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
|
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
|
||||||
|
@ -307,9 +291,6 @@ namespace garlic
|
||||||
// DeliveryStatus
|
// DeliveryStatus
|
||||||
std::mutex m_DeliveryStatusSessionsMutex;
|
std::mutex m_DeliveryStatusSessionsMutex;
|
||||||
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||||
// encryption
|
|
||||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
|
||||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -10,7 +10,6 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <charconv>
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "Base.h"
|
#include "Base.h"
|
||||||
#include "HTTP.h"
|
#include "HTTP.h"
|
||||||
|
@ -19,51 +18,54 @@ namespace i2p
|
||||||
{
|
{
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
// list of valid HTTP methods
|
const std::vector<std::string> HTTP_METHODS = {
|
||||||
static constexpr std::array<std::string_view, 16> HTTP_METHODS =
|
|
||||||
{
|
|
||||||
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods
|
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods
|
||||||
"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323
|
"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323
|
||||||
};
|
};
|
||||||
|
const std::vector<std::string> HTTP_VERSIONS = {
|
||||||
// list of valid HTTP versions
|
|
||||||
static constexpr std::array<std::string_view, 2> HTTP_VERSIONS =
|
|
||||||
{
|
|
||||||
"HTTP/1.0", "HTTP/1.1"
|
"HTTP/1.0", "HTTP/1.1"
|
||||||
};
|
};
|
||||||
|
const std::vector<const char *> weekdays = {
|
||||||
static constexpr std::array<const char *, 7> weekdays =
|
|
||||||
{
|
|
||||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||||
};
|
};
|
||||||
|
const std::vector<const char *> months = {
|
||||||
static constexpr std::array<const char *, 12> months =
|
|
||||||
{
|
|
||||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool is_http_version(std::string_view str)
|
inline bool is_http_version(const std::string & str) {
|
||||||
{
|
|
||||||
return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
|
return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool is_http_method(std::string_view str)
|
inline bool is_http_method(const std::string & str) {
|
||||||
{
|
|
||||||
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
|
return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void strsplit(std::string_view line, std::vector<std::string_view> &tokens, char delim, std::size_t limit = 0)
|
static void strsplit(std::stringstream& ss, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
|
||||||
{
|
{
|
||||||
size_t count = 0, pos;
|
std::size_t count = 0;
|
||||||
while ((pos = line.find (delim)) != line.npos)
|
std::string token;
|
||||||
|
while (1)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
if (limit > 0 && count >= limit) delim = '\n'; // reset delimiter
|
if (limit > 0 && count >= limit)
|
||||||
tokens.push_back (line.substr (0, pos));
|
delim = '\n'; /* reset delimiter */
|
||||||
line = line.substr (pos + 1);
|
if (!std::getline(ss, token, delim))
|
||||||
|
break;
|
||||||
|
tokens.push_back(token);
|
||||||
}
|
}
|
||||||
if (!line.empty ()) tokens.push_back (line);
|
}
|
||||||
|
|
||||||
|
static void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
|
||||||
|
{
|
||||||
|
std::stringstream ss{line};
|
||||||
|
strsplit (ss, tokens, delim, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strsplit(std::string_view line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0)
|
||||||
|
{
|
||||||
|
std::stringstream ss{std::string(line)};
|
||||||
|
strsplit (ss, tokens, delim, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<std::string, std::string> parse_header_line(std::string_view line)
|
static std::pair<std::string, std::string> parse_header_line(std::string_view line)
|
||||||
|
@ -101,7 +103,6 @@ namespace http
|
||||||
|
|
||||||
bool URL::parse(std::string_view url)
|
bool URL::parse(std::string_view url)
|
||||||
{
|
{
|
||||||
if (url.empty ()) return false;
|
|
||||||
std::size_t pos_p = 0; /* < current parse position */
|
std::size_t pos_p = 0; /* < current parse position */
|
||||||
std::size_t pos_c = 0; /* < work position */
|
std::size_t pos_c = 0; /* < work position */
|
||||||
if(url.at(0) != '/' || pos_p > 0)
|
if(url.at(0) != '/' || pos_p > 0)
|
||||||
|
@ -209,9 +210,8 @@ namespace http
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool URL::parse_query(std::map<std::string, std::string> & params)
|
bool URL::parse_query(std::map<std::string, std::string> & params) {
|
||||||
{
|
std::vector<std::string> tokens;
|
||||||
std::vector<std::string_view> tokens;
|
|
||||||
strsplit(query, tokens, '&');
|
strsplit(query, tokens, '&');
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
|
@ -307,9 +307,8 @@ namespace http
|
||||||
if (expect == REQ_LINE)
|
if (expect == REQ_LINE)
|
||||||
{
|
{
|
||||||
std::string_view line = str.substr(pos, eol - pos);
|
std::string_view line = str.substr(pos, eol - pos);
|
||||||
std::vector<std::string_view> tokens;
|
std::vector<std::string> tokens;
|
||||||
strsplit(line, tokens, ' ');
|
strsplit(line, tokens, ' ');
|
||||||
|
|
||||||
if (tokens.size() != 3)
|
if (tokens.size() != 3)
|
||||||
return -1;
|
return -1;
|
||||||
if (!is_http_method(tokens[0]))
|
if (!is_http_method(tokens[0]))
|
||||||
|
@ -333,11 +332,11 @@ namespace http
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pos = eol + CRLF.length();
|
pos = eol + strlen(CRLF);
|
||||||
if (pos >= eoh)
|
if (pos >= eoh)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return eoh + HTTP_EOH.length();
|
return eoh + strlen(HTTP_EOH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTTPReq::write(std::ostream & o)
|
void HTTPReq::write(std::ostream & o)
|
||||||
|
@ -381,7 +380,7 @@ namespace http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HTTPReq::GetHeader (std::string_view name) const
|
std::string HTTPReq::GetHeader (const std::string& name) const
|
||||||
{
|
{
|
||||||
for (auto& it : headers)
|
for (auto& it : headers)
|
||||||
if (it.first == name)
|
if (it.first == name)
|
||||||
|
@ -389,7 +388,7 @@ namespace http
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t HTTPReq::GetNumHeaders (std::string_view name) const
|
size_t HTTPReq::GetNumHeaders (const std::string& name) const
|
||||||
{
|
{
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
for (auto& it : headers)
|
for (auto& it : headers)
|
||||||
|
@ -451,15 +450,13 @@ namespace http
|
||||||
if (expect == RES_LINE)
|
if (expect == RES_LINE)
|
||||||
{
|
{
|
||||||
std::string_view line = str.substr(pos, eol - pos);
|
std::string_view line = str.substr(pos, eol - pos);
|
||||||
std::vector<std::string_view> tokens;
|
std::vector<std::string> tokens;
|
||||||
strsplit(line, tokens, ' ', 3);
|
strsplit(line, tokens, ' ', 3);
|
||||||
if (tokens.size() != 3)
|
if (tokens.size() != 3)
|
||||||
return -1;
|
return -1;
|
||||||
if (!is_http_version(tokens[0]))
|
if (!is_http_version(tokens[0]))
|
||||||
return -1;
|
return -1;
|
||||||
auto res = std::from_chars(tokens[1].data (), tokens[1].data() + tokens[1].size(), code);
|
code = atoi(tokens[1].c_str());
|
||||||
if (res.ec != std::errc())
|
|
||||||
return -1;
|
|
||||||
if (code < 100 || code >= 600)
|
if (code < 100 || code >= 600)
|
||||||
return -1;
|
return -1;
|
||||||
/* all ok */
|
/* all ok */
|
||||||
|
@ -476,11 +473,11 @@ namespace http
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pos = eol + CRLF.length();
|
pos = eol + strlen(CRLF);
|
||||||
if (pos >= eoh)
|
if (pos >= eoh)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return eoh + HTTP_EOH.length();
|
return eoh + strlen(HTTP_EOH);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HTTPRes::to_string() {
|
std::string HTTPRes::to_string() {
|
||||||
|
@ -505,11 +502,9 @@ namespace http
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view HTTPCodeToStatus(int code)
|
const char * HTTPCodeToStatus(int code) {
|
||||||
{
|
const char *ptr;
|
||||||
std::string_view ptr;
|
switch (code) {
|
||||||
switch (code)
|
|
||||||
{
|
|
||||||
case 105: ptr = "Name Not Resolved"; break;
|
case 105: ptr = "Name Not Resolved"; break;
|
||||||
/* success */
|
/* success */
|
||||||
case 200: ptr = "OK"; break;
|
case 200: ptr = "OK"; break;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -21,8 +21,10 @@ namespace i2p
|
||||||
{
|
{
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
constexpr std::string_view CRLF = "\r\n"; /**< HTTP line terminator */
|
const char CRLF[] = "\r\n"; /**< HTTP line terminator */
|
||||||
constexpr std::string_view HTTP_EOH = "\r\n\r\n"; /**< HTTP end-of-headers mark */
|
const char HTTP_EOH[] = "\r\n\r\n"; /**< HTTP end-of-headers mark */
|
||||||
|
extern const std::vector<std::string> HTTP_METHODS; /**< list of valid HTTP methods */
|
||||||
|
extern const std::vector<std::string> HTTP_VERSIONS; /**< list of valid HTTP versions */
|
||||||
|
|
||||||
struct URL
|
struct URL
|
||||||
{
|
{
|
||||||
|
@ -101,8 +103,8 @@ namespace http
|
||||||
void UpdateHeader (const std::string& name, const std::string& value);
|
void UpdateHeader (const std::string& name, const std::string& value);
|
||||||
void RemoveHeader (const std::string& name, const std::string& exempt); // remove all headers starting with name, but exempt
|
void RemoveHeader (const std::string& name, const std::string& exempt); // remove all headers starting with name, but exempt
|
||||||
void RemoveHeader (const std::string& name) { RemoveHeader (name, ""); };
|
void RemoveHeader (const std::string& name) { RemoveHeader (name, ""); };
|
||||||
std::string GetHeader (std::string_view name) const;
|
std::string GetHeader (const std::string& name) const;
|
||||||
size_t GetNumHeaders (std::string_view name) const;
|
size_t GetNumHeaders (const std::string& name) const;
|
||||||
size_t GetNumHeaders () const { return headers.size (); };
|
size_t GetNumHeaders () const { return headers.size (); };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ namespace http
|
||||||
* @param code HTTP code [100, 599]
|
* @param code HTTP code [100, 599]
|
||||||
* @return Immutable string with status
|
* @return Immutable string with status
|
||||||
*/
|
*/
|
||||||
std::string_view HTTPCodeToStatus(int code);
|
const char * HTTPCodeToStatus(int code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Replaces %-encoded characters in string with their values
|
* @brief Replaces %-encoded characters in string with their values
|
||||||
|
|
|
@ -10,15 +10,20 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include "Base.h"
|
#include "Base.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "Crypto.h"
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
#include "RouterContext.h"
|
#include "RouterContext.h"
|
||||||
#include "NetDb.hpp"
|
#include "NetDb.hpp"
|
||||||
#include "Tunnel.h"
|
#include "Tunnel.h"
|
||||||
#include "TransitTunnel.h"
|
#include "Transports.h"
|
||||||
|
#include "Garlic.h"
|
||||||
|
#include "ECIESX25519AEADRatchetSession.h"
|
||||||
#include "I2NPProtocol.h"
|
#include "I2NPProtocol.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
using namespace i2p::transport;
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
|
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
|
||||||
|
@ -371,6 +376,337 @@ namespace i2p
|
||||||
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
|
return !msg->GetPayload ()[DATABASE_STORE_TYPE_OFFSET]; // 0- RouterInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
|
||||||
|
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||||
|
{
|
||||||
|
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
|
||||||
|
if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: Failed to decrypt tunnel build record");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours
|
||||||
|
!(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: Next ident is ours in tunnel build record");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t retCode = 0;
|
||||||
|
// replace record to reply
|
||||||
|
if (i2p::context.AcceptsTunnels () && i2p::context.GetCongestionLevel (false) < CONGESTION_LEVEL_FULL)
|
||||||
|
{
|
||||||
|
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||||
|
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||||
|
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||||
|
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||||
|
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
|
||||||
|
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
|
||||||
|
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||||
|
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||||
|
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||||
|
retCode = 30;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
retCode = 30; // always reject with bandwidth reason (30)
|
||||||
|
|
||||||
|
memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||||
|
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||||
|
// encrypt reply
|
||||||
|
i2p::crypto::CBCEncryption encryption;
|
||||||
|
for (int j = 0; j < num; j++)
|
||||||
|
{
|
||||||
|
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
|
||||||
|
if (j == i)
|
||||||
|
{
|
||||||
|
uint8_t nonce[12];
|
||||||
|
memset (nonce, 0, 12);
|
||||||
|
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||||
|
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||||
|
noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
||||||
|
encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
|
||||||
|
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
int num = buf[0];
|
||||||
|
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
|
||||||
|
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2NP: Too many records in VaribleTunnelBuild message ", num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
|
||||||
|
if (tunnel)
|
||||||
|
{
|
||||||
|
// endpoint of inbound tunnel
|
||||||
|
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||||
|
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||||
|
{
|
||||||
|
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||||
|
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||||
|
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||||
|
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||||
|
if (HandleBuildRequestRecords (num, buf + 1, clearText))
|
||||||
|
{
|
||||||
|
if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel
|
||||||
|
{
|
||||||
|
// so we send it to reply tunnel
|
||||||
|
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||||
|
CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||||
|
eI2NPVariableTunnelBuildReply, buf, len,
|
||||||
|
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||||
|
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
|
||||||
|
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort)
|
||||||
|
{
|
||||||
|
int num = buf[0];
|
||||||
|
LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID);
|
||||||
|
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2NP: Too many records in TunnelBuildReply message ", num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE;
|
||||||
|
if (len < num*recordSize + 1)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (replyMsgID);
|
||||||
|
if (tunnel)
|
||||||
|
{
|
||||||
|
// reply for outbound tunnel
|
||||||
|
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||||
|
{
|
||||||
|
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||||
|
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||||
|
i2p::tunnel::tunnels.AddOutboundTunnel (tunnel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint (eLogInfo, "I2NP: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||||
|
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||||
|
{
|
||||||
|
int num = buf[0];
|
||||||
|
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records");
|
||||||
|
if (num > i2p::tunnel::MAX_NUM_RECORDS)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2NP: Too many records in ShortTunnelBuild message ", num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID);
|
||||||
|
if (tunnel)
|
||||||
|
{
|
||||||
|
// endpoint of inbound tunnel
|
||||||
|
LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||||
|
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||||
|
{
|
||||||
|
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||||
|
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||||
|
i2p::tunnel::tunnels.AddInboundTunnel (tunnel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||||
|
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t * record = buf + 1;
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||||
|
{
|
||||||
|
LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours");
|
||||||
|
uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
||||||
|
if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText))
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& noiseState = i2p::context.GetCurrentNoiseState ();
|
||||||
|
uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305
|
||||||
|
i2p::crypto::AESKey layerKey, ivKey; // AES
|
||||||
|
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK);
|
||||||
|
memcpy (replyKey, noiseState.m_CK + 32, 32);
|
||||||
|
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK);
|
||||||
|
memcpy (layerKey, noiseState.m_CK + 32, 32);
|
||||||
|
bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG;
|
||||||
|
if (isEndpoint)
|
||||||
|
{
|
||||||
|
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK);
|
||||||
|
memcpy (ivKey, noiseState.m_CK + 32, 32);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: Next ident is ours in short request record");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy (ivKey, noiseState.m_CK , 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we accept this tunnel
|
||||||
|
std::shared_ptr<i2p::tunnel::TransitTunnel> transitTunnel;
|
||||||
|
uint8_t retCode = 0;
|
||||||
|
if (!i2p::context.AcceptsTunnels () || i2p::context.GetCongestionLevel (false) >= CONGESTION_LEVEL_FULL)
|
||||||
|
retCode = 30;
|
||||||
|
if (!retCode)
|
||||||
|
{
|
||||||
|
// create new transit tunnel
|
||||||
|
transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||||
|
bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||||
|
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||||
|
bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||||
|
layerKey, ivKey,
|
||||||
|
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG,
|
||||||
|
clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG);
|
||||||
|
if (!i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel))
|
||||||
|
retCode = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt reply
|
||||||
|
uint8_t nonce[12];
|
||||||
|
memset (nonce, 0, 12);
|
||||||
|
uint8_t * reply = buf + 1;
|
||||||
|
for (int j = 0; j < num; j++)
|
||||||
|
{
|
||||||
|
nonce[4] = j; // nonce is record #
|
||||||
|
if (j == i)
|
||||||
|
{
|
||||||
|
memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
|
||||||
|
reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode;
|
||||||
|
if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16,
|
||||||
|
noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply);
|
||||||
|
reply += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||||
|
}
|
||||||
|
// send reply
|
||||||
|
auto onDrop = [transitTunnel]()
|
||||||
|
{
|
||||||
|
if (transitTunnel)
|
||||||
|
{
|
||||||
|
auto t = transitTunnel->GetCreationTime ();
|
||||||
|
if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT)
|
||||||
|
// make transit tunnel expired
|
||||||
|
transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (isEndpoint)
|
||||||
|
{
|
||||||
|
auto replyMsg = NewI2NPShortMessage ();
|
||||||
|
replyMsg->Concat (buf, len);
|
||||||
|
replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
|
||||||
|
if (transitTunnel) replyMsg->onDrop = onDrop;
|
||||||
|
if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (),
|
||||||
|
clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local?
|
||||||
|
{
|
||||||
|
i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK);
|
||||||
|
uint64_t tag;
|
||||||
|
memcpy (&tag, noiseState.m_CK, 8);
|
||||||
|
// we send it to reply tunnel
|
||||||
|
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
||||||
|
CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
||||||
|
i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// IBGW is local
|
||||||
|
uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET);
|
||||||
|
auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID);
|
||||||
|
if (tunnel)
|
||||||
|
{
|
||||||
|
tunnel->SendTunnelDataMsg (replyMsg);
|
||||||
|
tunnel->FlushTunnelDataMsgs ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len,
|
||||||
|
bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET));
|
||||||
|
if (transitTunnel) msg->onDrop = onDrop;
|
||||||
|
transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
record += SHORT_TUNNEL_BUILD_RECORD_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
|
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
|
||||||
{
|
{
|
||||||
auto msg = NewI2NPTunnelMessage (false);
|
auto msg = NewI2NPTunnelMessage (false);
|
||||||
|
@ -466,6 +802,41 @@ namespace i2p
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HandleTunnelBuildI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
|
{
|
||||||
|
if (msg)
|
||||||
|
{
|
||||||
|
uint8_t typeID = msg->GetTypeID();
|
||||||
|
uint32_t msgID = msg->GetMsgID();
|
||||||
|
LogPrint (eLogDebug, "I2NP: Handling tunnel build message with len=", msg->GetLength(),", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
|
||||||
|
uint8_t * payload = msg->GetPayload();
|
||||||
|
auto size = msg->GetPayloadLength();
|
||||||
|
switch (typeID)
|
||||||
|
{
|
||||||
|
case eI2NPVariableTunnelBuild:
|
||||||
|
HandleVariableTunnelBuildMsg (msgID, payload, size);
|
||||||
|
break;
|
||||||
|
case eI2NPShortTunnelBuild:
|
||||||
|
HandleShortTunnelBuildMsg (msgID, payload, size);
|
||||||
|
break;
|
||||||
|
case eI2NPVariableTunnelBuildReply:
|
||||||
|
HandleTunnelBuildReplyMsg (msgID, payload, size, false);
|
||||||
|
break;
|
||||||
|
case eI2NPShortTunnelBuildReply:
|
||||||
|
HandleTunnelBuildReplyMsg (msgID, payload, size, true);
|
||||||
|
break;
|
||||||
|
case eI2NPTunnelBuild:
|
||||||
|
HandleTunnelBuildMsg (payload, size);
|
||||||
|
break;
|
||||||
|
case eI2NPTunnelBuildReply:
|
||||||
|
// TODO:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LogPrint (eLogError, "I2NP: Unexpected message with type", (int)typeID, " during handling TBM; skipping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
if (msg)
|
if (msg)
|
||||||
|
@ -561,8 +932,14 @@ namespace i2p
|
||||||
void I2NPMessagesHandler::Flush ()
|
void I2NPMessagesHandler::Flush ()
|
||||||
{
|
{
|
||||||
if (!m_TunnelMsgs.empty ())
|
if (!m_TunnelMsgs.empty ())
|
||||||
|
{
|
||||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
|
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
|
||||||
|
m_TunnelMsgs.clear ();
|
||||||
|
}
|
||||||
if (!m_TunnelGatewayMsgs.empty ())
|
if (!m_TunnelGatewayMsgs.empty ())
|
||||||
|
{
|
||||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
|
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
|
||||||
|
m_TunnelGatewayMsgs.clear ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <list>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
|
@ -316,6 +315,7 @@ namespace tunnel
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
|
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
|
||||||
|
|
||||||
size_t GetI2NPMessageLength (const uint8_t * msg, size_t len);
|
size_t GetI2NPMessageLength (const uint8_t * msg, size_t len);
|
||||||
|
void HandleTunnelBuildI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
|
|
||||||
class I2NPMessagesHandler
|
class I2NPMessagesHandler
|
||||||
|
@ -328,7 +328,7 @@ namespace tunnel
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::list<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -10,7 +10,6 @@
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -28,15 +27,18 @@ namespace data
|
||||||
|
|
||||||
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
|
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
if (len < DEFAULT_IDENTITY_SIZE) return 0; // buffer too small, don't overflow
|
if ( len < DEFAULT_IDENTITY_SIZE ) {
|
||||||
memcpy (this, buf, DEFAULT_IDENTITY_SIZE);
|
// buffer too small, don't overflow
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
|
||||||
return DEFAULT_IDENTITY_SIZE;
|
return DEFAULT_IDENTITY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentHash Identity::Hash () const
|
IdentHash Identity::Hash () const
|
||||||
{
|
{
|
||||||
IdentHash hash;
|
IdentHash hash;
|
||||||
SHA256((const uint8_t *)this, DEFAULT_IDENTITY_SIZE, hash);
|
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,16 +122,6 @@ namespace data
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH);
|
memcpy (m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#if OPENSSL_PQ
|
|
||||||
case SIGNING_KEY_TYPE_MLDSA44:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity, signingKey, 384);
|
|
||||||
excessLen = i2p::crypto::MLDSA44_PUBLIC_KEY_LENGTH - 384;
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 384, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
|
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
|
||||||
}
|
}
|
||||||
|
@ -270,19 +262,23 @@ namespace data
|
||||||
return fullLen;
|
return fullLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t IdentityEx::FromBase64(std::string_view s)
|
size_t IdentityEx::FromBase64(const std::string& s)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> buf(s.length ()); // binary data can't exceed base64
|
const size_t slen = s.length();
|
||||||
auto len = Base64ToByteStream (s, buf.data(), buf.size ());
|
std::vector<uint8_t> buf(slen); // binary data can't exceed base64
|
||||||
|
const size_t len = Base64ToByteStream (s.c_str(), slen, buf.data(), slen);
|
||||||
return FromBuffer (buf.data(), len);
|
return FromBuffer (buf.data(), len);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IdentityEx::ToBase64 () const
|
std::string IdentityEx::ToBase64 () const
|
||||||
{
|
{
|
||||||
const size_t bufLen = GetFullLen();
|
const size_t bufLen = GetFullLen();
|
||||||
|
const size_t strLen = Base64EncodingBufferSize(bufLen);
|
||||||
std::vector<uint8_t> buf(bufLen);
|
std::vector<uint8_t> buf(bufLen);
|
||||||
|
std::vector<char> str(strLen);
|
||||||
size_t l = ToBuffer (buf.data(), bufLen);
|
size_t l = ToBuffer (buf.data(), bufLen);
|
||||||
return i2p::data::ByteStreamToBase64 (buf.data(), l);
|
size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen);
|
||||||
|
return std::string (str.data(), l1);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t IdentityEx::GetSigningPublicKeyLen () const
|
size_t IdentityEx::GetSigningPublicKeyLen () const
|
||||||
|
@ -359,10 +355,6 @@ namespace data
|
||||||
return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512);
|
return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512);
|
||||||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||||
return new i2p::crypto::RedDSA25519Verifier ();
|
return new i2p::crypto::RedDSA25519Verifier ();
|
||||||
#if OPENSSL_PQ
|
|
||||||
case SIGNING_KEY_TYPE_MLDSA44:
|
|
||||||
return new i2p::crypto::MLDSA44Verifier ();
|
|
||||||
#endif
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||||
|
@ -384,18 +376,6 @@ namespace data
|
||||||
auto keyLen = verifier->GetPublicKeyLen ();
|
auto keyLen = verifier->GetPublicKeyLen ();
|
||||||
if (keyLen <= 128)
|
if (keyLen <= 128)
|
||||||
verifier->SetPublicKey (m_StandardIdentity.signingKey + 128 - keyLen);
|
verifier->SetPublicKey (m_StandardIdentity.signingKey + 128 - keyLen);
|
||||||
#if OPENSSL_PQ
|
|
||||||
else if (keyLen > 384)
|
|
||||||
{
|
|
||||||
// for post-quantum
|
|
||||||
uint8_t * signingKey = new uint8_t[keyLen];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 384);
|
|
||||||
size_t excessLen = keyLen - 384;
|
|
||||||
memcpy (signingKey + 384, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
verifier->SetPublicKey (signingKey);
|
|
||||||
delete[] signingKey;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// for P521
|
// for P521
|
||||||
|
@ -419,14 +399,15 @@ namespace data
|
||||||
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
|
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
|
||||||
break;
|
break;
|
||||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
|
||||||
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
|
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
|
||||||
break;
|
break;
|
||||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||||
|
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||||
return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key);
|
return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key);
|
||||||
break;
|
break;
|
||||||
|
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||||
|
return std::make_shared<i2p::crypto::ECIESGOSTR3410Encryptor>(key);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType);
|
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType);
|
||||||
};
|
};
|
||||||
|
@ -570,18 +551,26 @@ namespace data
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t PrivateKeys::FromBase64(std::string_view s)
|
size_t PrivateKeys::FromBase64(const std::string& s)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> buf(s.length ());
|
uint8_t * buf = new uint8_t[s.length ()];
|
||||||
size_t l = i2p::data::Base64ToByteStream (s, buf.data (), buf.size ());
|
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
|
||||||
return FromBuffer (buf.data (), l);
|
size_t ret = FromBuffer (buf, l);
|
||||||
|
delete[] buf;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PrivateKeys::ToBase64 () const
|
std::string PrivateKeys::ToBase64 () const
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> buf(GetFullLen ());
|
uint8_t * buf = new uint8_t[GetFullLen ()];
|
||||||
size_t l = ToBuffer (buf.data (), buf.size ());
|
char * str = new char[GetFullLen ()*2];
|
||||||
return i2p::data::ByteStreamToBase64 (buf.data (), l);
|
size_t l = ToBuffer (buf, GetFullLen ());
|
||||||
|
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
|
||||||
|
str[l1] = 0;
|
||||||
|
delete[] buf;
|
||||||
|
std::string ret(str);
|
||||||
|
delete[] str;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||||
|
@ -644,11 +633,6 @@ namespace data
|
||||||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||||
return new i2p::crypto::RedDSA25519Signer (priv);
|
return new i2p::crypto::RedDSA25519Signer (priv);
|
||||||
break;
|
break;
|
||||||
#if OPENSSL_PQ
|
|
||||||
case SIGNING_KEY_TYPE_MLDSA44:
|
|
||||||
return new i2p::crypto::MLDSA44Signer (priv);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
|
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
|
||||||
}
|
}
|
||||||
|
@ -662,7 +646,8 @@ namespace data
|
||||||
|
|
||||||
size_t PrivateKeys::GetPrivateKeyLen () const
|
size_t PrivateKeys::GetPrivateKeyLen () const
|
||||||
{
|
{
|
||||||
return i2p::crypto::GetCryptoPrivateKeyLen (m_Public->GetCryptoKeyType ());
|
// private key length always 256, but type 4
|
||||||
|
return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t * PrivateKeys::GetPadding()
|
uint8_t * PrivateKeys::GetPadding()
|
||||||
|
@ -688,14 +673,15 @@ namespace data
|
||||||
return std::make_shared<i2p::crypto::ElGamalDecryptor>(key);
|
return std::make_shared<i2p::crypto::ElGamalDecryptor>(key);
|
||||||
break;
|
break;
|
||||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
|
||||||
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
|
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
|
||||||
break;
|
break;
|
||||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||||
|
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||||
return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key);
|
return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key);
|
||||||
break;
|
break;
|
||||||
|
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||||
|
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
|
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
|
||||||
};
|
};
|
||||||
|
@ -742,7 +728,9 @@ namespace data
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||||
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
|
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
|
||||||
|
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
#endif
|
||||||
// no break here
|
// no break here
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||||
i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub);
|
i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub);
|
||||||
|
@ -756,11 +744,6 @@ namespace data
|
||||||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||||
i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub);
|
i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub);
|
||||||
break;
|
break;
|
||||||
#if OPENSSL_PQ
|
|
||||||
case SIGNING_KEY_TYPE_MLDSA44:
|
|
||||||
i2p::crypto::CreateMLDSA44RandomKeys (priv, pub);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
||||||
i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1
|
i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1
|
||||||
|
@ -775,12 +758,13 @@ namespace data
|
||||||
i2p::crypto::GenerateElGamalKeyPair(priv, pub);
|
i2p::crypto::GenerateElGamalKeyPair(priv, pub);
|
||||||
break;
|
break;
|
||||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||||
|
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||||
i2p::crypto::CreateECIESP256RandomKeys (priv, pub);
|
i2p::crypto::CreateECIESP256RandomKeys (priv, pub);
|
||||||
break;
|
break;
|
||||||
|
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||||
|
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
|
||||||
|
break;
|
||||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD:
|
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
|
||||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
|
||||||
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
|
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -12,19 +12,14 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Base.h"
|
#include "Base.h"
|
||||||
#include "Signature.h"
|
#include "Signature.h"
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
class CryptoKeyEncryptor;
|
|
||||||
class CryptoKeyDecryptor;
|
|
||||||
}
|
|
||||||
namespace data
|
namespace data
|
||||||
{
|
{
|
||||||
typedef Tag<32> IdentHash;
|
typedef Tag<32> IdentHash;
|
||||||
|
@ -59,8 +54,6 @@ namespace data
|
||||||
Identity& operator=(const Keys& keys);
|
Identity& operator=(const Keys& keys);
|
||||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||||
IdentHash Hash () const;
|
IdentHash Hash () const;
|
||||||
operator uint8_t * () { return reinterpret_cast<uint8_t *>(this); }
|
|
||||||
operator const uint8_t * () const { return reinterpret_cast<const uint8_t *>(this); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Keys CreateRandomKeys ();
|
Keys CreateRandomKeys ();
|
||||||
|
@ -70,9 +63,8 @@ namespace data
|
||||||
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
|
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
|
||||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
|
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
|
||||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
|
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
|
||||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD = 5;
|
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
|
||||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD = 6;
|
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
|
||||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD = 7;
|
|
||||||
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
|
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
|
||||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
|
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
|
||||||
|
@ -82,11 +74,10 @@ namespace data
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
|
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
|
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
|
||||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
|
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
|
||||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // since openssl 3.0.0
|
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // not implemented
|
||||||
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9;
|
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9;
|
||||||
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB
|
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB
|
||||||
const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only
|
const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only
|
||||||
const uint16_t SIGNING_KEY_TYPE_MLDSA44 = 12;
|
|
||||||
|
|
||||||
typedef uint16_t SigningKeyType;
|
typedef uint16_t SigningKeyType;
|
||||||
typedef uint16_t CryptoKeyType;
|
typedef uint16_t CryptoKeyType;
|
||||||
|
@ -108,7 +99,7 @@ namespace data
|
||||||
|
|
||||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
||||||
size_t FromBase64(std::string_view s);
|
size_t FromBase64(const std::string& s);
|
||||||
std::string ToBase64 () const;
|
std::string ToBase64 () const;
|
||||||
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
|
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
|
||||||
|
|
||||||
|
@ -142,7 +133,7 @@ namespace data
|
||||||
IdentHash m_IdentHash;
|
IdentHash m_IdentHash;
|
||||||
std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
||||||
size_t m_ExtendedLen;
|
size_t m_ExtendedLen;
|
||||||
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; // TODO: support PQ keys
|
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer
|
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer
|
||||||
|
@ -171,7 +162,7 @@ namespace data
|
||||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
||||||
|
|
||||||
size_t FromBase64(std::string_view s);
|
size_t FromBase64(const std::string& s);
|
||||||
std::string ToBase64 () const;
|
std::string ToBase64 () const;
|
||||||
|
|
||||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
|
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -14,7 +14,6 @@
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
#include "NetDb.hpp"
|
#include "NetDb.hpp"
|
||||||
#include "Tunnel.h"
|
#include "Tunnel.h"
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "LeaseSet.h"
|
#include "LeaseSet.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -400,7 +399,6 @@ namespace data
|
||||||
offset += propertiesLen; // skip for now. TODO: implement properties
|
offset += propertiesLen; // skip for now. TODO: implement properties
|
||||||
// key sections
|
// key sections
|
||||||
CryptoKeyType preferredKeyType = m_EncryptionType;
|
CryptoKeyType preferredKeyType = m_EncryptionType;
|
||||||
m_EncryptionType = 0;
|
|
||||||
bool preferredKeyFound = false;
|
bool preferredKeyFound = false;
|
||||||
if (offset + 1 > len) return 0;
|
if (offset + 1 > len) return 0;
|
||||||
int numKeySections = buf[offset]; offset++;
|
int numKeySections = buf[offset]; offset++;
|
||||||
|
@ -412,39 +410,21 @@ namespace data
|
||||||
if (offset + encryptionKeyLen > len) return 0;
|
if (offset + encryptionKeyLen > len) return 0;
|
||||||
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
|
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
|
||||||
{
|
{
|
||||||
// we pick max key type if preferred not found
|
// we pick first valid key if preferred not found
|
||||||
#if !OPENSSL_PQ
|
|
||||||
if (keyType <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // skip PQ keys if not supported
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (keyType == preferredKeyType || !m_Encryptor || keyType > m_EncryptionType)
|
|
||||||
{
|
|
||||||
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
|
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
|
||||||
if (encryptor)
|
if (encryptor && (!m_Encryptor || keyType == preferredKeyType))
|
||||||
{
|
{
|
||||||
m_Encryptor = encryptor; // TODO: atomic
|
m_Encryptor = encryptor; // TODO: atomic
|
||||||
m_EncryptionType = keyType;
|
m_EncryptionType = keyType;
|
||||||
if (keyType == preferredKeyType) preferredKeyFound = true;
|
if (keyType == preferredKeyType) preferredKeyFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
offset += encryptionKeyLen;
|
offset += encryptionKeyLen;
|
||||||
}
|
}
|
||||||
// leases
|
// leases
|
||||||
if (offset + 1 > len) return 0;
|
if (offset + 1 > len) return 0;
|
||||||
int numLeases = buf[offset]; offset++;
|
int numLeases = buf[offset]; offset++;
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
if (GetExpirationTime () > ts + LEASESET_EXPIRATION_TIME_THRESHOLD)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "LeaseSet2: Expiration time is from future ", GetExpirationTime ()/1000LL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ts > m_PublishedTimestamp*1000LL + LEASESET_EXPIRATION_TIME_THRESHOLD)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "LeaseSet2: Published time is too old ", m_PublishedTimestamp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (IsStoreLeases ())
|
if (IsStoreLeases ())
|
||||||
{
|
{
|
||||||
UpdateLeasesBegin ();
|
UpdateLeasesBegin ();
|
||||||
|
@ -455,11 +435,6 @@ namespace data
|
||||||
lease.tunnelGateway = buf + offset; offset += 32; // gateway
|
lease.tunnelGateway = buf + offset; offset += 32; // gateway
|
||||||
lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID
|
lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID
|
||||||
lease.endDate = bufbe32toh (buf + offset)*1000LL; offset += 4; // end date
|
lease.endDate = bufbe32toh (buf + offset)*1000LL; offset += 4; // end date
|
||||||
if (lease.endDate > ts + LEASESET_EXPIRATION_TIME_THRESHOLD)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "LeaseSet2: Lease end date is from future ", lease.endDate);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
UpdateLease (lease, ts);
|
UpdateLease (lease, ts);
|
||||||
}
|
}
|
||||||
UpdateLeasesEnd ();
|
UpdateLeasesEnd ();
|
||||||
|
@ -848,7 +823,7 @@ namespace data
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||||
const EncryptionKeys& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||||
bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted):
|
bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted):
|
||||||
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
|
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
|
||||||
{
|
{
|
||||||
|
@ -858,7 +833,7 @@ namespace data
|
||||||
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
|
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
|
||||||
size_t keySectionsLen = 0;
|
size_t keySectionsLen = 0;
|
||||||
for (const auto& it: encryptionKeys)
|
for (const auto& it: encryptionKeys)
|
||||||
keySectionsLen += 2/*key type*/ + 2/*key len*/ + it->pub.size()/*key*/;
|
keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/;
|
||||||
m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ +
|
m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ +
|
||||||
1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen ();
|
1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen ();
|
||||||
uint16_t flags = 0;
|
uint16_t flags = 0;
|
||||||
|
@ -893,9 +868,9 @@ namespace data
|
||||||
m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key
|
m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key
|
||||||
for (const auto& it: encryptionKeys)
|
for (const auto& it: encryptionKeys)
|
||||||
{
|
{
|
||||||
htobe16buf (m_Buffer + offset, it->keyType); offset += 2; // key type
|
htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type
|
||||||
htobe16buf (m_Buffer + offset, it->pub.size()); offset += 2; // key len
|
htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len
|
||||||
memcpy (m_Buffer + offset, it->pub.data(), it->pub.size()); offset += it->pub.size(); // key
|
memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key
|
||||||
}
|
}
|
||||||
// leases
|
// leases
|
||||||
uint32_t expirationTime = 0; // in seconds
|
uint32_t expirationTime = 0; // in seconds
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -12,14 +12,12 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
#include "Blinding.h"
|
#include "Blinding.h"
|
||||||
#include "CryptoKey.h"
|
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
|
@ -64,7 +62,6 @@ namespace data
|
||||||
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
|
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
|
||||||
const size_t LEASE2_SIZE = 40; // 32 + 4 + 4
|
const size_t LEASE2_SIZE = 40; // 32 + 4 + 4
|
||||||
const uint8_t MAX_NUM_LEASES = 16;
|
const uint8_t MAX_NUM_LEASES = 16;
|
||||||
const uint64_t LEASESET_EXPIRATION_TIME_THRESHOLD = 12*60*1000; // in milliseconds
|
|
||||||
|
|
||||||
const uint8_t NETDB_STORE_TYPE_LEASESET = 1;
|
const uint8_t NETDB_STORE_TYPE_LEASESET = 1;
|
||||||
class LeaseSet: public RoutingDestination
|
class LeaseSet: public RoutingDestination
|
||||||
|
@ -152,8 +149,8 @@ namespace data
|
||||||
public:
|
public:
|
||||||
|
|
||||||
LeaseSet2 (uint8_t storeType): LeaseSet (true), m_StoreType (storeType) {}; // for update
|
LeaseSet2 (uint8_t storeType): LeaseSet (true), m_StoreType (storeType) {}; // for update
|
||||||
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
|
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
|
||||||
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // store type 5, called from local netdb only
|
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
|
||||||
uint8_t GetStoreType () const { return m_StoreType; };
|
uint8_t GetStoreType () const { return m_StoreType; };
|
||||||
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
|
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
|
||||||
bool IsPublic () const { return m_IsPublic; };
|
bool IsPublic () const { return m_IsPublic; };
|
||||||
|
@ -183,7 +180,7 @@ namespace data
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint8_t m_StoreType;
|
uint8_t m_StoreType;
|
||||||
uint32_t m_PublishedTimestamp = 0; // seconds
|
uint32_t m_PublishedTimestamp = 0;
|
||||||
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
|
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
|
||||||
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
|
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
|
||||||
CryptoKeyType m_EncryptionType;
|
CryptoKeyType m_EncryptionType;
|
||||||
|
@ -249,10 +246,15 @@ namespace data
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef std::list<std::shared_ptr<const i2p::crypto::LocalEncryptionKey> > EncryptionKeys;
|
struct KeySection
|
||||||
|
{
|
||||||
|
uint16_t keyType, keyLen;
|
||||||
|
const uint8_t * encryptionPublicKey;
|
||||||
|
};
|
||||||
|
typedef std::vector<KeySection> KeySections;
|
||||||
|
|
||||||
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||||
const EncryptionKeys& encryptionKeys,
|
const KeySections& encryptionKeys,
|
||||||
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||||
bool isPublic, uint64_t publishedTimestamp,
|
bool isPublic, uint64_t publishedTimestamp,
|
||||||
bool isPublishedEncrypted = false);
|
bool isPublishedEncrypted = false);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -42,29 +42,28 @@ namespace transport
|
||||||
delete[] m_SessionConfirmedBuffer;
|
delete[] m_SessionConfirmedBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
|
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
|
||||||
{
|
{
|
||||||
i2p::crypto::InitNoiseXKState (*this, rs);
|
i2p::crypto::InitNoiseXKState (*this, rs);
|
||||||
// h = SHA256(h || epub)
|
// h = SHA256(h || epub)
|
||||||
MixHash (epub, 32);
|
MixHash (epub, 32);
|
||||||
// x25519 between pub and priv
|
// x25519 between pub and priv
|
||||||
uint8_t inputKeyMaterial[32];
|
uint8_t inputKeyMaterial[32];
|
||||||
if (!priv.Agree (pub, inputKeyMaterial)) return false;
|
priv.Agree (pub, inputKeyMaterial);
|
||||||
MixKey (inputKeyMaterial);
|
MixKey (inputKeyMaterial);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KDF1Alice ()
|
void NTCP2Establisher::KDF1Alice ()
|
||||||
{
|
{
|
||||||
return KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
|
KeyDerivationFunction1 (m_RemoteStaticKey, *m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KDF1Bob ()
|
void NTCP2Establisher::KDF1Bob ()
|
||||||
{
|
{
|
||||||
return KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
||||||
{
|
{
|
||||||
MixHash (sessionRequest + 32, 32); // encrypted payload
|
MixHash (sessionRequest + 32, 32); // encrypted payload
|
||||||
|
|
||||||
|
@ -75,35 +74,33 @@ namespace transport
|
||||||
|
|
||||||
// x25519 between remote pub and ephemaral priv
|
// x25519 between remote pub and ephemaral priv
|
||||||
uint8_t inputKeyMaterial[32];
|
uint8_t inputKeyMaterial[32];
|
||||||
if (!m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial);
|
||||||
|
|
||||||
MixKey (inputKeyMaterial);
|
MixKey (inputKeyMaterial);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KDF2Alice ()
|
void NTCP2Establisher::KDF2Alice ()
|
||||||
{
|
{
|
||||||
return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ());
|
KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KDF2Bob ()
|
void NTCP2Establisher::KDF2Bob ()
|
||||||
{
|
{
|
||||||
return KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ());
|
KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KDF3Alice ()
|
void NTCP2Establisher::KDF3Alice ()
|
||||||
{
|
{
|
||||||
uint8_t inputKeyMaterial[32];
|
uint8_t inputKeyMaterial[32];
|
||||||
if (!i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial);
|
||||||
MixKey (inputKeyMaterial);
|
MixKey (inputKeyMaterial);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::KDF3Bob ()
|
void NTCP2Establisher::KDF3Bob ()
|
||||||
{
|
{
|
||||||
uint8_t inputKeyMaterial[32];
|
uint8_t inputKeyMaterial[32];
|
||||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial)) return false;
|
m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial);
|
||||||
MixKey (inputKeyMaterial);
|
MixKey (inputKeyMaterial);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Establisher::CreateEphemeralKey ()
|
void NTCP2Establisher::CreateEphemeralKey ()
|
||||||
|
@ -111,7 +108,7 @@ namespace transport
|
||||||
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
|
void NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
|
||||||
{
|
{
|
||||||
// create buffer and fill padding
|
// create buffer and fill padding
|
||||||
auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
|
auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
|
||||||
|
@ -120,10 +117,11 @@ namespace transport
|
||||||
// encrypt X
|
// encrypt X
|
||||||
i2p::crypto::CBCEncryption encryption;
|
i2p::crypto::CBCEncryption encryption;
|
||||||
encryption.SetKey (m_RemoteIdentHash);
|
encryption.SetKey (m_RemoteIdentHash);
|
||||||
encryption.Encrypt (GetPub (), 32, m_IV, m_SessionRequestBuffer); // X
|
encryption.SetIV (m_IV);
|
||||||
memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated
|
encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X
|
||||||
|
encryption.GetIV (m_IV); // save IV for SessionCreated
|
||||||
// encryption key for next block
|
// encryption key for next block
|
||||||
if (!KDF1Alice ()) return false;
|
KDF1Alice ();
|
||||||
// fill options
|
// fill options
|
||||||
uint8_t options[32]; // actual options size is 16 bytes
|
uint8_t options[32]; // actual options size is 16 bytes
|
||||||
memset (options, 0, 16);
|
memset (options, 0, 16);
|
||||||
|
@ -145,16 +143,13 @@ namespace transport
|
||||||
// 2 bytes reserved
|
// 2 bytes reserved
|
||||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
|
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
|
||||||
// 4 bytes reserved
|
// 4 bytes reserved
|
||||||
// encrypt options
|
// sign and encrypt options, use m_H as AD
|
||||||
if (!Encrypt (options, m_SessionRequestBuffer + 32, 16))
|
uint8_t nonce[12];
|
||||||
{
|
memset (nonce, 0, 12); // set nonce to zero
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionRequest failed to encrypt options");
|
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
|
void NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
|
||||||
{
|
{
|
||||||
auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
|
auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
|
||||||
m_SessionCreatedBufferLen = paddingLen + 64;
|
m_SessionCreatedBufferLen = paddingLen + 64;
|
||||||
|
@ -162,23 +157,22 @@ namespace transport
|
||||||
// encrypt Y
|
// encrypt Y
|
||||||
i2p::crypto::CBCEncryption encryption;
|
i2p::crypto::CBCEncryption encryption;
|
||||||
encryption.SetKey (i2p::context.GetIdentHash ());
|
encryption.SetKey (i2p::context.GetIdentHash ());
|
||||||
encryption.Encrypt (GetPub (), 32, m_IV, m_SessionCreatedBuffer); // Y
|
encryption.SetIV (m_IV);
|
||||||
|
encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y
|
||||||
// encryption key for next block (m_K)
|
// encryption key for next block (m_K)
|
||||||
if (!KDF2Bob ()) return false;
|
KDF2Bob ();
|
||||||
uint8_t options[16];
|
uint8_t options[16];
|
||||||
memset (options, 0, 16);
|
memset (options, 0, 16);
|
||||||
htobe16buf (options + 2, paddingLen); // padLen
|
htobe16buf (options + 2, paddingLen); // padLen
|
||||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds
|
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds
|
||||||
// encrypt options
|
// sign and encrypt options, use m_H as AD
|
||||||
if (!Encrypt (options, m_SessionCreatedBuffer + 32, 16))
|
uint8_t nonce[12];
|
||||||
{
|
memset (nonce, 0, 12); // set nonce to zero
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionCreated failed to encrypt options");
|
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart1 ()
|
void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||||
{
|
{
|
||||||
// update AD
|
// update AD
|
||||||
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
||||||
|
@ -186,31 +180,21 @@ namespace transport
|
||||||
if (paddingLength > 0)
|
if (paddingLength > 0)
|
||||||
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
||||||
|
|
||||||
// part1 48 bytes, n = 1
|
// part1 48 bytes
|
||||||
if (!Encrypt (i2p::context.GetNTCP2StaticPublicKey (), m_SessionConfirmedBuffer, 32))
|
i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part1");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 ()
|
void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
|
||||||
{
|
{
|
||||||
// part 2
|
// part 2
|
||||||
// update AD again
|
// update AD again
|
||||||
MixHash (m_SessionConfirmedBuffer, 48);
|
MixHash (m_SessionConfirmedBuffer, 48);
|
||||||
// encrypt m3p2, it must be filled in SessionRequest
|
// encrypt m3p2, it must be filled in SessionRequest
|
||||||
if (!KDF3Alice ()) return false; // MixKey, n = 0
|
KDF3Alice ();
|
||||||
uint8_t * m3p2 = m_SessionConfirmedBuffer + 48;
|
uint8_t * m3p2 = m_SessionConfirmedBuffer + 48;
|
||||||
if (!Encrypt (m3p2, m3p2, m3p2Len - 16))
|
i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part2");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// update h again
|
// update h again
|
||||||
MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext)
|
MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext)
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew)
|
bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew)
|
||||||
|
@ -219,17 +203,15 @@ namespace transport
|
||||||
// decrypt X
|
// decrypt X
|
||||||
i2p::crypto::CBCDecryption decryption;
|
i2p::crypto::CBCDecryption decryption;
|
||||||
decryption.SetKey (i2p::context.GetIdentHash ());
|
decryption.SetKey (i2p::context.GetIdentHash ());
|
||||||
decryption.Decrypt (m_SessionRequestBuffer, 32, i2p::context.GetNTCP2IV (), GetRemotePub ());
|
decryption.SetIV (i2p::context.GetNTCP2IV ());
|
||||||
memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated
|
decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ());
|
||||||
|
decryption.GetIV (m_IV); // save IV for SessionCreated
|
||||||
// decryption key for next block
|
// decryption key for next block
|
||||||
if (!KDF1Bob ())
|
KDF1Bob ();
|
||||||
{
|
// verify MAC and decrypt options block (32 bytes), use m_H as AD
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionRequest KDF failed");
|
uint8_t nonce[12], options[16];
|
||||||
return false;
|
memset (nonce, 0, 12); // set nonce to zero
|
||||||
}
|
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt
|
||||||
// verify MAC and decrypt options block (32 bytes)
|
|
||||||
uint8_t options[16];
|
|
||||||
if (Decrypt (m_SessionRequestBuffer + 32, options, 16))
|
|
||||||
{
|
{
|
||||||
// options
|
// options
|
||||||
if (options[0] && options[0] != i2p::context.GetNetID ())
|
if (options[0] && options[0] != i2p::context.GetNetID ())
|
||||||
|
@ -277,16 +259,15 @@ namespace transport
|
||||||
// decrypt Y
|
// decrypt Y
|
||||||
i2p::crypto::CBCDecryption decryption;
|
i2p::crypto::CBCDecryption decryption;
|
||||||
decryption.SetKey (m_RemoteIdentHash);
|
decryption.SetKey (m_RemoteIdentHash);
|
||||||
decryption.Decrypt (m_SessionCreatedBuffer, 32, m_IV, GetRemotePub ());
|
decryption.SetIV (m_IV);
|
||||||
|
decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ());
|
||||||
// decryption key for next block (m_K)
|
// decryption key for next block (m_K)
|
||||||
if (!KDF2Alice ())
|
KDF2Alice ();
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionCreated KDF failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// decrypt and verify MAC
|
// decrypt and verify MAC
|
||||||
uint8_t payload[16];
|
uint8_t payload[16];
|
||||||
if (Decrypt (m_SessionCreatedBuffer + 32, payload, 16))
|
uint8_t nonce[12];
|
||||||
|
memset (nonce, 0, 12); // set nonce to zero
|
||||||
|
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, GetH (), 32, GetK (), nonce, payload, 16, false)) // decrypt
|
||||||
{
|
{
|
||||||
// options
|
// options
|
||||||
paddingLen = bufbe16toh(payload + 2);
|
paddingLen = bufbe16toh(payload + 2);
|
||||||
|
@ -307,7 +288,7 @@ namespace transport
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 ()
|
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||||
{
|
{
|
||||||
// update AD
|
// update AD
|
||||||
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
||||||
|
@ -315,8 +296,7 @@ namespace transport
|
||||||
if (paddingLength > 0)
|
if (paddingLength > 0)
|
||||||
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
||||||
|
|
||||||
// decrypt S, n = 1
|
if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S
|
||||||
if (!Decrypt (m_SessionConfirmedBuffer, m_RemoteStaticKey, 32))
|
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
|
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
|
||||||
return false;
|
return false;
|
||||||
|
@ -324,17 +304,13 @@ namespace transport
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf)
|
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf)
|
||||||
{
|
{
|
||||||
// update AD again
|
// update AD again
|
||||||
MixHash (m_SessionConfirmedBuffer, 48);
|
MixHash (m_SessionConfirmedBuffer, 48);
|
||||||
|
|
||||||
if (!KDF3Bob ()) // MixKey, n = 0
|
KDF3Bob ();
|
||||||
{
|
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 KDF failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Decrypt (m_SessionConfirmedBuffer + 48, m3p2Buf, m3p2Len - 16))
|
|
||||||
// calculate new h again for KDF data
|
// calculate new h again for KDF data
|
||||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||||
else
|
else
|
||||||
|
@ -351,7 +327,6 @@ namespace transport
|
||||||
m_Server (server), m_Socket (m_Server.GetService ()),
|
m_Server (server), m_Socket (m_Server.GetService ()),
|
||||||
m_IsEstablished (false), m_IsTerminated (false),
|
m_IsEstablished (false), m_IsTerminated (false),
|
||||||
m_Establisher (new NTCP2Establisher),
|
m_Establisher (new NTCP2Establisher),
|
||||||
m_SendKey (nullptr), m_ReceiveKey (nullptr),
|
|
||||||
#if OPENSSL_SIPHASH
|
#if OPENSSL_SIPHASH
|
||||||
m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
|
m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
|
||||||
#else
|
#else
|
||||||
|
@ -400,8 +375,6 @@ namespace transport
|
||||||
m_Socket.close ();
|
m_Socket.close ();
|
||||||
transports.PeerDisconnected (shared_from_this ());
|
transports.PeerDisconnected (shared_from_this ());
|
||||||
m_Server.RemoveNTCP2Session (shared_from_this ());
|
m_Server.RemoveNTCP2Session (shared_from_this ());
|
||||||
if (!m_IntermediateQueue.empty ())
|
|
||||||
m_SendQueue.splice (m_SendQueue.end (), m_IntermediateQueue);
|
|
||||||
for (auto& it: m_SendQueue)
|
for (auto& it: m_SendQueue)
|
||||||
it->Drop ();
|
it->Drop ();
|
||||||
m_SendQueue.clear ();
|
m_SendQueue.clear ();
|
||||||
|
@ -431,7 +404,7 @@ namespace transport
|
||||||
|
|
||||||
void NTCP2Session::Done ()
|
void NTCP2Session::Done ()
|
||||||
{
|
{
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Session::Established ()
|
void NTCP2Session::Established ()
|
||||||
|
@ -491,12 +464,7 @@ namespace transport
|
||||||
|
|
||||||
void NTCP2Session::SendSessionRequest ()
|
void NTCP2Session::SendSessionRequest ()
|
||||||
{
|
{
|
||||||
if (!m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ()))
|
m_Establisher->CreateSessionRequestMessage (m_Server.GetRng ());
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "NTCP2: Send SessionRequest KDF failed");
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// send message
|
// send message
|
||||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (),
|
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (),
|
||||||
|
@ -521,6 +489,7 @@ namespace transport
|
||||||
|
|
||||||
void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||||
{
|
{
|
||||||
|
(void) bytes_transferred;
|
||||||
if (ecode)
|
if (ecode)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ());
|
LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ());
|
||||||
|
@ -528,17 +497,7 @@ namespace transport
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
boost::asio::post (m_Server.GetEstablisherService (),
|
LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred);
|
||||||
[s = shared_from_this (), bytes_transferred] ()
|
|
||||||
{
|
|
||||||
s->ProcessSessionRequest (bytes_transferred);;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCP2Session::ProcessSessionRequest (size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "NTCP2: SessionRequest received ", len);
|
|
||||||
uint16_t paddingLen = 0;
|
uint16_t paddingLen = 0;
|
||||||
bool clockSkew = false;
|
bool clockSkew = false;
|
||||||
if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew))
|
if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew))
|
||||||
|
@ -547,7 +506,7 @@ namespace transport
|
||||||
{
|
{
|
||||||
// we don't care about padding, send SessionCreated and close session
|
// we don't care about padding, send SessionCreated and close session
|
||||||
SendSessionCreated ();
|
SendSessionCreated ();
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||||
}
|
}
|
||||||
else if (paddingLen > 0)
|
else if (paddingLen > 0)
|
||||||
{
|
{
|
||||||
|
@ -559,14 +518,15 @@ namespace transport
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long");
|
LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long");
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
Terminate ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
SendSessionCreated ();
|
SendSessionCreated ();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
Terminate ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||||
|
@ -577,23 +537,12 @@ namespace transport
|
||||||
Terminate ();
|
Terminate ();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
SendSessionCreated ();
|
||||||
boost::asio::post (m_Server.GetEstablisherService (),
|
|
||||||
[s = shared_from_this ()] ()
|
|
||||||
{
|
|
||||||
s->SendSessionCreated ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Session::SendSessionCreated ()
|
void NTCP2Session::SendSessionCreated ()
|
||||||
{
|
{
|
||||||
if (!m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ()))
|
m_Establisher->CreateSessionCreatedMessage (m_Server.GetRng ());
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "NTCP2: Send SessionCreated KDF failed");
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// send message
|
// send message
|
||||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (),
|
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (),
|
||||||
|
@ -610,17 +559,7 @@ namespace transport
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
||||||
boost::asio::post (m_Server.GetEstablisherService (),
|
LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred);
|
||||||
[s = shared_from_this (), bytes_transferred] ()
|
|
||||||
{
|
|
||||||
s->ProcessSessionCreated (bytes_transferred);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCP2Session::ProcessSessionCreated (size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "NTCP2: SessionCreated received ", len);
|
|
||||||
uint16_t paddingLen = 0;
|
uint16_t paddingLen = 0;
|
||||||
if (m_Establisher->ProcessSessionCreatedMessage (paddingLen))
|
if (m_Establisher->ProcessSessionCreatedMessage (paddingLen))
|
||||||
{
|
{
|
||||||
|
@ -634,7 +573,7 @@ namespace transport
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long");
|
LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long");
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
Terminate ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -644,7 +583,8 @@ namespace transport
|
||||||
{
|
{
|
||||||
if (GetRemoteIdentity ())
|
if (GetRemoteIdentity ())
|
||||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key
|
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
Terminate ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,27 +598,17 @@ namespace transport
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_Establisher->m_SessionCreatedBufferLen += bytes_transferred;
|
m_Establisher->m_SessionCreatedBufferLen += bytes_transferred;
|
||||||
boost::asio::post (m_Server.GetEstablisherService (),
|
SendSessionConfirmed ();
|
||||||
[s = shared_from_this ()] ()
|
|
||||||
{
|
|
||||||
s->SendSessionConfirmed ();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Session::SendSessionConfirmed ()
|
void NTCP2Session::SendSessionConfirmed ()
|
||||||
{
|
{
|
||||||
if (!m_Establisher->CreateSessionConfirmedMessagePart1 ())
|
uint8_t nonce[12];
|
||||||
{
|
CreateNonce (1, nonce); // set nonce to 1
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
m_Establisher->CreateSessionConfirmedMessagePart1 (nonce);
|
||||||
return;
|
memset (nonce, 0, 12); // set nonce back to 0
|
||||||
}
|
m_Establisher->CreateSessionConfirmedMessagePart2 (nonce);
|
||||||
if (!m_Establisher->CreateSessionConfirmedMessagePart2 ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "NTCP2: Send SessionConfirmed Part2 KDF failed");
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// send message
|
// send message
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (),
|
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (),
|
||||||
std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||||
|
@ -730,7 +660,6 @@ namespace transport
|
||||||
|
|
||||||
void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||||
{
|
{
|
||||||
(void) bytes_transferred;
|
|
||||||
if (ecode)
|
if (ecode)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ());
|
LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ());
|
||||||
|
@ -739,56 +668,17 @@ namespace transport
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
||||||
boost::asio::post (m_Server.GetEstablisherService (),
|
|
||||||
[s = shared_from_this ()] ()
|
|
||||||
{
|
|
||||||
s->ProcessSessionConfirmed ();;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCP2Session::ProcessSessionConfirmed ()
|
|
||||||
{
|
|
||||||
// run on establisher thread
|
|
||||||
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
|
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
|
||||||
// part 1
|
// part 1
|
||||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 ())
|
uint8_t nonce[12];
|
||||||
|
CreateNonce (1, nonce);
|
||||||
|
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
|
||||||
{
|
{
|
||||||
// part 2
|
// part 2
|
||||||
auto buf = std::make_shared<std::vector<uint8_t> > (m_Establisher->m3p2Len - 16); // -MAC
|
std::vector<uint8_t> buf(m_Establisher->m3p2Len - 16); // -MAC
|
||||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (buf->data ())) // TODO:handle in establisher thread
|
memset (nonce, 0, 12); // set nonce to 0 again
|
||||||
|
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ()))
|
||||||
{
|
{
|
||||||
// payload
|
|
||||||
// RI block must be first
|
|
||||||
if ((*buf)[0] != eNTCP2BlkRouterInfo)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)(*buf)[0], " in SessionConfirmed");
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto size = bufbe16toh (buf->data () + 1);
|
|
||||||
if (size > buf->size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boost::asio::post (m_Server.GetService (),
|
|
||||||
[s = shared_from_this (), buf, size] ()
|
|
||||||
{
|
|
||||||
s->EstablishSessionAfterSessionConfirmed (buf, size);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCP2Session::EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size)
|
|
||||||
{
|
|
||||||
// run on main NTCP2 thread
|
|
||||||
KeyDerivationFunctionDataPhase ();
|
KeyDerivationFunctionDataPhase ();
|
||||||
// Bob data phase keys
|
// Bob data phase keys
|
||||||
m_SendKey = m_Kba;
|
m_SendKey = m_Kba;
|
||||||
|
@ -796,9 +686,23 @@ namespace transport
|
||||||
SetSipKeys (m_Sipkeysba, m_Sipkeysab);
|
SetSipKeys (m_Sipkeysba, m_Sipkeysab);
|
||||||
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8);
|
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8);
|
||||||
memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8);
|
memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8);
|
||||||
// we need to set keys for SendTerminationAndTerminate
|
// payload
|
||||||
|
// process RI
|
||||||
|
if (buf[0] != eNTCP2BlkRouterInfo)
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)buf[0], " in SessionConfirmed");
|
||||||
|
Terminate ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto size = bufbe16toh (buf.data () + 1);
|
||||||
|
if (size > buf.size () - 3 || size > i2p::data::MAX_RI_BUFFER_SIZE + 1)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||||
|
Terminate ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
// TODO: check flag
|
// TODO: check flag
|
||||||
i2p::data::RouterInfo ri (buf->data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||||
if (ri.IsUnreachable ())
|
if (ri.IsUnreachable ())
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
|
LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
|
||||||
|
@ -828,22 +732,17 @@ namespace transport
|
||||||
Terminate ();
|
Terminate ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
|
||||||
bool isOlder = false;
|
|
||||||
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
|
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
|
||||||
{
|
{
|
||||||
// received RouterInfo is older than one in netdb
|
// received RouterInfo is older than one in netdb
|
||||||
isOlder = true;
|
profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
|
||||||
if (ri1->HasProfile ())
|
|
||||||
{
|
|
||||||
auto profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
|
|
||||||
if (profile && profile->IsDuplicated ())
|
if (profile && profile->IsDuplicated ())
|
||||||
{
|
{
|
||||||
SendTerminationAndTerminate (eNTCP2Banned);
|
SendTerminationAndTerminate (eNTCP2Banned);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
|
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
|
||||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
|
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
|
||||||
|
@ -858,18 +757,14 @@ namespace transport
|
||||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
|
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
|
||||||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
||||||
{
|
{
|
||||||
if (isOlder) // older router?
|
if (profile) // older router?
|
||||||
i2p::data::UpdateRouterProfile (ri1->GetIdentHash (),
|
profile->Duplicated (); // mark router as duplicated in profile
|
||||||
[](std::shared_ptr<i2p::data::RouterProfile> profile)
|
|
||||||
{
|
|
||||||
if (profile) profile->Duplicated (); // mark router as duplicated in profile
|
|
||||||
});
|
|
||||||
else
|
else
|
||||||
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||||
SendTerminationAndTerminate (eNTCP2Banned);
|
SendTerminationAndTerminate (eNTCP2Banned);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: process options block
|
// TODO: process options
|
||||||
|
|
||||||
// ready to communicate
|
// ready to communicate
|
||||||
SetRemoteIdentity (ri1->GetRouterIdentity ());
|
SetRemoteIdentity (ri1->GetRouterIdentity ());
|
||||||
|
@ -881,6 +776,13 @@ namespace transport
|
||||||
else
|
else
|
||||||
Terminate ();
|
Terminate ();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
Terminate ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Terminate ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey)
|
void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey)
|
||||||
{
|
{
|
||||||
|
@ -907,11 +809,7 @@ namespace transport
|
||||||
void NTCP2Session::ClientLogin ()
|
void NTCP2Session::ClientLogin ()
|
||||||
{
|
{
|
||||||
m_Establisher->CreateEphemeralKey ();
|
m_Establisher->CreateEphemeralKey ();
|
||||||
boost::asio::post (m_Server.GetEstablisherService (),
|
SendSessionRequest ();
|
||||||
[s = shared_from_this ()] ()
|
|
||||||
{
|
|
||||||
s->SendSessionRequest ();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Session::ServerLogin ()
|
void NTCP2Session::ServerLogin ()
|
||||||
|
@ -1009,7 +907,7 @@ namespace transport
|
||||||
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred + 2);
|
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred + 2);
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++;
|
CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++;
|
||||||
if (m_Server.AEADChaCha20Poly1305Decrypt (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen))
|
if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false))
|
||||||
{
|
{
|
||||||
LogPrint (eLogDebug, "NTCP2: Received message decrypted");
|
LogPrint (eLogDebug, "NTCP2: Received message decrypted");
|
||||||
ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16);
|
ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16);
|
||||||
|
@ -1198,7 +1096,7 @@ namespace transport
|
||||||
}
|
}
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
|
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
|
||||||
m_Server.AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers
|
i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers
|
||||||
SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 5); // frame length right before first block
|
SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 5); // frame length right before first block
|
||||||
|
|
||||||
// send buffers
|
// send buffers
|
||||||
|
@ -1229,7 +1127,7 @@ namespace transport
|
||||||
// encrypt
|
// encrypt
|
||||||
uint8_t nonce[12];
|
uint8_t nonce[12];
|
||||||
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
|
CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++;
|
||||||
m_Server.AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2);
|
i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2);
|
||||||
SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer);
|
SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer);
|
||||||
// send
|
// send
|
||||||
m_IsSending = true;
|
m_IsSending = true;
|
||||||
|
@ -1309,7 +1207,7 @@ namespace transport
|
||||||
void NTCP2Session::MoveSendQueue (std::shared_ptr<NTCP2Session> other)
|
void NTCP2Session::MoveSendQueue (std::shared_ptr<NTCP2Session> other)
|
||||||
{
|
{
|
||||||
if (!other || m_SendQueue.empty ()) return;
|
if (!other || m_SendQueue.empty ()) return;
|
||||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
std::vector<std::shared_ptr<I2NPMessage> > msgs;
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
for (auto it: m_SendQueue)
|
for (auto it: m_SendQueue)
|
||||||
if (!it->IsExpired (ts))
|
if (!it->IsExpired (ts))
|
||||||
|
@ -1318,7 +1216,7 @@ namespace transport
|
||||||
it->Drop ();
|
it->Drop ();
|
||||||
m_SendQueue.clear ();
|
m_SendQueue.clear ();
|
||||||
if (!msgs.empty ())
|
if (!msgs.empty ())
|
||||||
other->SendI2NPMessages (msgs);
|
other->PostI2NPMessages (msgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len)
|
size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len)
|
||||||
|
@ -1396,45 +1294,23 @@ namespace transport
|
||||||
void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason reason)
|
void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason reason)
|
||||||
{
|
{
|
||||||
SendTermination (reason);
|
SendTermination (reason);
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go
|
m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); // let termination message go
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Session::SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs)
|
void NTCP2Session::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||||
{
|
{
|
||||||
if (m_IsTerminated || msgs.empty ())
|
m_Server.GetService ().post (std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this (), msgs));
|
||||||
{
|
|
||||||
msgs.clear ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool empty = false;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_IntermediateQueueMutex);
|
|
||||||
empty = m_IntermediateQueue.empty ();
|
|
||||||
m_IntermediateQueue.splice (m_IntermediateQueue.end (), msgs);
|
|
||||||
}
|
|
||||||
if (empty)
|
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this ()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Session::PostI2NPMessages ()
|
void NTCP2Session::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
|
||||||
{
|
{
|
||||||
if (m_IsTerminated) return;
|
if (m_IsTerminated) return;
|
||||||
std::list<std::shared_ptr<I2NPMessage> > msgs;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_IntermediateQueueMutex);
|
|
||||||
m_IntermediateQueue.swap (msgs);
|
|
||||||
}
|
|
||||||
bool isSemiFull = m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE/2;
|
bool isSemiFull = m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE/2;
|
||||||
if (isSemiFull)
|
|
||||||
{
|
|
||||||
for (auto it: msgs)
|
for (auto it: msgs)
|
||||||
if (it->onDrop)
|
if (isSemiFull && it->onDrop)
|
||||||
it->Drop (); // drop earlier because we can handle it
|
it->Drop (); // drop earlier because we can handle it
|
||||||
else
|
else
|
||||||
m_SendQueue.push_back (std::move (it));
|
m_SendQueue.push_back (std::move (it));
|
||||||
}
|
|
||||||
else
|
|
||||||
m_SendQueue.splice (m_SendQueue.end (), msgs);
|
|
||||||
|
|
||||||
if (!m_IsSending && m_IsEstablished)
|
if (!m_IsSending && m_IsEstablished)
|
||||||
SendQueue ();
|
SendQueue ();
|
||||||
|
@ -1450,13 +1326,7 @@ namespace transport
|
||||||
void NTCP2Session::SendLocalRouterInfo (bool update)
|
void NTCP2Session::SendLocalRouterInfo (bool update)
|
||||||
{
|
{
|
||||||
if (update || !IsOutgoing ()) // we send it in SessionConfirmed for outgoing session
|
if (update || !IsOutgoing ()) // we send it in SessionConfirmed for outgoing session
|
||||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
|
m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
|
||||||
}
|
|
||||||
|
|
||||||
i2p::data::RouterInfo::SupportedTransports NTCP2Session::GetTransportType () const
|
|
||||||
{
|
|
||||||
if (m_RemoteEndpoint.address ().is_v4 ()) return i2p::data::RouterInfo::eNTCP2V4;
|
|
||||||
return i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? i2p::data::RouterInfo::eNTCP2V6Mesh : i2p::data::RouterInfo::eNTCP2V6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NTCP2Server::NTCP2Server ():
|
NTCP2Server::NTCP2Server ():
|
||||||
|
@ -1473,7 +1343,6 @@ namespace transport
|
||||||
|
|
||||||
void NTCP2Server::Start ()
|
void NTCP2Server::Start ()
|
||||||
{
|
{
|
||||||
m_EstablisherService.Start ();
|
|
||||||
if (!IsRunning ())
|
if (!IsRunning ())
|
||||||
{
|
{
|
||||||
StartIOService ();
|
StartIOService ();
|
||||||
|
@ -1481,13 +1350,14 @@ namespace transport
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers");
|
LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers");
|
||||||
// TODO: resolve proxy until it is resolved
|
// TODO: resolve proxy until it is resolved
|
||||||
|
boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort));
|
||||||
boost::system::error_code e;
|
boost::system::error_code e;
|
||||||
auto itr = m_Resolver.resolve(m_ProxyAddress, std::to_string(m_ProxyPort), e);
|
auto itr = m_Resolver.resolve(q, e);
|
||||||
if(e)
|
if(e)
|
||||||
LogPrint(eLogCritical, "NTCP2: Failed to resolve proxy ", e.message());
|
LogPrint(eLogCritical, "NTCP2: Failed to resolve proxy ", e.message());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr.begin ()));
|
m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr));
|
||||||
if (m_ProxyEndpoint)
|
if (m_ProxyEndpoint)
|
||||||
LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint);
|
LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint);
|
||||||
}
|
}
|
||||||
|
@ -1568,7 +1438,6 @@ namespace transport
|
||||||
|
|
||||||
void NTCP2Server::Stop ()
|
void NTCP2Server::Stop ()
|
||||||
{
|
{
|
||||||
m_EstablisherService.Stop ();
|
|
||||||
{
|
{
|
||||||
// we have to copy it because Terminate changes m_NTCP2Sessions
|
// we have to copy it because Terminate changes m_NTCP2Sessions
|
||||||
auto ntcpSessions = m_NTCP2Sessions;
|
auto ntcpSessions = m_NTCP2Sessions;
|
||||||
|
@ -1648,7 +1517,7 @@ namespace transport
|
||||||
}
|
}
|
||||||
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint (),
|
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint (),
|
||||||
" (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")");
|
" (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")");
|
||||||
boost::asio::post (GetService (), [this, conn]()
|
GetService ().post([this, conn]()
|
||||||
{
|
{
|
||||||
if (this->AddNTCP2Session (conn))
|
if (this->AddNTCP2Session (conn))
|
||||||
{
|
{
|
||||||
|
@ -1869,7 +1738,7 @@ namespace transport
|
||||||
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boost::asio::post (GetService(), [this, conn]()
|
GetService().post([this, conn]()
|
||||||
{
|
{
|
||||||
if (this->AddNTCP2Session (conn))
|
if (this->AddNTCP2Session (conn))
|
||||||
{
|
{
|
||||||
|
@ -1953,7 +1822,7 @@ namespace transport
|
||||||
LogPrint(eLogError, "NTCP2: HTTP proxy write error ", ec.message());
|
LogPrint(eLogError, "NTCP2: HTTP proxy write error ", ec.message());
|
||||||
});
|
});
|
||||||
|
|
||||||
auto readbuff = std::make_shared<boost::asio::streambuf>();
|
boost::asio::streambuf * readbuff = new boost::asio::streambuf;
|
||||||
boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n",
|
boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n",
|
||||||
[readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred)
|
[readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred)
|
||||||
{
|
{
|
||||||
|
@ -1967,12 +1836,13 @@ namespace transport
|
||||||
{
|
{
|
||||||
readbuff->commit(transferred);
|
readbuff->commit(transferred);
|
||||||
i2p::http::HTTPRes res;
|
i2p::http::HTTPRes res;
|
||||||
if(res.parse(std::string {boost::asio::buffers_begin(readbuff->data ()), boost::asio::buffers_begin(readbuff->data ()) + readbuff->size ()}) > 0)
|
if(res.parse(boost::asio::buffer_cast<const char*>(readbuff->data()), readbuff->size()) > 0)
|
||||||
{
|
{
|
||||||
if(res.code == 200)
|
if(res.code == 200)
|
||||||
{
|
{
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
conn->ClientLogin();
|
conn->ClientLogin();
|
||||||
|
delete readbuff;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1982,6 +1852,7 @@ namespace transport
|
||||||
LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response");
|
LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response");
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
conn->Terminate();
|
conn->Terminate();
|
||||||
|
delete readbuff;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -2004,17 +1875,5 @@ namespace transport
|
||||||
else
|
else
|
||||||
m_Address4 = addr;
|
m_Address4 = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Server::AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
|
|
||||||
const uint8_t * key, const uint8_t * nonce, uint8_t * mac)
|
|
||||||
{
|
|
||||||
return m_Encryptor.Encrypt (bufs, key, nonce, mac);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NTCP2Server::AEADChaCha20Poly1305Decrypt (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)
|
|
||||||
{
|
|
||||||
return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,29 +91,30 @@ namespace transport
|
||||||
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
|
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
|
||||||
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
|
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
|
||||||
|
|
||||||
|
const uint8_t * GetK () const { return m_CK + 32; };
|
||||||
const uint8_t * GetCK () const { return m_CK; };
|
const uint8_t * GetCK () const { return m_CK; };
|
||||||
const uint8_t * GetH () const { return m_H; };
|
const uint8_t * GetH () const { return m_H; };
|
||||||
|
|
||||||
bool KDF1Alice ();
|
void KDF1Alice ();
|
||||||
bool KDF1Bob ();
|
void KDF1Bob ();
|
||||||
bool KDF2Alice ();
|
void KDF2Alice ();
|
||||||
bool KDF2Bob ();
|
void KDF2Bob ();
|
||||||
bool KDF3Alice (); // for SessionConfirmed part 2
|
void KDF3Alice (); // for SessionConfirmed part 2
|
||||||
bool KDF3Bob ();
|
void KDF3Bob ();
|
||||||
|
|
||||||
bool KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
||||||
bool KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||||
void CreateEphemeralKey ();
|
void CreateEphemeralKey ();
|
||||||
|
|
||||||
bool CreateSessionRequestMessage (std::mt19937& rng);
|
void CreateSessionRequestMessage (std::mt19937& rng);
|
||||||
bool CreateSessionCreatedMessage (std::mt19937& rng);
|
void CreateSessionCreatedMessage (std::mt19937& rng);
|
||||||
bool CreateSessionConfirmedMessagePart1 ();
|
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||||
bool CreateSessionConfirmedMessagePart2 ();
|
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
|
||||||
|
|
||||||
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew);
|
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew);
|
||||||
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
|
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
|
||||||
bool ProcessSessionConfirmedMessagePart1 ();
|
bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||||
bool ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf);
|
bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
|
||||||
|
|
||||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||||
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
|
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
|
||||||
|
@ -146,14 +147,13 @@ namespace transport
|
||||||
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
||||||
|
|
||||||
bool IsEstablished () const override { return m_IsEstablished; };
|
bool IsEstablished () const override { return m_IsEstablished; };
|
||||||
i2p::data::RouterInfo::SupportedTransports GetTransportType () const override;
|
|
||||||
bool IsTerminated () const { return m_IsTerminated; };
|
bool IsTerminated () const { return m_IsTerminated; };
|
||||||
|
|
||||||
void ClientLogin (); // Alice
|
void ClientLogin (); // Alice
|
||||||
void ServerLogin (); // Bob
|
void ServerLogin (); // Bob
|
||||||
|
|
||||||
void SendLocalRouterInfo (bool update) override; // after handshake or by update
|
void SendLocalRouterInfo (bool update) override; // after handshake or by update
|
||||||
void SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs) override;
|
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||||
void MoveSendQueue (std::shared_ptr<NTCP2Session> other);
|
void MoveSendQueue (std::shared_ptr<NTCP2Session> other);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -172,16 +172,12 @@ namespace transport
|
||||||
|
|
||||||
void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
void HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||||
void HandleSessionRequestReceived (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 ProcessSessionRequest (size_t len);
|
|
||||||
void HandleSessionRequestPaddingReceived (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 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 HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||||
void ProcessSessionCreated (size_t len);
|
|
||||||
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||||
void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||||
void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||||
void ProcessSessionConfirmed ();
|
|
||||||
void EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size);
|
|
||||||
|
|
||||||
// data
|
// data
|
||||||
void ReceiveLength ();
|
void ReceiveLength ();
|
||||||
|
@ -200,7 +196,7 @@ namespace transport
|
||||||
void SendRouterInfo ();
|
void SendRouterInfo ();
|
||||||
void SendTermination (NTCP2TerminationReason reason);
|
void SendTermination (NTCP2TerminationReason reason);
|
||||||
void SendTerminationAndTerminate (NTCP2TerminationReason reason);
|
void SendTerminationAndTerminate (NTCP2TerminationReason reason);
|
||||||
void PostI2NPMessages ();
|
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -234,27 +230,12 @@ namespace transport
|
||||||
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||||
uint64_t m_NextRouterInfoResendTime; // seconds since epoch
|
uint64_t m_NextRouterInfoResendTime; // seconds since epoch
|
||||||
|
|
||||||
std::list<std::shared_ptr<I2NPMessage> > m_IntermediateQueue; // from transports
|
|
||||||
mutable std::mutex m_IntermediateQueueMutex;
|
|
||||||
|
|
||||||
uint16_t m_PaddingSizes[16];
|
uint16_t m_PaddingSizes[16];
|
||||||
int m_NextPaddingSize;
|
int m_NextPaddingSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NTCP2Server: private i2p::util::RunnableServiceWithWork
|
class NTCP2Server: private i2p::util::RunnableServiceWithWork
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
|
|
||||||
class EstablisherService: public i2p::util::RunnableServiceWithWork
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
EstablisherService (): RunnableServiceWithWork ("NTCP2e") {};
|
|
||||||
auto& GetService () { return GetIOService (); };
|
|
||||||
void Start () { StartIOService (); };
|
|
||||||
void Stop () { StopIOService (); };
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum ProxyType
|
enum ProxyType
|
||||||
|
@ -269,14 +250,8 @@ namespace transport
|
||||||
|
|
||||||
void Start ();
|
void Start ();
|
||||||
void Stop ();
|
void Stop ();
|
||||||
auto& GetService () { return GetIOService (); };
|
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||||
auto& GetEstablisherService () { return m_EstablisherService.GetService (); };
|
|
||||||
std::mt19937& GetRng () { return m_Rng; };
|
std::mt19937& GetRng () { return m_Rng; };
|
||||||
void AEADChaCha20Poly1305Encrypt (const std::vector<std::pair<uint8_t *, size_t> >& bufs,
|
|
||||||
const uint8_t * key, const uint8_t * nonce, uint8_t * mac);
|
|
||||||
bool AEADChaCha20Poly1305Decrypt (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 AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
|
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
|
||||||
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
|
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
|
||||||
|
@ -314,12 +289,8 @@ namespace transport
|
||||||
uint16_t m_ProxyPort;
|
uint16_t m_ProxyPort;
|
||||||
boost::asio::ip::tcp::resolver m_Resolver;
|
boost::asio::ip::tcp::resolver m_Resolver;
|
||||||
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
|
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
|
||||||
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
|
std::shared_ptr<boost::asio::ip::tcp::endpoint> m_Address4, m_Address6, m_YggdrasilAddress;
|
||||||
std::mt19937 m_Rng;
|
std::mt19937 m_Rng;
|
||||||
EstablisherService m_EstablisherService;
|
|
||||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
|
||||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <random>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ namespace data
|
||||||
|
|
||||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr),
|
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr),
|
||||||
m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true),
|
m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true),
|
||||||
m_LastExploratorySelectionUpdateTime (0), m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL)
|
m_LastExploratorySelectionUpdateTime (0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,22 +119,19 @@ namespace data
|
||||||
i2p::util::SetThreadName("NetDB");
|
i2p::util::SetThreadName("NetDB");
|
||||||
|
|
||||||
uint64_t lastManage = 0;
|
uint64_t lastManage = 0;
|
||||||
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (),
|
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup;
|
||||||
lastObsoleteProfilesCleanup = lastProfilesCleanup, lastApplyingProfileUpdates = lastProfilesCleanup;
|
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0;
|
||||||
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0, applyingProfileUpdatesVariance = 0;
|
|
||||||
|
|
||||||
std::list<std::shared_ptr<const I2NPMessage> > msgs;
|
|
||||||
while (m_IsRunning)
|
while (m_IsRunning)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (m_Queue.Wait (1,0)) // 1 sec
|
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
|
||||||
|
if (msg)
|
||||||
{
|
{
|
||||||
m_Queue.GetWholeQueue (msgs);
|
int numMsgs = 0;
|
||||||
while (!msgs.empty ())
|
while (msg)
|
||||||
{
|
{
|
||||||
auto msg = msgs.front (); msgs.pop_front ();
|
|
||||||
if (!msg) continue;
|
|
||||||
LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ());
|
LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ());
|
||||||
switch (msg->GetTypeID ())
|
switch (msg->GetTypeID ())
|
||||||
{
|
{
|
||||||
|
@ -147,6 +145,9 @@ namespace data
|
||||||
LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ());
|
LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ());
|
||||||
//i2p::HandleI2NPMessage (msg);
|
//i2p::HandleI2NPMessage (msg);
|
||||||
}
|
}
|
||||||
|
if (numMsgs > 100) break;
|
||||||
|
msg = m_Queue.Get ();
|
||||||
|
numMsgs++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!m_IsRunning) break;
|
if (!m_IsRunning) break;
|
||||||
|
@ -181,7 +182,7 @@ namespace data
|
||||||
LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk");
|
LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk");
|
||||||
}
|
}
|
||||||
lastProfilesCleanup = mts;
|
lastProfilesCleanup = mts;
|
||||||
profilesCleanupVariance = m_Rng () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE;
|
profilesCleanupVariance = rand () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000)
|
if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000)
|
||||||
|
@ -197,20 +198,7 @@ namespace data
|
||||||
else
|
else
|
||||||
LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk");
|
LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk");
|
||||||
lastObsoleteProfilesCleanup = mts;
|
lastObsoleteProfilesCleanup = mts;
|
||||||
obsoleteProfilesCleanVariance = m_Rng () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE;
|
obsoleteProfilesCleanVariance = rand () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE;
|
||||||
}
|
|
||||||
if (mts >= lastApplyingProfileUpdates + i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT + applyingProfileUpdatesVariance)
|
|
||||||
{
|
|
||||||
bool isApplying = m_ApplyingProfileUpdates.valid ();
|
|
||||||
if (isApplying && m_ApplyingProfileUpdates.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active?
|
|
||||||
{
|
|
||||||
m_ApplyingProfileUpdates.get ();
|
|
||||||
isApplying = false;
|
|
||||||
}
|
|
||||||
if (!isApplying)
|
|
||||||
m_ApplyingProfileUpdates = i2p::data::FlushPostponedRouterProfileUpdates ();
|
|
||||||
lastApplyingProfileUpdates = mts;
|
|
||||||
applyingProfileUpdatesVariance = m_Rng () % i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& ex)
|
catch (std::exception& ex)
|
||||||
|
@ -294,7 +282,6 @@ namespace data
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
r->CancelBufferToDelete (); // since an update received
|
|
||||||
if (CheckLogLevel (eLogDebug))
|
if (CheckLogLevel (eLogDebug))
|
||||||
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
|
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
|
||||||
updated = false;
|
updated = false;
|
||||||
|
@ -398,7 +385,8 @@ namespace data
|
||||||
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
|
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
|
||||||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
|
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
|
||||||
{
|
{
|
||||||
if (leaseSet->IsPublic () && !leaseSet->IsExpired ())
|
if (leaseSet->IsPublic () && !leaseSet->IsExpired () &&
|
||||||
|
i2p::util::GetSecondsSinceEpoch () + NETDB_EXPIRATION_TIMEOUT_THRESHOLD > leaseSet->GetPublishedTimestamp ())
|
||||||
{
|
{
|
||||||
// TODO: implement actual update
|
// TODO: implement actual update
|
||||||
if (CheckLogLevel (eLogInfo))
|
if (CheckLogLevel (eLogInfo))
|
||||||
|
@ -493,7 +481,7 @@ namespace data
|
||||||
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
|
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64());
|
LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64());
|
||||||
std::list<std::shared_ptr<i2p::I2NPMessage> > requests;
|
std::vector<std::shared_ptr<i2p::I2NPMessage> > requests;
|
||||||
|
|
||||||
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
|
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
|
||||||
i2p::data::IdentHash ih = ri.GetIdentHash();
|
i2p::data::IdentHash ih = ri.GetIdentHash();
|
||||||
|
@ -516,7 +504,7 @@ namespace data
|
||||||
}
|
}
|
||||||
|
|
||||||
// send them off
|
// send them off
|
||||||
i2p::transport::transports.SendMessages(ih, std::move (requests));
|
i2p::transport::transports.SendMessages(ih, requests);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts)
|
bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts)
|
||||||
|
@ -571,7 +559,7 @@ namespace data
|
||||||
while(n > 0)
|
while(n > 0)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_RouterInfosMutex);
|
std::lock_guard<std::mutex> lock(m_RouterInfosMutex);
|
||||||
uint32_t idx = m_Rng () % m_RouterInfos.size ();
|
uint32_t idx = rand () % m_RouterInfos.size ();
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
for (const auto & it : m_RouterInfos) {
|
for (const auto & it : m_RouterInfos) {
|
||||||
if(i >= idx) // are we at the random start point?
|
if(i >= idx) // are we at the random start point?
|
||||||
|
@ -651,80 +639,70 @@ namespace data
|
||||||
if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour
|
if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour
|
||||||
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
|
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
|
||||||
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
|
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
|
||||||
bool isOffline = checkForExpiration && i2p::transport::transports.GetNumPeers () < NETDB_MIN_TRANSPORTS; // enough routers and uptime, but no transports
|
|
||||||
|
|
||||||
std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk;
|
std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk;
|
||||||
std::list<std::string> removeFromDisk;
|
std::list<std::string> removeFromDisk;
|
||||||
|
|
||||||
auto own = i2p::context.GetSharedRouterInfo ();
|
auto own = i2p::context.GetSharedRouterInfo ();
|
||||||
for (auto [ident, r]: m_RouterInfos)
|
for (auto& it: m_RouterInfos)
|
||||||
{
|
{
|
||||||
if (!r || r == own) continue; // skip own
|
if (!it.second || it.second == own) continue; // skip own
|
||||||
if (r->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete
|
std::string ident = it.second->GetIdentHashBase64();
|
||||||
|
if (it.second->IsUpdated ())
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
|
if (it.second->GetBuffer ())
|
||||||
r->DeleteBuffer ();
|
|
||||||
}
|
|
||||||
if (r->IsUpdated ())
|
|
||||||
{
|
|
||||||
if (r->GetBuffer () && !r->IsUnreachable ())
|
|
||||||
{
|
{
|
||||||
// we have something to save
|
// we have something to save
|
||||||
std::shared_ptr<RouterInfo::Buffer> buffer;
|
std::shared_ptr<RouterInfo::Buffer> buffer;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
|
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
|
||||||
buffer = r->CopyBuffer ();
|
buffer = it.second->GetSharedBuffer ();
|
||||||
|
it.second->DeleteBuffer ();
|
||||||
}
|
}
|
||||||
if (!i2p::transport::transports.IsConnected (ident))
|
if (buffer && !it.second->IsUnreachable ()) // don't save bad router
|
||||||
r->ScheduleBufferToDelete ();
|
saveToDisk.push_back(std::make_pair(ident, buffer));
|
||||||
if (buffer)
|
it.second->SetUnreachable (false);
|
||||||
saveToDisk.emplace_back(ident.ToBase64 (), buffer);
|
|
||||||
}
|
}
|
||||||
r->SetUpdated (false);
|
it.second->SetUpdated (false);
|
||||||
updatedCount++;
|
updatedCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (r->GetBuffer () && ts > r->GetTimestamp () + NETDB_MIN_EXPIRATION_TIMEOUT*1000LL)
|
if (it.second->GetProfile ()->IsUnreachable ())
|
||||||
// since update was long time ago we assume that router is not connected anymore
|
it.second->SetUnreachable (true);
|
||||||
r->ScheduleBufferToDelete ();
|
|
||||||
|
|
||||||
if (r->HasProfile () && r->GetProfile ()->IsUnreachable ())
|
|
||||||
r->SetUnreachable (true);
|
|
||||||
// make router reachable back if too few routers or floodfills
|
// make router reachable back if too few routers or floodfills
|
||||||
if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline ||
|
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate ||
|
||||||
(r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||||
r->SetUnreachable (false);
|
it.second->SetUnreachable (false);
|
||||||
if (!r->IsUnreachable ())
|
if (!it.second->IsUnreachable ())
|
||||||
{
|
{
|
||||||
// find & mark expired routers
|
// find & mark expired routers
|
||||||
if (!r->GetCompatibleTransports (true)) // non reachable by any transport
|
if (!it.second->GetCompatibleTransports (true)) // non reachable by any transport
|
||||||
r->SetUnreachable (true);
|
it.second->SetUnreachable (true);
|
||||||
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ())
|
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ())
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (r->GetTimestamp () - ts)/1000LL, " seconds");
|
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds");
|
||||||
r->SetUnreachable (true);
|
it.second->SetUnreachable (true);
|
||||||
}
|
}
|
||||||
else if (checkForExpiration)
|
else if (checkForExpiration)
|
||||||
{
|
{
|
||||||
if (ts > r->GetTimestamp () + expirationTimeout)
|
if (ts > it.second->GetTimestamp () + expirationTimeout)
|
||||||
r->SetUnreachable (true);
|
it.second->SetUnreachable (true);
|
||||||
else if ((ts > r->GetTimestamp () + expirationTimeout/2) && // more than half of expiration
|
else if ((ts > it.second->GetTimestamp () + expirationTimeout/2) && // more than half of expiration
|
||||||
total > NETDB_NUM_ROUTERS_THRESHOLD && !r->IsHighBandwidth() && // low bandwidth
|
total > NETDB_NUM_ROUTERS_THRESHOLD && !it.second->IsHighBandwidth() && // low bandwidth
|
||||||
!r->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill
|
!it.second->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill
|
||||||
(CreateRoutingKey (ident) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits
|
(CreateRoutingKey (it.second->GetIdentHash ()) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits
|
||||||
r->SetUnreachable (true);
|
it.second->SetUnreachable (true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// make router reachable back if connected now or trusted router
|
// make router reachable back if connected now
|
||||||
if (r->IsUnreachable () && (i2p::transport::transports.IsConnected (ident) ||
|
if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ()))
|
||||||
i2p::transport::transports.IsTrustedRouter (ident)))
|
it.second->SetUnreachable (false);
|
||||||
r->SetUnreachable (false);
|
|
||||||
|
|
||||||
if (r->IsUnreachable ())
|
if (it.second->IsUnreachable ())
|
||||||
{
|
{
|
||||||
if (r->IsFloodfill ()) deletedFloodfillsCount++;
|
if (it.second->IsFloodfill ()) deletedFloodfillsCount++;
|
||||||
// delete RI file
|
// delete RI file
|
||||||
removeFromDisk.emplace_back (ident.ToBase64());
|
removeFromDisk.push_back (ident);
|
||||||
deletedCount++;
|
deletedCount++;
|
||||||
if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false;
|
if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false;
|
||||||
}
|
}
|
||||||
|
@ -951,13 +929,14 @@ namespace data
|
||||||
LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored");
|
LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string key;
|
char key[48];
|
||||||
if (CheckLogLevel (eLogInfo))
|
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||||
key = i2p::data::ByteStreamToBase64 (buf, 32);
|
key[l] = 0;
|
||||||
|
|
||||||
IdentHash replyIdent(buf + 32);
|
IdentHash replyIdent(buf + 32);
|
||||||
uint8_t flag = buf[64];
|
uint8_t flag = buf[64];
|
||||||
|
|
||||||
|
|
||||||
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag);
|
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag);
|
||||||
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
|
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
|
||||||
const uint8_t * excluded = buf + 65;
|
const uint8_t * excluded = buf + 65;
|
||||||
|
@ -1350,7 +1329,7 @@ namespace data
|
||||||
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
|
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
|
||||||
{
|
{
|
||||||
std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection),
|
std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection),
|
||||||
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, m_Rng);
|
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
std::swap (m_ExploratorySelection, eligible);
|
std::swap (m_ExploratorySelection, eligible);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -16,7 +16,6 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include "Base.h"
|
#include "Base.h"
|
||||||
#include "Gzip.h"
|
#include "Gzip.h"
|
||||||
|
@ -40,7 +39,6 @@ namespace data
|
||||||
{
|
{
|
||||||
const int NETDB_MIN_ROUTERS = 90;
|
const int NETDB_MIN_ROUTERS = 90;
|
||||||
const int NETDB_MIN_FLOODFILLS = 5;
|
const int NETDB_MIN_FLOODFILLS = 5;
|
||||||
const int NETDB_MIN_TRANSPORTS = 10 ; // otherwise assume offline
|
|
||||||
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200;
|
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200;
|
||||||
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
|
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
|
||||||
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
|
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
|
||||||
|
@ -53,7 +51,6 @@ namespace data
|
||||||
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 58); // 0.9.58
|
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 58); // 0.9.58
|
||||||
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 59); // 0.9.59
|
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 59); // 0.9.59
|
||||||
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
|
const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51
|
||||||
const int NETDB_MIN_PEER_TEST_VERSION = MAKE_VERSION_NUMBER(0, 9, 62); // 0.9.62
|
|
||||||
const size_t NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES = 16;
|
const size_t NETDB_MAX_NUM_SEARCH_REPLY_PEER_HASHES = 16;
|
||||||
const size_t NETDB_MAX_EXPLORATORY_SELECTION_SIZE = 500;
|
const size_t NETDB_MAX_EXPLORATORY_SELECTION_SIZE = 500;
|
||||||
const int NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL = 82; // in seconds. for floodfill
|
const int NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL = 82; // in seconds. for floodfill
|
||||||
|
@ -187,11 +184,10 @@ namespace data
|
||||||
std::shared_ptr<NetDbRequests> m_Requests;
|
std::shared_ptr<NetDbRequests> m_Requests;
|
||||||
|
|
||||||
bool m_PersistProfiles;
|
bool m_PersistProfiles;
|
||||||
std::future<void> m_SavingProfiles, m_DeletingProfiles, m_ApplyingProfileUpdates, m_PersistingRouters;
|
std::future<void> m_SavingProfiles, m_DeletingProfiles, m_PersistingRouters;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const RouterInfo> > m_ExploratorySelection;
|
std::vector<std::shared_ptr<const RouterInfo> > m_ExploratorySelection;
|
||||||
uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds
|
uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds
|
||||||
std::mt19937 m_Rng;
|
|
||||||
|
|
||||||
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
|
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
|
||||||
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;
|
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -20,10 +20,8 @@ namespace i2p
|
||||||
namespace data
|
namespace data
|
||||||
{
|
{
|
||||||
RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct):
|
RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct):
|
||||||
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct),
|
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), m_IsActive (true),
|
||||||
m_IsActive (true), m_IsSentDirectly (false),
|
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_LastRequestTime (0), m_NumAttempts (0)
|
||||||
m_CreationTime (i2p::util::GetMillisecondsSinceEpoch ()),
|
|
||||||
m_LastRequestTime (0), m_NumAttempts (0)
|
|
||||||
{
|
{
|
||||||
if (i2p::context.IsFloodfill ())
|
if (i2p::context.IsFloodfill ())
|
||||||
m_ExcludedPeers.insert (i2p::context.GetIdentHash ()); // exclude self if floodfill
|
m_ExcludedPeers.insert (i2p::context.GetIdentHash ()); // exclude self if floodfill
|
||||||
|
@ -46,9 +44,8 @@ namespace data
|
||||||
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
|
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
|
||||||
if(router)
|
if(router)
|
||||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||||
m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
|
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||||
m_NumAttempts++;
|
m_NumAttempts++;
|
||||||
m_IsSentDirectly = false;
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +55,7 @@ namespace data
|
||||||
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
|
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
|
||||||
m_ExcludedPeers.insert (floodfill);
|
m_ExcludedPeers.insert (floodfill);
|
||||||
m_NumAttempts++;
|
m_NumAttempts++;
|
||||||
m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
|
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||||
m_IsSentDirectly = true;
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +179,7 @@ namespace data
|
||||||
|
|
||||||
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
|
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
|
||||||
{
|
{
|
||||||
boost::asio::post (GetIOService (), [this, ident, r]()
|
GetIOService ().post ([this, ident, r]()
|
||||||
{
|
{
|
||||||
std::shared_ptr<RequestedDestination> request;
|
std::shared_ptr<RequestedDestination> request;
|
||||||
auto it = m_RequestedDestinations.find (ident);
|
auto it = m_RequestedDestinations.find (ident);
|
||||||
|
@ -214,7 +210,7 @@ namespace data
|
||||||
|
|
||||||
void NetDbRequests::ManageRequests ()
|
void NetDbRequests::ManageRequests ()
|
||||||
{
|
{
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
|
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
|
||||||
{
|
{
|
||||||
auto& dest = it->second;
|
auto& dest = it->second;
|
||||||
|
@ -226,8 +222,7 @@ namespace data
|
||||||
bool done = false;
|
bool done = false;
|
||||||
if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME)
|
if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME)
|
||||||
{
|
{
|
||||||
if (ts > dest->GetLastRequestTime () + (dest->IsSentDirectly () ? MIN_DIRECT_REQUEST_TIME : MIN_REQUEST_TIME))
|
if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval
|
||||||
// try next floodfill if no response after min interval
|
|
||||||
done = !SendNextRequest (dest);
|
done = !SendNextRequest (dest);
|
||||||
}
|
}
|
||||||
else // request is expired
|
else // request is expired
|
||||||
|
@ -272,7 +267,7 @@ namespace data
|
||||||
{
|
{
|
||||||
if (dest->IsActive ())
|
if (dest->IsActive ())
|
||||||
{
|
{
|
||||||
boost::asio::post (s->GetIOService (), [s, dest]()
|
s->GetIOService ().post ([s, dest]()
|
||||||
{
|
{
|
||||||
if (dest->IsActive ()) s->SendNextRequest (dest);
|
if (dest->IsActive ()) s->SendNextRequest (dest);
|
||||||
});
|
});
|
||||||
|
@ -333,8 +328,7 @@ namespace data
|
||||||
|
|
||||||
void NetDbRequests::ScheduleManageRequests ()
|
void NetDbRequests::ScheduleManageRequests ()
|
||||||
{
|
{
|
||||||
m_ManageRequestsTimer.expires_from_now (boost::posix_time::milliseconds(MANAGE_REQUESTS_INTERVAL +
|
m_ManageRequestsTimer.expires_from_now (boost::posix_time::seconds(MANAGE_REQUESTS_INTERVAL));
|
||||||
m_Rng () % MANAGE_REQUESTS_INTERVAL_VARIANCE));
|
|
||||||
m_ManageRequestsTimer.async_wait (std::bind (&NetDbRequests::HandleManageRequestsTimer,
|
m_ManageRequestsTimer.async_wait (std::bind (&NetDbRequests::HandleManageRequestsTimer,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
@ -351,7 +345,7 @@ namespace data
|
||||||
|
|
||||||
void NetDbRequests::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
void NetDbRequests::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
boost::asio::post (GetIOService (), [this, msg]()
|
GetIOService ().post ([this, msg]()
|
||||||
{
|
{
|
||||||
HandleDatabaseSearchReplyMsg (msg);
|
HandleDatabaseSearchReplyMsg (msg);
|
||||||
});
|
});
|
||||||
|
@ -360,12 +354,11 @@ namespace data
|
||||||
void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
const uint8_t * buf = msg->GetPayload ();
|
const uint8_t * buf = msg->GetPayload ();
|
||||||
std::string key;
|
char key[48];
|
||||||
|
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||||
|
key[l] = 0;
|
||||||
size_t num = buf[32]; // num
|
size_t num = buf[32]; // num
|
||||||
if (CheckLogLevel (eLogInfo))
|
|
||||||
key = i2p::data::ByteStreamToBase64 (buf, 32);
|
|
||||||
LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num);
|
LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num);
|
||||||
|
|
||||||
IdentHash ident (buf);
|
IdentHash ident (buf);
|
||||||
bool isExploratory = false;
|
bool isExploratory = false;
|
||||||
auto dest = FindRequest (ident);
|
auto dest = FindRequest (ident);
|
||||||
|
@ -438,7 +431,7 @@ namespace data
|
||||||
void NetDbRequests::PostRequestDestination (const IdentHash& destination,
|
void NetDbRequests::PostRequestDestination (const IdentHash& destination,
|
||||||
const RequestedDestination::RequestComplete& requestComplete, bool direct)
|
const RequestedDestination::RequestComplete& requestComplete, bool direct)
|
||||||
{
|
{
|
||||||
boost::asio::post (GetIOService (), [this, destination, requestComplete, direct]()
|
GetIOService ().post ([this, destination, requestComplete, direct]()
|
||||||
{
|
{
|
||||||
RequestDestination (destination, requestComplete, direct);
|
RequestDestination (destination, requestComplete, direct);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -24,17 +24,15 @@ namespace i2p
|
||||||
namespace data
|
namespace data
|
||||||
{
|
{
|
||||||
const int MAX_NUM_REQUEST_ATTEMPTS = 5;
|
const int MAX_NUM_REQUEST_ATTEMPTS = 5;
|
||||||
const uint64_t MANAGE_REQUESTS_INTERVAL = 400; // in milliseconds
|
const uint64_t MANAGE_REQUESTS_INTERVAL = 1; // in seconds
|
||||||
const uint64_t MANAGE_REQUESTS_INTERVAL_VARIANCE = 300; // in milliseconds
|
const uint64_t MIN_REQUEST_TIME = 5; // in seconds
|
||||||
const uint64_t MIN_REQUEST_TIME = 1200; // in milliseconds
|
const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL);
|
||||||
const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL + MANAGE_REQUESTS_INTERVAL_VARIANCE);
|
|
||||||
const uint64_t MIN_DIRECT_REQUEST_TIME = 600; // in milliseconds
|
|
||||||
const uint64_t EXPLORATORY_REQUEST_INTERVAL = 55; // in seconds
|
const uint64_t EXPLORATORY_REQUEST_INTERVAL = 55; // in seconds
|
||||||
const uint64_t EXPLORATORY_REQUEST_INTERVAL_VARIANCE = 170; // in seconds
|
const uint64_t EXPLORATORY_REQUEST_INTERVAL_VARIANCE = 170; // in seconds
|
||||||
const uint64_t DISCOVERED_REQUEST_INTERVAL = 360; // in milliseconds
|
const uint64_t DISCOVERED_REQUEST_INTERVAL = 360; // in milliseconds
|
||||||
const uint64_t DISCOVERED_REQUEST_INTERVAL_VARIANCE = 540; // in milliseconds
|
const uint64_t DISCOVERED_REQUEST_INTERVAL_VARIANCE = 540; // in milliseconds
|
||||||
const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30000; // in milliseconds
|
const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30; // in seconds
|
||||||
const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40000; // in milliseconds
|
const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40; // in seconds
|
||||||
const uint64_t REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL = 191; // in seconds
|
const uint64_t REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL = 191; // in seconds
|
||||||
|
|
||||||
class RequestedDestination
|
class RequestedDestination
|
||||||
|
@ -53,7 +51,6 @@ namespace data
|
||||||
bool IsExploratory () const { return m_IsExploratory; };
|
bool IsExploratory () const { return m_IsExploratory; };
|
||||||
bool IsDirect () const { return m_IsDirect; };
|
bool IsDirect () const { return m_IsDirect; };
|
||||||
bool IsActive () const { return m_IsActive; };
|
bool IsActive () const { return m_IsActive; };
|
||||||
bool IsSentDirectly () const { return m_IsSentDirectly; };
|
|
||||||
bool IsExcluded (const IdentHash& ident) const;
|
bool IsExcluded (const IdentHash& ident) const;
|
||||||
uint64_t GetCreationTime () const { return m_CreationTime; };
|
uint64_t GetCreationTime () const { return m_CreationTime; };
|
||||||
uint64_t GetLastRequestTime () const { return m_LastRequestTime; };
|
uint64_t GetLastRequestTime () const { return m_LastRequestTime; };
|
||||||
|
@ -72,9 +69,9 @@ namespace data
|
||||||
private:
|
private:
|
||||||
|
|
||||||
IdentHash m_Destination;
|
IdentHash m_Destination;
|
||||||
bool m_IsExploratory, m_IsDirect, m_IsActive, m_IsSentDirectly;
|
bool m_IsExploratory, m_IsDirect, m_IsActive;
|
||||||
std::unordered_set<IdentHash> m_ExcludedPeers;
|
std::unordered_set<IdentHash> m_ExcludedPeers;
|
||||||
uint64_t m_CreationTime, m_LastRequestTime; // in milliseconds
|
uint64_t m_CreationTime, m_LastRequestTime; // in seconds
|
||||||
std::list<RequestComplete> m_RequestComplete;
|
std::list<RequestComplete> m_RequestComplete;
|
||||||
int m_NumAttempts;
|
int m_NumAttempts;
|
||||||
};
|
};
|
||||||
|
@ -118,9 +115,9 @@ namespace data
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool;
|
|
||||||
std::unordered_map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
|
std::unordered_map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
|
||||||
std::list<IdentHash> m_DiscoveredRouterHashes;
|
std::list<IdentHash> m_DiscoveredRouterHashes;
|
||||||
|
i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool;
|
||||||
boost::asio::deadline_timer m_ManageRequestsTimer, m_ExploratoryTimer,
|
boost::asio::deadline_timer m_ManageRequestsTimer, m_ExploratoryTimer,
|
||||||
m_CleanupTimer, m_DiscoveredRoutersTimer;
|
m_CleanupTimer, m_DiscoveredRoutersTimer;
|
||||||
std::mt19937 m_Rng;
|
std::mt19937 m_Rng;
|
||||||
|
|
|
@ -1,160 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025, The PurpleI2P Project
|
|
||||||
*
|
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
*
|
|
||||||
* See full license text in LICENSE file at top of project tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Log.h"
|
|
||||||
#include "PostQuantum.h"
|
|
||||||
|
|
||||||
#if OPENSSL_PQ
|
|
||||||
|
|
||||||
#include <openssl/param_build.h>
|
|
||||||
#include <openssl/core_names.h>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
MLKEMKeys::MLKEMKeys (MLKEMTypes type):
|
|
||||||
m_Name (std::get<0>(MLKEMS[type])), m_KeyLen (std::get<1>(MLKEMS[type])),
|
|
||||||
m_CTLen (std::get<2>(MLKEMS[type])), m_Pkey (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
MLKEMKeys::~MLKEMKeys ()
|
|
||||||
{
|
|
||||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLKEMKeys::GenerateKeys ()
|
|
||||||
{
|
|
||||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
|
||||||
m_Pkey = EVP_PKEY_Q_keygen(NULL, NULL, m_Name.c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLKEMKeys::GetPublicKey (uint8_t * pub) const
|
|
||||||
{
|
|
||||||
if (m_Pkey)
|
|
||||||
{
|
|
||||||
size_t len = m_KeyLen;
|
|
||||||
EVP_PKEY_get_octet_string_param (m_Pkey, OSSL_PKEY_PARAM_PUB_KEY, pub, m_KeyLen, &len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLKEMKeys::SetPublicKey (const uint8_t * pub)
|
|
||||||
{
|
|
||||||
if (m_Pkey)
|
|
||||||
{
|
|
||||||
EVP_PKEY_free (m_Pkey);
|
|
||||||
m_Pkey = nullptr;
|
|
||||||
}
|
|
||||||
OSSL_PARAM params[] =
|
|
||||||
{
|
|
||||||
OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PUB_KEY, (uint8_t *)pub, m_KeyLen),
|
|
||||||
OSSL_PARAM_END
|
|
||||||
};
|
|
||||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, m_Name.c_str (), NULL);
|
|
||||||
if (ctx)
|
|
||||||
{
|
|
||||||
EVP_PKEY_fromdata_init (ctx);
|
|
||||||
EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, params);
|
|
||||||
EVP_PKEY_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "MLKEM can't create PKEY context");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLKEMKeys::Encaps (uint8_t * ciphertext, uint8_t * shared)
|
|
||||||
{
|
|
||||||
if (!m_Pkey) return;
|
|
||||||
auto ctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL);
|
|
||||||
if (ctx)
|
|
||||||
{
|
|
||||||
EVP_PKEY_encapsulate_init (ctx, NULL);
|
|
||||||
size_t len = m_CTLen, sharedLen = 32;
|
|
||||||
EVP_PKEY_encapsulate (ctx, ciphertext, &len, shared, &sharedLen);
|
|
||||||
EVP_PKEY_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "MLKEM can't create PKEY context");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MLKEMKeys::Decaps (const uint8_t * ciphertext, uint8_t * shared)
|
|
||||||
{
|
|
||||||
if (!m_Pkey) return;
|
|
||||||
auto ctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL);
|
|
||||||
if (ctx)
|
|
||||||
{
|
|
||||||
EVP_PKEY_decapsulate_init (ctx, NULL);
|
|
||||||
size_t sharedLen = 32;
|
|
||||||
EVP_PKEY_decapsulate (ctx, shared, &sharedLen, ciphertext, m_CTLen);
|
|
||||||
EVP_PKEY_CTX_free (ctx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "MLKEM can't create PKEY context");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<MLKEMKeys> CreateMLKEMKeys (i2p::data::CryptoKeyType type)
|
|
||||||
{
|
|
||||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
|
||||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return nullptr;
|
|
||||||
return std::make_unique<MLKEMKeys>((MLKEMTypes)(type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::array<std::pair<std::array<uint8_t, 32>, std::array<uint8_t, 32> >, 3> NoiseIKInitMLKEMKeys =
|
|
||||||
{
|
|
||||||
std::make_pair
|
|
||||||
(
|
|
||||||
std::array<uint8_t, 32>
|
|
||||||
{
|
|
||||||
0xb0, 0x8f, 0xb1, 0x73, 0x92, 0x66, 0xc9, 0x90, 0x45, 0x7f, 0xdd, 0xc6, 0x4e, 0x55, 0x40, 0xd8,
|
|
||||||
0x0a, 0x37, 0x99, 0x06, 0x92, 0x2a, 0x78, 0xc4, 0xb1, 0xef, 0x86, 0x06, 0xd0, 0x15, 0x9f, 0x4d
|
|
||||||
}, // SHA256("Noise_IKhfselg2_25519+MLKEM512_ChaChaPoly_SHA256")
|
|
||||||
std::array<uint8_t, 32>
|
|
||||||
{
|
|
||||||
0x95, 0x8d, 0xf6, 0x6c, 0x95, 0xce, 0xa9, 0xf7, 0x42, 0xfc, 0xfa, 0x62, 0x71, 0x36, 0x1e, 0xa7,
|
|
||||||
0xdc, 0x7a, 0xc0, 0x75, 0x01, 0xcf, 0xf9, 0xfc, 0x9f, 0xdb, 0x4c, 0x68, 0x3a, 0x53, 0x49, 0xeb
|
|
||||||
} // SHA256 (first)
|
|
||||||
),
|
|
||||||
std::make_pair
|
|
||||||
(
|
|
||||||
std::array<uint8_t, 32>
|
|
||||||
{
|
|
||||||
0x36, 0x03, 0x90, 0x2d, 0xf9, 0xa2, 0x2a, 0x5e, 0xc9, 0x3d, 0xdb, 0x8f, 0xa8, 0x1b, 0xdb, 0x4b,
|
|
||||||
0xae, 0x9d, 0x93, 0x9c, 0xdf, 0xaf, 0xde, 0x55, 0x49, 0x13, 0xfe, 0x98, 0xf8, 0x4a, 0xd4, 0xbd
|
|
||||||
}, // SHA256("Noise_IKhfselg2_25519+MLKEM768_ChaChaPoly_SHA256")
|
|
||||||
std::array<uint8_t, 32>
|
|
||||||
{
|
|
||||||
0x15, 0x44, 0x89, 0xbf, 0x30, 0xf0, 0xc9, 0x77, 0x66, 0x10, 0xcb, 0xb1, 0x57, 0x3f, 0xab, 0x68,
|
|
||||||
0x79, 0x57, 0x39, 0x57, 0x0a, 0xe7, 0xc0, 0x31, 0x8a, 0xa2, 0x96, 0xef, 0xbf, 0xa9, 0x6a, 0xbb
|
|
||||||
} // SHA256 (first)
|
|
||||||
),
|
|
||||||
std::make_pair
|
|
||||||
(
|
|
||||||
std::array<uint8_t, 32>
|
|
||||||
{
|
|
||||||
0x86, 0xa5, 0x36, 0x44, 0xc6, 0x12, 0xd5, 0x71, 0xa1, 0x2d, 0xd8, 0xb6, 0x0a, 0x00, 0x9f, 0x2c,
|
|
||||||
0x1a, 0xa8, 0x7d, 0x22, 0xa4, 0xff, 0x2b, 0xcd, 0x61, 0x34, 0x97, 0x6d, 0xa1, 0x49, 0xeb, 0x4a
|
|
||||||
}, // SHA256("Noise_IKhfselg2_25519+MLKEM1024_ChaChaPoly_SHA256")
|
|
||||||
std::array<uint8_t, 32>
|
|
||||||
{
|
|
||||||
0x42, 0x0d, 0xc2, 0x1c, 0x7b, 0x18, 0x61, 0xb7, 0x4a, 0x04, 0x3d, 0xae, 0x0f, 0xdc, 0xf2, 0x71,
|
|
||||||
0xb9, 0xba, 0x19, 0xbb, 0xbd, 0x5f, 0xd4, 0x9c, 0x3f, 0x4b, 0x01, 0xed, 0x6d, 0x13, 0x1d, 0xa2
|
|
||||||
} // SHA256 (first)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
void InitNoiseIKStateMLKEM (NoiseSymmetricState& state, i2p::data::CryptoKeyType type, const uint8_t * pub)
|
|
||||||
{
|
|
||||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
|
||||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)NoiseIKInitMLKEMKeys.size ()) return;
|
|
||||||
auto ind = type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1;
|
|
||||||
state.Init (NoiseIKInitMLKEMKeys[ind].first.data(), NoiseIKInitMLKEMKeys[ind].second.data(), pub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,88 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025, The PurpleI2P Project
|
|
||||||
*
|
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
*
|
|
||||||
* See full license text in LICENSE file at top of project tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef POST_QUANTUM_H__
|
|
||||||
#define POST_QUANTUM_H__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string_view>
|
|
||||||
#include <array>
|
|
||||||
#include <tuple>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
|
|
||||||
#if OPENSSL_PQ
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
enum MLKEMTypes
|
|
||||||
{
|
|
||||||
eMLKEM512 = 0,
|
|
||||||
eMLKEM768,
|
|
||||||
eMLKEM1024
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr size_t MLKEM512_KEY_LENGTH = 800;
|
|
||||||
constexpr size_t MLKEM512_CIPHER_TEXT_LENGTH = 768;
|
|
||||||
constexpr size_t MLKEM768_KEY_LENGTH = 1184;
|
|
||||||
constexpr size_t MLKEM768_CIPHER_TEXT_LENGTH = 1088;
|
|
||||||
constexpr size_t MLKEM1024_KEY_LENGTH = 1568;
|
|
||||||
constexpr size_t MLKEM1024_CIPHER_TEXT_LENGTH = 1568;
|
|
||||||
|
|
||||||
constexpr std::array<std::tuple<std::string_view, size_t, size_t>, 3> MLKEMS =
|
|
||||||
{
|
|
||||||
std::make_tuple ("ML-KEM-512", MLKEM512_KEY_LENGTH, MLKEM512_CIPHER_TEXT_LENGTH),
|
|
||||||
std::make_tuple ("ML-KEM-768", MLKEM768_KEY_LENGTH, MLKEM768_CIPHER_TEXT_LENGTH),
|
|
||||||
std::make_tuple ("ML-KEM-1024", MLKEM1024_KEY_LENGTH, MLKEM1024_CIPHER_TEXT_LENGTH)
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr size_t GetMLKEMPublicKeyLen (i2p::data::CryptoKeyType type)
|
|
||||||
{
|
|
||||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
|
||||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return 0;
|
|
||||||
return std::get<1>(MLKEMS[type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t GetMLKEMCipherTextLen (i2p::data::CryptoKeyType type)
|
|
||||||
{
|
|
||||||
if (type <= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ||
|
|
||||||
type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD > (int)MLKEMS.size ()) return 0;
|
|
||||||
return std::get<2>(MLKEMS[type - i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MLKEMKeys
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
MLKEMKeys (MLKEMTypes type);
|
|
||||||
~MLKEMKeys ();
|
|
||||||
|
|
||||||
void GenerateKeys ();
|
|
||||||
void GetPublicKey (uint8_t * pub) const;
|
|
||||||
void SetPublicKey (const uint8_t * pub);
|
|
||||||
void Encaps (uint8_t * ciphertext, uint8_t * shared);
|
|
||||||
void Decaps (const uint8_t * ciphertext, uint8_t * shared);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const std::string m_Name;
|
|
||||||
const size_t m_KeyLen, m_CTLen;
|
|
||||||
EVP_PKEY * m_Pkey;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<MLKEMKeys> CreateMLKEMKeys (i2p::data::CryptoKeyType type);
|
|
||||||
|
|
||||||
void InitNoiseIKStateMLKEM (NoiseSymmetricState& state, i2p::data::CryptoKeyType type, const uint8_t * pub); // Noise_IK (ratchets) PQ ML-KEM5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -27,15 +27,13 @@ namespace data
|
||||||
static i2p::fs::HashedStorage g_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
|
static i2p::fs::HashedStorage g_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
|
||||||
static std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > g_Profiles;
|
static std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > g_Profiles;
|
||||||
static std::mutex g_ProfilesMutex;
|
static std::mutex g_ProfilesMutex;
|
||||||
static std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > > g_PostponedUpdates;
|
|
||||||
static std::mutex g_PostponedUpdatesMutex;
|
|
||||||
|
|
||||||
RouterProfile::RouterProfile ():
|
RouterProfile::RouterProfile ():
|
||||||
m_IsUpdated (false), m_LastDeclineTime (0), m_LastUnreachableTime (0),
|
m_IsUpdated (false), m_LastDeclineTime (0), m_LastUnreachableTime (0),
|
||||||
m_LastUpdateTime (i2p::util::GetSecondsSinceEpoch ()), m_LastAccessTime (0),
|
m_LastUpdateTime (i2p::util::GetSecondsSinceEpoch ()),
|
||||||
m_LastPersistTime (0), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0),
|
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
|
||||||
m_NumTunnelsNonReplied (0),m_NumTimesTaken (0), m_NumTimesRejected (0),
|
m_NumTimesTaken (0), m_NumTimesRejected (0), m_HasConnected (false),
|
||||||
m_HasConnected (false), m_IsDuplicated (false)
|
m_IsDuplicated (false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +78,6 @@ namespace data
|
||||||
|
|
||||||
void RouterProfile::Load (const IdentHash& identHash)
|
void RouterProfile::Load (const IdentHash& identHash)
|
||||||
{
|
{
|
||||||
m_IsUpdated = false;
|
|
||||||
std::string ident = identHash.ToBase64 ();
|
std::string ident = identHash.ToBase64 ();
|
||||||
std::string path = g_ProfilesStorage.Path(ident);
|
std::string path = g_ProfilesStorage.Path(ident);
|
||||||
boost::property_tree::ptree pt;
|
boost::property_tree::ptree pt;
|
||||||
|
@ -209,9 +206,10 @@ namespace data
|
||||||
return m_NumTunnelsNonReplied > 10*(total + 1);
|
return m_NumTunnelsNonReplied > 10*(total + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterProfile::IsDeclinedRecently (uint64_t ts)
|
bool RouterProfile::IsDeclinedRecently ()
|
||||||
{
|
{
|
||||||
if (!m_LastDeclineTime) return false;
|
if (!m_LastDeclineTime) return false;
|
||||||
|
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL ||
|
if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL ||
|
||||||
ts + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL < m_LastDeclineTime)
|
ts + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL < m_LastDeclineTime)
|
||||||
m_LastDeclineTime = 0;
|
m_LastDeclineTime = 0;
|
||||||
|
@ -220,10 +218,7 @@ namespace data
|
||||||
|
|
||||||
bool RouterProfile::IsBad ()
|
bool RouterProfile::IsBad ()
|
||||||
{
|
{
|
||||||
if (IsUnreachable () || m_IsDuplicated) return true;
|
if (IsDeclinedRecently () || IsUnreachable () || m_IsDuplicated) return true;
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (ts > PEER_PROFILE_MAX_DECLINED_INTERVAL + m_LastDeclineTime) return false;
|
|
||||||
if (IsDeclinedRecently (ts)) return true;
|
|
||||||
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
|
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
|
||||||
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
|
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
|
||||||
{
|
{
|
||||||
|
@ -258,36 +253,24 @@ namespace data
|
||||||
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||||
auto it = g_Profiles.find (identHash);
|
auto it = g_Profiles.find (identHash);
|
||||||
if (it != g_Profiles.end ())
|
if (it != g_Profiles.end ())
|
||||||
{
|
|
||||||
it->second->SetLastAccessTime (i2p::util::GetSecondsSinceEpoch ());
|
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
auto profile = netdb.NewRouterProfile ();
|
auto profile = netdb.NewRouterProfile ();
|
||||||
profile->Load (identHash); // if possible
|
profile->Load (identHash); // if possible
|
||||||
std::lock_guard<std::mutex> l(g_ProfilesMutex);
|
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||||
g_Profiles.emplace (identHash, profile);
|
g_Profiles.emplace (identHash, profile);
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRouterBanned (const IdentHash& identHash)
|
bool IsRouterBanned (const IdentHash& identHash)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(g_ProfilesMutex);
|
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||||
auto it = g_Profiles.find (identHash);
|
auto it = g_Profiles.find (identHash);
|
||||||
if (it != g_Profiles.end ())
|
if (it != g_Profiles.end ())
|
||||||
return it->second->IsUnreachable ();
|
return it->second->IsUnreachable ();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRouterDuplicated (const IdentHash& identHash)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(g_ProfilesMutex);
|
|
||||||
auto it = g_Profiles.find (identHash);
|
|
||||||
if (it != g_Profiles.end ())
|
|
||||||
return it->second->IsDuplicated ();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitProfilesStorage ()
|
void InitProfilesStorage ()
|
||||||
{
|
{
|
||||||
g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
|
g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
|
||||||
|
@ -305,17 +288,15 @@ namespace data
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp;
|
std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(g_ProfilesMutex);
|
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||||
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
|
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
|
||||||
{
|
{
|
||||||
if (it->second->IsUpdated () && ts > it->second->GetLastPersistTime () + PEER_PROFILE_PERSIST_INTERVAL)
|
if (ts - it->second->GetLastUpdateTime () > PEER_PROFILE_PERSIST_INTERVAL)
|
||||||
{
|
{
|
||||||
tmp.push_back (*it);
|
if (it->second->IsUpdated ())
|
||||||
it->second->SetLastPersistTime (ts);
|
tmp.push_back (std::make_pair (it->first, it->second));
|
||||||
it->second->SetUpdated (false);
|
|
||||||
}
|
|
||||||
if (!it->second->IsUpdated () && ts > std::max (it->second->GetLastUpdateTime (), it->second->GetLastAccessTime ()) + PEER_PROFILE_PERSIST_INTERVAL)
|
|
||||||
it = g_Profiles.erase (it);
|
it = g_Profiles.erase (it);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +310,7 @@ namespace data
|
||||||
{
|
{
|
||||||
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp;
|
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(g_ProfilesMutex);
|
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||||
std::swap (tmp, g_Profiles);
|
std::swap (tmp, g_Profiles);
|
||||||
}
|
}
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
|
@ -364,7 +345,7 @@ namespace data
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
std::lock_guard<std::mutex> l(g_ProfilesMutex);
|
std::unique_lock<std::mutex> l(g_ProfilesMutex);
|
||||||
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
|
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
|
||||||
{
|
{
|
||||||
if (ts - it->second->GetLastUpdateTime () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
|
if (ts - it->second->GetLastUpdateTime () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
|
||||||
|
@ -376,47 +357,5 @@ namespace data
|
||||||
|
|
||||||
return std::async (std::launch::async, DeleteFilesFromDisk);
|
return std::async (std::launch::async, DeleteFilesFromDisk);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdateRouterProfile (const IdentHash& identHash, std::function<void (std::shared_ptr<RouterProfile>)> update)
|
|
||||||
{
|
|
||||||
if (!update) return true;
|
|
||||||
std::shared_ptr<RouterProfile> profile;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(g_ProfilesMutex);
|
|
||||||
auto it = g_Profiles.find (identHash);
|
|
||||||
if (it != g_Profiles.end ())
|
|
||||||
profile = it->second;
|
|
||||||
}
|
|
||||||
if (profile)
|
|
||||||
{
|
|
||||||
update (profile);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// postpone
|
|
||||||
std::lock_guard<std::mutex> l(g_PostponedUpdatesMutex);
|
|
||||||
g_PostponedUpdates.emplace_back (identHash, update);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ApplyPostponedUpdates (std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > >&& updates)
|
|
||||||
{
|
|
||||||
for (const auto& [ident, update] : updates)
|
|
||||||
{
|
|
||||||
auto profile = GetRouterProfile (ident);
|
|
||||||
update (profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::future<void> FlushPostponedRouterProfileUpdates ()
|
|
||||||
{
|
|
||||||
if (g_PostponedUpdates.empty ()) return std::future<void>();
|
|
||||||
|
|
||||||
std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > > updates;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(g_PostponedUpdatesMutex);
|
|
||||||
g_PostponedUpdates.swap (updates);
|
|
||||||
}
|
|
||||||
return std::async (std::launch::async, ApplyPostponedUpdates, std::move (updates));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <functional>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -39,14 +37,10 @@ namespace data
|
||||||
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes)
|
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes)
|
||||||
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours)
|
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT = 5400; // in seconds (1.5 hours)
|
||||||
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
|
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
|
||||||
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 330; // in seconds (5.5 minutes)
|
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
|
||||||
const int PEER_PROFILE_MAX_DECLINED_INTERVAL = 4400; // in second (1.5 hours)
|
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes)
|
||||||
const int PEER_PROFILE_PERSIST_INTERVAL = 1320; // in seconds (22 minutes)
|
|
||||||
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
|
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
|
||||||
const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
|
const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
|
||||||
const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined
|
|
||||||
const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT = 2100; // in milliseconds
|
|
||||||
const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE = 500; // in milliseconds
|
|
||||||
|
|
||||||
class RouterProfile
|
class RouterProfile
|
||||||
{
|
{
|
||||||
|
@ -70,20 +64,10 @@ namespace data
|
||||||
|
|
||||||
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
||||||
bool IsUpdated () const { return m_IsUpdated; };
|
bool IsUpdated () const { return m_IsUpdated; };
|
||||||
void SetUpdated (bool updated) { m_IsUpdated = updated; }
|
|
||||||
uint64_t GetLastAccessTime () const { return m_LastAccessTime; };
|
|
||||||
void SetLastAccessTime (uint64_t ts) { m_LastAccessTime = ts; };
|
|
||||||
uint64_t GetLastPersistTime () const { return m_LastPersistTime; };
|
|
||||||
void SetLastPersistTime (uint64_t ts) { m_LastPersistTime = ts; };
|
|
||||||
|
|
||||||
bool IsUseful() const;
|
bool IsUseful() const;
|
||||||
bool IsDuplicated () const { return m_IsDuplicated; };
|
bool IsDuplicated () const { return m_IsDuplicated; };
|
||||||
|
|
||||||
const boost::asio::ip::udp::endpoint& GetLastEndpoint () const { return m_LastEndpoint; }
|
|
||||||
void SetLastEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_LastEndpoint = ep; }
|
|
||||||
bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () &&
|
|
||||||
((v4 && m_LastEndpoint.address ().is_v4 ()) || (!v4 && m_LastEndpoint.address ().is_v6 ())); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void UpdateTime ();
|
void UpdateTime ();
|
||||||
|
@ -91,13 +75,12 @@ namespace data
|
||||||
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
|
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
|
||||||
bool IsLowPartcipationRate () const;
|
bool IsLowPartcipationRate () const;
|
||||||
bool IsLowReplyRate () const;
|
bool IsLowReplyRate () const;
|
||||||
bool IsDeclinedRecently (uint64_t ts);
|
bool IsDeclinedRecently ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool m_IsUpdated;
|
bool m_IsUpdated;
|
||||||
uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime,
|
uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime; // in seconds
|
||||||
m_LastAccessTime, m_LastPersistTime; // in seconds
|
|
||||||
// participation
|
// participation
|
||||||
uint32_t m_NumTunnelsAgreed;
|
uint32_t m_NumTunnelsAgreed;
|
||||||
uint32_t m_NumTunnelsDeclined;
|
uint32_t m_NumTunnelsDeclined;
|
||||||
|
@ -107,19 +90,14 @@ namespace data
|
||||||
uint32_t m_NumTimesRejected;
|
uint32_t m_NumTimesRejected;
|
||||||
bool m_HasConnected; // successful trusted(incoming or NTCP2) connection
|
bool m_HasConnected; // successful trusted(incoming or NTCP2) connection
|
||||||
bool m_IsDuplicated;
|
bool m_IsDuplicated;
|
||||||
// connectivity
|
|
||||||
boost::asio::ip::udp::endpoint m_LastEndpoint; // SSU2 for non-published addresses
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
|
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
|
||||||
bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles
|
bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles
|
||||||
bool IsRouterDuplicated (const IdentHash& identHash); // check only existing profiles
|
|
||||||
void InitProfilesStorage ();
|
void InitProfilesStorage ();
|
||||||
std::future<void> DeleteObsoleteProfiles ();
|
std::future<void> DeleteObsoleteProfiles ();
|
||||||
void SaveProfiles ();
|
void SaveProfiles ();
|
||||||
std::future<void> PersistProfiles ();
|
std::future<void> PersistProfiles ();
|
||||||
bool UpdateRouterProfile (const IdentHash& identHash, std::function<void (std::shared_ptr<RouterProfile>)> update); // return true if updated immediately, and false if postponed
|
|
||||||
std::future<void> FlushPostponedRouterProfileUpdates ();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -9,7 +9,8 @@
|
||||||
#ifndef QUEUE_H__
|
#ifndef QUEUE_H__
|
||||||
#define QUEUE_H__
|
#define QUEUE_H__
|
||||||
|
|
||||||
#include <list>
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
@ -28,16 +29,18 @@ namespace util
|
||||||
void Put (Element e)
|
void Put (Element e)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||||
m_Queue.push_back (std::move(e));
|
m_Queue.push (std::move(e));
|
||||||
m_NonEmpty.notify_one ();
|
m_NonEmpty.notify_one ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Put (std::list<Element>& list)
|
template<template<typename, typename...>class Container, typename... R>
|
||||||
|
void Put (const Container<Element, R...>& vec)
|
||||||
{
|
{
|
||||||
if (!list.empty ())
|
if (!vec.empty ())
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||||
m_Queue.splice (m_Queue.end (), list);
|
for (const auto& it: vec)
|
||||||
|
m_Queue.push (std::move(it));
|
||||||
m_NonEmpty.notify_one ();
|
m_NonEmpty.notify_one ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +87,7 @@ namespace util
|
||||||
return m_Queue.empty ();
|
return m_Queue.empty ();
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetSize () const
|
int GetSize ()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
std::unique_lock<std::mutex> l(m_QueueMutex);
|
||||||
return m_Queue.size ();
|
return m_Queue.size ();
|
||||||
|
@ -104,19 +107,6 @@ namespace util
|
||||||
return GetNonThreadSafe (true);
|
return GetNonThreadSafe (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetWholeQueue (std::list<Element>& queue)
|
|
||||||
{
|
|
||||||
if (!queue.empty ())
|
|
||||||
{
|
|
||||||
std::list<Element> newQueue;
|
|
||||||
queue.swap (newQueue);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_QueueMutex);
|
|
||||||
m_Queue.swap (queue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Element GetNonThreadSafe (bool peek = false)
|
Element GetNonThreadSafe (bool peek = false)
|
||||||
|
@ -125,7 +115,7 @@ namespace util
|
||||||
{
|
{
|
||||||
auto el = m_Queue.front ();
|
auto el = m_Queue.front ();
|
||||||
if (!peek)
|
if (!peek)
|
||||||
m_Queue.pop_front ();
|
m_Queue.pop ();
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -133,8 +123,8 @@ namespace util
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::list<Element> m_Queue;
|
std::queue<Element> m_Queue;
|
||||||
mutable std::mutex m_QueueMutex;
|
std::mutex m_QueueMutex;
|
||||||
std::condition_variable m_NonEmpty;
|
std::condition_variable m_NonEmpty;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -552,7 +552,7 @@ namespace data
|
||||||
if (!url.port)
|
if (!url.port)
|
||||||
url.port = 443;
|
url.port = 443;
|
||||||
|
|
||||||
boost::asio::io_context service;
|
boost::asio::io_service service;
|
||||||
boost::system::error_code ecode;
|
boost::system::error_code ecode;
|
||||||
|
|
||||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
||||||
|
@ -562,10 +562,11 @@ namespace data
|
||||||
if(proxyUrl.schema.size())
|
if(proxyUrl.schema.size())
|
||||||
{
|
{
|
||||||
// proxy connection
|
// proxy connection
|
||||||
auto it = boost::asio::ip::tcp::resolver(service).resolve (proxyUrl.host, std::to_string(proxyUrl.port), ecode);
|
auto it = boost::asio::ip::tcp::resolver(service).resolve (
|
||||||
|
boost::asio::ip::tcp::resolver::query (proxyUrl.host, std::to_string(proxyUrl.port)), ecode);
|
||||||
if(!ecode)
|
if(!ecode)
|
||||||
{
|
{
|
||||||
s.lowest_layer().connect(*it.begin (), ecode);
|
s.lowest_layer().connect(*it, ecode);
|
||||||
if(!ecode)
|
if(!ecode)
|
||||||
{
|
{
|
||||||
auto & sock = s.next_layer();
|
auto & sock = s.next_layer();
|
||||||
|
@ -598,7 +599,7 @@ namespace data
|
||||||
LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message());
|
LogPrint(eLogError, "Reseed: HTTP CONNECT read error: ", ecode.message());
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if(proxyRes.parse(std::string {boost::asio::buffers_begin(readbuf.data ()), boost::asio::buffers_begin(readbuf.data ()) + readbuf.size ()}) <= 0)
|
if(proxyRes.parse(boost::asio::buffer_cast<const char *>(readbuf.data()), readbuf.size()) <= 0)
|
||||||
{
|
{
|
||||||
sock.close();
|
sock.close();
|
||||||
LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply");
|
LogPrint(eLogError, "Reseed: HTTP CONNECT malformed reply");
|
||||||
|
@ -637,13 +638,15 @@ namespace data
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// direct connection
|
// direct connection
|
||||||
auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode);
|
auto it = boost::asio::ip::tcp::resolver(service).resolve (
|
||||||
|
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
|
||||||
if (!ecode)
|
if (!ecode)
|
||||||
{
|
{
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
for (const auto& it: endpoints)
|
boost::asio::ip::tcp::resolver::iterator end;
|
||||||
|
while (it != end)
|
||||||
{
|
{
|
||||||
boost::asio::ip::tcp::endpoint ep = it;
|
boost::asio::ip::tcp::endpoint ep = *it;
|
||||||
bool supported = false;
|
bool supported = false;
|
||||||
if (!ep.address ().is_unspecified ())
|
if (!ep.address ().is_unspecified ())
|
||||||
{
|
{
|
||||||
|
@ -663,6 +666,7 @@ namespace data
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
it++;
|
||||||
}
|
}
|
||||||
if (!connected)
|
if (!connected)
|
||||||
{
|
{
|
||||||
|
@ -742,16 +746,19 @@ namespace data
|
||||||
if (!url.port) url.port = 80;
|
if (!url.port) url.port = 80;
|
||||||
|
|
||||||
boost::system::error_code ecode;
|
boost::system::error_code ecode;
|
||||||
boost::asio::io_context service;
|
boost::asio::io_service service;
|
||||||
boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6());
|
boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6());
|
||||||
|
|
||||||
auto endpoints = boost::asio::ip::tcp::resolver(service).resolve (url.host, std::to_string(url.port), ecode);
|
auto it = boost::asio::ip::tcp::resolver(service).resolve (
|
||||||
|
boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode);
|
||||||
|
|
||||||
if (!ecode)
|
if (!ecode)
|
||||||
{
|
{
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
for (const auto& it: endpoints)
|
boost::asio::ip::tcp::resolver::iterator end;
|
||||||
|
while (it != end)
|
||||||
{
|
{
|
||||||
boost::asio::ip::tcp::endpoint ep = it;
|
boost::asio::ip::tcp::endpoint ep = *it;
|
||||||
if (
|
if (
|
||||||
i2p::util::net::IsYggdrasilAddress (ep.address ()) &&
|
i2p::util::net::IsYggdrasilAddress (ep.address ()) &&
|
||||||
i2p::context.SupportsMesh ()
|
i2p::context.SupportsMesh ()
|
||||||
|
@ -765,6 +772,7 @@ namespace data
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
it++;
|
||||||
}
|
}
|
||||||
if (!connected)
|
if (!connected)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -22,7 +22,6 @@
|
||||||
#include "ECIESX25519AEADRatchetSession.h"
|
#include "ECIESX25519AEADRatchetSession.h"
|
||||||
#include "Transports.h"
|
#include "Transports.h"
|
||||||
#include "Tunnel.h"
|
#include "Tunnel.h"
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "RouterContext.h"
|
#include "RouterContext.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -34,14 +33,13 @@ namespace i2p
|
||||||
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
|
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
|
||||||
m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone),
|
m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone),
|
||||||
m_Testing (false), m_TestingV6 (false), m_NetID (I2PD_NET_ID),
|
m_Testing (false), m_TestingV6 (false), m_NetID (I2PD_NET_ID),
|
||||||
m_PublishReplyToken (0), m_IsHiddenMode (false),
|
m_PublishReplyToken (0), m_IsHiddenMode (false)
|
||||||
m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL), m_IsSaving (false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void RouterContext::Init ()
|
void RouterContext::Init ()
|
||||||
{
|
{
|
||||||
srand (m_Rng () % 1000);
|
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
|
||||||
m_StartupTime = i2p::util::GetMonotonicSeconds ();
|
m_StartupTime = i2p::util::GetMonotonicSeconds ();
|
||||||
|
|
||||||
if (!Load ())
|
if (!Load ())
|
||||||
|
@ -142,7 +140,7 @@ namespace i2p
|
||||||
{
|
{
|
||||||
boost::asio::ip::address addr;
|
boost::asio::ip::address addr;
|
||||||
if (!host.empty ())
|
if (!host.empty ())
|
||||||
addr = boost::asio::ip::make_address (host);
|
addr = boost::asio::ip::address::from_string (host);
|
||||||
if (!addr.is_v4())
|
if (!addr.is_v4())
|
||||||
addr = boost::asio::ip::address_v4 ();
|
addr = boost::asio::ip::address_v4 ();
|
||||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||||
|
@ -163,7 +161,7 @@ namespace i2p
|
||||||
{
|
{
|
||||||
boost::asio::ip::address addr;
|
boost::asio::ip::address addr;
|
||||||
if (!host.empty ())
|
if (!host.empty ())
|
||||||
addr = boost::asio::ip::make_address (host);
|
addr = boost::asio::ip::address::from_string (host);
|
||||||
if (!addr.is_v4())
|
if (!addr.is_v4())
|
||||||
addr = boost::asio::ip::address_v4 ();
|
addr = boost::asio::ip::address_v4 ();
|
||||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||||
|
@ -194,7 +192,7 @@ namespace i2p
|
||||||
ntcp2Host = host;
|
ntcp2Host = host;
|
||||||
boost::asio::ip::address addr;
|
boost::asio::ip::address addr;
|
||||||
if (!ntcp2Host.empty ())
|
if (!ntcp2Host.empty ())
|
||||||
addr = boost::asio::ip::make_address (ntcp2Host);
|
addr = boost::asio::ip::address::from_string (ntcp2Host);
|
||||||
if (!addr.is_v6())
|
if (!addr.is_v6())
|
||||||
addr = boost::asio::ip::address_v6 ();
|
addr = boost::asio::ip::address_v6 ();
|
||||||
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||||
|
@ -213,7 +211,7 @@ namespace i2p
|
||||||
{
|
{
|
||||||
boost::asio::ip::address addr;
|
boost::asio::ip::address addr;
|
||||||
if (!host.empty ())
|
if (!host.empty ())
|
||||||
addr = boost::asio::ip::make_address (host);
|
addr = boost::asio::ip::address::from_string (host);
|
||||||
if (!addr.is_v6())
|
if (!addr.is_v6())
|
||||||
addr = boost::asio::ip::address_v6 ();
|
addr = boost::asio::ip::address_v6 ();
|
||||||
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||||
|
@ -255,36 +253,11 @@ namespace i2p
|
||||||
|
|
||||||
void RouterContext::UpdateRouterInfo ()
|
void RouterContext::UpdateRouterInfo ()
|
||||||
{
|
{
|
||||||
std::shared_ptr<i2p::data::RouterInfo::Buffer> buffer;
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(m_RouterInfoMutex);
|
std::lock_guard<std::mutex> l(m_RouterInfoMutex);
|
||||||
m_RouterInfo.CreateBuffer (m_Keys);
|
m_RouterInfo.CreateBuffer (m_Keys);
|
||||||
buffer = m_RouterInfo.CopyBuffer ();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// update save buffer to latest
|
|
||||||
std::lock_guard<std::mutex> l(m_SaveBufferMutex);
|
|
||||||
m_SaveBuffer = buffer;
|
|
||||||
}
|
|
||||||
bool isSaving = false;
|
|
||||||
if (m_IsSaving.compare_exchange_strong (isSaving, true)) // try to save only if not being saved
|
|
||||||
{
|
|
||||||
auto savingRouterInfo = std::async (std::launch::async, [this]()
|
|
||||||
{
|
|
||||||
std::shared_ptr<i2p::data::RouterInfo::Buffer> buffer;
|
|
||||||
while (m_SaveBuffer)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_SaveBufferMutex);
|
|
||||||
buffer = m_SaveBuffer;
|
|
||||||
m_SaveBuffer = nullptr;
|
|
||||||
}
|
|
||||||
if (buffer)
|
|
||||||
i2p::data::RouterInfo::SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO), buffer);
|
|
||||||
}
|
|
||||||
m_IsSaving = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO));
|
||||||
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
|
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,11 +323,9 @@ namespace i2p
|
||||||
case eRouterStatusFirewalled:
|
case eRouterStatusFirewalled:
|
||||||
SetUnreachable (true, false); // ipv4
|
SetUnreachable (true, false); // ipv4
|
||||||
break;
|
break;
|
||||||
case eRouterStatusMesh:
|
|
||||||
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eReachable);
|
|
||||||
break;
|
|
||||||
case eRouterStatusProxy:
|
case eRouterStatusProxy:
|
||||||
m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eUnreachable);
|
m_AcceptsTunnels = false;
|
||||||
|
UpdateCongestion ();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
@ -676,11 +647,11 @@ namespace i2p
|
||||||
|
|
||||||
void RouterContext::SetBandwidth (int limit)
|
void RouterContext::SetBandwidth (int limit)
|
||||||
{
|
{
|
||||||
if (limit > (int)i2p::data::EXTRA_BANDWIDTH_LIMIT) { SetBandwidth('X'); }
|
if (limit > 2000) { SetBandwidth('X'); }
|
||||||
else if (limit > (int)i2p::data::HIGH_BANDWIDTH_LIMIT) { SetBandwidth('P'); }
|
else if (limit > 256) { SetBandwidth('P'); }
|
||||||
else if (limit > 128) { SetBandwidth('O'); }
|
else if (limit > 128) { SetBandwidth('O'); }
|
||||||
else if (limit > 64) { SetBandwidth('N'); }
|
else if (limit > 64) { SetBandwidth('N'); }
|
||||||
else if (limit > (int)i2p::data::LOW_BANDWIDTH_LIMIT) { SetBandwidth('M'); }
|
else if (limit > 48) { SetBandwidth('M'); }
|
||||||
else if (limit > 12) { SetBandwidth('L'); }
|
else if (limit > 12) { SetBandwidth('L'); }
|
||||||
else { SetBandwidth('K'); }
|
else { SetBandwidth('K'); }
|
||||||
m_BandwidthLimit = limit; // set precise limit
|
m_BandwidthLimit = limit; // set precise limit
|
||||||
|
@ -829,7 +800,7 @@ namespace i2p
|
||||||
i2p::config::GetOption("host", ntcp2Host);
|
i2p::config::GetOption("host", ntcp2Host);
|
||||||
if (!ntcp2Host.empty () && ntcp2Port)
|
if (!ntcp2Host.empty () && ntcp2Port)
|
||||||
{
|
{
|
||||||
auto addr = boost::asio::ip::make_address (ntcp2Host);
|
auto addr = boost::asio::ip::address::from_string (ntcp2Host);
|
||||||
if (addr.is_v6 ())
|
if (addr.is_v6 ())
|
||||||
{
|
{
|
||||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||||
|
@ -858,7 +829,7 @@ namespace i2p
|
||||||
std::string host; i2p::config::GetOption("host", host);
|
std::string host; i2p::config::GetOption("host", host);
|
||||||
if (!host.empty ())
|
if (!host.empty ())
|
||||||
{
|
{
|
||||||
auto addr = boost::asio::ip::make_address (host);
|
auto addr = boost::asio::ip::address::from_string (host);
|
||||||
if (addr.is_v6 ())
|
if (addr.is_v6 ())
|
||||||
{
|
{
|
||||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||||
|
@ -927,7 +898,7 @@ namespace i2p
|
||||||
std::string host; i2p::config::GetOption("host", host);
|
std::string host; i2p::config::GetOption("host", host);
|
||||||
if (!host.empty ())
|
if (!host.empty ())
|
||||||
{
|
{
|
||||||
auto addr = boost::asio::ip::make_address (host);
|
auto addr = boost::asio::ip::address::from_string (host);
|
||||||
if (addr.is_v4 ())
|
if (addr.is_v4 ())
|
||||||
{
|
{
|
||||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, addr, ntcp2Port);
|
||||||
|
@ -957,7 +928,7 @@ namespace i2p
|
||||||
std::string host; i2p::config::GetOption("host", host);
|
std::string host; i2p::config::GetOption("host", host);
|
||||||
if (!host.empty ())
|
if (!host.empty ())
|
||||||
{
|
{
|
||||||
auto addr = boost::asio::ip::make_address (host);
|
auto addr = boost::asio::ip::address::from_string (host);
|
||||||
if (addr.is_v4 ())
|
if (addr.is_v4 ())
|
||||||
{
|
{
|
||||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addr, ssu2Port);
|
||||||
|
@ -1194,8 +1165,7 @@ namespace i2p
|
||||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
|
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
|
||||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
|
||||||
{
|
{
|
||||||
if (typeID == eI2NPTunnelTest)
|
if (typeID == eI2NPTunnelTest)
|
||||||
{
|
{
|
||||||
|
@ -1213,7 +1183,7 @@ namespace i2p
|
||||||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
if (m_Service)
|
if (m_Service)
|
||||||
boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostGarlicMessage, this, msg));
|
m_Service->GetService ().post (std::bind (&RouterContext::PostGarlicMessage, this, msg));
|
||||||
else
|
else
|
||||||
LogPrint (eLogError, "Router: service is NULL");
|
LogPrint (eLogError, "Router: service is NULL");
|
||||||
}
|
}
|
||||||
|
@ -1241,7 +1211,7 @@ namespace i2p
|
||||||
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
if (m_Service)
|
if (m_Service)
|
||||||
boost::asio::post (m_Service->GetService (), std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg));
|
m_Service->GetService ().post (std::bind (&RouterContext::PostDeliveryStatusMessage, this, msg));
|
||||||
else
|
else
|
||||||
LogPrint (eLogError, "Router: service is NULL");
|
LogPrint (eLogError, "Router: service is NULL");
|
||||||
}
|
}
|
||||||
|
@ -1270,7 +1240,7 @@ namespace i2p
|
||||||
} data;
|
} data;
|
||||||
memcpy (data.k, key, 32);
|
memcpy (data.k, key, 32);
|
||||||
data.t = tag;
|
data.t = tag;
|
||||||
boost::asio::post (m_Service->GetService (), [this,data](void)
|
m_Service->GetService ().post ([this,data](void)
|
||||||
{
|
{
|
||||||
AddECIESx25519Key (data.k, data.t);
|
AddECIESx25519Key (data.k, data.t);
|
||||||
});
|
});
|
||||||
|
@ -1387,7 +1357,7 @@ namespace i2p
|
||||||
{
|
{
|
||||||
m_PublishTimer->cancel ();
|
m_PublishTimer->cancel ();
|
||||||
m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_PUBLISH_INTERVAL +
|
m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_PUBLISH_INTERVAL +
|
||||||
m_Rng () % ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE));
|
rand () % ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE));
|
||||||
m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishTimer,
|
m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishTimer,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
@ -1436,7 +1406,7 @@ namespace i2p
|
||||||
auto onDrop = [this]()
|
auto onDrop = [this]()
|
||||||
{
|
{
|
||||||
if (m_Service)
|
if (m_Service)
|
||||||
boost::asio::post (m_Service->GetService (), [this]() { HandlePublishResendTimer (boost::system::error_code ()); });
|
m_Service->GetService ().post ([this]() { HandlePublishResendTimer (boost::system::error_code ()); });
|
||||||
};
|
};
|
||||||
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()) || // already connected
|
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()) || // already connected
|
||||||
(floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && // are we able to connect
|
(floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && // are we able to connect
|
||||||
|
@ -1462,7 +1432,7 @@ namespace i2p
|
||||||
i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ()));
|
i2p::garlic::WrapECIESX25519MessageForRouter (msg, floodfill->GetIdentity ()->GetEncryptionPublicKey ()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LogPrint (eLogInfo, "Router: Can't publish our RouterInfo. No tunnels. Try again in ", ROUTER_INFO_CONFIRMATION_TIMEOUT, " milliseconds");
|
LogPrint (eLogInfo, "Router: Can't publish our RouterInfo. No tunnles. Try again in ", ROUTER_INFO_CONFIRMATION_TIMEOUT, " seconds");
|
||||||
}
|
}
|
||||||
m_PublishExcluded.insert (floodfill->GetIdentHash ());
|
m_PublishExcluded.insert (floodfill->GetIdentHash ());
|
||||||
m_PublishReplyToken = replyToken;
|
m_PublishReplyToken = replyToken;
|
||||||
|
@ -1476,7 +1446,7 @@ namespace i2p
|
||||||
if (m_PublishTimer)
|
if (m_PublishTimer)
|
||||||
{
|
{
|
||||||
m_PublishTimer->cancel ();
|
m_PublishTimer->cancel ();
|
||||||
m_PublishTimer->expires_from_now (boost::posix_time::milliseconds(ROUTER_INFO_CONFIRMATION_TIMEOUT));
|
m_PublishTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CONFIRMATION_TIMEOUT));
|
||||||
m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishResendTimer,
|
m_PublishTimer->async_wait (std::bind (&RouterContext::HandlePublishResendTimer,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
@ -1499,8 +1469,7 @@ namespace i2p
|
||||||
if (m_CongestionUpdateTimer)
|
if (m_CongestionUpdateTimer)
|
||||||
{
|
{
|
||||||
m_CongestionUpdateTimer->cancel ();
|
m_CongestionUpdateTimer->cancel ();
|
||||||
m_CongestionUpdateTimer->expires_from_now (boost::posix_time::seconds(
|
m_CongestionUpdateTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CONGESTION_UPDATE_INTERVAL));
|
||||||
ROUTER_INFO_CONGESTION_UPDATE_INTERVAL + m_Rng () % ROUTER_INFO_CONGESTION_UPDATE_INTERVAL_VARIANCE));
|
|
||||||
m_CongestionUpdateTimer->async_wait (std::bind (&RouterContext::HandleCongestionUpdateTimer,
|
m_CongestionUpdateTimer->async_wait (std::bind (&RouterContext::HandleCongestionUpdateTimer,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
@ -1539,7 +1508,7 @@ namespace i2p
|
||||||
if (m_CleanupTimer)
|
if (m_CleanupTimer)
|
||||||
{
|
{
|
||||||
m_CleanupTimer->cancel ();
|
m_CleanupTimer->cancel ();
|
||||||
m_CleanupTimer->expires_from_now (boost::posix_time::seconds(ROUTER_INFO_CLEANUP_INTERVAL));
|
m_CleanupTimer->expires_from_now (boost::posix_time::minutes(ROUTER_INFO_CLEANUP_INTERVAL));
|
||||||
m_CleanupTimer->async_wait (std::bind (&RouterContext::HandleCleanupTimer,
|
m_CleanupTimer->async_wait (std::bind (&RouterContext::HandleCleanupTimer,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -12,7 +12,6 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <random>
|
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
|
@ -35,11 +34,10 @@ namespace garlic
|
||||||
const int ROUTER_INFO_PUBLISH_INTERVAL = 39*60; // in seconds
|
const int ROUTER_INFO_PUBLISH_INTERVAL = 39*60; // in seconds
|
||||||
const int ROUTER_INFO_INITIAL_PUBLISH_INTERVAL = 10; // in seconds
|
const int ROUTER_INFO_INITIAL_PUBLISH_INTERVAL = 10; // in seconds
|
||||||
const int ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE = 105;// in seconds
|
const int ROUTER_INFO_PUBLISH_INTERVAL_VARIANCE = 105;// in seconds
|
||||||
const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 1600; // in milliseconds
|
const int ROUTER_INFO_CONFIRMATION_TIMEOUT = 5; // in seconds
|
||||||
const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
|
const int ROUTER_INFO_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
|
||||||
const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 11*60; // in seconds
|
const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL = 12*60; // in seconds
|
||||||
const int ROUTER_INFO_CONGESTION_UPDATE_INTERVAL_VARIANCE = 130; // in seconds
|
const int ROUTER_INFO_CLEANUP_INTERVAL = 5; // in minutes
|
||||||
const int ROUTER_INFO_CLEANUP_INTERVAL = 102; // in seconds
|
|
||||||
|
|
||||||
enum RouterStatus
|
enum RouterStatus
|
||||||
{
|
{
|
||||||
|
@ -92,7 +90,7 @@ namespace garlic
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RouterService (): RunnableServiceWithWork ("Router") {};
|
RouterService (): RunnableServiceWithWork ("Router") {};
|
||||||
auto& GetService () { return GetIOService (); };
|
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||||
void Start () { StartIOService (); };
|
void Start () { StartIOService (); };
|
||||||
void Stop () { StopIOService (); };
|
void Stop () { StopIOService (); };
|
||||||
};
|
};
|
||||||
|
@ -148,6 +146,7 @@ namespace garlic
|
||||||
void SetNetID (int netID) { m_NetID = netID; };
|
void SetNetID (int netID) { m_NetID = netID; };
|
||||||
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
|
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
|
||||||
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
|
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
|
||||||
|
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||||
|
|
||||||
void UpdatePort (int port); // called from Daemon
|
void UpdatePort (int port); // called from Daemon
|
||||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon
|
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon
|
||||||
|
@ -187,25 +186,24 @@ namespace garlic
|
||||||
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
|
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
|
||||||
|
|
||||||
// implements LocalDestination
|
// implements LocalDestination
|
||||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override{ return m_Keys.GetPublic (); };
|
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||||
void SetLeaseSetUpdated (bool post) override {};
|
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||||
|
void SetLeaseSetUpdated () {};
|
||||||
|
|
||||||
// implements GarlicDestination
|
// implements GarlicDestination
|
||||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override { return nullptr; };
|
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override;
|
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
|
||||||
|
|
||||||
// override GarlicDestination
|
// override GarlicDestination
|
||||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
|
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
|
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// implements GarlicDestination
|
// implements GarlicDestination
|
||||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
|
||||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -218,7 +216,6 @@ namespace garlic
|
||||||
void UpdateSSU2Keys ();
|
void UpdateSSU2Keys ();
|
||||||
bool Load ();
|
bool Load ();
|
||||||
void SaveKeys ();
|
void SaveKeys ();
|
||||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
|
||||||
uint16_t SelectRandomPort () const;
|
uint16_t SelectRandomPort () const;
|
||||||
void PublishNTCP2Address (std::shared_ptr<i2p::data::RouterInfo::Address> address, int port, bool publish) const;
|
void PublishNTCP2Address (std::shared_ptr<i2p::data::RouterInfo::Address> address, int port, bool publish) const;
|
||||||
|
|
||||||
|
@ -266,10 +263,6 @@ namespace garlic
|
||||||
uint32_t m_PublishReplyToken;
|
uint32_t m_PublishReplyToken;
|
||||||
bool m_IsHiddenMode; // not publish
|
bool m_IsHiddenMode; // not publish
|
||||||
mutable std::mutex m_RouterInfoMutex;
|
mutable std::mutex m_RouterInfoMutex;
|
||||||
std::mt19937 m_Rng;
|
|
||||||
std::shared_ptr<i2p::data::RouterInfo::Buffer> m_SaveBuffer;
|
|
||||||
std::mutex m_SaveBufferMutex; // TODO: make m_SaveBuffer atomic
|
|
||||||
std::atomic<bool> m_IsSaving;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RouterContext context;
|
extern RouterContext context;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
#include "I2PEndian.h"
|
#include "I2PEndian.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <charconv>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/algorithm/string.hpp> // for boost::to_lower
|
#include <boost/algorithm/string.hpp> // for boost::to_lower
|
||||||
#ifndef __cpp_lib_atomic_shared_ptr
|
#ifndef __cpp_lib_atomic_shared_ptr
|
||||||
#include <boost/atomic.hpp>
|
#include <boost/atomic.hpp>
|
||||||
|
@ -25,7 +25,6 @@
|
||||||
#include "Transports.h"
|
#include "Transports.h"
|
||||||
#include "NetDb.hpp"
|
#include "NetDb.hpp"
|
||||||
#include "RouterContext.h"
|
#include "RouterContext.h"
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "RouterInfo.h"
|
#include "RouterInfo.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
@ -46,9 +45,8 @@ namespace data
|
||||||
|
|
||||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
RouterInfo::RouterInfo (const std::string& fullPath):
|
||||||
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false),
|
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false),
|
||||||
m_IsBufferScheduledToDelete (false), m_SupportedTransports (0),
|
m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0),
|
||||||
m_ReachableTransports (0), m_PublishedTransports (0), m_Caps (0), m_Version (0),
|
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
|
||||||
m_Congestion (eLowCongestion)
|
|
||||||
{
|
{
|
||||||
m_Addresses = AddressesPtr(new Addresses ()); // create empty list
|
m_Addresses = AddressesPtr(new Addresses ()); // create empty list
|
||||||
m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's
|
m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's
|
||||||
|
@ -57,7 +55,7 @@ namespace data
|
||||||
|
|
||||||
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
|
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
|
||||||
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false),
|
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false),
|
||||||
m_IsBufferScheduledToDelete (false), m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0),
|
m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0),
|
||||||
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
|
m_Caps (0), m_Version (0), m_Congestion (eLowCongestion)
|
||||||
{
|
{
|
||||||
if (len <= MAX_RI_BUFFER_SIZE)
|
if (len <= MAX_RI_BUFFER_SIZE)
|
||||||
|
@ -107,7 +105,8 @@ namespace data
|
||||||
// skip identity
|
// skip identity
|
||||||
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
||||||
// read new RI
|
// read new RI
|
||||||
ReadFromBuffer (buf + identityLen, len - identityLen);
|
std::stringstream str (std::string ((char *)buf + identityLen, len - identityLen));
|
||||||
|
ReadFromStream (str);
|
||||||
if (!m_IsUnreachable)
|
if (!m_IsUnreachable)
|
||||||
UpdateBuffer (buf, len); // save buffer
|
UpdateBuffer (buf, len); // save buffer
|
||||||
// don't delete buffer until saved to the file
|
// don't delete buffer until saved to the file
|
||||||
|
@ -195,34 +194,39 @@ namespace data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// parse RI
|
// parse RI
|
||||||
if (!ReadFromBuffer (m_Buffer->data () + identityLen, bufferLen - identityLen))
|
std::stringstream str;
|
||||||
|
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen);
|
||||||
|
ReadFromStream (str);
|
||||||
|
if (!str)
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "RouterInfo: Malformed message");
|
LogPrint (eLogError, "RouterInfo: Malformed message");
|
||||||
m_IsUnreachable = true;
|
m_IsUnreachable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterInfo::ReadFromBuffer (const uint8_t * buf, size_t len)
|
void RouterInfo::ReadFromStream (std::istream& s)
|
||||||
{
|
{
|
||||||
if (len < 9) return false;
|
if (!s) return;
|
||||||
m_Caps = 0; m_Congestion = eLowCongestion;
|
m_Caps = 0; m_Congestion = eLowCongestion;
|
||||||
m_Timestamp = bufbe64toh (buf);
|
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||||
size_t offset = 8; // timestamp
|
m_Timestamp = be64toh (m_Timestamp);
|
||||||
// read addresses
|
// read addresses
|
||||||
auto addresses = NewAddresses ();
|
auto addresses = NewAddresses ();
|
||||||
uint8_t numAddresses = buf[offset]; offset++;
|
uint8_t numAddresses;
|
||||||
|
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
||||||
for (int i = 0; i < numAddresses; i++)
|
for (int i = 0; i < numAddresses; i++)
|
||||||
{
|
{
|
||||||
if (offset + 9 > len) return false; // 1 byte cost + 8 bytes date
|
|
||||||
uint8_t supportedTransports = 0;
|
uint8_t supportedTransports = 0;
|
||||||
auto address = NewAddress ();
|
auto address = NewAddress ();
|
||||||
offset++; // cost, ignore
|
uint8_t cost; // ignore
|
||||||
address->date = bufbe64toh (buf + offset); offset += 8; // date
|
s.read ((char *)&cost, sizeof (cost));
|
||||||
|
s.read ((char *)&address->date, sizeof (address->date));
|
||||||
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
|
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
|
||||||
auto transportStyle = ExtractString (buf + offset, len - offset); offset += transportStyle.length () + 1;
|
char transportStyle[6];
|
||||||
if (!transportStyle.compare (0, 4, "NTCP")) // NTCP or NTCP2
|
ReadString (transportStyle, 6, s);
|
||||||
|
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
|
||||||
address->transportStyle = eTransportNTCP2;
|
address->transportStyle = eTransportNTCP2;
|
||||||
else if (!transportStyle.compare (0, 3, "SSU")) // SSU or SSU2
|
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2
|
||||||
{
|
{
|
||||||
address->transportStyle = eTransportSSU2;
|
address->transportStyle = eTransportSSU2;
|
||||||
address->ssu.reset (new SSUExt ());
|
address->ssu.reset (new SSUExt ());
|
||||||
|
@ -232,25 +236,27 @@ namespace data
|
||||||
address->transportStyle = eTransportUnknown;
|
address->transportStyle = eTransportUnknown;
|
||||||
address->caps = 0;
|
address->caps = 0;
|
||||||
address->port = 0;
|
address->port = 0;
|
||||||
if (offset + 2 > len) return false;
|
uint16_t size, r = 0;
|
||||||
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
|
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||||
if (offset + size >= len) return false;
|
size = be16toh (size);
|
||||||
if (address->transportStyle == eTransportUnknown)
|
if (address->transportStyle == eTransportUnknown)
|
||||||
{
|
{
|
||||||
// skip unknown address
|
// skip unknown address
|
||||||
offset += size;
|
s.seekg (size, std::ios_base::cur);
|
||||||
continue;
|
if (s) continue; else return;
|
||||||
}
|
}
|
||||||
size_t r = 0;
|
|
||||||
while (r < size)
|
while (r < size)
|
||||||
{
|
{
|
||||||
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
|
char key[255], value[255];
|
||||||
r += sz; offset += sz;
|
r += ReadString (key, 255, s);
|
||||||
if (key.empty ()) continue;
|
s.seekg (1, std::ios_base::cur); r++; // =
|
||||||
if (key == "host")
|
r += ReadString (value, 255, s);
|
||||||
|
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||||
|
if (!s) return;
|
||||||
|
if (!strcmp (key, "host"))
|
||||||
{
|
{
|
||||||
boost::system::error_code ecode;
|
boost::system::error_code ecode;
|
||||||
address->host = boost::asio::ip::make_address (value, ecode);
|
address->host = boost::asio::ip::address::from_string (value, ecode);
|
||||||
if (!ecode && !address->host.is_unspecified ())
|
if (!ecode && !address->host.is_unspecified ())
|
||||||
{
|
{
|
||||||
if (!i2p::transport::transports.IsInReservedRange (address->host) ||
|
if (!i2p::transport::transports.IsInReservedRange (address->host) ||
|
||||||
|
@ -261,53 +267,63 @@ namespace data
|
||||||
address->transportStyle = eTransportUnknown;
|
address->transportStyle = eTransportUnknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (key == "port")
|
else if (!strcmp (key, "port"))
|
||||||
{
|
{
|
||||||
auto res = std::from_chars(value.data(), value.data() + value.size(), address->port);
|
try
|
||||||
if (res.ec != std::errc())
|
{
|
||||||
LogPrint (eLogWarning, "RouterInfo: 'port' conversion error: ", std::make_error_code (res.ec).message ());
|
address->port = boost::lexical_cast<int>(value);
|
||||||
}
|
}
|
||||||
else if (key == "mtu")
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp (key, "mtu"))
|
||||||
{
|
{
|
||||||
if (address->ssu)
|
if (address->ssu)
|
||||||
{
|
{
|
||||||
auto res = std::from_chars(value.data(), value.data() + value.size(), address->ssu->mtu);
|
try
|
||||||
if (res.ec != std::errc())
|
{
|
||||||
LogPrint (eLogWarning, "RouterInfo: 'mtu' conversion error: ", std::make_error_code (res.ec).message ());
|
address->ssu->mtu = boost::lexical_cast<int>(value);
|
||||||
|
}
|
||||||
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "RouterInfo: 'mtu' exception ", ex.what ());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
|
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
|
||||||
}
|
}
|
||||||
else if (key == "caps")
|
else if (!strcmp (key, "caps"))
|
||||||
address->caps = ExtractAddressCaps (value);
|
address->caps = ExtractAddressCaps (value);
|
||||||
else if (key == "s") // ntcp2 or ssu2 static key
|
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key
|
||||||
{
|
{
|
||||||
if (Base64ToByteStream (value, address->s, 32) == 32 &&
|
if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 &&
|
||||||
!(address->s[31] & 0x80)) // check if x25519 public key
|
!(address->s[31] & 0x80)) // check if x25519 public key
|
||||||
isStaticKey = true;
|
isStaticKey = true;
|
||||||
else
|
else
|
||||||
address->transportStyle = eTransportUnknown; // invalid address
|
address->transportStyle = eTransportUnknown; // invalid address
|
||||||
}
|
}
|
||||||
else if (key == "i") // ntcp2 iv or ssu2 intro
|
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro
|
||||||
{
|
{
|
||||||
if (address->IsNTCP2 ())
|
if (address->IsNTCP2 ())
|
||||||
{
|
{
|
||||||
if (Base64ToByteStream (value, address->i, 16) == 16)
|
if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16)
|
||||||
address->published = true; // presence of "i" means "published" NTCP2
|
address->published = true; // presence of "i" means "published" NTCP2
|
||||||
else
|
else
|
||||||
address->transportStyle = eTransportUnknown; // invalid address
|
address->transportStyle = eTransportUnknown; // invalid address
|
||||||
}
|
}
|
||||||
else if (address->IsSSU2 ())
|
else if (address->IsSSU2 ())
|
||||||
{
|
{
|
||||||
if (Base64ToByteStream (value, address->i, 32) == 32)
|
if (Base64ToByteStream (value, strlen (value), address->i, 32) == 32)
|
||||||
isIntroKey = true;
|
isIntroKey = true;
|
||||||
else
|
else
|
||||||
address->transportStyle = eTransportUnknown; // invalid address
|
address->transportStyle = eTransportUnknown; // invalid address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (key == "v")
|
else if (!strcmp (key, "v"))
|
||||||
{
|
{
|
||||||
if (value == "2")
|
if (!strcmp (value, "2"))
|
||||||
isV2 = true;
|
isV2 = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -323,11 +339,13 @@ namespace data
|
||||||
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
|
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unsigned char index = key[key.length () - 1] - '0'; // TODO:
|
size_t l = strlen(key);
|
||||||
|
unsigned char index = key[l-1] - '0'; // TODO:
|
||||||
|
key[l-1] = 0;
|
||||||
if (index > 9)
|
if (index > 9)
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
|
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
|
||||||
continue;
|
if (s) continue; else return;
|
||||||
}
|
}
|
||||||
if (index >= address->ssu->introducers.size ())
|
if (index >= address->ssu->introducers.size ())
|
||||||
{
|
{
|
||||||
|
@ -336,23 +354,34 @@ namespace data
|
||||||
address->ssu->introducers.resize (index + 1);
|
address->ssu->introducers.resize (index + 1);
|
||||||
}
|
}
|
||||||
Introducer& introducer = address->ssu->introducers.at (index);
|
Introducer& introducer = address->ssu->introducers.at (index);
|
||||||
auto key1 = key.substr(0, key.length () - 1);
|
if (!strcmp (key, "itag"))
|
||||||
if (key1 == "itag")
|
|
||||||
{
|
{
|
||||||
auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iTag);
|
try
|
||||||
if (res.ec != std::errc())
|
|
||||||
LogPrint (eLogWarning, "RouterInfo: 'itag' conversion error: ", std::make_error_code (res.ec).message ());
|
|
||||||
}
|
|
||||||
else if (key1 == "ih")
|
|
||||||
Base64ToByteStream (value, introducer.iH, 32);
|
|
||||||
else if (key1 == "iexp")
|
|
||||||
{
|
{
|
||||||
auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iExp);
|
introducer.iTag = boost::lexical_cast<uint32_t>(value);
|
||||||
if (res.ec != std::errc())
|
}
|
||||||
LogPrint (eLogWarning, "RouterInfo: 'iexp' conversion error: ", std::make_error_code (res.ec).message ());
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp (key, "ih"))
|
||||||
|
Base64ToByteStream (value, strlen (value), introducer.iH, 32);
|
||||||
|
else if (!strcmp (key, "iexp"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
introducer.iExp = boost::lexical_cast<uint32_t>(value);
|
||||||
|
}
|
||||||
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
LogPrint (eLogWarning, "RouterInfo: 'iexp' exception ", ex.what ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!s) return;
|
||||||
|
}
|
||||||
|
|
||||||
if (address->transportStyle == eTransportNTCP2)
|
if (address->transportStyle == eTransportNTCP2)
|
||||||
{
|
{
|
||||||
if (isStaticKey)
|
if (isStaticKey)
|
||||||
|
@ -416,73 +445,66 @@ namespace data
|
||||||
boost::atomic_store (&m_Addresses, addresses);
|
boost::atomic_store (&m_Addresses, addresses);
|
||||||
#endif
|
#endif
|
||||||
// read peers
|
// read peers
|
||||||
if (offset + 1 > len) return false;
|
uint8_t numPeers;
|
||||||
uint8_t numPeers = buf[offset]; offset++; // num peers
|
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
|
||||||
offset += numPeers*32; // TODO: read peers
|
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
|
||||||
// read properties
|
// read properties
|
||||||
if (offset + 2 > len) return false;
|
|
||||||
m_Version = 0;
|
m_Version = 0;
|
||||||
bool isNetId = false;
|
bool isNetId = false;
|
||||||
std::string family;
|
std::string family;
|
||||||
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
|
uint16_t size, r = 0;
|
||||||
if (offset + size > len) return false;
|
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||||
size_t r = 0;
|
size = be16toh (size);
|
||||||
while (r < size)
|
while (r < size)
|
||||||
{
|
{
|
||||||
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
|
char key[255], value[255];
|
||||||
r += sz; offset += sz;
|
r += ReadString (key, 255, s);
|
||||||
if (key.empty ()) continue;
|
s.seekg (1, std::ios_base::cur); r++; // =
|
||||||
|
r += ReadString (value, 255, s);
|
||||||
|
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||||
|
if (!s) return;
|
||||||
SetProperty (key, value);
|
SetProperty (key, value);
|
||||||
|
|
||||||
// extract caps
|
// extract caps
|
||||||
if (key == "caps")
|
if (!strcmp (key, "caps"))
|
||||||
{
|
{
|
||||||
ExtractCaps (value);
|
ExtractCaps (value);
|
||||||
m_IsFloodfill = IsDeclaredFloodfill ();
|
m_IsFloodfill = IsDeclaredFloodfill ();
|
||||||
}
|
}
|
||||||
// extract version
|
// extract version
|
||||||
else if (key == ROUTER_INFO_PROPERTY_VERSION)
|
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION))
|
||||||
{
|
{
|
||||||
m_Version = 0;
|
m_Version = 0;
|
||||||
for (auto ch: value)
|
char * ch = value;
|
||||||
|
while (*ch)
|
||||||
{
|
{
|
||||||
if (ch >= '0' && ch <= '9')
|
if (*ch >= '0' && *ch <= '9')
|
||||||
{
|
{
|
||||||
m_Version *= 10;
|
m_Version *= 10;
|
||||||
m_Version += (ch - '0');
|
m_Version += (*ch - '0');
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6)))
|
|
||||||
{
|
|
||||||
auto addresses = GetAddresses ();
|
|
||||||
if (addresses)
|
|
||||||
{
|
|
||||||
if ((*addresses)[eSSU2V4Idx]) (*addresses)[eSSU2V4Idx]->caps &= ~eSSUTesting;
|
|
||||||
if ((*addresses)[eSSU2V6Idx]) (*addresses)[eSSU2V6Idx]->caps &= ~eSSUTesting;
|
|
||||||
}
|
}
|
||||||
|
ch++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check netId
|
// check netId
|
||||||
else if (key == ROUTER_INFO_PROPERTY_NETID)
|
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID))
|
||||||
{
|
{
|
||||||
isNetId = true;
|
isNetId = true;
|
||||||
int netID;
|
if (atoi (value) != i2p::context.GetNetID ())
|
||||||
auto res = std::from_chars(value.data(), value.data() + value.size(), netID);
|
|
||||||
if (res.ec != std::errc() || netID != i2p::context.GetNetID ())
|
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
|
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
|
||||||
m_IsUnreachable = true;
|
m_IsUnreachable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// family
|
// family
|
||||||
else if (key == ROUTER_INFO_PROPERTY_FAMILY)
|
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
|
||||||
{
|
{
|
||||||
family = value;
|
family = value;
|
||||||
boost::to_lower (family);
|
boost::to_lower (family);
|
||||||
}
|
}
|
||||||
else if (key == ROUTER_INFO_PROPERTY_FAMILY_SIG)
|
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
|
||||||
{
|
{
|
||||||
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) // TODO
|
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value))
|
||||||
m_FamilyID = netdb.GetFamilies ().GetFamilyID (family);
|
m_FamilyID = netdb.GetFamilies ().GetFamilyID (family);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -490,12 +512,12 @@ namespace data
|
||||||
SetUnreachable (true);
|
SetUnreachable (true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!s) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_SupportedTransports || !isNetId || !m_Version)
|
if (!m_SupportedTransports || !isNetId || !m_Version)
|
||||||
SetUnreachable (true);
|
SetUnreachable (true);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterInfo::IsFamily (FamilyID famid) const
|
bool RouterInfo::IsFamily (FamilyID famid) const
|
||||||
|
@ -503,11 +525,12 @@ namespace data
|
||||||
return m_FamilyID == famid;
|
return m_FamilyID == famid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RouterInfo::ExtractCaps (std::string_view value)
|
void RouterInfo::ExtractCaps (const char * value)
|
||||||
{
|
{
|
||||||
for (auto cap: value)
|
const char * cap = value;
|
||||||
|
while (*cap)
|
||||||
{
|
{
|
||||||
switch (cap)
|
switch (*cap)
|
||||||
{
|
{
|
||||||
case CAPS_FLAG_FLOODFILL:
|
case CAPS_FLAG_FLOODFILL:
|
||||||
m_Caps |= Caps::eFloodfill;
|
m_Caps |= Caps::eFloodfill;
|
||||||
|
@ -516,16 +539,16 @@ namespace data
|
||||||
case CAPS_FLAG_LOW_BANDWIDTH2:
|
case CAPS_FLAG_LOW_BANDWIDTH2:
|
||||||
case CAPS_FLAG_LOW_BANDWIDTH3:
|
case CAPS_FLAG_LOW_BANDWIDTH3:
|
||||||
case CAPS_FLAG_LOW_BANDWIDTH4:
|
case CAPS_FLAG_LOW_BANDWIDTH4:
|
||||||
m_BandwidthCap = cap;
|
m_BandwidthCap = *cap;
|
||||||
break;
|
break;
|
||||||
case CAPS_FLAG_HIGH_BANDWIDTH:
|
case CAPS_FLAG_HIGH_BANDWIDTH:
|
||||||
m_Caps |= Caps::eHighBandwidth;
|
m_Caps |= Caps::eHighBandwidth;
|
||||||
m_BandwidthCap = cap;
|
m_BandwidthCap = *cap;
|
||||||
break;
|
break;
|
||||||
case CAPS_FLAG_EXTRA_BANDWIDTH1:
|
case CAPS_FLAG_EXTRA_BANDWIDTH1:
|
||||||
case CAPS_FLAG_EXTRA_BANDWIDTH2:
|
case CAPS_FLAG_EXTRA_BANDWIDTH2:
|
||||||
m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth;
|
m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth;
|
||||||
m_BandwidthCap = cap;
|
m_BandwidthCap = *cap;
|
||||||
break;
|
break;
|
||||||
case CAPS_FLAG_HIDDEN:
|
case CAPS_FLAG_HIDDEN:
|
||||||
m_Caps |= Caps::eHidden;
|
m_Caps |= Caps::eHidden;
|
||||||
|
@ -547,15 +570,17 @@ namespace data
|
||||||
break;
|
break;
|
||||||
default: ;
|
default: ;
|
||||||
}
|
}
|
||||||
|
cap++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t RouterInfo::ExtractAddressCaps (std::string_view value) const
|
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const
|
||||||
{
|
{
|
||||||
uint8_t caps = 0;
|
uint8_t caps = 0;
|
||||||
for (auto cap: value)
|
const char * cap = value;
|
||||||
|
while (*cap)
|
||||||
{
|
{
|
||||||
switch (cap)
|
switch (*cap)
|
||||||
{
|
{
|
||||||
case CAPS_FLAG_V4:
|
case CAPS_FLAG_V4:
|
||||||
caps |= AddressCaps::eV4;
|
caps |= AddressCaps::eV4;
|
||||||
|
@ -571,6 +596,7 @@ namespace data
|
||||||
break;
|
break;
|
||||||
default: ;
|
default: ;
|
||||||
}
|
}
|
||||||
|
cap++;
|
||||||
}
|
}
|
||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
@ -634,39 +660,23 @@ namespace data
|
||||||
return SaveToFile (fullPath, m_Buffer);
|
return SaveToFile (fullPath, m_Buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view RouterInfo::ExtractString (const uint8_t * buf, size_t len) const
|
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
|
||||||
{
|
{
|
||||||
uint8_t l = buf[0];
|
uint8_t l;
|
||||||
if (l > len)
|
s.read ((char *)&l, 1);
|
||||||
|
if (l < len)
|
||||||
|
{
|
||||||
|
s.read (str, l);
|
||||||
|
if (!s) l = 0; // failed, return empty string
|
||||||
|
str[l] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len);
|
LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len);
|
||||||
l = len;
|
s.seekg (l, std::ios::cur); // skip
|
||||||
|
str[0] = 0;
|
||||||
}
|
}
|
||||||
return { (const char *)(buf + 1), l };
|
return l+1;
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<std::string_view, std::string_view, size_t> RouterInfo::ExtractParam (const uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
auto key = ExtractString (buf, len);
|
|
||||||
size_t offset = key.length () + 1;
|
|
||||||
if (offset >= len) return { std::string_view(), std::string_view(), len };
|
|
||||||
if (buf[offset] != '=')
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead '=' after ", key);
|
|
||||||
key = std::string_view();
|
|
||||||
}
|
|
||||||
offset++;
|
|
||||||
if (offset >= len) return { key, std::string_view(), len };
|
|
||||||
auto value = ExtractString (buf + offset, len - offset);
|
|
||||||
offset += value.length () + 1;
|
|
||||||
if (offset >= len) return { key, std::string_view(), len };
|
|
||||||
if (buf[offset] != ';')
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead ';' after ", value);
|
|
||||||
value = std::string_view();
|
|
||||||
}
|
|
||||||
offset++;
|
|
||||||
return { key, value, offset };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps)
|
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps)
|
||||||
|
@ -1125,7 +1135,6 @@ namespace data
|
||||||
|
|
||||||
void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len)
|
void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
m_IsBufferScheduledToDelete = false;
|
|
||||||
if (!m_Buffer)
|
if (!m_Buffer)
|
||||||
m_Buffer = NewBuffer ();
|
m_Buffer = NewBuffer ();
|
||||||
if (len > m_Buffer->size ()) len = m_Buffer->size ();
|
if (len > m_Buffer->size ()) len = m_Buffer->size ();
|
||||||
|
@ -1185,19 +1194,6 @@ namespace data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RouterInfo::GetTransportName (SupportedTransports tr)
|
|
||||||
{
|
|
||||||
switch (tr)
|
|
||||||
{
|
|
||||||
case eNTCP2V4: return "NTCP2V4";
|
|
||||||
case eNTCP2V6: return "NTCP2V6";
|
|
||||||
case eSSU2V4: return "SSU2V4";
|
|
||||||
case eSSU2V6: return "SSU2V6";
|
|
||||||
case eNTCP2V6Mesh: return "Mesh";
|
|
||||||
default: return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys)
|
void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys)
|
||||||
{
|
{
|
||||||
RefreshTimestamp ();
|
RefreshTimestamp ();
|
||||||
|
@ -1382,9 +1378,9 @@ namespace data
|
||||||
if (!introducer.iTag) continue;
|
if (!introducer.iTag) continue;
|
||||||
if (introducer.iExp) // expiration is specified
|
if (introducer.iExp) // expiration is specified
|
||||||
{
|
{
|
||||||
WriteString ("iexp" + std::to_string(i), properties);
|
WriteString ("iexp" + boost::lexical_cast<std::string>(i), properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
WriteString (std::to_string(introducer.iExp), properties);
|
WriteString (boost::lexical_cast<std::string>(introducer.iExp), properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
|
@ -1393,9 +1389,11 @@ namespace data
|
||||||
for (const auto& introducer: address.ssu->introducers)
|
for (const auto& introducer: address.ssu->introducers)
|
||||||
{
|
{
|
||||||
if (!introducer.iTag) continue;
|
if (!introducer.iTag) continue;
|
||||||
WriteString ("ih" + std::to_string(i), properties);
|
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
auto value = ByteStreamToBase64 (introducer.iH, 32);
|
char value[64];
|
||||||
|
size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64);
|
||||||
|
value[l] = 0;
|
||||||
WriteString (value, properties);
|
WriteString (value, properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
i++;
|
i++;
|
||||||
|
@ -1404,9 +1402,9 @@ namespace data
|
||||||
for (const auto& introducer: address.ssu->introducers)
|
for (const auto& introducer: address.ssu->introducers)
|
||||||
{
|
{
|
||||||
if (!introducer.iTag) continue;
|
if (!introducer.iTag) continue;
|
||||||
WriteString ("itag" + std::to_string(i), properties);
|
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
WriteString (std::to_string(introducer.iTag), properties);
|
WriteString (boost::lexical_cast<std::string>(introducer.iTag), properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -1420,7 +1418,7 @@ namespace data
|
||||||
{
|
{
|
||||||
WriteString ("mtu", properties);
|
WriteString ("mtu", properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
WriteString (std::to_string(address.ssu->mtu), properties);
|
WriteString (boost::lexical_cast<std::string>(address.ssu->mtu), properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1428,7 +1426,7 @@ namespace data
|
||||||
{
|
{
|
||||||
WriteString ("port", properties);
|
WriteString ("port", properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
WriteString (std::to_string(address.port), properties);
|
WriteString (boost::lexical_cast<std::string>(address.port), properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
}
|
}
|
||||||
if (address.IsNTCP2 () || address.IsSSU2 ())
|
if (address.IsNTCP2 () || address.IsSSU2 ())
|
||||||
|
@ -1463,11 +1461,9 @@ namespace data
|
||||||
s.write (properties.str ().c_str (), properties.str ().size ());
|
s.write (properties.str ().c_str (), properties.str ().size ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalRouterInfo::SetProperty (std::string_view key, std::string_view value)
|
void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value)
|
||||||
{
|
{
|
||||||
auto [it, inserted] = m_Properties.emplace (key, value);
|
m_Properties[key] = value;
|
||||||
if (!inserted)
|
|
||||||
it->second = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalRouterInfo::DeleteProperty (const std::string& key)
|
void LocalRouterInfo::DeleteProperty (const std::string& key)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2025, The PurpleI2P Project
|
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <tuple>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
@ -210,8 +208,8 @@ namespace data
|
||||||
typedef boost::shared_ptr<Addresses> AddressesPtr;
|
typedef boost::shared_ptr<Addresses> AddressesPtr;
|
||||||
#endif
|
#endif
|
||||||
RouterInfo (const std::string& fullPath);
|
RouterInfo (const std::string& fullPath);
|
||||||
RouterInfo (const RouterInfo& ) = delete;
|
RouterInfo (const RouterInfo& ) = default;
|
||||||
RouterInfo& operator=(const RouterInfo& ) = delete;
|
RouterInfo& operator=(const RouterInfo& ) = default;
|
||||||
RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len);
|
RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len);
|
||||||
RouterInfo (const uint8_t * buf, size_t len);
|
RouterInfo (const uint8_t * buf, size_t len);
|
||||||
virtual ~RouterInfo ();
|
virtual ~RouterInfo ();
|
||||||
|
@ -221,7 +219,7 @@ namespace data
|
||||||
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
|
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
|
||||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||||
int GetVersion () const { return m_Version; };
|
int GetVersion () const { return m_Version; };
|
||||||
virtual void SetProperty (std::string_view key, std::string_view value) {};
|
virtual void SetProperty (const std::string& key, const std::string& value) {};
|
||||||
virtual void ClearProperties () {};
|
virtual void ClearProperties () {};
|
||||||
AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
|
AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
|
||||||
std::shared_ptr<const Address> GetNTCP2V4Address () const;
|
std::shared_ptr<const Address> GetNTCP2V4Address () const;
|
||||||
|
@ -292,12 +290,9 @@ namespace data
|
||||||
const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; };
|
const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; };
|
||||||
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
|
const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary
|
||||||
size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; };
|
size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; };
|
||||||
void DeleteBuffer () { m_Buffer = nullptr; m_IsBufferScheduledToDelete = false; };
|
void DeleteBuffer () { m_Buffer = nullptr; };
|
||||||
std::shared_ptr<Buffer> GetSharedBuffer () const { return m_Buffer; };
|
std::shared_ptr<Buffer> GetSharedBuffer () const { return m_Buffer; };
|
||||||
std::shared_ptr<Buffer> CopyBuffer () const;
|
std::shared_ptr<Buffer> CopyBuffer () const;
|
||||||
void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = true; };
|
|
||||||
void CancelBufferToDelete () { m_IsBufferScheduledToDelete = false; };
|
|
||||||
bool IsBufferScheduledToDelete () const { return m_IsBufferScheduledToDelete; };
|
|
||||||
|
|
||||||
bool IsUpdated () const { return m_IsUpdated; };
|
bool IsUpdated () const { return m_IsUpdated; };
|
||||||
void SetUpdated (bool updated) { m_IsUpdated = updated; };
|
void SetUpdated (bool updated) { m_IsUpdated = updated; };
|
||||||
|
@ -335,12 +330,11 @@ namespace data
|
||||||
|
|
||||||
bool LoadFile (const std::string& fullPath);
|
bool LoadFile (const std::string& fullPath);
|
||||||
void ReadFromFile (const std::string& fullPath);
|
void ReadFromFile (const std::string& fullPath);
|
||||||
bool ReadFromBuffer (const uint8_t * buf, size_t len); // return false if malformed
|
void ReadFromStream (std::istream& s);
|
||||||
void ReadFromBuffer (bool verifySignature);
|
void ReadFromBuffer (bool verifySignature);
|
||||||
std::string_view ExtractString (const uint8_t * buf, size_t len) const;
|
size_t ReadString (char* str, size_t len, std::istream& s) const;
|
||||||
std::tuple<std::string_view, std::string_view, size_t> ExtractParam (const uint8_t * buf, size_t len) const;
|
void ExtractCaps (const char * value);
|
||||||
void ExtractCaps (std::string_view value);
|
uint8_t ExtractAddressCaps (const char * value) const;
|
||||||
uint8_t ExtractAddressCaps (std::string_view value) const;
|
|
||||||
void UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts);
|
void UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts);
|
||||||
template<typename Filter>
|
template<typename Filter>
|
||||||
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
||||||
|
@ -360,17 +354,13 @@ namespace data
|
||||||
#else
|
#else
|
||||||
AddressesPtr m_Addresses;
|
AddressesPtr m_Addresses;
|
||||||
#endif
|
#endif
|
||||||
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill, m_IsBufferScheduledToDelete;
|
bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill;
|
||||||
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports;
|
CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports;
|
||||||
uint8_t m_Caps;
|
uint8_t m_Caps;
|
||||||
char m_BandwidthCap;
|
char m_BandwidthCap;
|
||||||
int m_Version;
|
int m_Version;
|
||||||
Congestion m_Congestion;
|
Congestion m_Congestion;
|
||||||
mutable std::shared_ptr<RouterProfile> m_Profile;
|
mutable std::shared_ptr<RouterProfile> m_Profile;
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
static std::string GetTransportName (SupportedTransports tr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LocalRouterInfo: public RouterInfo
|
class LocalRouterInfo: public RouterInfo
|
||||||
|
@ -382,7 +372,7 @@ namespace data
|
||||||
void UpdateCaps (uint8_t caps);
|
void UpdateCaps (uint8_t caps);
|
||||||
bool UpdateCongestion (Congestion c); // returns true if updated
|
bool UpdateCongestion (Congestion c); // returns true if updated
|
||||||
|
|
||||||
void SetProperty (std::string_view key, std::string_view value) override;
|
void SetProperty (const std::string& key, const std::string& value) override;
|
||||||
void DeleteProperty (const std::string& key);
|
void DeleteProperty (const std::string& key);
|
||||||
std::string GetProperty (const std::string& key) const;
|
std::string GetProperty (const std::string& key) const;
|
||||||
void ClearProperties () override { m_Properties.clear (); };
|
void ClearProperties () override { m_Properties.clear (); };
|
||||||
|
|
235
libi2pd/SSU2.cpp
235
libi2pd/SSU2.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2025, The PurpleI2P Project
|
* Copyright (c) 2022-2024, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
|
@ -81,7 +81,7 @@ namespace transport
|
||||||
found = true;
|
found = true;
|
||||||
LogPrint (eLogDebug, "SSU2: Opening IPv4 socket at Start");
|
LogPrint (eLogDebug, "SSU2: Opening IPv4 socket at Start");
|
||||||
OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port));
|
OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port));
|
||||||
boost::asio::post (m_ReceiveService.GetService (),
|
m_ReceiveService.GetService ().post(
|
||||||
[this]()
|
[this]()
|
||||||
{
|
{
|
||||||
Receive (m_SocketV4);
|
Receive (m_SocketV4);
|
||||||
|
@ -93,7 +93,7 @@ namespace transport
|
||||||
found = true;
|
found = true;
|
||||||
LogPrint (eLogDebug, "SSU2: Opening IPv6 socket at Start");
|
LogPrint (eLogDebug, "SSU2: Opening IPv6 socket at Start");
|
||||||
OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port));
|
OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port));
|
||||||
boost::asio::post (m_ReceiveService.GetService (),
|
m_ReceiveService.GetService ().post(
|
||||||
[this]()
|
[this]()
|
||||||
{
|
{
|
||||||
Receive (m_SocketV6);
|
Receive (m_SocketV6);
|
||||||
|
@ -157,9 +157,6 @@ namespace transport
|
||||||
m_IntroducersV6.clear ();
|
m_IntroducersV6.clear ();
|
||||||
m_ConnectedRecently.clear ();
|
m_ConnectedRecently.clear ();
|
||||||
m_RequestedPeerTests.clear ();
|
m_RequestedPeerTests.clear ();
|
||||||
|
|
||||||
m_PacketsPool.ReleaseMt (m_ReceivedPacketsQueue);
|
|
||||||
m_ReceivedPacketsQueue.clear ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||||
|
@ -216,16 +213,15 @@ namespace transport
|
||||||
return ep.port ();
|
return ep.port ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max)
|
bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep)
|
||||||
{
|
{
|
||||||
if (!ep.port () || ep.address ().is_unspecified ()) return false;
|
if (!ep.port () || ep.address ().is_unspecified ()) return false;
|
||||||
std::lock_guard<std::mutex> l(m_ConnectedRecentlyMutex);
|
|
||||||
auto it = m_ConnectedRecently.find (ep);
|
auto it = m_ConnectedRecently.find (ep);
|
||||||
if (it != m_ConnectedRecently.end ())
|
if (it != m_ConnectedRecently.end ())
|
||||||
{
|
{
|
||||||
if (i2p::util::GetSecondsSinceEpoch () <= it->second + (max ? SSU2_MAX_HOLE_PUNCH_EXPIRATION : SSU2_MIN_HOLE_PUNCH_EXPIRATION))
|
if (i2p::util::GetSecondsSinceEpoch () <= it->second + SSU2_HOLE_PUNCH_EXPIRATION)
|
||||||
return true;
|
return true;
|
||||||
else if (max)
|
else
|
||||||
m_ConnectedRecently.erase (it);
|
m_ConnectedRecently.erase (it);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -234,8 +230,7 @@ namespace transport
|
||||||
void SSU2Server::AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts)
|
void SSU2Server::AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts)
|
||||||
{
|
{
|
||||||
if (!ep.port () || ep.address ().is_unspecified () ||
|
if (!ep.port () || ep.address ().is_unspecified () ||
|
||||||
i2p::util::GetSecondsSinceEpoch () > ts + SSU2_MAX_HOLE_PUNCH_EXPIRATION) return;
|
i2p::util::GetSecondsSinceEpoch () > ts + SSU2_HOLE_PUNCH_EXPIRATION) return;
|
||||||
std::lock_guard<std::mutex> l(m_ConnectedRecentlyMutex);
|
|
||||||
auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts);
|
auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts);
|
||||||
if (!added && ts > it->second)
|
if (!added && ts > it->second)
|
||||||
it->second = ts; // renew timestamp of existing endpoint
|
it->second = ts; // renew timestamp of existing endpoint
|
||||||
|
@ -374,9 +369,9 @@ namespace transport
|
||||||
size_t moreBytes = socket.available (ec);
|
size_t moreBytes = socket.available (ec);
|
||||||
if (!ec && moreBytes)
|
if (!ec && moreBytes)
|
||||||
{
|
{
|
||||||
std::list<Packet *> packets;
|
auto packets = m_PacketsArrayPool.AcquireMt ();
|
||||||
packets.push_back (packet);
|
packets->AddPacket (packet);
|
||||||
while (moreBytes && packets.size () < SSU2_MAX_NUM_PACKETS_PER_BATCH)
|
while (moreBytes && packets->numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH)
|
||||||
{
|
{
|
||||||
packet = m_PacketsPool.AcquireMt ();
|
packet = m_PacketsPool.AcquireMt ();
|
||||||
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec);
|
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec);
|
||||||
|
@ -384,7 +379,13 @@ namespace transport
|
||||||
{
|
{
|
||||||
i2p::transport::transports.UpdateReceivedBytes (packet->len);
|
i2p::transport::transports.UpdateReceivedBytes (packet->len);
|
||||||
if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE)
|
if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE)
|
||||||
packets.push_back (packet);
|
{
|
||||||
|
if (!packets->AddPacket (packet))
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "SSU2: Received packets array is full");
|
||||||
|
m_PacketsPool.ReleaseMt (packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
else // drop too short packets
|
else // drop too short packets
|
||||||
m_PacketsPool.ReleaseMt (packet);
|
m_PacketsPool.ReleaseMt (packet);
|
||||||
moreBytes = socket.available(ec);
|
moreBytes = socket.available(ec);
|
||||||
|
@ -397,10 +398,10 @@ namespace transport
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsertToReceivedPacketsQueue (packets);
|
GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
InsertToReceivedPacketsQueue (packet);
|
GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet));
|
||||||
Receive (socket);
|
Receive (socket);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -427,76 +428,50 @@ namespace transport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSU2Server::HandleReceivedPackets (std::list<Packet *>&& packets)
|
void SSU2Server::HandleReceivedPacket (Packet * packet)
|
||||||
|
{
|
||||||
|
if (packet)
|
||||||
{
|
{
|
||||||
if (packets.empty ()) return;
|
|
||||||
if (m_IsThroughProxy)
|
if (m_IsThroughProxy)
|
||||||
for (auto it: packets)
|
ProcessNextPacketFromProxy (packet->buf, packet->len);
|
||||||
ProcessNextPacketFromProxy (it->buf, it->len);
|
|
||||||
else
|
else
|
||||||
for (auto it: packets)
|
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||||
ProcessNextPacket (it->buf, it->len, it->from);
|
m_PacketsPool.ReleaseMt (packet);
|
||||||
m_PacketsPool.ReleaseMt (packets);
|
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
|
||||||
|
m_LastSession->FlushData ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::HandleReceivedPackets (Packets * packets)
|
||||||
|
{
|
||||||
|
if (!packets) return;
|
||||||
|
if (m_IsThroughProxy)
|
||||||
|
for (size_t i = 0; i < packets->numPackets; i++)
|
||||||
|
{
|
||||||
|
auto& packet = (*packets)[i];
|
||||||
|
ProcessNextPacketFromProxy (packet->buf, packet->len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
for (size_t i = 0; i < packets->numPackets; i++)
|
||||||
|
{
|
||||||
|
auto& packet = (*packets)[i];
|
||||||
|
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||||
|
}
|
||||||
|
m_PacketsPool.ReleaseMt (packets->data (), packets->numPackets);
|
||||||
|
m_PacketsArrayPool.ReleaseMt (packets);
|
||||||
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
|
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
|
||||||
m_LastSession->FlushData ();
|
m_LastSession->FlushData ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSU2Server::InsertToReceivedPacketsQueue (Packet * packet)
|
void SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
|
||||||
{
|
|
||||||
if (!packet) return;
|
|
||||||
bool empty = false;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_ReceivedPacketsQueueMutex);
|
|
||||||
empty = m_ReceivedPacketsQueue.empty ();
|
|
||||||
m_ReceivedPacketsQueue.push_back (packet);
|
|
||||||
}
|
|
||||||
if (empty)
|
|
||||||
boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSU2Server::InsertToReceivedPacketsQueue (std::list<Packet *>& packets)
|
|
||||||
{
|
|
||||||
if (packets.empty ()) return;
|
|
||||||
size_t queueSize = 0;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_ReceivedPacketsQueueMutex);
|
|
||||||
queueSize = m_ReceivedPacketsQueue.size ();
|
|
||||||
if (queueSize < SSU2_MAX_RECEIVED_QUEUE_SIZE)
|
|
||||||
m_ReceivedPacketsQueue.splice (m_ReceivedPacketsQueue.end (), packets);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU2: Received queue size ", queueSize, " exceeds max size", SSU2_MAX_RECEIVED_QUEUE_SIZE);
|
|
||||||
m_PacketsPool.ReleaseMt (packets);
|
|
||||||
queueSize = 0; // invoke processing just in case
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!queueSize)
|
|
||||||
boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSU2Server::HandleReceivedPacketsQueue ()
|
|
||||||
{
|
|
||||||
std::list<Packet *> receivedPackets;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(m_ReceivedPacketsQueueMutex);
|
|
||||||
m_ReceivedPacketsQueue.swap (receivedPackets);
|
|
||||||
}
|
|
||||||
HandleReceivedPackets (std::move (receivedPackets));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
|
|
||||||
{
|
{
|
||||||
if (session)
|
if (session)
|
||||||
{
|
{
|
||||||
if (m_Sessions.emplace (session->GetConnID (), session).second)
|
m_Sessions.emplace (session->GetConnID (), session);
|
||||||
{
|
|
||||||
if (session->GetState () != eSSU2SessionStatePeerTest)
|
if (session->GetState () != eSSU2SessionStatePeerTest)
|
||||||
AddSessionByRouterHash (session);
|
AddSessionByRouterHash (session);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSU2Server::RemoveSession (uint64_t connID)
|
void SSU2Server::RemoveSession (uint64_t connID)
|
||||||
{
|
{
|
||||||
|
@ -522,7 +497,7 @@ namespace transport
|
||||||
|
|
||||||
void SSU2Server::RequestRemoveSession (uint64_t connID)
|
void SSU2Server::RequestRemoveSession (uint64_t connID)
|
||||||
{
|
{
|
||||||
boost::asio::post (GetService (), [connID, this]() { RemoveSession (connID); });
|
GetService ().post ([connID, this]() { RemoveSession (connID); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSU2Server::AddSessionByRouterHash (std::shared_ptr<SSU2Session> session)
|
void SSU2Server::AddSessionByRouterHash (std::shared_ptr<SSU2Session> session)
|
||||||
|
@ -550,7 +525,7 @@ namespace transport
|
||||||
// move unsent msgs to new session
|
// move unsent msgs to new session
|
||||||
oldSession->MoveSendQueue (session);
|
oldSession->MoveSendQueue (session);
|
||||||
// terminate existing
|
// terminate existing
|
||||||
boost::asio::post (GetService (), std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession));
|
GetService ().post (std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -728,9 +703,6 @@ namespace transport
|
||||||
m_LastSession->SetRemoteEndpoint (senderEndpoint);
|
m_LastSession->SetRemoteEndpoint (senderEndpoint);
|
||||||
m_LastSession->ProcessPeerTest (buf, len);
|
m_LastSession->ProcessPeerTest (buf, len);
|
||||||
break;
|
break;
|
||||||
case eSSU2SessionStateHolePunch:
|
|
||||||
m_LastSession->ProcessFirstIncomingMessage (connID, buf, len); // SessionRequest
|
|
||||||
break;
|
|
||||||
case eSSU2SessionStateClosing:
|
case eSSU2SessionStateClosing:
|
||||||
m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block
|
m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block
|
||||||
if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing)
|
if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing)
|
||||||
|
@ -844,9 +816,26 @@ namespace transport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SSU2Server::CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest)
|
bool SSU2Server::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||||
{
|
{
|
||||||
auto s = FindPendingOutgoingSession (ep);
|
if (router && address)
|
||||||
|
{
|
||||||
|
// check if no session
|
||||||
|
auto existingSession = FindSession (router->GetIdentHash ());
|
||||||
|
if (existingSession)
|
||||||
|
{
|
||||||
|
// session with router found, trying to send peer test if requested
|
||||||
|
if (peerTest && existingSession->IsEstablished ())
|
||||||
|
GetService ().post ([existingSession]() { existingSession->SendPeerTest (); });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// check is no pending session
|
||||||
|
bool isValidEndpoint = !address->host.is_unspecified () && address->port;
|
||||||
|
if (isValidEndpoint)
|
||||||
|
{
|
||||||
|
if (i2p::transport::transports.IsInReservedRange(address->host)) return false;
|
||||||
|
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||||
if (s)
|
if (s)
|
||||||
{
|
{
|
||||||
if (peerTest)
|
if (peerTest)
|
||||||
|
@ -862,52 +851,18 @@ namespace transport
|
||||||
else
|
else
|
||||||
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SSU2Server::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
|
||||||
{
|
|
||||||
if (router && address)
|
|
||||||
{
|
|
||||||
// check if no session
|
|
||||||
auto existingSession = FindSession (router->GetIdentHash ());
|
|
||||||
if (existingSession)
|
|
||||||
{
|
|
||||||
// session with router found, trying to send peer test if requested
|
|
||||||
if (peerTest && existingSession->IsEstablished ())
|
|
||||||
boost::asio::post (GetService (), [existingSession]() { existingSession->SendPeerTest (); });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// check is no pending session
|
|
||||||
bool isValidEndpoint = !address->host.is_unspecified () && address->port;
|
|
||||||
if (isValidEndpoint)
|
|
||||||
{
|
|
||||||
if (i2p::transport::transports.IsInReservedRange(address->host)) return false;
|
|
||||||
if (CheckPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port), peerTest)) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto session = std::make_shared<SSU2Session> (*this, router, address);
|
auto session = std::make_shared<SSU2Session> (*this, router, address);
|
||||||
if (!isValidEndpoint && router->HasProfile () && router->GetProfile ()->HasLastEndpoint (address->IsV4 ()))
|
|
||||||
{
|
|
||||||
// router doesn't publish endpoint, but we connected before and hole punch might be alive
|
|
||||||
auto ep = router->GetProfile ()->GetLastEndpoint ();
|
|
||||||
if (IsConnectedRecently (ep, false))
|
|
||||||
{
|
|
||||||
if (CheckPendingOutgoingSession (ep, peerTest)) return false;
|
|
||||||
session->SetRemoteEndpoint (ep);
|
|
||||||
isValidEndpoint = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (peerTest)
|
if (peerTest)
|
||||||
session->SetOnEstablished ([session]() {session->SendPeerTest (); });
|
session->SetOnEstablished ([session]() {session->SendPeerTest (); });
|
||||||
|
|
||||||
if (isValidEndpoint) // we know endpoint
|
if (address->UsesIntroducer ())
|
||||||
boost::asio::post (GetService (), [session]() { session->Connect (); });
|
GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session));
|
||||||
else if (address->UsesIntroducer ()) // we don't know endpoint yet
|
else if (isValidEndpoint) // we can't connect without endpoint
|
||||||
boost::asio::post (GetService (), std::bind (&SSU2Server::ConnectThroughIntroducer, this, session));
|
GetService ().post ([session]() { session->Connect (); });
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1050,7 +1005,7 @@ namespace transport
|
||||||
if (!remoteAddr || !remoteAddr->IsPeerTesting () ||
|
if (!remoteAddr || !remoteAddr->IsPeerTesting () ||
|
||||||
(v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false;
|
(v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false;
|
||||||
if (session->IsEstablished ())
|
if (session->IsEstablished ())
|
||||||
boost::asio::post (GetService (), [session]() { session->SendPeerTest (); });
|
GetService ().post ([session]() { session->SendPeerTest (); });
|
||||||
else
|
else
|
||||||
session->SetOnEstablished ([session]() { session->SendPeerTest (); });
|
session->SetOnEstablished ([session]() { session->SendPeerTest (); });
|
||||||
return true;
|
return true;
|
||||||
|
@ -1157,7 +1112,7 @@ namespace transport
|
||||||
|
|
||||||
for (auto it = m_ConnectedRecently.begin (); it != m_ConnectedRecently.end (); )
|
for (auto it = m_ConnectedRecently.begin (); it != m_ConnectedRecently.end (); )
|
||||||
{
|
{
|
||||||
if (ts > it->second + SSU2_MAX_HOLE_PUNCH_EXPIRATION)
|
if (ts > it->second + SSU2_HOLE_PUNCH_EXPIRATION)
|
||||||
it = m_ConnectedRecently.erase (it);
|
it = m_ConnectedRecently.erase (it);
|
||||||
else
|
else
|
||||||
it++;
|
it++;
|
||||||
|
@ -1183,6 +1138,7 @@ namespace transport
|
||||||
}
|
}
|
||||||
|
|
||||||
m_PacketsPool.CleanUpMt ();
|
m_PacketsPool.CleanUpMt ();
|
||||||
|
m_PacketsArrayPool.CleanUpMt ();
|
||||||
m_SentPacketsPool.CleanUp ();
|
m_SentPacketsPool.CleanUp ();
|
||||||
m_IncompleteMessagesPool.CleanUp ();
|
m_IncompleteMessagesPool.CleanUp ();
|
||||||
m_FragmentsPool.CleanUp ();
|
m_FragmentsPool.CleanUp ();
|
||||||
|
@ -1251,21 +1207,18 @@ namespace transport
|
||||||
}
|
}
|
||||||
uint64_t token;
|
uint64_t token;
|
||||||
RAND_bytes ((uint8_t *)&token, 8);
|
RAND_bytes ((uint8_t *)&token, 8);
|
||||||
if (!token) token = 1; // token can't be zero
|
m_IncomingTokens.emplace (ep, std::make_pair (token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT)));
|
||||||
m_IncomingTokens.try_emplace (ep, token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT));
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<uint64_t, uint32_t> SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep)
|
std::pair<uint64_t, uint32_t> SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep)
|
||||||
{
|
{
|
||||||
|
m_IncomingTokens.erase (ep); // drop previous
|
||||||
uint64_t token;
|
uint64_t token;
|
||||||
RAND_bytes ((uint8_t *)&token, 8);
|
RAND_bytes ((uint8_t *)&token, 8);
|
||||||
if (!token) token = 1; // token can't be zero
|
auto ret = std::make_pair (token, uint32_t(i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT));
|
||||||
uint32_t expires = i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT;
|
m_IncomingTokens.emplace (ep, ret);
|
||||||
auto [it, inserted] = m_IncomingTokens.try_emplace (ep, token, expires);
|
return ret;
|
||||||
if (!inserted)
|
|
||||||
it->second = { token, expires }; // override
|
|
||||||
return it->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
|
std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
|
||||||
|
@ -1287,9 +1240,6 @@ namespace transport
|
||||||
eligible.push_back (s.second);
|
eligible.push_back (s.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eligible.size () <= (size_t)maxNumIntroducers)
|
|
||||||
return eligible;
|
|
||||||
else
|
|
||||||
std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng);
|
std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1392,7 +1342,7 @@ namespace transport
|
||||||
excluded.insert (ident);
|
excluded.insert (ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
// session about to expire are not counted
|
// sesssion about to expire are not counted
|
||||||
for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS + numOldSessions; i++)
|
for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS + numOldSessions; i++)
|
||||||
{
|
{
|
||||||
auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded);
|
auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded);
|
||||||
|
@ -1519,23 +1469,6 @@ namespace transport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SSU2Server::AEADChaCha20Poly1305Encrypt (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)
|
|
||||||
{
|
|
||||||
return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SSU2Server::AEADChaCha20Poly1305Decrypt (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)
|
|
||||||
{
|
|
||||||
return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSU2Server::ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out)
|
|
||||||
{
|
|
||||||
m_ChaCha20 (msg, msgLen, key, nonce, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSU2Server::SendThroughProxy (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
|
void SSU2Server::SendThroughProxy (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
|
||||||
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to)
|
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to)
|
||||||
{
|
{
|
||||||
|
@ -1784,7 +1717,7 @@ namespace transport
|
||||||
bool SSU2Server::SetProxy (const std::string& address, uint16_t port)
|
bool SSU2Server::SetProxy (const std::string& address, uint16_t port)
|
||||||
{
|
{
|
||||||
boost::system::error_code ecode;
|
boost::system::error_code ecode;
|
||||||
auto addr = boost::asio::ip::make_address (address, ecode);
|
auto addr = boost::asio::ip::address::from_string (address, ecode);
|
||||||
if (!ecode && !addr.is_unspecified () && port)
|
if (!ecode && !addr.is_unspecified () && port)
|
||||||
{
|
{
|
||||||
m_IsThroughProxy = true;
|
m_IsThroughProxy = true;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue