mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-07-02 02:34:36 +02:00
Compare commits
No commits in common. "openssl" and "2.53.1" have entirely different histories.
169 changed files with 5478 additions and 10237 deletions
23
.github/workflows/build-windows.yml
vendored
23
.github/workflows/build-windows.yml
vendored
|
@ -128,13 +128,8 @@ jobs:
|
|||
cache: true
|
||||
update: true
|
||||
|
||||
- name: Clone MinGW packages repository and revert boost to 1.85.0
|
||||
run: |
|
||||
git clone https://github.com/msys2/MINGW-packages
|
||||
cd MINGW-packages
|
||||
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
|
||||
cd mingw-w64-boost
|
||||
sed -i 's/boostorg.jfrog.io\/artifactory\/main/archives.boost.io/' PKGBUILD
|
||||
- name: Clone MinGW packages repository
|
||||
run: git clone https://github.com/msys2/MINGW-packages
|
||||
|
||||
# headers
|
||||
- name: Get headers package version
|
||||
|
@ -210,30 +205,26 @@ jobs:
|
|||
run: |
|
||||
cd MINGW-packages/mingw-w64-openssl
|
||||
gpg --recv-keys D894E2CE8B3D79F5
|
||||
gpg --recv-keys 216094DFD0CB81EF
|
||||
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||
- name: Install openssl package
|
||||
run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst
|
||||
|
||||
# Boost
|
||||
#- name: Get boost package version
|
||||
# id: version-boost
|
||||
# run: |
|
||||
# echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||
- name: Get boost package version
|
||||
id: version-boost
|
||||
run: |
|
||||
echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||
- name: Cache boost package
|
||||
uses: actions/cache@v4
|
||||
id: cache-boost
|
||||
with:
|
||||
path: MINGW-packages/mingw-w64-boost/*.zst
|
||||
key: winxp-boost-1.85.0+crt-${{ steps.version-headers.outputs.version }}+ossl-${{ steps.version-openssl.outputs.version }}
|
||||
# Rebuild package if packages above has changed
|
||||
key: winxp-winpthreads-${{ steps.version-boost.outputs.version }}
|
||||
- name: Build WinXP-capable boost package
|
||||
if: steps.cache-boost.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd MINGW-packages/mingw-w64-boost
|
||||
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
|
||||
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
|
||||
with:
|
||||
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
|
||||
|
||||
- name: Create and push latest manifest image to GHCR
|
||||
|
@ -116,7 +116,7 @@ jobs:
|
|||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
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
|
||||
|
||||
- name: Store release version to env
|
||||
|
@ -128,7 +128,7 @@ jobs:
|
|||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
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
|
||||
|
||||
- name: Create and push release manifest to GHCR
|
||||
|
@ -136,5 +136,5 @@ jobs:
|
|||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
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
|
||||
|
|
130
ChangeLog
130
ChangeLog
|
@ -1,136 +1,6 @@
|
|||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.57.0] - 2025-06-02
|
||||
### Added
|
||||
- Local domain sockets for I2PControl
|
||||
- "keys=shareddest" tunnel param to run on shared local destination
|
||||
- HTTP and SOCKS proxy through BOB
|
||||
- Localization to Hebrew and Hindi
|
||||
- NTCP2 probing resistance
|
||||
- Support SAM v1 datagram sessions without port
|
||||
- OpenIndiana support
|
||||
### Changed
|
||||
- Don't request LeaseSet until I2CP destination is ready
|
||||
- Keep receiving new data from I2PTunnel/SAM socket while previous is being sent to stream
|
||||
- Insert phony record to inbound tunnel build message with real x25519 ephemeral key
|
||||
- Set min peer test version to 0.9.62
|
||||
- Increase I2NP message expiration timeout in SSU2
|
||||
- Cleanup ECIESx25519 new session reply keys on Alice side
|
||||
- Reduced router profile persist interval to 22 minutes
|
||||
- SSU2 max padding size to 32 bytes
|
||||
- Send SSU2 path challenge of 8 bytes. Add datetime and address blocks
|
||||
- Don't delete trusted routers from netdb
|
||||
- Disable loss-control in streaming
|
||||
- Reseeds list
|
||||
### Fixed
|
||||
- Crash after SAM stream disconnect
|
||||
- FORWARD session host handling in SAM
|
||||
- x86 build for Haiku
|
||||
- SSU2 session's remote endpoint after receiving path response
|
||||
|
||||
## [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
|
||||
### Added
|
||||
- Maintain recently connected routers list to avoid false-positive peer test
|
||||
- Limited connectivity mode(through proxy)
|
||||
- "i2p.streaming.profile" tunnel's param to let tunnel select also low-bandwidth routers
|
||||
- Limit stream's inbound speed
|
||||
- Periodic ack requests in ratchets session
|
||||
- Set congestion cap G immediately if through proxy
|
||||
- Show tunnel's routers bandwidth caps in web console
|
||||
- Handle immediate ack requested flag in SSU2 data packets
|
||||
- Resend and ack peer test and relay messages
|
||||
- "senduseragent" HTTP proxy's param to pass through user's User-Agent
|
||||
### Changed
|
||||
- Exclude 'N' routers from high-bandwidth routers for client tunnels
|
||||
- C++11 support has been dropped, the minimal requirement is C++17 now, C++20 for some compilers
|
||||
- Removed dependency from boost::date_time and boost::filesystem
|
||||
- Set default i2cp.leaseSetEncType to 0,4 and to 4 for server tunnels
|
||||
- Handle i2cp.inboundlimit and i2cp.outboundlimit params in I2CP
|
||||
- Publish LeaseSet with new timestamp update if tunnel was replaced in the same second
|
||||
- Increase max number of generated tags to 800 per tagset
|
||||
- Routing path expiration by time instead num attempts
|
||||
- Save timestamp from epoch instead local time to profiles
|
||||
- Update introducer's iTag if session to introducer was replaced to new one
|
||||
- RTT, window size and number of NACKs calculation for streaming
|
||||
- Don't select same peer for tunnel too often
|
||||
- Use WinApi for data path UTF-8 conversion for Windows
|
||||
### Fixed
|
||||
- Jump link crash if address book is disabled
|
||||
- Race condition if connect through an introducer
|
||||
- "Date" header in I2PControl response
|
||||
- Incomplete response from web console
|
||||
- AEAD verification with LibreSSL
|
||||
- Number of generated tags and new keys for follow-on tagsets
|
||||
- Expired leases in LeaseSet
|
||||
- Attempts to send HolePunch to 0.0.0.0
|
||||
- Incorrect options size in quick ack streaming packet
|
||||
- Low bandwidth router appeared as first peer in high-bandwidth client tunnel
|
||||
|
||||
## [2.53.1] - 2024-07-29
|
||||
### Changed
|
||||
- I2CP performance improvement
|
||||
|
|
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.
|
||||
|
||||
|
|
4
Makefile
4
Makefile
|
@ -29,6 +29,7 @@ DAEMON_SRC_DIR := daemon
|
|||
# import source files lists
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||
USE_STATIC := $(or $(USE_STATIC),no)
|
||||
USE_UPNP := $(or $(USE_UPNP),no)
|
||||
DEBUG := $(or $(DEBUG),yes)
|
||||
|
@ -69,9 +70,6 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
|||
else ifneq (, $(findstring haiku, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.haiku
|
||||
else ifneq (, $(findstring solaris, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.solaris
|
||||
else # not supported
|
||||
$(error Not supported platform)
|
||||
endif
|
||||
|
|
20
Makefile.bsd
20
Makefile.bsd
|
@ -1,22 +1,18 @@
|
|||
CXX = clang++
|
||||
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_system -lboost_program_options
|
||||
|
||||
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
## For example, when adding 'hardening flags' to the build
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
CXXVER := $(shell $(CXX) -dumpversion|cut -c 1-2)
|
||||
ifeq (${CXXVER}, "4.") # older clang always returned 4.2.1
|
||||
$(error Compiler too old)
|
||||
else ifeq (${CXXVER}, ${filter ${CXXVER},16 17 18 19}) # clang 16 - 19
|
||||
NEEDED_CXXFLAGS = -std=c++20
|
||||
else
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
ifeq (${CXXVER}, "4.2.1") # older clang always returned 4.2.1
|
||||
NEEDED_CXXFLAGS = -std=c++11
|
||||
else # newer versions support C++17
|
||||
NEEDED_CXXFLAGS = -std=c++17
|
||||
endif
|
||||
|
||||
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
ifeq ($(shell $(CXX) -dumpmachine | cut -c 1-4), i586)
|
||||
CXX = g++-x86
|
||||
else
|
||||
CXX = g++
|
||||
endif
|
||||
CXXFLAGS := -Wall -std=c++20
|
||||
CXXFLAGS := -Wall -std=c++11
|
||||
INCFLAGS = -I/system/develop/headers
|
||||
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_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
DEFINES += -DUSE_UPNP
|
||||
|
|
|
@ -5,20 +5,20 @@ SSLROOT = ${BREWROOT}/opt/openssl@1.1
|
|||
UPNPROOT = ${BREWROOT}/opt/miniupnpc
|
||||
|
||||
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual
|
||||
NEEDED_CXXFLAGS ?= -std=c++17
|
||||
NEEDED_CXXFLAGS ?= -std=c++11
|
||||
INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include
|
||||
LDFLAGS ?= ${LD_DEBUG}
|
||||
DEFINES += -DMAC_OSX
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a
|
||||
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_date_time.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
|
||||
endif
|
||||
LDLIBS += -lpthread -ldl
|
||||
else
|
||||
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_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -L${UPNPROOT}/lib
|
||||
LDLIBS += -lminiupnpc
|
||||
|
@ -30,6 +30,13 @@ ifeq ($(USE_UPNP),yes)
|
|||
INCFLAGS += -I${UPNPROOT}/include
|
||||
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 -d ${PREFIX}/bin
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||
|
|
|
@ -9,17 +9,24 @@ LDFLAGS ?= ${LD_DEBUG}
|
|||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FDLAGS to work at build-time.
|
||||
|
||||
# detect proper flag for c++17 support by compilers
|
||||
# detect proper flag for c++11 support by compilers
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
|
||||
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
LDLIBS = -latomic
|
||||
else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
|
||||
LDLIBS = -latomic
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
|
||||
# NEEDED_CXXFLAGS += -std=c++20
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
LDLIBS = -lboost_system -lstdc++fs
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+
|
||||
NEEDED_CXXFLAGS += -std=c++20
|
||||
LDLIBS = -latomic
|
||||
else # not supported
|
||||
$(error Compiler too old)
|
||||
endif
|
||||
|
@ -31,6 +38,9 @@ ifeq ($(USE_STATIC),yes)
|
|||
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||
# the shared libraries from the glibc version used for linking
|
||||
LIBDIR := /usr/lib/$(SYS)
|
||||
LDLIBS += $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
|
@ -40,7 +50,7 @@ ifeq ($(USE_UPNP),yes)
|
|||
endif
|
||||
LDLIBS += -lpthread -ldl
|
||||
else
|
||||
LDLIBS += -lssl -lcrypto -lz -lboost_program_options -lpthread -latomic
|
||||
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
|
@ -51,6 +61,13 @@ ifeq ($(USE_UPNP),yes)
|
|||
DEFINES += -DUSE_UPNP
|
||||
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 -d ${PREFIX}/bin
|
||||
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||
|
|
|
@ -7,7 +7,7 @@ CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
|
|||
INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32
|
||||
LDFLAGS := ${LD_DEBUG} -static -fPIC -msse
|
||||
|
||||
NEEDED_CXXFLAGS += -std=c++20
|
||||
NEEDED_CXXFLAGS += -std=c++17
|
||||
DEFINES += -DWIN32_LEAN_AND_MEAN
|
||||
|
||||
# UPNP Support
|
||||
|
@ -16,11 +16,9 @@ ifeq ($(USE_UPNP),yes)
|
|||
LDLIBS = -lminiupnpc
|
||||
endif
|
||||
|
||||
ifeq ($(USE_WINXP_FLAGS), yes)
|
||||
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||
endif
|
||||
|
||||
LDLIBS += \
|
||||
$(MINGW_PREFIX)/lib/libboost_system-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libboost_date_time-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libboost_program_options-mt.a \
|
||||
$(MINGW_PREFIX)/lib/libssl.a \
|
||||
|
@ -42,6 +40,16 @@ ifeq ($(USE_WIN32_APP), yes)
|
|||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
endif
|
||||
|
||||
ifeq ($(USE_WINXP_FLAGS), yes)
|
||||
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
NEEDED_CXXFLAGS += -maes
|
||||
LDFLAGS += -maes
|
||||
DEFINES += -D__AES__
|
||||
endif
|
||||
|
||||
ifeq ($(USE_ASLR),yes)
|
||||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||
endif
|
||||
|
|
10
Makefile.osx
10
Makefile.osx
|
@ -1,5 +1,5 @@
|
|||
CXX = clang++
|
||||
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++17
|
||||
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11
|
||||
INCFLAGS = -I/usr/local/include
|
||||
DEFINES := -DMAC_OSX
|
||||
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
|
@ -7,9 +7,9 @@ LDFLAGS += -Wl,-dead_strip
|
|||
LDFLAGS += -Wl,-dead_strip_dylibs
|
||||
|
||||
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_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||
else
|
||||
LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
|
@ -25,5 +25,9 @@ endif
|
|||
OSARCH = $(shell uname -p)
|
||||
|
||||
ifneq ($(OSARCH),powerpc)
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
CXXFLAGS += -D__AES__ -maes
|
||||
else
|
||||
CXXFLAGS += -msse
|
||||
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
|
|
@ -313,7 +313,7 @@ namespace win32
|
|||
}
|
||||
case ID_DATADIR:
|
||||
{
|
||||
std::string datadir(i2p::fs::GetDataDir());
|
||||
std::string datadir(i2p::fs::GetUTF8DataDir());
|
||||
ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
return 0;
|
||||
}
|
||||
|
@ -355,7 +355,9 @@ namespace win32
|
|||
}
|
||||
}
|
||||
}
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
}
|
||||
case WM_TRAYICON:
|
||||
{
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -29,6 +29,7 @@ project(
|
|||
)
|
||||
|
||||
# configurable options
|
||||
option(WITH_AESNI "Use AES-NI instructions set" ON)
|
||||
option(WITH_HARDENING "Use hardening compiler flags" OFF)
|
||||
option(WITH_LIBRARY "Build library" ON)
|
||||
option(WITH_BINARY "Build binary" ON)
|
||||
|
@ -155,6 +156,20 @@ else()
|
|||
endif()
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # -flto is added from above
|
||||
|
||||
# check for c++17 & c++11 support
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED)
|
||||
|
||||
if(CXX17_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
elseif(CXX11_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
else()
|
||||
message(SEND_ERROR "C++17 nor C++11 standard not seems to be supported by compiler. Too old version?")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
|
@ -184,6 +199,16 @@ if(UNIX)
|
|||
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)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
|
@ -198,10 +223,6 @@ if(WITH_THREADSANITIZER)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) # gcc 8-9
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++fs")
|
||||
endif()
|
||||
|
||||
# Use std::atomic instead of GCC builtins on macOS PowerPC:
|
||||
# For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
|
||||
# This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility.
|
||||
|
@ -256,14 +277,14 @@ else()
|
|||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
endif()
|
||||
add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK)
|
||||
add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
|
||||
if(WIN32)
|
||||
set(Boost_USE_STATIC_LIBS OFF)
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem program_options)
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem program_options date_time OPTIONAL_COMPONENTS atomic)
|
||||
if(NOT DEFINED Boost_FOUND)
|
||||
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
|
||||
endif()
|
||||
|
@ -291,26 +312,6 @@ if(ZLIB_FOUND)
|
|||
link_directories(${ZLIB_ROOT}/lib)
|
||||
endif()
|
||||
|
||||
# C++ standard to use, based on compiler and version of boost
|
||||
if(NOT MSVC)
|
||||
# check for c++20 & c++17 support
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if(Boost_VERSION VERSION_GREATER_EQUAL "1.83") # min boost version for c++20
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++20" CXX20_SUPPORTED)
|
||||
endif()
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED)
|
||||
|
||||
|
||||
if(CXX20_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
|
||||
elseif(CXX17_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
else()
|
||||
message(SEND_ERROR "C++20 nor C++17 standard not seems to be supported by compiler. Too old version?")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# load includes
|
||||
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
|
@ -321,9 +322,9 @@ message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}")
|
|||
message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}")
|
||||
message(STATUS "Architecture : ${ARCHITECTURE}")
|
||||
message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}")
|
||||
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
|
||||
message(STATUS "Options:")
|
||||
message(STATUS " AESNI : ${WITH_AESNI}")
|
||||
message(STATUS " HARDENING : ${WITH_HARDENING}")
|
||||
message(STATUS " LIBRARY : ${WITH_LIBRARY}")
|
||||
message(STATUS " BINARY : ${WITH_BINARY}")
|
||||
|
|
|
@ -8,7 +8,7 @@ INCLUDE(CheckLibraryExists)
|
|||
|
||||
function(check_working_cxx_atomics varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++17")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <atomic>
|
||||
std::atomic<int> x;
|
||||
|
@ -25,7 +25,7 @@ endfunction(check_working_cxx_atomics)
|
|||
|
||||
function(check_working_cxx_atomics64 varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++17 ${CMAKE_REQUIRED_FLAGS}")
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
|
|
@ -59,7 +59,7 @@ get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
|||
# function returns an empty string via _git_dir_var.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
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-----
|
33
contrib/certificates/reseed/hottuna_at_mail.i2p.crt
Normal file
33
contrib/certificates/reseed/hottuna_at_mail.i2p.crt
Normal file
|
@ -0,0 +1,33 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFxzCCA6+gAwIBAgIQZfqn0yiJL3dGgCjeOeWS6DANBgkqhkiG9w0BAQsFADBw
|
||||
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
|
||||
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQ
|
||||
aG90dHVuYUBtYWlsLmkycDAeFw0xNjExMDkwMzE1MzJaFw0yNjExMDkwMzE1MzJa
|
||||
MHAxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgxHjAcBgNV
|
||||
BAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQD
|
||||
DBBob3R0dW5hQG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
|
||||
AgEA21Bfgcc9VVH4l2u1YvYlTw2OPUyQb16X2IOW0PzdsUO5W78Loueu974BkiKi
|
||||
84lQZanLr0OwEopdfutGc6gegSLmwaWx5YCG5uwpLOPkDiObfX+nptH6As/B1cn+
|
||||
mzejYdVKRnWd7EtHW0iseSsILBK1YbGw4AGpXJ8k18DJSzUt2+spOkpBW6XqectN
|
||||
8y2JDSTns8yiNxietVeRN/clolDXT9ZwWHkd+QMHTKhgl3Uz1knOffU0L9l4ij4E
|
||||
oFgPfQo8NL63kLM24hF1hM/At7XvE4iOlObFwPXE+H5EGZpT5+A7Oezepvd/VMzM
|
||||
tCJ49hM0OlR393tKFONye5GCYeSDJGdPEB6+rBptpRrlch63tG9ktpCRrg2wQWgC
|
||||
e3aOE1xVRrmwiTZ+jpfsOCbZrrSA/C4Bmp6AfGchyHuDGGkRU/FJwa1YLJe0dkWG
|
||||
ITLWeh4zeVuAS5mctdv9NQ5wflSGz9S8HjsPBS5+CDOFHh4cexXRG3ITfk6aLhuY
|
||||
KTMlkIO4SHKmnwAvy1sFlsqj6PbfVjpHPLg625fdNxBpe57TLxtIdBB3C7ccQSRW
|
||||
+UG6Cmbcmh80PbsSR132NLMlzLhbaOjxeCWWJRo6cLuHBptAFMNwqsXt8xVf9M0N
|
||||
NdJoKUmblyvjnq0N8aMEqtQ1uGMTaCB39cutHQq+reD/uzsCAwEAAaNdMFswDgYD
|
||||
VR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNV
|
||||
HRMBAf8EBTADAQH/MBkGA1UdDgQSBBBob3R0dW5hQG1haWwuaTJwMA0GCSqGSIb3
|
||||
DQEBCwUAA4ICAQCibFV8t4pajP176u3jx31x1kgqX6Nd+0YFARPZQjq99kUyoZer
|
||||
GyHGsMWgM281RxiZkveHxR7Hm7pEd1nkhG3rm+d7GdJ2p2hujr9xUvl0zEqAAqtm
|
||||
lkYI6uJ13WBjFc9/QuRIdeIeSUN+eazSXNg2nJhoV4pF9n2Q2xDc9dH4GWO93cMX
|
||||
JPKVGujT3s0b7LWsEguZBPdaPW7wwZd902Cg/M5fE1hZQ8/SIAGUtylb/ZilVeTS
|
||||
spxWP1gX3NT1SSvv0s6oL7eADCgtggWaMxEjZhi6WMnPUeeFY8X+6trkTlnF9+r/
|
||||
HiVvvzQKrPPtB3j1xfQCAF6gUKN4iY+2AOExv4rl/l+JJbPhpd/FuvD8AVkLMZ8X
|
||||
uPe0Ew2xv30cc8JjGDzQvoSpBmVTra4f+xqH+w8UEmxnx97Ye2aUCtnPykACnFte
|
||||
oT97K5052B1zq+4fu4xaHZnEzPYVK5POzOufNLPgciJsWrR5GDWtHd+ht/ZD37+b
|
||||
+j1BXpeBWUBQgluFv+lNMVNPJxc2OMELR1EtEwXD7mTuuUEtF5Pi63IerQ5LzD3G
|
||||
KBvXhMB0XhpE6WG6pBwAvkGf5zVv/CxClJH4BQbdZwj9HYddfEQlPl0z/XFR2M0+
|
||||
9/8nBfGSPYIt6KeHBCeyQWTdE9gqSzMwTMFsennXmaT8gyc7eKqKF6adqw==
|
||||
-----END CERTIFICATE-----
|
|
@ -1,34 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF2TCCA8GgAwIBAgIQVpTNnJZlUTDqmZiHRU4wCjANBgkqhkiG9w0BAQsFADB2
|
||||
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
|
||||
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEfMB0GA1UEAwwW
|
||||
dW5peGVub0BjdWJpY2NoYW9zLm5ldDAeFw0yNTAzMDQxODU5NDZaFw0zNTAzMDQx
|
||||
ODU5NDZaMHYxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx
|
||||
HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR8w
|
||||
HQYDVQQDDBZ1bml4ZW5vQGN1YmljY2hhb3MubmV0MIICIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAg8AMIICCgKCAgEAr/JoAzLDtHXoAc7QcP4IxO+xNTeiYs78Wlg/Sl/sa6qz
|
||||
gJoGaKH/X++z4Xe9lBSZalXCamnO4QMTgsWOIeoMy6XVbGzNTXPl8JUcblTIXwkP
|
||||
pv848b1nxLfgLHzPRz1mJMpMikBugJ3Iz1sQzDVlUdye2fgbGChWliz9P4ClEODv
|
||||
A/4+7i6uvJgEZ7A+jx3vBCXhiJppE3wTuz5D9BQqG8NuEwwjwBTBByoCC4oxOe0h
|
||||
Qu1k7kEr+n4qpSEg/1eJ/RYSm+I8BftK1RUfykTwxlfmyEmKsfLBQWczE8Ca9nUB
|
||||
5V34UH2bRy1cvavJYcNW3EPsGNf4naRs+Gy8XIFrb315GgWC1Z6+tzk+QFli9YeF
|
||||
0DgtYEZciqu/407o8ZEURTnPjB7GhLDDp1LAQ7CQRhzaraXjHj0hyO+6rFpFdD0D
|
||||
mXhvI/Eph3QIldsgnQc7nPhU2csN8Vi6bNDgm0HZ8cdmIBpI2Uxn/acZX/9G40oj
|
||||
czrhsCBEecu/BluLJsfaWCYg90rvONP8Fc4edHAMonzYZR4r0q4hbv7AM8GmDRDN
|
||||
J9/DZFi+Qs9NAe06jJC3jSsj7IdIs8TMhw8FX3xWOlXwjmVETAgY/dta/MpLJ6tJ
|
||||
i+E+TH/Ndntj/D6WUwdQq+LyCR6gqHUWR6rl6EDQz+08DWb7j/72JSLb/DaXrDUC
|
||||
AwEAAaNjMGEwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr
|
||||
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB8GA1UdDgQYBBZ1bml4ZW5vQGN1Ymlj
|
||||
Y2hhb3MubmV0MA0GCSqGSIb3DQEBCwUAA4ICAQBBVoPeuOkmJDUdzIrzmxTmjMyz
|
||||
gpfrZnjirTQKWhbv53sAWNeJ3kZ9l9m+0YpqEtFDrZPL5LTBXcSci5gGuxPkp+i/
|
||||
f/axsdcFMKbI9B/M53fyXLLJY0EM4mdhNAWtph1kTowFPhhReefCdqxYIy9uk2pL
|
||||
gfb6NYJf+w9//fKYFZXb9SsiRchfv81+lbN+PIprnCpV3cTZWmpLRi2hN86pMW20
|
||||
3rh7rqJ4dPnA/NoyM+JstL10IU/4StqInweEvoo4W44+cC0zYGvfkkrKL4LB8w5S
|
||||
6DKebtk7NQDtzuw2QnA9Ms4bmqWQpbL6/7uGaauS0+nmF+2gkqi9hcv0W5ZoBb/D
|
||||
IVRauySnCtp9PbYM7pIJP9a1U6naLj7L1VixqsJGfPQ8V9TCOOi5bDc3RTetI/DX
|
||||
bXHhAqHYzboakptylCp+Ao5h2hu0+w4rqnG63HwsHDJWcETbdVFQfzlzUmbx53yV
|
||||
GnBsUxDgMOiHTZdKLkEnH4Q/XI76uc0ntTRlK9ktKWZPSISUlHrFnFl6I5UdeBMy
|
||||
6vpB9sJO5L5RPRi4945K5Xdennywdi508mNXtMMmNCqrk1SMYbwaY6ZtIvXEGam9
|
||||
uHQTiTEX9LED/VXzFGqzdyDbG43HgS0PksgzedelHWfVAEnc06U3JX2lqUyihYHa
|
||||
N4jAXWQ7s5p4GYaf4Q==
|
||||
-----END CERTIFICATE-----
|
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
|||
Author: r4sas <r4sas@i2pmail.org>
|
||||
|
||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||
Last-Update: 2024-12-30
|
||||
Last-Update: 2022-03-23
|
||||
|
||||
--- i2pd.orig/Makefile
|
||||
+++ i2pd/Makefile
|
||||
@@ -31,7 +31,7 @@ # import source files lists
|
||||
include filelist.mk
|
||||
@@ -31,7 +31,7 @@ include filelist.mk
|
||||
|
||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||
USE_STATIC := $(or $(USE_STATIC),no)
|
||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||
+USE_UPNP := $(or $(USE_UPNP),yes)
|
||||
|
|
|
@ -2,13 +2,13 @@ Description: Enable UPnP usage in package
|
|||
Author: r4sas <r4sas@i2pmail.org>
|
||||
|
||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||
Last-Update: 2024-12-30
|
||||
Last-Update: 2022-03-23
|
||||
|
||||
--- i2pd.orig/Makefile
|
||||
+++ i2pd/Makefile
|
||||
@@ -31,7 +31,7 @@ # import source files lists
|
||||
include filelist.mk
|
||||
@@ -31,7 +31,7 @@ include filelist.mk
|
||||
|
||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||
USE_STATIC := $(or $(USE_STATIC),no)
|
||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||
+USE_UPNP := $(or $(USE_UPNP),yes)
|
||||
|
|
|
@ -243,12 +243,12 @@ verify = true
|
|||
## Default: reg.i2p at "mainline" I2P Network
|
||||
# defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt
|
||||
## 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]
|
||||
## Maximum active transit sessions (default: 10000)
|
||||
## Maximum active transit sessions (default: 5000)
|
||||
## This value is doubled if floodfill mode is enabled!
|
||||
# transittunnels = 10000
|
||||
# transittunnels = 5000
|
||||
## Limit number of open file descriptors (0 - use system limit)
|
||||
# openfiles = 0
|
||||
## Maximum size of corefile in Kb (0 - use system limit)
|
||||
|
@ -277,3 +277,9 @@ verify = true
|
|||
## Save full addresses on disk (default: 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]
|
||||
Description=I2P Router written in C++
|
||||
Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/
|
||||
Wants=network.target
|
||||
After=network.target network-online.target
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=i2pd
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.57.0
|
||||
Version: 2.53.1
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
|
@ -10,7 +10,11 @@ License: BSD
|
|||
URL: https://github.com/PurpleI2P/i2pd
|
||||
Source0: https://github.com/PurpleI2P/i2pd/archive/openssl/i2pd-openssl.tar.gz
|
||||
|
||||
%if 0%{?rhel} == 7
|
||||
BuildRequires: cmake3
|
||||
%else
|
||||
BuildRequires: cmake
|
||||
%endif
|
||||
|
||||
BuildRequires: chrpath
|
||||
BuildRequires: gcc-c++
|
||||
|
@ -20,7 +24,7 @@ BuildRequires: openssl-devel
|
|||
BuildRequires: miniupnpc-devel
|
||||
BuildRequires: systemd-units
|
||||
|
||||
%if 0%{?fedora} == 41
|
||||
%if 0%{?fedora} > 40 || 0%{?eln}
|
||||
BuildRequires: openssl-devel-engine
|
||||
%endif
|
||||
|
||||
|
@ -39,18 +43,26 @@ C++ implementation of I2P.
|
|||
|
||||
%build
|
||||
cd build
|
||||
%cmake \
|
||||
%if 0%{?rhel} == 7
|
||||
%cmake3 \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%else
|
||||
%cmake \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF \
|
||||
.
|
||||
%else
|
||||
%else
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
@ -64,7 +76,7 @@ cd build
|
|||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
popd
|
||||
%endif
|
||||
|
||||
|
@ -72,7 +84,7 @@ make %{?_smp_mflags}
|
|||
%install
|
||||
pushd build
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
@ -136,18 +148,6 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%changelog
|
||||
* Mon Jun 02 2025 orignal <orignal@i2pmail.org> - 2.57.0
|
||||
- update to 2.57.0
|
||||
|
||||
* 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
|
||||
- update to 2.54.0
|
||||
|
||||
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
|
||||
- update to 2.53.1
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: i2pd
|
||||
Version: 2.57.0
|
||||
Version: 2.53.1
|
||||
Release: 1%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
|
@ -8,7 +8,11 @@ License: BSD
|
|||
URL: https://github.com/PurpleI2P/i2pd
|
||||
Source0: https://github.com/PurpleI2P/i2pd/archive/%{version}/%name-%version.tar.gz
|
||||
|
||||
%if 0%{?rhel} == 7
|
||||
BuildRequires: cmake3
|
||||
%else
|
||||
BuildRequires: cmake
|
||||
%endif
|
||||
|
||||
BuildRequires: chrpath
|
||||
BuildRequires: gcc-c++
|
||||
|
@ -18,7 +22,7 @@ BuildRequires: openssl-devel
|
|||
BuildRequires: miniupnpc-devel
|
||||
BuildRequires: systemd-units
|
||||
|
||||
%if 0%{?fedora} == 41
|
||||
%if 0%{?fedora} > 40 || 0%{?eln}
|
||||
BuildRequires: openssl-devel-engine
|
||||
%endif
|
||||
|
||||
|
@ -37,18 +41,26 @@ C++ implementation of I2P.
|
|||
|
||||
%build
|
||||
cd build
|
||||
%cmake \
|
||||
%if 0%{?rhel} == 7
|
||||
%cmake3 \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%else
|
||||
%cmake \
|
||||
-DWITH_LIBRARY=OFF \
|
||||
-DWITH_UPNP=ON \
|
||||
-DWITH_HARDENING=ON \
|
||||
%if 0%{?fedora} > 29
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF \
|
||||
.
|
||||
%else
|
||||
%else
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
@ -62,7 +74,7 @@ cd build
|
|||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7
|
||||
popd
|
||||
%endif
|
||||
|
||||
|
@ -70,7 +82,7 @@ make %{?_smp_mflags}
|
|||
%install
|
||||
pushd build
|
||||
|
||||
%if 0%{?rhel} >= 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln}
|
||||
pushd redhat-linux-build
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
|
@ -134,18 +146,6 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%changelog
|
||||
* Mon Jun 02 2025 orignal <orignal@i2pmail.org> - 2.57.0
|
||||
- update to 2.57.0
|
||||
|
||||
* 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
|
||||
- update to 2.54.0
|
||||
|
||||
* Tue Jul 30 2024 orignal <orignal@i2pmail.org> - 2.53.1
|
||||
- update to 2.53.1
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ port = 6668
|
|||
destination = irc.ilita.i2p
|
||||
destinationport = 6667
|
||||
keys = irc-keys.dat
|
||||
i2p.streaming.profile=2
|
||||
|
||||
#[IRC-IRC2P]
|
||||
#type = client
|
||||
|
|
|
@ -149,10 +149,12 @@ namespace util
|
|||
LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir);
|
||||
|
||||
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);
|
||||
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -132,8 +132,7 @@ namespace http {
|
|||
|
||||
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
|
||||
{
|
||||
std::string state;
|
||||
std::string_view stateText;
|
||||
std::string state, stateText;
|
||||
switch (eState)
|
||||
{
|
||||
case i2p::tunnel::eTunnelStateBuildReplyReceived :
|
||||
|
@ -147,7 +146,7 @@ namespace http {
|
|||
}
|
||||
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);
|
||||
s << "\r\n";
|
||||
}
|
||||
|
@ -172,12 +171,9 @@ namespace http {
|
|||
auto it = i2p::i18n::languages.find(currLang);
|
||||
std::string langCode = it->second.ShortCode;
|
||||
|
||||
// Right to Left language option
|
||||
bool rtl = i2p::client::context.GetLanguage ()->GetRTL();
|
||||
|
||||
s <<
|
||||
"<!DOCTYPE html>\r\n"
|
||||
"<html lang=\"" << langCode << "\"" << (rtl ? " dir=\"rtl\"" : "") << ">\r\n"
|
||||
"<html lang=\"" << langCode << "\">\r\n"
|
||||
" <head>\r\n" /* TODO: Find something to parse html/template system. This is horrible. */
|
||||
" <meta charset=\"UTF-8\">\r\n"
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
|
||||
|
@ -217,7 +213,7 @@ namespace http {
|
|||
"</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";
|
||||
}
|
||||
|
@ -661,7 +657,7 @@ namespace http {
|
|||
else
|
||||
{
|
||||
ls.reset (new i2p::data::LeaseSet2 (storeType));
|
||||
ls->Update (leaseSet->GetBuffer(), leaseSet->GetBufferLen(), nullptr, false);
|
||||
ls->Update (leaseSet->GetBuffer(), leaseSet->GetBufferLen(), false);
|
||||
}
|
||||
if (!ls) return;
|
||||
s << "<div class=\"leaseset listitem";
|
||||
|
@ -705,7 +701,6 @@ namespace http {
|
|||
{
|
||||
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("TBM Queue size") << ":</b> " << i2p::tunnel::tunnels.GetTBMQueueSize () << "<br>\r\n<br>\r\n";
|
||||
|
||||
auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
|
||||
|
@ -830,7 +825,7 @@ namespace http {
|
|||
if (i2p::tunnel::tunnels.CountTransitTunnels())
|
||||
{
|
||||
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 ())
|
||||
{
|
||||
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
|
||||
|
@ -840,7 +835,7 @@ namespace http {
|
|||
else
|
||||
s << "<tr><td>⇒</td><td>" << it->GetTunnelID () << "</td><td>⇒</td><td>";
|
||||
ShowTraffic(s, it->GetNumTransmittedBytes ());
|
||||
s << "</td><td>" << it->GetNextPeerName () << "</td></tr>\r\n";
|
||||
s << "</td></tr>\r\n";
|
||||
}
|
||||
s << "</tbody></table>\r\n";
|
||||
}
|
||||
|
@ -1266,7 +1261,7 @@ namespace http {
|
|||
ShowLeasesSets(s);
|
||||
else {
|
||||
res.code = 400;
|
||||
ShowError(s, std::string (tr("Unknown page")) + ": " + page); // TODO
|
||||
ShowError(s, tr("Unknown page") + ": " + page);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1422,11 +1417,13 @@ namespace http {
|
|||
{
|
||||
auto signatureLen = dest->GetIdentity ()->GetSignatureLen ();
|
||||
uint8_t * signature = new uint8_t[signatureLen];
|
||||
char * sig = new char[signatureLen*2];
|
||||
std::stringstream out;
|
||||
|
||||
out << name << "=" << dest->GetIdentity ()->ToBase64 ();
|
||||
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;
|
||||
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"
|
||||
|
@ -1435,6 +1432,7 @@ namespace http {
|
|||
"<input type=\"submit\" value=\"" << tr("Submit") << "\">\r\n"
|
||||
"</form>\r\n<br>\r\n";
|
||||
delete[] signature;
|
||||
delete[] sig;
|
||||
}
|
||||
else
|
||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("Domain can't end with .b32.i2p") << "\r\n<br>\r\n<br>\r\n";
|
||||
|
@ -1463,7 +1461,7 @@ namespace http {
|
|||
else
|
||||
{
|
||||
res.code = 400;
|
||||
ShowError(s, std::string (tr("Unknown command")) + ": " + cmd); // TODO
|
||||
ShowError(s, tr("Unknown command") + ": " + cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1482,13 +1480,13 @@ namespace http {
|
|||
reply.body = content;
|
||||
|
||||
m_SendBuffer = reply.to_string();
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer), boost::asio::transfer_all (),
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
|
||||
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
HTTPServer::HTTPServer (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service.get_executor ()),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port)),
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)),
|
||||
m_Hostname(address)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -83,8 +83,8 @@ namespace http
|
|||
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_Work;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
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
|
||||
*
|
||||
|
@ -8,13 +8,16 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
|
@ -29,24 +32,11 @@ namespace i2p
|
|||
namespace client
|
||||
{
|
||||
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_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);
|
||||
|
||||
// certificate / keys
|
||||
|
@ -57,29 +47,15 @@ namespace client
|
|||
i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt);
|
||||
if (i2pcp_key.at(0) != '/')
|
||||
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");
|
||||
CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str());
|
||||
}
|
||||
else
|
||||
} else {
|
||||
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
|
||||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
||||
|
@ -110,7 +86,7 @@ namespace client
|
|||
{
|
||||
Accept ();
|
||||
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 +95,12 @@ namespace client
|
|||
if (m_IsRunning)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
if (m_Acceptor) 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_Acceptor.cancel ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -153,60 +122,40 @@ namespace client
|
|||
|
||||
void I2PControlService::Accept ()
|
||||
{
|
||||
if (m_Acceptor)
|
||||
{
|
||||
auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> > (m_Service, m_SSLContext);
|
||||
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
|
||||
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext);
|
||||
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::HandleAccepted (const boost::system::error_code& ecode,
|
||||
std::shared_ptr<ssl_socket> newSocket)
|
||||
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Accept ();
|
||||
|
||||
if (ecode)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
LogPrint (eLogDebug, "I2PControl: New request from ", newSocket->lowest_layer ().remote_endpoint ());
|
||||
Handshake (newSocket);
|
||||
LogPrint (eLogDebug, "I2PControl: New request from ", socket->lowest_layer ().remote_endpoint ());
|
||||
Handshake (socket);
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
socket->async_handshake(boost::asio::ssl::stream_base::server,
|
||||
[this, socket](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket));
|
||||
}
|
||||
|
||||
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
if (ecode) {
|
||||
LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
|
||||
ReadRequest (socket);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
|
||||
{
|
||||
auto request = std::make_shared<I2PControlBuffer>();
|
||||
|
@ -216,13 +165,10 @@ namespace client
|
|||
#else
|
||||
boost::asio::buffer (request->data (), request->size ()),
|
||||
#endif
|
||||
[this, socket, request](const boost::system::error_code& ecode, size_t bytes_transferred)
|
||||
{
|
||||
HandleRequestReceived (ecode, bytes_transferred, socket, request);
|
||||
});
|
||||
std::bind(&I2PControlService::HandleRequestReceived, this,
|
||||
std::placeholders::_1, std::placeholders::_2, socket, request));
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
||||
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf)
|
||||
|
@ -300,7 +246,6 @@ namespace client
|
|||
}
|
||||
}
|
||||
|
||||
template<typename ssl_socket>
|
||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||
{
|
||||
|
@ -310,12 +255,12 @@ namespace client
|
|||
std::ostringstream header;
|
||||
header << "HTTP/1.1 200 OK\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 << "Date: ";
|
||||
std::time_t t = std::time (nullptr);
|
||||
std::tm tm = *std::gmtime (&t);
|
||||
header << std::put_time(&tm, "%a, %d %b %Y %T GMT") << "\r\n";
|
||||
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
|
||||
header.imbue(std::locale (header.getloc(), facet));
|
||||
header << boost::posix_time::second_clock::local_time() << "\r\n";
|
||||
header << "\r\n";
|
||||
offset = header.str ().size ();
|
||||
memcpy (buf->data (), header.str ().c_str (), offset);
|
||||
|
@ -323,11 +268,16 @@ namespace client
|
|||
memcpy (buf->data () + offset, response.str ().c_str (), len);
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
|
||||
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 ());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// handlers
|
||||
|
@ -437,22 +387,14 @@ namespace client
|
|||
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
EVP_PKEY * pkey = EVP_RSA_gen(4096); // e = 65537
|
||||
#else
|
||||
EVP_PKEY * pkey = EVP_PKEY_new ();
|
||||
RSA * rsa = RSA_new ();
|
||||
BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ());
|
||||
RSA_generate_key_ex (rsa, 4096, e, NULL);
|
||||
BN_free (e);
|
||||
if (rsa) EVP_PKEY_assign_RSA (pkey, rsa);
|
||||
else
|
||||
if (rsa)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: Can't create RSA key for certificate");
|
||||
EVP_PKEY_free (pkey);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
EVP_PKEY_assign_RSA (pkey, rsa);
|
||||
X509 * x509 = X509_new ();
|
||||
ASN1_INTEGER_set (X509_get_serialNumber (x509), 1);
|
||||
X509_gmtime_adj (X509_getm_notBefore (x509), 0);
|
||||
|
@ -463,28 +405,30 @@ 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, "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_sign (x509, pkey, EVP_sha1 ()); // sign, last param must be NULL for EdDSA
|
||||
X509_sign (x509, pkey, EVP_sha1 ()); // sign
|
||||
|
||||
// save cert
|
||||
if ((f = fopen (crt_path, "wb")) != NULL)
|
||||
{
|
||||
if ((f = fopen (crt_path, "wb")) != NULL) {
|
||||
LogPrint (eLogInfo, "I2PControl: Saving new cert to ", crt_path);
|
||||
PEM_write_X509 (f, x509);
|
||||
fclose (f);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: Can't write cert: ", strerror(errno));
|
||||
X509_free (x509);
|
||||
}
|
||||
|
||||
// save key
|
||||
if ((f = fopen (key_path, "wb")) != NULL)
|
||||
{
|
||||
if ((f = fopen (key_path, "wb")) != NULL) {
|
||||
LogPrint (eLogInfo, "I2PControl: saving cert key to ", key_path);
|
||||
PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL);
|
||||
fclose (f);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: Can't write key: ", strerror(errno));
|
||||
}
|
||||
|
||||
X509_free (x509);
|
||||
} else {
|
||||
LogPrint (eLogError, "I2PControl: Can't create RSA key for certificate");
|
||||
}
|
||||
EVP_PKEY_free (pkey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -35,6 +35,8 @@ namespace client
|
|||
|
||||
class I2PControlService: public I2PControlHandlers
|
||||
{
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||
|
||||
public:
|
||||
|
||||
I2PControlService (const std::string& address, int port);
|
||||
|
@ -47,18 +49,16 @@ namespace client
|
|||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
template<typename ssl_socket>
|
||||
void HandleAccepted (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> newSocket);
|
||||
template<typename ssl_socket>
|
||||
void HandleAccept(const boost::system::error_code& ecode, 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);
|
||||
template<typename ssl_socket>
|
||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
template<typename ssl_socket>
|
||||
void SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
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);
|
||||
|
||||
|
@ -86,13 +86,10 @@ namespace client
|
|||
|
||||
std::string m_Password;
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::thread * m_Thread;
|
||||
|
||||
boost::asio::io_context m_Service;
|
||||
std::unique_ptr<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::io_service m_Service;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::ssl::context m_SSLContext;
|
||||
boost::asio::deadline_timer m_ShutdownTimer;
|
||||
std::set<std::string> m_Tokens;
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace transport
|
|||
{
|
||||
m_IsRunning = true;
|
||||
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);
|
||||
m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this)));
|
||||
m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum
|
||||
|
@ -150,7 +150,7 @@ namespace transport
|
|||
|
||||
// UPnP discovered
|
||||
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
|
||||
PortMapping ();
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace transport
|
|||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::condition_variable m_Started;
|
||||
std::mutex m_StartedMutex;
|
||||
boost::asio::io_context m_Service;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
bool m_upnpUrlsInitialized = false;
|
||||
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
|
||||
*
|
||||
|
@ -25,7 +25,6 @@
|
|||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Transports.h"
|
||||
#include "util.h"
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
|
@ -221,7 +220,6 @@ namespace i2p
|
|||
|
||||
void DaemonLinux::run ()
|
||||
{
|
||||
i2p::util::SetThreadName ("i2pd-daemon");
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
|
|
24
debian/changelog
vendored
24
debian/changelog
vendored
|
@ -1,27 +1,3 @@
|
|||
i2pd (2.57.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.57.0/0.9.66
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Mon, 02 Jun 2025 16:00:00 +0000
|
||||
|
||||
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
|
||||
|
||||
* updated to version 2.54.0/0.9.64
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Sun, 6 Oct 2024 16:00:00 +0000
|
||||
|
||||
i2pd (2.53.1-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.53.1
|
||||
|
|
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>
|
||||
|
||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||
Last-Update: 2024-12-30
|
||||
Last-Update: 2022-03-23
|
||||
|
||||
--- i2pd.orig/Makefile
|
||||
+++ i2pd/Makefile
|
||||
@@ -31,7 +31,7 @@ # import source files lists
|
||||
include filelist.mk
|
||||
@@ -31,7 +31,7 @@ include filelist.mk
|
||||
|
||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||
USE_STATIC := $(or $(USE_STATIC),no)
|
||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||
+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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace afrikaans // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"failed", "Het misluk"},
|
||||
{"unknown", "onbekend"},
|
||||
|
@ -76,7 +73,7 @@ namespace afrikaans // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace armenian // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f ԿիԲ"},
|
||||
{"%.2f MiB", "%.2f ՄիԲ"},
|
||||
|
@ -199,7 +196,7 @@ namespace armenian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace chinese // language namespace
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -67,7 +64,7 @@ namespace chinese // language namespace
|
|||
{"Full cone NAT", "全锥型NAT"},
|
||||
{"No Descriptors", "无描述符"},
|
||||
{"Uptime", "运行时间"},
|
||||
{"Network status", "网络状态"},
|
||||
{"Network status", "IPv4 网络状态"},
|
||||
{"Network status v6", "IPv6 网络状态"},
|
||||
{"Stopping in", "距停止还有:"},
|
||||
{"Family", "家族"},
|
||||
|
@ -218,7 +215,7 @@ namespace chinese // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace czech // language namespace
|
|||
return (n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -218,7 +215,7 @@ namespace czech // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -30,10 +30,7 @@ namespace english // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"", ""},
|
||||
};
|
||||
|
@ -45,7 +42,7 @@ namespace english // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace french // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f Kio"},
|
||||
{"%.2f MiB", "%.2f Mio"},
|
||||
|
@ -47,7 +44,7 @@ namespace french // language namespace
|
|||
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
|
||||
{"Main page", "Page principale"},
|
||||
{"Router commands", "Commandes du routeur"},
|
||||
{"Local Destinations", "Destinatioans localeAlger"},
|
||||
{"Local Destinations", "Destinations locales"},
|
||||
{"LeaseSets", "Jeu de baux"},
|
||||
{"Tunnels", "Tunnels"},
|
||||
{"Transit Tunnels", "Tunnels transitoires"},
|
||||
|
@ -218,7 +215,7 @@ namespace french // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace german // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -213,7 +210,7 @@ namespace german // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
202
i18n/Hebrew.cpp
202
i18n/Hebrew.cpp
|
@ -1,202 +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 <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "I18N.h"
|
||||
|
||||
// Hebrew localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace hebrew // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "hebrew";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = true;
|
||||
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f קי״ב"},
|
||||
{"%.2f MiB", "%.2f מי״ב"},
|
||||
{"%.2f GiB", "%.2f קי״ב"},
|
||||
{"Purple I2P Webconsole", "קונסולת Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "קונסולת <b>i2pd</b>"},
|
||||
{"Main page", "עמוד ראשי"},
|
||||
{"Router commands", "פקודות נתב"},
|
||||
{"Local Destinations", "יעדים מקומיים"},
|
||||
{"Tunnels", "מנהרות"},
|
||||
{"Transit Tunnels", "מנהרות מעבר"},
|
||||
{"Transports", "מובילים"},
|
||||
{"I2P tunnels", "מנהרות I2P"},
|
||||
{"SAM sessions", "הפעלות SAM"},
|
||||
{"Unknown", "לא מוכר"},
|
||||
{"Proxy", "פרוקסי"},
|
||||
{"Mesh", "סיבוך"},
|
||||
{"Clock skew", "לכסון שעון"},
|
||||
{"Offline", "לא מקוון"},
|
||||
{"Symmetric NAT", "NAT סימטרי"},
|
||||
{"Full cone NAT", "NAT חסום לחלוטין"},
|
||||
{"No Descriptors", "אין מתארים"},
|
||||
{"Uptime", "זמן הפעלה"},
|
||||
{"Network status", "מצב רשת תקשורת"},
|
||||
{"Network status v6", "מצב רשת תקשורת v6"},
|
||||
{"Stopping in", "מפסיק בעוד"},
|
||||
{"Family", "משפחה"},
|
||||
{"Tunnel creation success rate", "שיעור הצלחה של יצירת מנהרות"},
|
||||
{"Total tunnel creation success rate", "שיעור הצלחה כולל של יצירת מנהרות"},
|
||||
{"Received", "נתקבל"},
|
||||
{"%.2f KiB/s", "%.2f קי״ב/ש"},
|
||||
{"Sent", "נשלח"},
|
||||
{"Transit", "מעבר"},
|
||||
{"Data path", "נתיב מידע"},
|
||||
{"Hidden content. Press on text to see.", "תוכן מוסתר. לחץ על הטקסט כדי לראותו."},
|
||||
{"Router Ident", "מזהה נתב"},
|
||||
{"Router Family", "משפחת נתב"},
|
||||
{"Version", "גרסא"},
|
||||
{"Our external address", "הכתובת החיצונית שלנו"},
|
||||
{"supported", "נתמך"},
|
||||
{"Routers", "נתבים"},
|
||||
{"Client Tunnels", "מנהרות לקוח"},
|
||||
{"Services", "שירותים"},
|
||||
{"Enabled", "מאופשר"},
|
||||
{"Disabled", "מנוטרל"},
|
||||
{"Encrypted B33 address", "כתובת B33 מוצפנת"},
|
||||
{"Address registration line", "שורת רישום כתובת"},
|
||||
{"Domain", "תחום"},
|
||||
{"Generate", "צור"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>הערה</b> מחרוזת תוצאה יכולה להיות מועילה רק לצורך רישום תחומים 2LD (example.i2p). לשם רישום תתי-תחום עליך להיוועץ עם i2pd-tools."},
|
||||
{"Address", "כתובת"},
|
||||
{"Type", "טיפוס"},
|
||||
{"Expire LeaseSet", "פקיעת LeaseSet"},
|
||||
{"Inbound tunnels", "מנהרות פנימיות"},
|
||||
{"%dms", "מילישניות %d"},
|
||||
{"Outbound tunnels", "מנהרות חיצוניות"},
|
||||
{"Tags", "תוויות"},
|
||||
{"Incoming", "נכנס"},
|
||||
{"Outgoing", "יוצא"},
|
||||
{"Destination", "יעד"},
|
||||
{"Amount", "כמות"},
|
||||
{"Incoming Tags", "תוויות נכנסות"},
|
||||
{"Tags sessions", "הפעלות תוויות"},
|
||||
{"Status", "מצב"},
|
||||
{"Local Destination", "יעד מקומי"},
|
||||
{"Streams", "זרמים"},
|
||||
{"Close stream", "סגור זרם"},
|
||||
{"Such destination is not found", "יעד כזה לא נמצא"},
|
||||
{"I2CP session not found", "הפעלת I2CP לא נמצאה"},
|
||||
{"I2CP is not enabled", "I2CP לא מאופשר"},
|
||||
{"Invalid", "לא תקין"},
|
||||
{"Store type", "טיפוס אחסון"},
|
||||
{"Expires", "פוקע"},
|
||||
{"Non Expired Leases", "חכירות בלתי פקיעות"},
|
||||
{"Gateway", "שער-דרך"},
|
||||
{"TunnelID", "מזהה מנהרה"},
|
||||
{"EndDate", "תאריך סיום"},
|
||||
{"floodfill mode is disabled", "מצב floodfill הינו מנוטרל"},
|
||||
{"Queue size", "גודל תור"},
|
||||
{"Run peer test", "הרץ בדיקת עמית"},
|
||||
{"Reload tunnels configuration", "טען מחדש תצורת מנהרות"},
|
||||
{"Decline transit tunnels", "דחה מנהרות מעבר"},
|
||||
{"Accept transit tunnels", "קבל מנהרות מעבר"},
|
||||
{"Cancel graceful shutdown", "בטל כיבוי עדין"},
|
||||
{"Start graceful shutdown", "התחל כיבוי עדין"},
|
||||
{"Force shutdown", "כפה כיבוי"},
|
||||
{"Reload external CSS styles", "טען מחדש סגנונות CSS חיצוניים"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>הערה</b> כל פעולה אשר מבוצעת כאן אינה המשכית ולא משנה את קובצי התצורה שלך."},
|
||||
{"Logging level", "דרגת רישום יומן"},
|
||||
{"Transit tunnels limit", "מגבלת מנהרות מעבר"},
|
||||
{"Change", "שנה"},
|
||||
{"Change language", "שנה שפה"},
|
||||
{"no transit tunnels currently built", "אין מנהרות מעבר אשר בנויות כעת"},
|
||||
{"SAM disabled", "SAM מנוטרל"},
|
||||
{"no sessions currently running", "אין הפעלה אשר מורצת כעת"},
|
||||
{"SAM session not found", "הפעלת SAM לא נמצאה"},
|
||||
{"SAM Session", "הפעלת SAM"},
|
||||
{"Server Tunnels", "מנהרות שרת"},
|
||||
{"Unknown page", "עמוד לא מוכר"},
|
||||
{"Invalid token", "סימן לא תקין"},
|
||||
{"SUCCESS", "הצלחה"},
|
||||
{"Stream closed", "זרם סגור"},
|
||||
{"Stream not found or already was closed", "זרם לא נמצא או שהוא היה כבר סגור"},
|
||||
{"Destination not found", "יעד לא נמצא"},
|
||||
{"StreamID can't be null", "מזהה זרם (StreamID) לא יכול להיות אפסי"},
|
||||
{"Return to destination page", "חזור לעמוד יעד"},
|
||||
{"You will be redirected in %d seconds", "אתה תכוון מחדש בעוד %d שניות"},
|
||||
{"LeaseSet expiration time updated", "זמן פקיעה של LeaseSet עודכן"},
|
||||
{"LeaseSet is not found or already expired", "LeaseSet אינו נמצא או שהוא כבר פקע"},
|
||||
{"Transit tunnels count must not exceed %d", "אסור לספירת מנהרות מעבר לעלות על %d"},
|
||||
{"Back to commands list", "חזור לרשימת פקודות"},
|
||||
{"Register at reg.i2p", "הירשם באתר reg.i2p"},
|
||||
{"Description", "תיאור"},
|
||||
{"A bit information about service on domain", "מידע אודות שירות על תחום"},
|
||||
{"Submit", "שלח"},
|
||||
{"Domain can't end with .b32.i2p", "תחום לא יכול להסתיים עם ‎.b32.i2p"},
|
||||
{"Domain must end with .i2p", "תחום חייב להסתיים עם ‎.i2p"},
|
||||
{"Unknown command", "פקודה לא מוכרת"},
|
||||
{"Command accepted", "פקודה נתקבלה"},
|
||||
{"Proxy error", "שגיאת פרוקסי"},
|
||||
{"Proxy info", "מידע פרוקסי"},
|
||||
{"Proxy error: Host not found", "שגיאת פרוקסי: מארח לא נמצא"},
|
||||
{"Remote host not found in router's addressbook", "ארח מרוחק לא נמצא בתוך הפנקס כתובות של הנתב"},
|
||||
{"You may try to find this host on jump services below", "באפשרותך לנסות למצוא את מארח זה דרך שירותי קפיצה להלן"},
|
||||
{"Invalid request", "בקשה לא תקינה"},
|
||||
{"Proxy unable to parse your request", "פרוקסי לא מסוגל לנתח את בקשתך"},
|
||||
{"Addresshelper is not supported", "סייען-כתובות אינו נתמך"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. <b>Be careful: source of this URL may be harmful!</b> Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "מארח %s is <font color=red>כבר נמצא בפנקס כתובות של הנתב</font>. <b>זהירות: מקור URL זה עלול להזיק!</b> לחץ כאן כדי לעדכן מרשם: <a href=\"%s%s%s&update=true\">המשך</a>."},
|
||||
{"Addresshelper forced update rejected", "אילוץ עדכון של סייען-כתובות נדחה"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "Tכדי להוסיף את מארח <b>%s</b> לפנקס כתובות של הנתב: <a href=\"%s%s%s\">המשך</a>."},
|
||||
{"Addresshelper request", "בקשת סייען-כתובות"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "מארח %s נתווסף לסייען-כתובות של הנתב דרך סייען. לחץ כאן כדי proceed: <a href=\"%s\">המשך</a>."},
|
||||
{"Addresshelper adding", "הוספת סייען-כתובות"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "מארח %s <font color=red>כבר נמצא בספר כתובות של הנתב</font>. לחץ כאן כדי לעדכן מרשם: <a href=\"%s%s%s&update=true\">המשך</a>."},
|
||||
{"Addresshelper update", "עדכון סייען-כתובות"},
|
||||
{"Invalid request URI", "בקשת URI לא תקינה"},
|
||||
{"Can't detect destination host from request", "לא יכול לאתר יעד מארח מתוך בקשה"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "מארח %s לא נמצא בתוך רשת I2P, אולם outproxy אינו מאופשר"},
|
||||
{"Hostname is too long", "שם-מארח הינו ארוך מדי"},
|
||||
{"Cannot negotiate with SOCKS proxy", "לא מסוגל להסדיר פרוקסי SOCKS"},
|
||||
{"CONNECT error", "שגיאת חיבור"},
|
||||
{"Failed to connect", "נכשל להתחבר"},
|
||||
{"SOCKS proxy error", "שגיאת פרוקסי SOCKS"},
|
||||
{"No reply from SOCKS proxy", "אין מענה מתוך פרוקסי SOCKS"},
|
||||
{"Cannot connect", "לא מסוגל להתחבר"},
|
||||
{"Host is down", "מארח הינו מושבת"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "לא יכול ליצור חיבור למארח מבוקש, המארח עשוי להיות מושבת. אנא נסה שוב מאוחר יותר."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"יום %d", "יומיים", "ימים %d", "ימים %d"}},
|
||||
{"%d hours", {"שעה %d", "שעתיים", "שעות %d", "שעות %d"}},
|
||||
{"%d minutes", {"דקה %d", "שתי דקות", "דקות %d", "דקות %d"}},
|
||||
{"%d seconds", {"שניה %d", "שתי שניות", "שניות %d", "שניות %d"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
} // i18n
|
||||
} // i2p
|
226
i18n/Hindi.cpp
226
i18n/Hindi.cpp
|
@ -1,226 +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 <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "I18N.h"
|
||||
|
||||
// Hindi localization file
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace i18n
|
||||
{
|
||||
namespace hindi // language namespace
|
||||
{
|
||||
// language name in lowercase
|
||||
static std::string language = "hindi";
|
||||
|
||||
// See for language plural forms here:
|
||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||
static int plural (int n) {
|
||||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f कीबी"},
|
||||
{"%.2f MiB", "%.2f मीबी"},
|
||||
{"%.2f GiB", "%.2f जीबी"},
|
||||
{"building", "निर्माण"},
|
||||
{"failed", "विफल"},
|
||||
{"expiring", "समाप्त होना"},
|
||||
{"established", "स्थापित"},
|
||||
{"unknown", "अज्ञात"},
|
||||
{"exploratory", "अन्वेषणात्मक"},
|
||||
{"Purple I2P Webconsole", "पर्पल I2P वेब कंसोल"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> वेब कंसोल"},
|
||||
{"Main page", "मुख्य पृष्ठ"},
|
||||
{"Router commands", "राउटर आदेश"},
|
||||
{"Local Destinations", "स्थानीय गंतव्य"},
|
||||
{"LeaseSets", "पट्ट समुच्चय"},
|
||||
{"Tunnels", "सुरंग"},
|
||||
{"Transit Tunnels", "संचरण सुरंगें"},
|
||||
{"Transports", "परिवहन"},
|
||||
{"I2P tunnels", "I2P सुरंगें"},
|
||||
{"SAM sessions", "SAM सत्र"},
|
||||
{"ERROR", "त्रुटि"},
|
||||
{"OK", "ठीक है"},
|
||||
{"Testing", "परीक्षण"},
|
||||
{"Firewalled", "फायरवॉल"},
|
||||
{"Unknown", "अज्ञात"},
|
||||
{"Proxy", "प्रॉक्सी"},
|
||||
{"Mesh", "जाली"},
|
||||
{"Clock skew", "घड़ी संकेत विचलन"},
|
||||
{"Offline", "ऑफलाइन"},
|
||||
{"Symmetric NAT", "सममितीय NAT"},
|
||||
{"Full cone NAT", "पूर्णकोण NAT"},
|
||||
{"No Descriptors", "कोई वर्णनकर्त्तृ नहीं हैं"},
|
||||
{"Uptime", "संचालन समय"},
|
||||
{"Network status", "संपर्क स्थिति"},
|
||||
{"Network status v6", "संपर्क स्थिति v6"},
|
||||
{"Stopping in", "में अवसान प्रारंभ हो रहा है"},
|
||||
{"Family", "परिवार"},
|
||||
{"Tunnel creation success rate", "सुरंग निर्माण सफलता दर"},
|
||||
{"Total tunnel creation success rate", "कुल सुरंग निर्माण सफलता दर"},
|
||||
{"Received", "प्राप्त हुआ"},
|
||||
{"%.2f KiB/s", "%.2f कीबी/से"},
|
||||
{"Sent", "प्रेषित"},
|
||||
{"Transit", "संचरण"},
|
||||
{"Data path", "डेटा पथ"},
|
||||
{"Hidden content. Press on text to see.", "सामग्री छिपाई गई है। देखने हेतु पाठ पर दबाएँ।"},
|
||||
{"Router Ident", "राउटर परिचय"},
|
||||
{"Router Family", "राउटर परिवार"},
|
||||
{"Router Caps", "राउटर कैप्स"},
|
||||
{"Version", "संस्करण"},
|
||||
{"Our external address", "हमारा बाह्य पता"},
|
||||
{"supported", "समर्थित"},
|
||||
{"Routers", "राउटर"},
|
||||
{"Floodfills", "पूर्णक संवाहक"},
|
||||
{"Client Tunnels", "क्लाइंट सुरंगें"},
|
||||
{"Services", "सेवाएँ"},
|
||||
{"Enabled", "सक्षम है"},
|
||||
{"Disabled", "निष्क्रिय है"},
|
||||
{"Encrypted B33 address", "कूटलिखित B33 पता"},
|
||||
{"Address registration line", "पता पंजीकरण पंक्ति"},
|
||||
{"Domain", "डोमेन"},
|
||||
{"Generate", "सृजित करें"},
|
||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>नोट:</b> परिणाम स्ट्रिंग का उपयोग केवल 2LD डोमेनों (जैसे example.i2p) को रजिस्टर करने के लिए किया जा सकता है। सबडोमेन रजिस्टर करने के लिए कृपया i2pd-tools का उपयोग करें।"},
|
||||
{"Address", "पता"},
|
||||
{"Type", "प्रकार"},
|
||||
{"EncType", "कूट प्रकार"},
|
||||
{"Expire LeaseSet", "पट्ट समुच्चय का अवसान करें"},
|
||||
{"Inbound tunnels", "आगमनशील सुरंगें"},
|
||||
{"%dms", "%dms"},
|
||||
{"Outbound tunnels", "प्रस्थानशील सुरंगें"},
|
||||
{"Tags", "चिन्हित"},
|
||||
{"Incoming", "आगामी"},
|
||||
{"Outgoing", "निर्गामी"},
|
||||
{"Destination", "गंतव्य"},
|
||||
{"Amount", "मात्रा"},
|
||||
{"Incoming Tags", "आगामी चिन्हित"},
|
||||
{"Tags sessions", "चिन्हित सत्र OR सत्र को चिन्हित करें"},
|
||||
{"Status", "स्थिति"},
|
||||
{"Local Destination", "स्थानीय गंतव्य"},
|
||||
{"Streams", "धाराएँ"},
|
||||
{"Close stream", "प्रवाह समाप्त करें"},
|
||||
{"Such destination is not found", "ऐसा गंतव्य नहीं मिला"},
|
||||
{"I2CP session not found", "I2CP सत्र नहीं मिला"},
|
||||
{"I2CP is not enabled", "I2CP निष्क्रिय है"},
|
||||
{"Invalid", "अमान्य"},
|
||||
{"Store type", "भण्डारगार का प्रकार"},
|
||||
{"Expires", "अवसान होता है"},
|
||||
{"Non Expired Leases", "अनवसित पट्ट"},
|
||||
{"Gateway", "प्रवेशद्वार"},
|
||||
{"TunnelID", "सुरंग ID"},
|
||||
{"EndDate", "समाप्ति तिथि"},
|
||||
{"floodfill mode is disabled", "पूर्णक संवाहक विधि निष्क्रिय है"},
|
||||
{"Queue size", "क्यू आकार"},
|
||||
{"Run peer test", "सहकर्मी परीक्षण चलाएँ"},
|
||||
{"Reload tunnels configuration", "सुरंग विन्यास पुनः लोड करें"},
|
||||
{"Decline transit tunnels", "संचरण सुरंगों को अस्वीकार करें"},
|
||||
{"Accept transit tunnels", "संचरण सुरंगों को स्वीकार करें"},
|
||||
{"Cancel graceful shutdown", "सौम्य अवसान निरस्त करें"},
|
||||
{"Start graceful shutdown", "सौम्य समापन प्रारंभ करें"},
|
||||
{"Force shutdown", "बाध्य अवसान"},
|
||||
{"Reload external CSS styles", "बाह्य CSS शैलियों को पुनः लोड करें"},
|
||||
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>टिप्पणी:</b> यहाँ किए गए कोई भी क्रियाएँ स्थायी नहीं हैं और आपके विन्यास संचिका में कोई परिवर्तन नहीं करतीं।"},
|
||||
{"Logging level", "लॉगिंग स्तर"},
|
||||
{"Transit tunnels limit", "संचरण सुरंगों की सीमा"},
|
||||
{"Change", "बदलना"},
|
||||
{"Change language", "भाषा बदलें"},
|
||||
{"no transit tunnels currently built", "संचरण सुरंगों का निर्माण नहीं हुआ है"},
|
||||
{"SAM disabled", "SAM निष्क्रिय है"},
|
||||
{"no sessions currently running", "वर्तमान में कोई सत्र सक्रिय नहीं है"},
|
||||
{"SAM session not found", "SAM सत्र नहीं मिला"},
|
||||
{"SAM Session", "SAM सत्र"},
|
||||
{"Server Tunnels", "सर्वर सुरंग"},
|
||||
{"Client Forwards", "क्लाइंट फॉरवर्ड्स"},
|
||||
{"Server Forwards", "सर्वर फॉरवर्ड्स"},
|
||||
{"Unknown page", "अज्ञात पृष्ठ"},
|
||||
{"Invalid token", "अमान्य टोकन"},
|
||||
{"SUCCESS", "सफलता"},
|
||||
{"Stream closed", "प्रवाह समाप्त हो गया है"},
|
||||
{"Stream not found or already was closed", "प्रवाह प्राप्त नहीं हुआ अथवा इसका पूर्व में ही समापन हो चुका है"},
|
||||
{"Destination not found", "गंतव्य नहीं मिला"},
|
||||
{"StreamID can't be null", "प्रवाह ID शून्य नहीं हो सकता है"},
|
||||
{"Return to destination page", "गंतव्य पृष्ठ पर पुनः वापस जाएँ"},
|
||||
{"You will be redirected in %d seconds", "आपको %d सेकंड में पुनर्निर्देशित किया जाएगा"},
|
||||
{"LeaseSet expiration time updated", "पट्ट समुच्चय की अवसान समय को अद्यतित किया गया है"},
|
||||
{"LeaseSet is not found or already expired", "पट्ट समुच्चय प्राप्त नहीं हुआ या इसका पूर्वमेव अवसान हो चुका है"},
|
||||
{"Transit tunnels count must not exceed %d", "संचरण सुरंगों की संख्या %d से अधिक नहीं होनी चाहिए"},
|
||||
{"Back to commands list", "आदेश सूची पर पुनः लौटें"},
|
||||
{"Register at reg.i2p", "reg.i2p पर पंजीकरण करें"},
|
||||
{"Description", "विवरण"},
|
||||
{"A bit information about service on domain", "डोमेन पर सेवा से संबंधित थोड़ी जानकारी"},
|
||||
{"Submit", "प्रस्तुत करें"},
|
||||
{"Domain can't end with .b32.i2p", "डोमेन का अंत .b32.i2p से नहीं हो सकता"},
|
||||
{"Domain must end with .i2p", "डोमेन का अंत .i2p से होना आवश्यक है"},
|
||||
{"Unknown command", "अज्ञात आदेश"},
|
||||
{"Command accepted", "आदेश स्वीकार किया गया"},
|
||||
{"Proxy error", "प्रॉक्सी त्रुटि"},
|
||||
{"Proxy info", "प्रॉक्सी जानकारी"},
|
||||
{"Proxy error: Host not found", "प्रॉक्सी त्रुटि: होस्ट नहीं मिला"},
|
||||
{"Remote host not found in router's addressbook", "राउटर की पता पुस्तक में दूरस्थ होस्ट नहीं मिला"},
|
||||
{"You may try to find this host on jump services below", "आप नीचे दिए गए जंप सेवाओं में इस होस्ट को खोजने की कोशिश कर सकते हैं"},
|
||||
{"Invalid request", "अमान्य अनुरोध"},
|
||||
{"Proxy unable to parse your request", "प्रॉक्सी आपके अनुरोध को विश्लेषित करने में असमर्थ है"},
|
||||
{"Addresshelper is not supported", "Addresshelper समर्थित नहीं है"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. <b>Be careful: source of this URL may be harmful!</b> Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "होस्ट %s पहले से ही राउटर की पता-पुस्तिका में <font color=red>उपस्थित है</font>। <b>सावधान रहें: इस URL का स्रोत हानिकारक हो सकता है!</b> अभिलेख को अद्यतन करने हेतु यहाँ क्लिक करें: <a href=\"%s%s%s&update=true\">जारी रखें</a>।"},
|
||||
{"Addresshelper forced update rejected", "Addresshelper का जबरन अद्यतन अस्वीकृत किया गया"},
|
||||
{"To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.", "राउटर की पता-पुस्तिका में होस्ट <b>%s</b> को जोड़ने हेतु, कृपया यहाँ क्लिक करें: <a href=\"%s%s%s\">जारी रखें</a>।"},
|
||||
{"Addresshelper request", "Addresshelper अनुरोध"},
|
||||
{"Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.", "सहायक से होस्ट %s राउटर की पता-पुस्तिका में जोड़ दिया गया है। आगे बढ़ने हेतु यहाँ क्लिक करें: <a href=\"%s\">जारी रखें</a>।"},
|
||||
{"Addresshelper adding", "Addresshelper जोड़ना"},
|
||||
{"Host %s is <font color=red>already in router's addressbook</font>. Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.", "होस्ट %s पहले से ही राउटर की पता-पुस्तिका में <font color=red>उपस्थित है</font>। अभिलेख को अद्यतन करने हेतु यहाँ क्लिक करें: <a href=\"%s%s%s&update=true\">जारी रखें</a>।"},
|
||||
{"Addresshelper update", "Addresshelper अद्यतन करना"},
|
||||
{"Invalid request URI", "अमान्य अनुरोध URI"},
|
||||
{"Can't detect destination host from request", "अनुरोध से गंतव्य होस्ट का पता नहीं लगा सकते"},
|
||||
{"Outproxy failure", "आउटप्रॉक्सी विफलता"},
|
||||
{"Bad outproxy settings", "गलत आउटप्रॉक्सी सेटिंग्स"},
|
||||
{"Host %s is not inside I2P network, but outproxy is not enabled", "होस्ट %s I2P नेटवर्क के भीतर नहीं है, लेकिन आउटप्रॉक्सी सक्षम नहीं है"},
|
||||
{"Unknown outproxy URL", "अज्ञात आउटप्रॉक्सी URL"},
|
||||
{"Cannot resolve upstream proxy", "ऊर्ध्वधारा प्रॉक्सी का समाधान नहीं किया जा सका"},
|
||||
{"Hostname is too long", "होस्टनाम अत्यधिक लंबा है"},
|
||||
{"Cannot connect to upstream SOCKS proxy", "उर्ध्वधारा SOCKS प्रॉक्सी से संपर्क स्थापित नहीं हो पा रहा है"},
|
||||
{"Cannot negotiate with SOCKS proxy", "SOCKS प्रॉक्सी के साथ समन्वयन स्थापित नहीं किया जा सका"},
|
||||
{"CONNECT error", "संपर्क त्रुटि"},
|
||||
{"Failed to connect", "संपर्क स्थापित करने में विफल"},
|
||||
{"SOCKS proxy error", "SOCKS प्रॉक्सी त्रुटि"},
|
||||
{"Failed to send request to upstream", "ऊर्ध्ववाहिनी को अनुरोध प्रेषित करने में विफलता हुई"},
|
||||
{"No reply from SOCKS proxy", "SOCKS प्रॉक्सी से कोई प्रत्युत्तर प्राप्त नहीं हुआ"},
|
||||
{"Cannot connect", "संपर्क नहीं हो पा रहा है"},
|
||||
{"HTTP out proxy not implemented", "HTTP आउट प्रॉक्सी कार्यान्वित नहीं किया गया है"},
|
||||
{"Cannot connect to upstream HTTP proxy", "उर्ध्वधारा HTTP प्रॉक्सी से संपर्क स्थापित नहीं हो पा रहा है"},
|
||||
{"Host is down", "होस्ट अनुपलब्ध है"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "अनुरोधित होस्ट से संपर्क स्थापित नहीं किया जा सका। संभवतः वह सक्रिय नहीं है। कृपया बाद में पुनः प्रयास करें।"},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::vector<std::string>> plurals
|
||||
{
|
||||
{"%d days", {"%d दिन", "%d दिन"}},
|
||||
{"%d hours", {"%d घंटा", "%dघंटे"}},
|
||||
{"%d minutes", {"%d मिनट", "%d मिनट"}},
|
||||
{"%d seconds", {"%d सेकंड", "%d सेकंड"}},
|
||||
{"", {"", "", ""}},
|
||||
};
|
||||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
} // i18n
|
||||
} // i2p
|
|
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
33
i18n/I18N.h
33
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
|
||||
*
|
||||
|
@ -10,7 +10,6 @@
|
|||
#define __I18N_H__
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
@ -19,17 +18,15 @@ namespace i2p
|
|||
{
|
||||
namespace i18n
|
||||
{
|
||||
typedef std::map<std::string_view, std::string_view> LocaleStrings;
|
||||
class Locale
|
||||
{
|
||||
public:
|
||||
Locale (
|
||||
const std::string& language,
|
||||
const bool& rtl,
|
||||
const LocaleStrings& strings,
|
||||
const std::map<std::string, std::string>& strings,
|
||||
const std::map<std::string, std::vector<std::string>>& plurals,
|
||||
std::function<int(int)> formula
|
||||
): m_Language (language), m_RTL (rtl), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
|
||||
|
||||
// Get activated language name for webconsole
|
||||
std::string GetLanguage() const
|
||||
|
@ -37,12 +34,7 @@ namespace i18n
|
|||
return m_Language;
|
||||
}
|
||||
|
||||
bool GetRTL() const
|
||||
{
|
||||
return m_RTL;
|
||||
}
|
||||
|
||||
std::string_view GetString (std::string_view arg) const
|
||||
std::string GetString (const std::string& arg) const
|
||||
{
|
||||
const auto it = m_Strings.find(arg);
|
||||
if (it == m_Strings.end())
|
||||
|
@ -55,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);
|
||||
if (it == m_Plurals.end()) // not found, fallback to english
|
||||
|
@ -71,15 +63,14 @@ namespace i18n
|
|||
|
||||
private:
|
||||
const std::string m_Language;
|
||||
const bool m_RTL;
|
||||
const LocaleStrings m_Strings;
|
||||
const std::map<std::string, std::string> m_Strings;
|
||||
const std::map<std::string, std::vector<std::string>> m_Plurals;
|
||||
std::function<int(int)> m_Formula;
|
||||
};
|
||||
|
||||
void SetLanguage(const std::string &lang);
|
||||
std::string_view translate (std::string_view arg);
|
||||
std::string translate (const std::string& arg, const std::string& arg2, int n);
|
||||
std::string translate (const std::string& arg);
|
||||
std::string translate (const std::string& arg, const std::string& arg2, const int& n);
|
||||
} // i18n
|
||||
} // i2p
|
||||
|
||||
|
@ -88,7 +79,7 @@ namespace i18n
|
|||
* @param arg String with message
|
||||
*/
|
||||
template<typename TValue>
|
||||
std::string_view tr (TValue&& arg)
|
||||
std::string tr (TValue&& arg)
|
||||
{
|
||||
return i2p::i18n::translate(std::forward<TValue>(arg));
|
||||
}
|
||||
|
@ -101,7 +92,7 @@ std::string_view tr (TValue&& arg)
|
|||
template<typename TValue, typename... TArgs>
|
||||
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)...);
|
||||
std::string str(size, 0);
|
||||
|
@ -117,7 +108,7 @@ std::string tr (TValue&& arg, TArgs&&... args)
|
|||
* @param n Integer, used for selection of form
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
@ -130,7 +121,7 @@ std::string ntr (TValue&& arg, TValue2&& arg2, int n)
|
|||
* @param args Array of arguments for string formatting
|
||||
*/
|
||||
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));
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -30,8 +30,6 @@ namespace i18n
|
|||
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace hebrew { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace hindi { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace polish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
namespace portuguese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
|
@ -55,8 +53,6 @@ namespace i18n
|
|||
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
||||
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
|
||||
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
||||
{ "hebrew", {"עִבְרִית", "he", i2p::i18n::hebrew::GetLocale} },
|
||||
{ "hindi", {"हिन्दी", "hi", i2p::i18n::hindi::GetLocale} },
|
||||
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
|
||||
{ "polish", {"Polski", "pl", i2p::i18n::polish::GetLocale} },
|
||||
{ "portuguese", {"Português", "pt", i2p::i18n::portuguese::GetLocale} },
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace italian // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -218,7 +215,7 @@ namespace italian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace polish // language namespace
|
|||
return (n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -218,7 +215,7 @@ namespace polish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace portuguese // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -218,7 +215,7 @@ namespace portuguese // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +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;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КиБ"},
|
||||
{"%.2f MiB", "%.2f МиБ"},
|
||||
|
@ -218,7 +215,7 @@ namespace russian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace spanish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -199,7 +196,7 @@ namespace spanish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace swedish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -214,7 +211,7 @@ namespace swedish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace turkish // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -109,7 +106,7 @@ namespace turkish // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace turkmen // language namespace
|
|||
return n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -199,7 +196,7 @@ namespace turkmen // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +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;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f КіБ"},
|
||||
{"%.2f MiB", "%.2f МіБ"},
|
||||
|
@ -218,7 +215,7 @@ namespace ukrainian // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -29,10 +29,7 @@ namespace uzbek // language namespace
|
|||
return n > 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Right to Left language?
|
||||
static bool rtl = false;
|
||||
|
||||
static const LocaleStrings strings
|
||||
static std::map<std::string, std::string> strings
|
||||
{
|
||||
{"%.2f KiB", "%.2f KiB"},
|
||||
{"%.2f MiB", "%.2f MiB"},
|
||||
|
@ -218,7 +215,7 @@ namespace uzbek // language namespace
|
|||
|
||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||
{
|
||||
return std::make_shared<i2p::i18n::Locale>(language, rtl, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||
}
|
||||
|
||||
} // language
|
||||
|
|
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
|
||||
*
|
||||
|
@ -15,7 +15,7 @@ namespace i2p
|
|||
{
|
||||
namespace data
|
||||
{
|
||||
static constexpr char T32[32] =
|
||||
static const char T32[32] =
|
||||
{
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
|
||||
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
|
||||
|
@ -28,6 +28,11 @@ namespace data
|
|||
return T32;
|
||||
}
|
||||
|
||||
bool IsBase32 (char ch)
|
||||
{
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
|
||||
}
|
||||
|
||||
static void iT64Build(void);
|
||||
|
||||
/*
|
||||
|
@ -38,7 +43,7 @@ namespace data
|
|||
* Direct Substitution Table
|
||||
*/
|
||||
|
||||
static constexpr char T64[64] =
|
||||
static const char T64[64] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
|
@ -55,16 +60,23 @@ namespace data
|
|||
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)
|
||||
*/
|
||||
|
||||
static char iT64[256];
|
||||
static int isFirstTime = 1;
|
||||
|
||||
/*
|
||||
* Padding
|
||||
*/
|
||||
static constexpr char P64 = '=';
|
||||
|
||||
static char P64 = '=';
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -74,69 +86,78 @@ namespace data
|
|||
* Converts binary encoded data to BASE64 format.
|
||||
*
|
||||
*/
|
||||
std::string ByteStreamToBase64 (// base64 encoded string
|
||||
const uint8_t * InBuffer, // Input buffer, binary data
|
||||
size_t InCount // Number of bytes in the input buffer
|
||||
|
||||
size_t ByteStreamToBase64 ( /* Number of bytes in the encoded 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 * pd;
|
||||
unsigned char acc_1;
|
||||
unsigned char acc_2;
|
||||
int i;
|
||||
int n;
|
||||
int m;
|
||||
size_t outCount;
|
||||
|
||||
ps = (unsigned char *)InBuffer;
|
||||
n = 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;
|
||||
out.reserve (outCount);
|
||||
if (outCount > len) return 0;
|
||||
|
||||
pd = (unsigned char *)OutBuffer;
|
||||
for ( i = 0; i < n; i++ )
|
||||
{
|
||||
acc_1 = *ps++;
|
||||
acc_2 = (acc_1 << 4) & 0x30;
|
||||
acc_1 >>= 2; // base64 digit #1
|
||||
out.push_back (T64[acc_1]);
|
||||
acc_1 >>= 2; /* base64 digit #1 */
|
||||
*pd++ = T64[acc_1];
|
||||
acc_1 = *ps++;
|
||||
acc_2 |= acc_1 >> 4; // base64 digit #2
|
||||
out.push_back (T64[acc_2]);
|
||||
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
||||
*pd++ = T64[acc_2];
|
||||
acc_1 &= 0x0f;
|
||||
acc_1 <<= 2;
|
||||
acc_2 = *ps++;
|
||||
acc_1 |= acc_2 >> 6; // base64 digit #3
|
||||
out.push_back (T64[acc_1]);
|
||||
acc_2 &= 0x3f; // base64 digit #4
|
||||
out.push_back (T64[acc_2]);
|
||||
acc_1 |= acc_2 >> 6; /* base64 digit #3 */
|
||||
*pd++ = T64[acc_1];
|
||||
acc_2 &= 0x3f; /* base64 digit #4 */
|
||||
*pd++ = T64[acc_2];
|
||||
}
|
||||
if ( m == 1 )
|
||||
{
|
||||
acc_1 = *ps++;
|
||||
acc_2 = (acc_1 << 4) & 0x3f; // base64 digit #2
|
||||
acc_1 >>= 2; // base64 digit #1
|
||||
out.push_back (T64[acc_1]);
|
||||
out.push_back (T64[acc_2]);
|
||||
out.push_back (P64);
|
||||
out.push_back (P64);
|
||||
acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */
|
||||
acc_1 >>= 2; /* base64 digit #1 */
|
||||
*pd++ = T64[acc_1];
|
||||
*pd++ = T64[acc_2];
|
||||
*pd++ = P64;
|
||||
*pd++ = P64;
|
||||
|
||||
}
|
||||
else if ( m == 2 )
|
||||
{
|
||||
acc_1 = *ps++;
|
||||
acc_2 = (acc_1 << 4) & 0x3f;
|
||||
acc_1 >>= 2; // base64 digit #1
|
||||
out.push_back (T64[acc_1]);
|
||||
acc_1 >>= 2; /* base64 digit #1 */
|
||||
*pd++ = T64[acc_1];
|
||||
acc_1 = *ps++;
|
||||
acc_2 |= acc_1 >> 4; // base64 digit #2
|
||||
out.push_back (T64[acc_2]);
|
||||
acc_2 |= acc_1 >> 4; /* base64 digit #2 */
|
||||
*pd++ = T64[acc_2];
|
||||
acc_1 &= 0x0f;
|
||||
acc_1 <<= 2; // base64 digit #3
|
||||
out.push_back (T64[acc_1]);
|
||||
out.push_back (P64);
|
||||
acc_1 <<= 2; /* base64 digit #3 */
|
||||
*pd++ = T64[acc_1];
|
||||
*pd++ = P64;
|
||||
}
|
||||
|
||||
return out;
|
||||
return outCount;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -144,42 +165,55 @@ namespace data
|
|||
* 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
|
||||
*
|
||||
*/
|
||||
size_t Base64ToByteStream ( // Number of output bytes
|
||||
std::string_view base64Str, // BASE64 encoded string
|
||||
uint8_t * OutBuffer, // output buffer length
|
||||
size_t len // length of output buffer
|
||||
|
||||
size_t Base64ToByteStream ( /* Number of output bytes */
|
||||
const char * InBuffer, /* BASE64 encoded 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 acc_1;
|
||||
unsigned char acc_2;
|
||||
int i;
|
||||
int n;
|
||||
int m;
|
||||
size_t outCount;
|
||||
|
||||
if (base64Str.empty () || base64Str[0] == P64) return 0;
|
||||
auto d = std::div (base64Str.length (), 4);
|
||||
if (!d.rem)
|
||||
outCount = 3 * d.quot;
|
||||
if (isFirstTime)
|
||||
iT64Build();
|
||||
|
||||
n = InCount / 4;
|
||||
m = InCount % 4;
|
||||
|
||||
if (InCount && !m)
|
||||
outCount = 3 * n;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (isFirstTime) iT64Build();
|
||||
if(*InBuffer == P64)
|
||||
return 0;
|
||||
|
||||
auto pos = base64Str.find_last_not_of (P64);
|
||||
if (pos == base64Str.npos) return 0;
|
||||
outCount -= (base64Str.length () - pos - 1);
|
||||
if (outCount > len) return 0;
|
||||
ps = (unsigned char *)(InBuffer + InCount - 1);
|
||||
while ( *ps-- == P64 )
|
||||
outCount--;
|
||||
ps = (unsigned char *)InBuffer;
|
||||
|
||||
if (outCount > len)
|
||||
return 0;
|
||||
|
||||
auto ps = base64Str.begin ();
|
||||
pd = OutBuffer;
|
||||
auto endOfOutBuffer = OutBuffer + outCount;
|
||||
for (int i = 0; i < d.quot; i++)
|
||||
for ( i = 0; i < n; i++ )
|
||||
{
|
||||
acc_1 = iT64[int(*ps++)];
|
||||
acc_2 = iT64[int(*ps++)];
|
||||
acc_1 = iT64[*ps++];
|
||||
acc_2 = iT64[*ps++];
|
||||
acc_1 <<= 2;
|
||||
acc_1 |= acc_2 >> 4;
|
||||
*pd++ = acc_1;
|
||||
|
@ -187,13 +221,13 @@ namespace data
|
|||
break;
|
||||
|
||||
acc_2 <<= 4;
|
||||
acc_1 = iT64[int(*ps++)];
|
||||
acc_1 = iT64[*ps++];
|
||||
acc_2 |= acc_1 >> 2;
|
||||
*pd++ = acc_2;
|
||||
if (pd >= endOfOutBuffer)
|
||||
break;
|
||||
|
||||
acc_2 = iT64[int(*ps++)];
|
||||
acc_2 = iT64[*ps++];
|
||||
acc_2 |= acc_1 << 6;
|
||||
*pd++ = acc_2;
|
||||
}
|
||||
|
@ -201,16 +235,31 @@ namespace data
|
|||
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 '/'
|
||||
for (auto& ch: str)
|
||||
if (ch == '-')
|
||||
ch = '+';
|
||||
else if (ch == '~')
|
||||
ch = '/';
|
||||
return str;
|
||||
for (size_t i = 0; i < l; i++)
|
||||
if (str[i] == '-')
|
||||
str[i] = '+';
|
||||
else if (str[i] == '~')
|
||||
str[i] = '/';
|
||||
|
||||
std::string s(str);
|
||||
delete[] str;
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -231,12 +280,13 @@ namespace data
|
|||
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;
|
||||
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
|
||||
ch = (ch - '2') + 26; // 26 means a-z
|
||||
else if (ch >= 'a' && ch <= 'z')
|
||||
|
@ -258,13 +308,11 @@ namespace data
|
|||
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;
|
||||
out.reserve ((len * 8 + 4) / 5);
|
||||
size_t pos = 1;
|
||||
size_t ret = 0, pos = 1;
|
||||
unsigned int bits = 8, tmp = inBuf[0];
|
||||
while (bits > 0 || pos < len)
|
||||
while (ret < outLen && (bits > 0 || pos < len))
|
||||
{
|
||||
if (bits < 5)
|
||||
{
|
||||
|
@ -284,9 +332,10 @@ namespace data
|
|||
|
||||
bits -= 5;
|
||||
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
|
||||
*
|
||||
|
@ -11,41 +11,26 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#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);
|
||||
#include <iostream>
|
||||
|
||||
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 * GetBase64SubstitutionTable ();
|
||||
constexpr bool IsBase64 (char ch)
|
||||
{
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '~';
|
||||
}
|
||||
bool IsBase64 (char ch);
|
||||
|
||||
size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen);
|
||||
std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len);
|
||||
constexpr bool IsBase32 (char ch)
|
||||
{
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');
|
||||
}
|
||||
size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen);
|
||||
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
||||
bool IsBase32 (char ch);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
auto d = std::div (input_size, 3);
|
||||
if (d.rem) d.quot++;
|
||||
return 4 * d.quot;
|
||||
}
|
||||
size_t Base64EncodingBufferSize(const size_t input_size);
|
||||
|
||||
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
|
||||
} // 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
|
||||
*
|
||||
|
@ -152,11 +152,11 @@ namespace data
|
|||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
LogPrint (eLogError, "Blinding: Malformed b33 ", b33);
|
||||
|
@ -198,7 +198,7 @@ namespace data
|
|||
std::string BlindedPublicKey::ToB33 () const
|
||||
{
|
||||
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;
|
||||
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
|
||||
addr[0] = flags; // flags
|
||||
|
@ -208,7 +208,8 @@ namespace data
|
|||
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ());
|
||||
// checksum is Little Endian
|
||||
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
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "Identity.h"
|
||||
|
||||
|
@ -24,7 +23,7 @@ namespace data
|
|||
public:
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -21,4 +21,20 @@
|
|||
# define IS_X86_64 0
|
||||
#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
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -117,14 +117,9 @@ namespace config {
|
|||
("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.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.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.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
|
||||
("httpproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of HTTP proxy stream in bytes/sec")
|
||||
("httpproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of HTTP proxy stream in bytes/sec")
|
||||
("httpproxy.i2p.streaming.profile", value<std::string>()->default_value("1"), "HTTP Proxy bandwidth usage profile. 1 - bulk(high), 2- interactive(low)")
|
||||
|
||||
;
|
||||
|
||||
options_description socksproxy("SOCKS Proxy options");
|
||||
|
@ -149,20 +144,6 @@ namespace config {
|
|||
("socksproxy.i2cp.leaseSetType", value<std::string>()->default_value("3"), "Local destination's LeaseSet type")
|
||||
("socksproxy.i2cp.leaseSetEncType", value<std::string>()->default_value("0,4"), "Local destination's LeaseSet encryption type")
|
||||
("socksproxy.i2cp.leaseSetPrivKey", value<std::string>()->default_value(""), "LeaseSet private key")
|
||||
("socksproxy.i2p.streaming.maxOutboundSpeed", value<std::string>()->default_value("1730000000"), "Max outbound speed of SOCKS proxy stream in bytes/sec")
|
||||
("socksproxy.i2p.streaming.maxInboundSpeed", value<std::string>()->default_value("1730000000"), "Max inbound speed of SOCKS proxy stream in bytes/sec")
|
||||
("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");
|
||||
|
@ -234,21 +215,22 @@ namespace config {
|
|||
"https://reseed2.i2p.net/,"
|
||||
"https://reseed.diva.exchange/,"
|
||||
"https://reseed-fr.i2pd.xyz/,"
|
||||
"https://reseed.memcpy.io/,"
|
||||
"https://reseed.onion.im/,"
|
||||
"https://i2pseed.creativecowpat.net:8443/,"
|
||||
"https://reseed.i2pgit.org/,"
|
||||
"https://coconut.incognet.io/,"
|
||||
"https://banana.incognet.io/,"
|
||||
"https://reseed-pl.i2pd.xyz/,"
|
||||
"https://www2.mk16.de/,"
|
||||
"https://i2p.ghativega.in/,"
|
||||
"https://i2p.novg.net/,"
|
||||
"https://reseed.stormycloud.org/,"
|
||||
"https://cubicchaos.net:8443/"
|
||||
"https://reseed.stormycloud.org/"
|
||||
), "Reseed URLs, separated by comma")
|
||||
("reseed.yggurls", value<std::string>()->default_value(
|
||||
"http://[324:71e:281a:9ed3::ace]:7070/,"
|
||||
"http://[301:65b9:c7cd:9a36::1]:18801/,"
|
||||
"http://[320:8936:ec1a:31f1::216]/,"
|
||||
"http://[306:3834:97b9:a00a::1]/,"
|
||||
"http://[316:f9e0:f22e:a74f::216]/"
|
||||
), "Reseed URLs through the Yggdrasil, separated by comma")
|
||||
;
|
||||
|
@ -305,8 +287,6 @@ namespace config {
|
|||
("ssu2.mtu4", value<uint16_t>()->default_value(0), "MTU for ipv4 address (default: detect)")
|
||||
("ssu2.mtu6", value<uint16_t>()->default_value(0), "MTU for ipv6 address (default: detect)")
|
||||
("ssu2.proxy", value<std::string>()->default_value(""), "Socks5 proxy URL for SSU2 transport")
|
||||
("ssu2.firewalled4", value<bool>()->default_value(false), "Set ipv4 network status to Firewalled even if OK (default: disabled)")
|
||||
("ssu2.firewalled6", value<bool>()->default_value(false), "Set ipv6 network status to Firewalled even if OK (default: disabled)")
|
||||
;
|
||||
|
||||
options_description nettime("Time sync options");
|
||||
|
@ -328,11 +308,11 @@ namespace config {
|
|||
("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.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.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");
|
||||
|
@ -354,7 +334,6 @@ namespace config {
|
|||
.add(httpserver)
|
||||
.add(httpproxy)
|
||||
.add(socksproxy)
|
||||
.add(shareddest)
|
||||
.add(sam)
|
||||
.add(bob)
|
||||
.add(i2cp)
|
||||
|
|
File diff suppressed because it is too large
Load diff
217
libi2pd/Crypto.h
217
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
|
||||
*
|
||||
|
@ -25,13 +25,16 @@
|
|||
|
||||
#include "Base.h"
|
||||
#include "Tag.h"
|
||||
#include "CPU.h"
|
||||
|
||||
// recognize openssl version and features
|
||||
#if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER != 0x030000000)) // 3.0.0, regression in SipHash, not implemented in LibreSSL
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
|
||||
# define OPENSSL_HKDF 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
|
||||
# define OPENSSL_SIPHASH 1
|
||||
#endif
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030500000) // 3.5.0
|
||||
# define OPENSSL_PQ 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
|
@ -41,11 +44,7 @@ namespace crypto
|
|||
bool bn2buf (const BIGNUM * bn, uint8_t * buf, size_t len);
|
||||
|
||||
// DSA
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
EVP_PKEY * CreateDSA (BIGNUM * pubKey = nullptr, BIGNUM * privKey = nullptr);
|
||||
#else
|
||||
DSA * CreateDSA ();
|
||||
#endif
|
||||
|
||||
// RSA
|
||||
const BIGNUM * GetRSAE ();
|
||||
|
@ -71,8 +70,13 @@ namespace crypto
|
|||
private:
|
||||
|
||||
uint8_t m_PublicKey[32];
|
||||
#if OPENSSL_X25519
|
||||
EVP_PKEY_CTX * m_Ctx;
|
||||
EVP_PKEY * m_Pkey;
|
||||
#else
|
||||
BN_CTX * m_Ctx;
|
||||
uint8_t m_PrivateKey[32];
|
||||
#endif
|
||||
bool m_IsElligatorIneligible = false; // true if definitely ineligible
|
||||
};
|
||||
|
||||
|
@ -87,70 +91,142 @@ namespace crypto
|
|||
void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub);
|
||||
|
||||
// AES
|
||||
typedef i2p::data::Tag<32> AESKey;
|
||||
|
||||
class ECBEncryption
|
||||
struct ChipherBlock
|
||||
{
|
||||
public:
|
||||
uint8_t buf[16];
|
||||
|
||||
ECBEncryption ();
|
||||
~ECBEncryption ();
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; };
|
||||
void Encrypt(const uint8_t * in, uint8_t * out);
|
||||
|
||||
private:
|
||||
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
void operator^=(const ChipherBlock& other) // XOR
|
||||
{
|
||||
if (!(((size_t)buf | (size_t)other.buf) & 0x03)) // multiple of 4 ?
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
reinterpret_cast<uint32_t *>(buf)[i] ^= reinterpret_cast<const uint32_t *>(other.buf)[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
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:
|
||||
|
||||
ECBDecryption ();
|
||||
~ECBDecryption ();
|
||||
AESAlignedBuffer ()
|
||||
{
|
||||
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; };
|
||||
void Decrypt (const uint8_t * in, uint8_t * out);
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
|
||||
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
|
||||
|
||||
private:
|
||||
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
||||
CBCEncryption ();
|
||||
~CBCEncryption ();
|
||||
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
|
||||
void Encrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
|
||||
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
|
||||
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_LastBlock, 16); };
|
||||
|
||||
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
void Encrypt (const uint8_t * in, uint8_t * out); // one block
|
||||
|
||||
ECBEncryption & ECB() { return m_ECBEncryption; }
|
||||
|
||||
private:
|
||||
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
AESAlignedBuffer<16> m_LastBlock;
|
||||
|
||||
ECBEncryption m_ECBEncryption;
|
||||
};
|
||||
|
||||
class CBCDecryption
|
||||
{
|
||||
public:
|
||||
|
||||
CBCDecryption ();
|
||||
~CBCDecryption ();
|
||||
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
|
||||
|
||||
void SetKey (const uint8_t * key) { m_Key = key; }; // 32 bytes
|
||||
void Decrypt (const uint8_t * in, size_t len, const uint8_t * iv, uint8_t * out);
|
||||
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
|
||||
void GetIV (uint8_t * iv) const { memcpy (iv, (const uint8_t *)m_IV, 16); };
|
||||
|
||||
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
void Decrypt (const uint8_t * in, uint8_t * out); // one block
|
||||
|
||||
ECBDecryption & ECB() { return m_ECBDecryption; }
|
||||
|
||||
private:
|
||||
|
||||
AESKey m_Key;
|
||||
EVP_CIPHER_CTX * m_Ctx;
|
||||
AESAlignedBuffer<16> m_IV;
|
||||
ECBDecryption m_ECBDecryption;
|
||||
};
|
||||
|
||||
class TunnelEncryption // with double IV encryption
|
||||
|
@ -190,58 +266,13 @@ namespace crypto
|
|||
};
|
||||
|
||||
// AEAD/ChaCha20/Poly1305
|
||||
bool AEADChaCha20Poly1305 (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len, bool encrypt); // msgLen is len without tag
|
||||
|
||||
class AEADChaCha20Poly1305Encryptor
|
||||
{
|
||||
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
|
||||
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
|
||||
|
||||
// ChaCha20
|
||||
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
|
||||
|
||||
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
|
||||
|
@ -251,16 +282,10 @@ namespace crypto
|
|||
struct NoiseSymmetricState
|
||||
{
|
||||
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 std::vector<std::pair<uint8_t *, size_t> >& bufs);
|
||||
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)
|
||||
|
@ -269,7 +294,7 @@ namespace crypto
|
|||
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
|
||||
|
||||
// init and terminate
|
||||
void InitCrypto (bool precomputation);
|
||||
void InitCrypto (bool precomputation, bool aesni, bool force);
|
||||
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
|
||||
*
|
||||
|
@ -181,21 +181,5 @@ namespace crypto
|
|||
k.GetPrivateKey (priv);
|
||||
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
|
||||
*
|
||||
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include "Crypto.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -158,50 +157,7 @@ namespace crypto
|
|||
X25519Keys m_StaticKeys;
|
||||
};
|
||||
|
||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); // including hybrid
|
||||
|
||||
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 256; // actual size is 32, but we use 256 for compatibility with old keys files
|
||||
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 ();
|
||||
};
|
||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -57,15 +57,6 @@ namespace datagram
|
|||
void DatagramDestination::SendDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
if (session->GetVersion () == eDatagramV3)
|
||||
{
|
||||
constexpr uint8_t flags[] = { 0x00, 0x03 }; // datagram3, no options
|
||||
auto msg = CreateDataMessage ({{m_Owner->GetIdentity ()->GetIdentHash (), 32},
|
||||
{flags, 2}, {payload, len}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM3, false); // datagram3
|
||||
session->SendMsg(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
|
@ -77,16 +68,15 @@ namespace datagram
|
|||
m_Owner->Sign (payload, len, m_Signature.data ());
|
||||
|
||||
auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}},
|
||||
fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM, !session->IsRatchets ()); // datagram1
|
||||
fromPort, toPort, false, !session->IsRatchets ()); // datagram
|
||||
session->SendMsg(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramDestination::SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
if (session)
|
||||
session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_RAW, !session->IsRatchets ())); // raw
|
||||
session->SendMsg(CreateDataMessage ({{payload, len}}, fromPort, toPort, true, !session->IsRatchets ())); // raw
|
||||
}
|
||||
|
||||
void DatagramDestination::FlushSendQueue (std::shared_ptr<DatagramSession> session)
|
||||
|
@ -114,7 +104,8 @@ namespace datagram
|
|||
|
||||
if (verified)
|
||||
{
|
||||
auto session = ObtainSession (identity.GetIdentHash());
|
||||
auto h = identity.GetIdentHash();
|
||||
auto session = ObtainSession(h);
|
||||
session->Ack();
|
||||
auto r = FindReceiver(toPort);
|
||||
if(r)
|
||||
|
@ -136,55 +127,6 @@ namespace datagram
|
|||
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram3 (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
{
|
||||
if (len < 34)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram3 is too short ", len);
|
||||
return;
|
||||
}
|
||||
if (from)
|
||||
{
|
||||
i2p::data::IdentHash ident(buf);
|
||||
auto ls = m_Owner->FindLeaseSet (ident);
|
||||
if (ls)
|
||||
{
|
||||
uint8_t staticKey[32];
|
||||
ls->Encrypt (nullptr, staticKey);
|
||||
if (!memcmp (from->GetRemoteStaticKey (), staticKey, 32))
|
||||
{
|
||||
auto session = ObtainSession (ident);
|
||||
session->SetVersion (eDatagramV3);
|
||||
session->SetRemoteLeaseSet (ls);
|
||||
session->Ack ();
|
||||
auto r = FindReceiver(toPort);
|
||||
if (r)
|
||||
{
|
||||
uint16_t flags = bufbe16toh (buf + 32);
|
||||
size_t offset = 34;
|
||||
if (flags & DATAGRAM3_FLAG_OPTIONS)
|
||||
offset += bufbe16toh (buf + offset) + 2;
|
||||
if (offset > len)
|
||||
{
|
||||
LogPrint (eLogWarning, "Datagram: datagram3 is too short ", len, " expected ", offset);
|
||||
return;
|
||||
}
|
||||
r(*ls->GetIdentity (), fromPort, toPort, buf + offset, len - offset);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram: no receiver for port ", toPort);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Datagram: Remote LeaseSet static key mismatch for datagram3 from ", ident.ToBase32 ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Datagram: No remote LeaseSet for ", ident.ToBase32 ());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Datagram: datagram3 received from non-ratchets session");
|
||||
}
|
||||
|
||||
void DatagramDestination::SetReceiver (const Receiver& receiver, uint16_t port)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
|
||||
|
@ -253,31 +195,17 @@ namespace datagram
|
|||
return r;
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort,
|
||||
const uint8_t * buf, size_t len, uint8_t protocolType, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw)
|
||||
{
|
||||
// unzip it
|
||||
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
|
||||
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
|
||||
if (uncompressedLen)
|
||||
{
|
||||
switch (protocolType)
|
||||
{
|
||||
case i2p::client::PROTOCOL_TYPE_RAW:
|
||||
if (isRaw)
|
||||
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
break;
|
||||
case i2p::client::PROTOCOL_TYPE_DATAGRAM3:
|
||||
HandleDatagram3 (fromPort, toPort, uncompressed, uncompressedLen, from);
|
||||
break;
|
||||
case i2p::client::PROTOCOL_TYPE_DATAGRAM:
|
||||
else
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
break;
|
||||
case i2p::client::PROTOCOL_TYPE_DATAGRAM2:
|
||||
// TODO:
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogInfo, "Datagram: unknown protocol type ", protocolType);
|
||||
};
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram: decompression failed");
|
||||
|
@ -285,7 +213,7 @@ namespace datagram
|
|||
|
||||
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (
|
||||
const std::vector<std::pair<const uint8_t *, size_t> >& payloads,
|
||||
uint16_t fromPort, uint16_t toPort, uint8_t protocolType, bool checksum)
|
||||
uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum)
|
||||
{
|
||||
size_t size;
|
||||
auto msg = m_I2NPMsgsPool.AcquireShared ();
|
||||
|
@ -302,7 +230,7 @@ namespace datagram
|
|||
htobe32buf (msg->GetPayload (), size); // length
|
||||
htobe16buf (buf + 4, fromPort); // source port
|
||||
htobe16buf (buf + 6, toPort); // destination port
|
||||
buf[9] = protocolType; // raw or datagram protocol
|
||||
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
|
||||
msg->len += size + 4;
|
||||
msg->FillI2NPMessageHeader (eI2NPData, 0, checksum);
|
||||
}
|
||||
|
@ -360,8 +288,9 @@ namespace datagram
|
|||
|
||||
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||
const i2p::data::IdentHash & remoteIdent) :
|
||||
m_LocalDestination(localDestination), m_RemoteIdent(remoteIdent),
|
||||
m_LastUse (0), m_LastFlush (0), m_RequestingLS (false), m_Version (eDatagramV1)
|
||||
m_LocalDestination(localDestination),
|
||||
m_RemoteIdent(remoteIdent),
|
||||
m_RequestingLS(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -381,12 +310,8 @@ namespace datagram
|
|||
if (msg || m_SendQueue.empty ())
|
||||
m_SendQueue.push_back(msg);
|
||||
// flush queue right away if full
|
||||
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE ||
|
||||
m_LastUse > m_LastFlush + DATAGRAM_MAX_FLUSH_INTERVAL)
|
||||
{
|
||||
if (!msg || m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE)
|
||||
FlushSendQueue();
|
||||
m_LastFlush = m_LastUse;
|
||||
}
|
||||
}
|
||||
|
||||
DatagramSession::Info DatagramSession::GetSessionInfo() const
|
||||
|
@ -419,7 +344,7 @@ namespace datagram
|
|||
if(path)
|
||||
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (IsRatchets ())
|
||||
SendMsg (nullptr); // send empty message in case if we don't have some data to send
|
||||
SendMsg (nullptr); // send empty message in case if we have some data to send
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
|
||||
|
@ -452,19 +377,15 @@ namespace datagram
|
|||
if (!found)
|
||||
{
|
||||
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 ())
|
||||
m_PendingRoutingSessions.push_back (m_RoutingSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto path = m_RoutingSession->GetSharedRoutingPath();
|
||||
if (path && m_RoutingSession->IsRatchets () && m_RoutingSession->CleanupUnconfirmedTags ())
|
||||
if (path && m_RoutingSession->IsRatchets () &&
|
||||
m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogDebug, "Datagram: path reset");
|
||||
m_RoutingSession->SetSharedRoutingPath (nullptr);
|
||||
path = nullptr;
|
||||
}
|
||||
|
@ -492,14 +413,7 @@ namespace datagram
|
|||
auto sz = ls.size();
|
||||
if (sz)
|
||||
{
|
||||
int idx = -1;
|
||||
if (m_LocalDestination)
|
||||
{
|
||||
auto pool = m_LocalDestination->GetTunnelPool ();
|
||||
if (pool)
|
||||
idx = pool->GetRng ()() % sz;
|
||||
}
|
||||
if (idx < 0) idx = rand () % sz;
|
||||
auto idx = rand() % sz;
|
||||
path->remoteLease = ls[idx];
|
||||
}
|
||||
else
|
||||
|
@ -525,14 +439,7 @@ namespace datagram
|
|||
auto sz = ls.size();
|
||||
if (sz)
|
||||
{
|
||||
int idx = -1;
|
||||
if (m_LocalDestination)
|
||||
{
|
||||
auto pool = m_LocalDestination->GetTunnelPool ();
|
||||
if (pool)
|
||||
idx = pool->GetRng ()() % sz;
|
||||
}
|
||||
if (idx < 0) idx = rand () % sz;
|
||||
auto idx = rand() % sz;
|
||||
path->remoteLease = ls[idx];
|
||||
}
|
||||
else
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -20,7 +20,6 @@
|
|||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Garlic.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -32,6 +31,8 @@ namespace datagram
|
|||
{
|
||||
// milliseconds for max session idle time
|
||||
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
|
||||
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
|
||||
// milliseconds before lease expire should we try switching leases
|
||||
|
@ -42,17 +43,6 @@ namespace datagram
|
|||
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
|
||||
// max 64 messages buffered in send queue for each datagram session
|
||||
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
|
||||
const uint64_t DATAGRAM_MAX_FLUSH_INTERVAL = 5; // in milliseconds
|
||||
const int DATAGRAM_SESSION_ACK_REQUEST_INTERVAL = 5500; // in milliseconds
|
||||
|
||||
enum DatagramVersion
|
||||
{
|
||||
eDatagramV1 = 1,
|
||||
eDatagramV2 = 2,
|
||||
eDatagramV3 = 3,
|
||||
};
|
||||
|
||||
constexpr uint16_t DATAGRAM3_FLAG_OPTIONS = 0x10;
|
||||
|
||||
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
|
||||
{
|
||||
|
@ -75,10 +65,6 @@ namespace datagram
|
|||
uint64_t LastActivity() const { return m_LastUse; }
|
||||
|
||||
bool IsRatchets () const { return m_RoutingSession && m_RoutingSession->IsRatchets (); }
|
||||
void SetRemoteLeaseSet (std::shared_ptr<const i2p::data::LeaseSet> ls) { m_RemoteLeaseSet = ls; }
|
||||
|
||||
DatagramVersion GetVersion () const { return m_Version; }
|
||||
void SetVersion (DatagramVersion version) { m_Version = version; }
|
||||
|
||||
struct Info
|
||||
{
|
||||
|
@ -112,9 +98,8 @@ namespace datagram
|
|||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
std::vector<std::shared_ptr<i2p::garlic::GarlicRoutingSession> > m_PendingRoutingSessions;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
uint64_t m_LastUse, m_LastFlush; // milliseconds
|
||||
uint64_t m_LastUse;
|
||||
bool m_RequestingLS;
|
||||
DatagramVersion m_Version;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<DatagramSession> DatagramSession_ptr;
|
||||
|
@ -139,8 +124,8 @@ namespace datagram
|
|||
void SendRawDatagram (std::shared_ptr<DatagramSession> session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
||||
void FlushSendQueue (std::shared_ptr<DatagramSession> session);
|
||||
|
||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort,
|
||||
const uint8_t * buf, size_t len, uint8_t protocolType, i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
|
||||
|
||||
|
||||
void SetReceiver (const Receiver& receiver, uint16_t port);
|
||||
void ResetReceiver (uint16_t port);
|
||||
|
@ -158,12 +143,10 @@ namespace datagram
|
|||
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const std::vector<std::pair<const uint8_t *, size_t> >& payloads,
|
||||
uint16_t fromPort, uint16_t toPort, uint8_t protocolType, bool checksum = true);
|
||||
uint16_t fromPort, uint16_t toPort, bool isRaw = false, bool checksum = true);
|
||||
|
||||
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
|
||||
void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
void HandleDatagram3 (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from);
|
||||
|
||||
Receiver FindReceiver(uint16_t port);
|
||||
RawReceiver FindRawReceiver(uint16_t port);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -13,7 +13,6 @@
|
|||
#include <vector>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "Log.h"
|
||||
#include "FS.h"
|
||||
#include "Timestamp.h"
|
||||
|
@ -24,7 +23,7 @@ namespace i2p
|
|||
{
|
||||
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):
|
||||
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
||||
|
@ -38,7 +37,6 @@ namespace client
|
|||
int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||
int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||
int numTags = DEFAULT_TAGS_TO_SEND;
|
||||
bool isHighBandwidth = true;
|
||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
||||
try
|
||||
{
|
||||
|
@ -94,7 +92,7 @@ namespace client
|
|||
it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET);
|
||||
if (it != params->end ())
|
||||
{
|
||||
// override isPublic
|
||||
// oveeride isPublic
|
||||
m_IsPublic = (it->second != "true");
|
||||
}
|
||||
it = params->find (I2CP_PARAM_LEASESET_TYPE);
|
||||
|
@ -123,9 +121,6 @@ namespace client
|
|||
m_LeaseSetPrivKey.reset (nullptr);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_STREAMING_PROFILE);
|
||||
if (it != params->end ())
|
||||
isHighBandwidth = std::stoi (it->second) != STREAMING_PROFILE_INTERACTIVE;
|
||||
}
|
||||
}
|
||||
catch (std::exception & ex)
|
||||
|
@ -133,7 +128,7 @@ namespace client
|
|||
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
|
||||
}
|
||||
SetNumTags (numTags);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar, isHighBandwidth);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar);
|
||||
if (explicitPeers)
|
||||
m_Pool->SetExplicitPeers (explicitPeers);
|
||||
if(params)
|
||||
|
@ -169,7 +164,7 @@ namespace client
|
|||
LoadTags ();
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
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,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
@ -196,7 +191,7 @@ namespace client
|
|||
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 = {
|
||||
{I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen},
|
||||
{I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen},
|
||||
|
@ -295,7 +290,7 @@ namespace client
|
|||
if (m_IsPublic)
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
boost::asio::post (m_Service, [s](void)
|
||||
m_Service.post ([s](void)
|
||||
{
|
||||
s->m_PublishVerificationTimer.cancel ();
|
||||
s->Publish ();
|
||||
|
@ -323,7 +318,7 @@ namespace client
|
|||
memcpy (data.k, key, 32);
|
||||
memcpy (data.t, tag, 32);
|
||||
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);
|
||||
});
|
||||
|
@ -340,7 +335,7 @@ namespace client
|
|||
memcpy (data.k, key, 32);
|
||||
data.t = tag;
|
||||
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);
|
||||
});
|
||||
|
@ -348,47 +343,28 @@ namespace client
|
|||
|
||||
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
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);
|
||||
});
|
||||
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
|
||||
}
|
||||
|
||||
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
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)
|
||||
{
|
||||
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
|
||||
uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET);
|
||||
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE,
|
||||
GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID, nullptr);
|
||||
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID);
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID)
|
||||
{
|
||||
switch (typeID)
|
||||
{
|
||||
case eI2NPData:
|
||||
HandleDataMessage (payload, len, from);
|
||||
HandleDataMessage (payload, len);
|
||||
break;
|
||||
case eI2NPDeliveryStatus:
|
||||
HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
|
||||
|
@ -398,7 +374,7 @@ namespace client
|
|||
m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET));
|
||||
break;
|
||||
case eI2NPDatabaseStore:
|
||||
HandleDatabaseStoreMessage (payload, len, from);
|
||||
HandleDatabaseStoreMessage (payload, len);
|
||||
break;
|
||||
case eI2NPDatabaseSearchReply:
|
||||
HandleDatabaseSearchReplyMessage (payload, len);
|
||||
|
@ -413,8 +389,7 @@ namespace client
|
|||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < DATABASE_STORE_HEADER_SIZE)
|
||||
{
|
||||
|
@ -450,7 +425,7 @@ namespace client
|
|||
leaseSet = it->second;
|
||||
if (leaseSet->IsNewer (buf + offset, len - offset))
|
||||
{
|
||||
leaseSet->Update (buf + offset, len - offset, shared_from_this(), true);
|
||||
leaseSet->Update (buf + offset, len - offset);
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
|
||||
else
|
||||
|
@ -469,29 +444,13 @@ namespace client
|
|||
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
|
||||
else
|
||||
{
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET],
|
||||
buf + offset, len - offset, true, shared_from_this (),
|
||||
from ? from->GetRemoteStaticKeyType () : GetPreferredCryptoType () ); // LeaseSet2
|
||||
if (from)
|
||||
{
|
||||
uint8_t pub[32];
|
||||
leaseSet->Encrypt (nullptr, pub);
|
||||
if (memcmp (from->GetRemoteStaticKey (), pub, 32))
|
||||
{
|
||||
LogPrint (eLogError, "Destination: Remote LeaseSet static key mismatch");
|
||||
leaseSet = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (leaseSet && leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key && !leaseSet->IsExpired ())
|
||||
{
|
||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
||||
{
|
||||
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
|
||||
m_RemoteLeaseSets.insert_or_assign (key, leaseSet);
|
||||
if (from)
|
||||
from->SetDestination (key);
|
||||
m_RemoteLeaseSets[key] = leaseSet;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
|
||||
|
@ -514,8 +473,7 @@ namespace client
|
|||
if (request->requestedBlindedKey)
|
||||
{
|
||||
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset,
|
||||
request->requestedBlindedKey, shared_from_this (),
|
||||
m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr, GetPreferredCryptoType ());
|
||||
request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ());
|
||||
if (ls2->IsValid () && !ls2->IsExpired ())
|
||||
{
|
||||
leaseSet = ls2;
|
||||
|
@ -618,8 +576,7 @@ namespace client
|
|||
m_ExcludedFloodfills.clear ();
|
||||
m_PublishReplyToken = 0;
|
||||
// schedule verification
|
||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT +
|
||||
(m_Pool ? m_Pool->GetRng ()() % PUBLISH_VERIFICATION_TIMEOUT_VARIANCE : 0)));
|
||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
|
||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
@ -627,11 +584,8 @@ namespace client
|
|||
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 ();
|
||||
}
|
||||
|
||||
|
@ -698,8 +652,8 @@ namespace client
|
|||
m_ExcludedFloodfills.clear ();
|
||||
m_PublishReplyToken = 1; // dummy non-zero value
|
||||
// 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");
|
||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::milliseconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
||||
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::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
return;
|
||||
|
@ -712,13 +666,13 @@ namespace client
|
|||
auto s = shared_from_this ();
|
||||
msg->onDrop = [s]()
|
||||
{
|
||||
boost::asio::post (s->GetService (), [s]()
|
||||
s->GetService ().post([s]()
|
||||
{
|
||||
s->m_PublishConfirmationTimer.cancel ();
|
||||
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,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
outbound->SendTunnelDataMsgTo (floodfill->GetIdentHash (), 0, msg);
|
||||
|
@ -731,10 +685,23 @@ namespace client
|
|||
{
|
||||
if (m_PublishReplyToken)
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " milliseconds or failed. will try again");
|
||||
m_PublishReplyToken = 0;
|
||||
if (GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds or failed. will try again");
|
||||
Publish ();
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
// assume it successive and try to verify
|
||||
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
|
||||
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -784,10 +751,10 @@ namespace client
|
|||
if (!m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete)
|
||||
boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
|
||||
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -796,7 +763,7 @@ namespace client
|
|||
if (!dest || !m_Pool || !IsReady ())
|
||||
{
|
||||
if (requestComplete)
|
||||
boost::asio::post (m_Service, [requestComplete](void){requestComplete (nullptr);});
|
||||
m_Service.post ([requestComplete](void){requestComplete (nullptr);});
|
||||
return false;
|
||||
}
|
||||
auto storeHash = dest->GetStoreHash ();
|
||||
|
@ -804,17 +771,17 @@ namespace client
|
|||
if (leaseSet)
|
||||
{
|
||||
if (requestComplete)
|
||||
boost::asio::post (m_Service, [requestComplete, leaseSet](void){requestComplete (leaseSet);});
|
||||
m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);});
|
||||
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;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify)
|
||||
{
|
||||
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);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
|
@ -842,7 +809,7 @@ namespace client
|
|||
request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
|
||||
if (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));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
|
@ -912,7 +879,7 @@ namespace client
|
|||
auto s = shared_from_this ();
|
||||
msg->onDrop = [s, dest, request]()
|
||||
{
|
||||
boost::asio::post (s->GetService (), [s, dest, request]()
|
||||
s->GetService ().post([s, dest, request]()
|
||||
{
|
||||
s->SendNextLeaseSetRequest (dest, request);
|
||||
});
|
||||
|
@ -925,7 +892,7 @@ namespace client
|
|||
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,
|
||||
shared_from_this (), std::placeholders::_1, dest));
|
||||
}
|
||||
|
@ -942,7 +909,7 @@ namespace client
|
|||
if (it != m_LeaseSetRequests.end ())
|
||||
{
|
||||
bool done = false;
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||
{
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
||||
|
@ -979,8 +946,7 @@ namespace client
|
|||
CleanupExpiredTags ();
|
||||
CleanupRemoteLeaseSets ();
|
||||
CleanupDestination ();
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::seconds (DESTINATION_CLEANUP_TIMEOUT +
|
||||
(m_Pool ? m_Pool->GetRng ()() % DESTINATION_CLEANUP_TIMEOUT_VARIANCE : 0)));
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
@ -994,7 +960,7 @@ namespace client
|
|||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
@ -1002,15 +968,20 @@ 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):
|
||||
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_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED),
|
||||
m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS),
|
||||
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0),
|
||||
m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0),
|
||||
m_DatagramDestination (nullptr), m_RefCounter (0),
|
||||
m_ReadyChecker(service)
|
||||
{
|
||||
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
|
@ -1030,15 +1001,7 @@ namespace client
|
|||
{
|
||||
try
|
||||
{
|
||||
i2p::data::CryptoKeyType cryptoType = 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);
|
||||
}
|
||||
encryptionKeyTypes.insert (std::stoi(it1));
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
|
@ -1049,21 +1012,29 @@ namespace client
|
|||
}
|
||||
}
|
||||
// if no param or valid crypto type use from identity
|
||||
bool isSingleKey = false;
|
||||
if (encryptionKeyTypes.empty ())
|
||||
encryptionKeyTypes.insert ( { GetIdentity ()->GetCryptoKeyType (),
|
||||
i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD }); // usually 0,4
|
||||
{
|
||||
isSingleKey = true;
|
||||
encryptionKeyTypes.insert (GetIdentity ()->GetCryptoKeyType ());
|
||||
}
|
||||
|
||||
for (auto& it: encryptionKeyTypes)
|
||||
{
|
||||
auto encryptionKey = std::make_shared<i2p::crypto::LocalEncryptionKey> (it);
|
||||
auto encryptionKey = new EncryptionKey (it);
|
||||
if (IsPublic ())
|
||||
PersistTemporaryKeys (encryptionKey);
|
||||
PersistTemporaryKeys (encryptionKey, isSingleKey);
|
||||
else
|
||||
encryptionKey->GenerateKeys ();
|
||||
encryptionKey->CreateDecryptor ();
|
||||
if (it > i2p::data::CRYPTO_KEY_TYPE_ELGAMAL && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Only DSA can use LeaseSet1
|
||||
m_EncryptionKeys.emplace (it, encryptionKey);
|
||||
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
{
|
||||
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 ())
|
||||
|
@ -1080,11 +1051,6 @@ namespace client
|
|||
it = params->find (I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED);
|
||||
if (it != params->end ())
|
||||
m_StreamingOutboundSpeed = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED);
|
||||
if (it != params->end ())
|
||||
m_StreamingInboundSpeed = std::stoi(it->second);
|
||||
if (it != params->end ())
|
||||
m_StreamingMaxConcurrentStreams = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
||||
if (it != params->end ())
|
||||
m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true
|
||||
|
@ -1135,6 +1101,7 @@ namespace client
|
|||
void ClientDestination::Stop ()
|
||||
{
|
||||
LogPrint(eLogDebug, "Destination: Stopping destination ", GetIdentHash().ToBase32(), ".b32.i2p");
|
||||
LeaseSetDestination::Stop ();
|
||||
m_ReadyChecker.cancel();
|
||||
LogPrint(eLogDebug, "Destination: -> Stopping Streaming Destination");
|
||||
m_StreamingDestination->Stop ();
|
||||
|
@ -1156,12 +1123,10 @@ namespace client
|
|||
delete m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
}
|
||||
LeaseSetDestination::Stop ();
|
||||
LogPrint(eLogDebug, "Destination: -> Stopping done");
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len,
|
||||
i2p::garlic::ECIESX25519AEADRatchetSession * from)
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
if(length > len - 4)
|
||||
|
@ -1186,21 +1151,25 @@ namespace client
|
|||
m_LastPort = toPort;
|
||||
}
|
||||
if (m_LastStreamingDestination)
|
||||
m_LastStreamingDestination->HandleDataMessagePayload (buf, length, from);
|
||||
m_LastStreamingDestination->HandleDataMessagePayload (buf, length);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing streaming destination");
|
||||
}
|
||||
break;
|
||||
case PROTOCOL_TYPE_DATAGRAM:
|
||||
case PROTOCOL_TYPE_RAW:
|
||||
case PROTOCOL_TYPE_DATAGRAM2:
|
||||
case PROTOCOL_TYPE_DATAGRAM3:
|
||||
// datagram protocol
|
||||
if (m_DatagramDestination)
|
||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, buf[9], from);
|
||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing datagram destination");
|
||||
break;
|
||||
case PROTOCOL_TYPE_RAW:
|
||||
// raw datagram
|
||||
if (m_DatagramDestination)
|
||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, true);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing raw datagram destination");
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Destination: Data: Unexpected protocol ", buf[9]);
|
||||
}
|
||||
|
@ -1217,7 +1186,7 @@ namespace client
|
|||
if (leaseSet)
|
||||
{
|
||||
auto stream = CreateStream (leaseSet, port);
|
||||
boost::asio::post (GetService (), [streamRequestComplete, stream]()
|
||||
GetService ().post ([streamRequestComplete, stream]()
|
||||
{
|
||||
streamRequestComplete(stream);
|
||||
});
|
||||
|
@ -1410,56 +1379,32 @@ namespace client
|
|||
return ret;
|
||||
}
|
||||
|
||||
void ClientDestination::PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys)
|
||||
void ClientDestination::PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey)
|
||||
{
|
||||
if (!keys) return;
|
||||
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",
|
||||
isSingleKey ? (ident + ".dat") : (ident + "." + std::to_string (keys->keyType) + ".dat"));
|
||||
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)
|
||||
{
|
||||
char pub[256], 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)
|
||||
if (f) {
|
||||
f.read ((char *)keys->pub, 256);
|
||||
f.read ((char *)keys->priv, 256);
|
||||
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");
|
||||
memset (keys->priv.data (), 0, keys->priv.size ());
|
||||
memset (keys->pub.data (), 0, keys->pub.size ());
|
||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
|
||||
memset (keys->priv, 0, 256);
|
||||
memset (keys->pub, 0, 256);
|
||||
keys->GenerateKeys ();
|
||||
|
||||
// TODO:: persist crypto key type
|
||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
||||
if (f1)
|
||||
{
|
||||
f1.write ((char *)keys->pub.data (), keys->pub.size ());
|
||||
f1.write ((char *)keys->priv.data (), keys->priv.size ());
|
||||
if (f1) {
|
||||
f1.write ((char *)keys->pub, 256);
|
||||
f1.write ((char *)keys->priv, 256);
|
||||
return;
|
||||
}
|
||||
if (!f1)
|
||||
LogPrint(eLogError, "Destination: Can't save keys to ", path);
|
||||
LogPrint(eLogCritical, "Destinations: Can't save keys to ", path);
|
||||
}
|
||||
|
||||
void ClientDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
|
||||
|
@ -1467,10 +1412,9 @@ namespace client
|
|||
std::shared_ptr<i2p::data::LocalLeaseSet> leaseSet;
|
||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
{
|
||||
auto it = m_EncryptionKeys.find (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
if (it != m_EncryptionKeys.end ())
|
||||
if (m_StandardEncryptionKey)
|
||||
{
|
||||
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 (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ());
|
||||
}
|
||||
|
@ -1480,40 +1424,18 @@ namespace client
|
|||
else
|
||||
{
|
||||
// standard LS2 (type 3) first
|
||||
if (m_EncryptionKeys.empty ())
|
||||
{
|
||||
LogPrint (eLogError, "Destinations: No encryption keys");
|
||||
return;
|
||||
}
|
||||
i2p::data::LocalLeaseSet2::KeySections keySections;
|
||||
if (m_ECIESx25519EncryptionKey)
|
||||
keySections.push_back ({m_ECIESx25519EncryptionKey->keyType, 32, m_ECIESx25519EncryptionKey->pub} );
|
||||
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 ();
|
||||
if (publishedTimestamp <= m_LastPublishedTimestamp)
|
||||
{
|
||||
LogPrint (eLogDebug, "Destination: LeaseSet update at the same second");
|
||||
publishedTimestamp++; // force newer timestamp
|
||||
}
|
||||
bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
|
||||
auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
|
||||
m_Keys, keySections, tunnels, IsPublic (), publishedTimestamp, isPublishedEncrypted);
|
||||
m_Keys, keySections, tunnels, IsPublic (), isPublishedEncrypted);
|
||||
if (isPublishedEncrypted) // encrypt if type 5
|
||||
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
|
||||
leaseSet = ls2;
|
||||
m_LastPublishedTimestamp = publishedTimestamp;
|
||||
}
|
||||
SetLeaseSet (leaseSet);
|
||||
}
|
||||
|
@ -1525,22 +1447,11 @@ namespace client
|
|||
|
||||
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const
|
||||
{
|
||||
std::shared_ptr<i2p::crypto::LocalEncryptionKey> encryptionKey;
|
||||
if (!m_EncryptionKeys.empty ())
|
||||
{
|
||||
if (m_EncryptionKeys.rbegin ()->first == preferredCrypto)
|
||||
encryptionKey = m_EncryptionKeys.rbegin ()->second;
|
||||
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);
|
||||
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor)
|
||||
return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data);
|
||||
if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor)
|
||||
return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data);
|
||||
else
|
||||
LogPrint (eLogError, "Destinations: Decryptor is not set");
|
||||
return false;
|
||||
|
@ -1548,26 +1459,14 @@ namespace client
|
|||
|
||||
bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
|
||||
{
|
||||
#if __cplusplus >= 202002L // C++20
|
||||
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;
|
||||
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey;
|
||||
}
|
||||
|
||||
const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
|
||||
{
|
||||
auto it = m_EncryptionKeys.find (keyType);
|
||||
if (it != m_EncryptionKeys.end ())
|
||||
return it->second->pub.data ();
|
||||
return nullptr;
|
||||
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr;
|
||||
return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr;
|
||||
}
|
||||
|
||||
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
|
||||
|
@ -1601,8 +1500,6 @@ namespace client
|
|||
RunnableService ("Destination"),
|
||||
ClientDestination (GetIOService (), keys, isPublic, params)
|
||||
{
|
||||
if (!GetNickname ().empty ())
|
||||
RunnableService::SetName (GetNickname ());
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -22,7 +22,6 @@
|
|||
#include "Identity.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Crypto.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.hpp"
|
||||
|
@ -37,17 +36,13 @@ namespace client
|
|||
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
|
||||
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
|
||||
const uint8_t PROTOCOL_TYPE_RAW = 18;
|
||||
const uint8_t PROTOCOL_TYPE_DATAGRAM2 = 19;
|
||||
const uint8_t PROTOCOL_TYPE_DATAGRAM3 = 20;
|
||||
const int PUBLISH_CONFIRMATION_TIMEOUT = 1800; // in milliseconds
|
||||
const int PUBLISH_VERIFICATION_TIMEOUT = 5; // in seconds after successful publish
|
||||
const int PUBLISH_VERIFICATION_TIMEOUT_VARIANCE = 3; // in seconds
|
||||
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
|
||||
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successful publish
|
||||
const int PUBLISH_MIN_INTERVAL = 20; // in seconds
|
||||
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
|
||||
const int LEASESET_REQUEST_TIMEOUT = 1600; // in milliseconds
|
||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 12000; // in milliseconds
|
||||
const int DESTINATION_CLEANUP_TIMEOUT = 44; // in seconds
|
||||
const int DESTINATION_CLEANUP_TIMEOUT_VARIANCE = 30; // in seconds
|
||||
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
|
||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
|
||||
const int DESTINATION_CLEANUP_TIMEOUT = 3; // in minutes
|
||||
const unsigned int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
||||
|
||||
// I2CP
|
||||
|
@ -91,16 +86,8 @@ namespace client
|
|||
const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds
|
||||
const char I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED[] = "i2p.streaming.maxOutboundSpeed"; // bytes/sec
|
||||
const int DEFAULT_MAX_OUTBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
|
||||
const char I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED[] = "i2p.streaming.maxInboundSpeed"; // bytes/sec
|
||||
const int DEFAULT_MAX_INBOUND_SPEED = 1730000000; // no more than 1.73 Gbytes/s
|
||||
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
|
||||
const int DEFAULT_ANSWER_PINGS = true;
|
||||
const char I2CP_PARAM_STREAMING_PROFILE[] = "i2p.streaming.profile";
|
||||
const int STREAMING_PROFILE_BULK = 1; // high bandwidth
|
||||
const int STREAMING_PROFILE_INTERACTIVE = 2; // low bandwidth
|
||||
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;
|
||||
|
||||
|
@ -111,7 +98,7 @@ namespace client
|
|||
// leaseSet = nullptr means not found
|
||||
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;
|
||||
uint64_t requestTime;
|
||||
boost::asio::deadline_timer requestTimeoutTimer;
|
||||
|
@ -129,10 +116,10 @@ namespace client
|
|||
|
||||
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 ();
|
||||
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 Stop ();
|
||||
|
@ -149,15 +136,15 @@ namespace client
|
|||
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override;
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override { return m_Pool; }
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
||||
|
||||
// override GarlicDestination
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag) override;
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void SetLeaseSetUpdated (bool post) override;
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
void SetPublic (bool pub) { m_IsPublic = pub; };
|
||||
|
@ -165,18 +152,16 @@ namespace client
|
|||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
|
||||
|
||||
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
|
||||
int GetLeaseSetType () const { return m_LeaseSetType; };
|
||||
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
|
||||
int GetAuthType () const { return m_AuthType; };
|
||||
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
||||
virtual i2p::data::CryptoKeyType GetPreferredCryptoType () const = 0;
|
||||
// I2CP
|
||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from) = 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;
|
||||
|
||||
private:
|
||||
|
@ -187,7 +172,7 @@ namespace client
|
|||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
||||
void HandlePublishVerificationTimer (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 HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
|
@ -197,17 +182,15 @@ namespace client
|
|||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||
void CleanupRemoteLeaseSets ();
|
||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const;
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_context& m_Service;
|
||||
boost::asio::io_service& m_Service;
|
||||
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<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::mutex m_LeaseSetMutex;
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||
|
@ -233,14 +216,25 @@ namespace client
|
|||
|
||||
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:
|
||||
|
||||
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);
|
||||
~ClientDestination ();
|
||||
|
||||
void Start () override;
|
||||
void Stop () override;
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
|
@ -268,8 +262,6 @@ namespace client
|
|||
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
||||
int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
|
||||
int GetStreamingOutboundSpeed () const { return m_StreamingOutboundSpeed; }
|
||||
int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; }
|
||||
int GetStreamingMaxConcurrentStreams () const { return m_StreamingMaxConcurrentStreams; }
|
||||
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
|
||||
|
||||
// datagram
|
||||
|
@ -277,28 +269,24 @@ namespace client
|
|||
i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true);
|
||||
|
||||
// implements LocalDestination
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override { return m_Keys.GetPublic (); };
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) 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 { return m_Keys.GetPublic (); };
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const;
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const;
|
||||
|
||||
protected:
|
||||
|
||||
// GarlicDestionation
|
||||
i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override;
|
||||
// LeaseSetDestination
|
||||
void CleanupDestination () override;
|
||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const override { return m_PreferredCryptoType; }
|
||||
void CleanupDestination ();
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len, i2p::garlic::ECIESX25519AEADRatchetSession * from) override;
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels) override;
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis () {
|
||||
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
|
||||
}
|
||||
void PersistTemporaryKeys (std::shared_ptr<i2p::crypto::LocalEncryptionKey> keys);
|
||||
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey);
|
||||
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
|
||||
|
||||
template<typename Dest>
|
||||
|
@ -307,17 +295,17 @@ namespace client
|
|||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
std::map<i2p::data::CryptoKeyType, std::shared_ptr<i2p::crypto::LocalEncryptionKey> > m_EncryptionKeys; // last is most preferable
|
||||
i2p::data::CryptoKeyType m_PreferredCryptoType;
|
||||
std::unique_ptr<EncryptionKey> m_StandardEncryptionKey;
|
||||
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
|
||||
|
||||
int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams;
|
||||
int m_StreamingAckDelay;
|
||||
int m_StreamingOutboundSpeed;
|
||||
bool m_IsStreamingAnswerPings;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels
|
||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
||||
int m_RefCounter; // how many clients(tunnels) use this destination
|
||||
uint64_t m_LastPublishedTimestamp;
|
||||
|
||||
boost::asio::deadline_timer m_ReadyChecker;
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -11,7 +11,6 @@
|
|||
#include "Log.h"
|
||||
#include "util.h"
|
||||
#include "Crypto.h"
|
||||
#include "PostQuantum.h"
|
||||
#include "Elligator.h"
|
||||
#include "Tag.h"
|
||||
#include "I2PEndian.h"
|
||||
|
@ -95,17 +94,6 @@ namespace garlic
|
|||
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 ()
|
||||
{
|
||||
if (!m_ExpirationTimestamp)
|
||||
|
@ -174,12 +162,12 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
if (m_Destination)
|
||||
m_Destination->HandleECIESx25519GarlicClove (buf + offset, size, nullptr);
|
||||
m_Destination->HandleECIESx25519GarlicClove (buf + offset, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS):
|
||||
GarlicRoutingSession (owner, true), m_RemoteStaticKeyType (0)
|
||||
GarlicRoutingSession (owner, true)
|
||||
{
|
||||
if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
|
||||
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
|
||||
|
@ -241,42 +229,12 @@ namespace garlic
|
|||
tagsetNsr->NextSessionTagRatchet ();
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::MessageConfirmed (uint32_t msgID)
|
||||
{
|
||||
auto ret = GarlicRoutingSession::MessageConfirmed (msgID); // LeaseSet
|
||||
if (m_AckRequestMsgID && m_AckRequestMsgID == msgID)
|
||||
{
|
||||
m_AckRequestMsgID = 0;
|
||||
m_AckRequestNumAttempts = 0;
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::CleanupUnconfirmedTags ()
|
||||
{
|
||||
if (m_AckRequestMsgID && m_AckRequestNumAttempts > ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS)
|
||||
{
|
||||
m_AckRequestMsgID = 0;
|
||||
m_AckRequestNumAttempts = 0;
|
||||
return true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (!GetOwner ()) return false;
|
||||
// we are Bob
|
||||
// KDF1
|
||||
i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
|
||||
|
||||
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
|
||||
{
|
||||
|
@ -284,61 +242,20 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
buf += 32; len -= 32;
|
||||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
||||
|
||||
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)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key");
|
||||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: No supported encryption type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// decrypt flags/static
|
||||
uint8_t fs[32];
|
||||
if (!Decrypt (buf, fs, 32))
|
||||
uint8_t nonce[12], fs[32];
|
||||
CreateNonce (0, nonce);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
|
||||
return false;
|
||||
|
@ -351,18 +268,20 @@ namespace garlic
|
|||
if (isStatic)
|
||||
{
|
||||
// static key, fs is apk
|
||||
SetRemoteStaticKey (cryptoType, fs);
|
||||
if (!GetOwner ()->Decrypt (fs, sharedSecret, m_RemoteStaticKeyType)) // x25519(bsk, apk)
|
||||
memcpy (m_RemoteStaticKey, fs, 32);
|
||||
if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
||||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
}
|
||||
else // all zeros flags
|
||||
CreateNonce (1, nonce);
|
||||
|
||||
// decrypt payload
|
||||
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");
|
||||
return false;
|
||||
|
@ -398,7 +317,7 @@ namespace garlic
|
|||
{
|
||||
case eECIESx25519BlkGalicClove:
|
||||
if (GetOwner ())
|
||||
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size, this);
|
||||
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size);
|
||||
break;
|
||||
case eECIESx25519BlkNextKey:
|
||||
LogPrint (eLogDebug, "Garlic: Next key");
|
||||
|
@ -416,7 +335,7 @@ namespace garlic
|
|||
{
|
||||
uint32_t tagsetid = bufbe16toh (buf + offset1); offset1 += 2; // tagsetid
|
||||
uint16_t n = bufbe16toh (buf + offset1); offset1 += 2; // N
|
||||
MessageConfirmed ((tagsetid << 16) + n); // msgid = (tagsetid << 16) + N
|
||||
MessageConfirmed ((tagsetid << 16) + n); // msgid
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -479,6 +398,7 @@ namespace garlic
|
|||
{
|
||||
uint16_t keyID = bufbe16toh (buf); buf += 2; // keyID
|
||||
bool newKey = flag & ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG;
|
||||
m_SendReverseKey = true;
|
||||
if (!m_NextReceiveRatchet)
|
||||
m_NextReceiveRatchet.reset (new DHRatchet ());
|
||||
else
|
||||
|
@ -490,14 +410,15 @@ namespace garlic
|
|||
}
|
||||
m_NextReceiveRatchet->keyID = keyID;
|
||||
}
|
||||
int tagsetID = 2*keyID;
|
||||
if (newKey)
|
||||
{
|
||||
m_NextReceiveRatchet->key = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||
m_NextReceiveRatchet->newKey = true;
|
||||
tagsetID++;
|
||||
}
|
||||
else
|
||||
m_NextReceiveRatchet->newKey = false;
|
||||
auto tagsetID = m_NextReceiveRatchet->GetReceiveTagSetID ();
|
||||
if (flag & ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG)
|
||||
memcpy (m_NextReceiveRatchet->remote, buf, 32);
|
||||
|
||||
|
@ -511,9 +432,7 @@ namespace garlic
|
|||
GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ?
|
||||
GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS);
|
||||
receiveTagset->Expire ();
|
||||
|
||||
LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created");
|
||||
m_SendReverseKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -550,15 +469,6 @@ namespace garlic
|
|||
offset += 32;
|
||||
|
||||
// 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
|
||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
|
||||
uint8_t sharedSecret[32];
|
||||
|
@ -568,32 +478,18 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
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
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
const uint8_t * fs;
|
||||
if (isStatic)
|
||||
fs = GetOwner ()->GetEncryptionPublicKey (m_RemoteStaticKeyType);
|
||||
fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
|
||||
else
|
||||
{
|
||||
memset (out + offset, 0, 32); // all zeros flags section
|
||||
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 ");
|
||||
return false;
|
||||
|
@ -604,11 +500,13 @@ namespace garlic
|
|||
// KDF2
|
||||
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);
|
||||
}
|
||||
else
|
||||
CreateNonce (1, nonce);
|
||||
// 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");
|
||||
return false;
|
||||
|
@ -657,33 +555,16 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Incorrect Alice static key");
|
||||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
// 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");
|
||||
return false;
|
||||
|
@ -704,7 +585,6 @@ namespace garlic
|
|||
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)
|
||||
// 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
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed");
|
||||
|
@ -726,34 +606,16 @@ namespace garlic
|
|||
memcpy (m_H, m_NSRH, 32);
|
||||
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
||||
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
|
||||
m_N = 0;
|
||||
size_t offset = 40;
|
||||
#if OPENSSL_PQ
|
||||
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)
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
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)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
|
||||
MixHash (out + 40, 16); // h = SHA256(h || ciphertext)
|
||||
// encrypt payload
|
||||
uint8_t nonce[12]; memset (nonce, 0, 12);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset + 16, len + 16, true)) // encrypt
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed");
|
||||
return false;
|
||||
|
@ -785,30 +647,13 @@ namespace garlic
|
|||
return false;
|
||||
}
|
||||
MixKey (sharedSecret);
|
||||
#if OPENSSL_PQ
|
||||
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)
|
||||
GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
|
||||
MixKey (sharedSecret);
|
||||
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
// 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");
|
||||
return false;
|
||||
|
@ -833,7 +678,6 @@ namespace garlic
|
|||
}
|
||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
||||
// 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
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
||||
|
@ -843,8 +687,7 @@ namespace garlic
|
|||
if (m_State == eSessionStateNewSessionSent)
|
||||
{
|
||||
m_State = eSessionStateEstablished;
|
||||
// don't delete m_EpehemralKey and m_PQKeys because delayd NSR's migth come
|
||||
// done in CleanupReceiveNSRKeys called from NSR tagset destructor
|
||||
//m_EphemeralKeys = nullptr; // TODO: delete after a while
|
||||
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
||||
}
|
||||
|
@ -859,8 +702,6 @@ namespace garlic
|
|||
|
||||
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];
|
||||
auto index = m_SendTagset->GetNextIndex ();
|
||||
CreateNonce (index, nonce); // tag's index
|
||||
|
@ -868,7 +709,8 @@ namespace garlic
|
|||
if (!tag)
|
||||
{
|
||||
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;
|
||||
}
|
||||
memcpy (out, &tag, 8);
|
||||
|
@ -876,7 +718,7 @@ namespace garlic
|
|||
// ciphertext = ENCRYPT(k, n, payload, ad)
|
||||
uint8_t key[32];
|
||||
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");
|
||||
return false;
|
||||
|
@ -895,27 +737,24 @@ namespace garlic
|
|||
uint8_t * payload = buf + 8;
|
||||
uint8_t key[32];
|
||||
receiveTagset->GetSymmKey (index, key);
|
||||
auto owner = GetOwner ();
|
||||
if (!owner) return true; // drop message
|
||||
|
||||
if (!owner->AEADChaCha20Poly1305Decrypt (payload, len - 16, buf, 8, key, nonce, payload, len - 16))
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 16, buf, 8, key, nonce, payload, len - 16, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
||||
return false;
|
||||
}
|
||||
HandlePayload (payload, len - 16, receiveTagset, index);
|
||||
|
||||
int moreTags = 0;
|
||||
if (owner->GetNumRatchetInboundTags () > 0) // override in settings?
|
||||
if (GetOwner ())
|
||||
{
|
||||
if (receiveTagset->GetNextIndex () - index < owner->GetNumRatchetInboundTags ()/2)
|
||||
moreTags = owner->GetNumRatchetInboundTags ();
|
||||
index -= owner->GetNumRatchetInboundTags (); // trim behind
|
||||
int moreTags = 0;
|
||||
if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings?
|
||||
{
|
||||
if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2)
|
||||
moreTags = GetOwner ()->GetNumRatchetInboundTags ();
|
||||
index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind
|
||||
}
|
||||
else
|
||||
{
|
||||
moreTags = (receiveTagset->GetTagSetID () > 0) ? ECIESX25519_MAX_NUM_GENERATED_TAGS : // for non first tagset
|
||||
(ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 1)); // N/2
|
||||
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4
|
||||
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
|
||||
moreTags -= (receiveTagset->GetNextIndex () - index);
|
||||
index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind
|
||||
|
@ -924,6 +763,7 @@ namespace garlic
|
|||
GenerateMoreReceiveTags (receiveTagset, moreTags);
|
||||
if (index > 0)
|
||||
receiveTagset->SetTrimBehind (index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -937,14 +777,10 @@ namespace garlic
|
|||
m_State = eSessionStateEstablished;
|
||||
m_NSRSendTagset = nullptr;
|
||||
m_EphemeralKeys = nullptr;
|
||||
#if OPENSSL_PQ
|
||||
m_PQKeys = nullptr;
|
||||
m_NSREncodedPQKey = nullptr;
|
||||
#endif
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
case eSessionStateEstablished:
|
||||
if (m_SendReverseKey && receiveTagset->GetTagSetID () == m_NextReceiveRatchet->GetReceiveTagSetID ())
|
||||
m_SendReverseKey = false; // tag received on new tagset
|
||||
if (receiveTagset->IsNS ())
|
||||
{
|
||||
// our of sequence NSR
|
||||
|
@ -971,12 +807,7 @@ namespace garlic
|
|||
if (!payload) return nullptr;
|
||||
size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload);
|
||||
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
|
||||
#endif
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
||||
|
||||
|
@ -991,28 +822,16 @@ namespace garlic
|
|||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
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;
|
||||
case eSessionStateNewSessionReceived:
|
||||
if (!NewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
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;
|
||||
case eSessionStateNewSessionReplySent:
|
||||
if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen))
|
||||
return nullptr;
|
||||
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;
|
||||
case eSessionStateOneTime:
|
||||
if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false))
|
||||
|
@ -1039,7 +858,6 @@ namespace garlic
|
|||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
size_t payloadLen = 0;
|
||||
bool sendAckRequest = false;
|
||||
if (first) payloadLen += 7;// datatime
|
||||
if (msg)
|
||||
{
|
||||
|
@ -1058,28 +876,13 @@ namespace garlic
|
|||
payloadLen += leaseSet->GetBufferLen () + DATABASE_STORE_HEADER_SIZE + 13;
|
||||
if (!first)
|
||||
{
|
||||
// ack request for LeaseSet
|
||||
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
|
||||
sendAckRequest = true;
|
||||
// update LeaseSet status
|
||||
// ack request
|
||||
SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
|
||||
SetLeaseSetUpdateMsgID (m_AckRequestMsgID);
|
||||
SetLeaseSetUpdateMsgID ((m_SendTagset->GetTagSetID () << 16) + m_SendTagset->GetNextIndex ()); // (tagsetid << 16) + N
|
||||
SetLeaseSetSubmissionTime (ts);
|
||||
payloadLen += 4;
|
||||
}
|
||||
}
|
||||
if (!sendAckRequest && !first &&
|
||||
((!m_AckRequestMsgID && ts > m_LastAckRequestSendTime + m_AckRequestInterval) || // regular request
|
||||
(m_AckRequestMsgID && ts > m_LastAckRequestSendTime + LEASESET_CONFIRMATION_TIMEOUT))) // previous request failed. try again
|
||||
{
|
||||
// not LeaseSet
|
||||
m_AckRequestMsgID = m_SendTagset->GetMsgID ();
|
||||
if (m_AckRequestMsgID)
|
||||
{
|
||||
m_AckRequestNumAttempts++;
|
||||
sendAckRequest = true;
|
||||
}
|
||||
}
|
||||
if (sendAckRequest) payloadLen += 4;
|
||||
if (m_AckRequests.size () > 0)
|
||||
payloadLen += m_AckRequests.size ()*4 + 3;
|
||||
if (m_SendReverseKey)
|
||||
|
@ -1131,14 +934,15 @@ namespace garlic
|
|||
}
|
||||
// LeaseSet
|
||||
if (leaseSet)
|
||||
offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
|
||||
// ack request
|
||||
if (sendAckRequest)
|
||||
{
|
||||
offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset);
|
||||
if (!first)
|
||||
{
|
||||
// ack request
|
||||
payload[offset] = eECIESx25519BlkAckRequest; offset++;
|
||||
htobe16buf (payload + offset, 1); offset += 2;
|
||||
payload[offset] = 0; offset++; // flags
|
||||
m_LastAckRequestSendTime = ts;
|
||||
}
|
||||
}
|
||||
// msg
|
||||
if (msg)
|
||||
|
@ -1174,6 +978,7 @@ namespace garlic
|
|||
memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32);
|
||||
offset += 32; // public key
|
||||
}
|
||||
m_SendReverseKey = false;
|
||||
}
|
||||
if (m_SendForwardKey)
|
||||
{
|
||||
|
@ -1269,8 +1074,6 @@ namespace garlic
|
|||
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t 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
|
||||
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
|
||||
*
|
||||
|
@ -14,12 +14,10 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include "Identity.h"
|
||||
#include "Crypto.h"
|
||||
#include "PostQuantum.h"
|
||||
#include "Garlic.h"
|
||||
#include "Tag.h"
|
||||
|
||||
|
@ -32,14 +30,10 @@ namespace garlic
|
|||
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_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_DEFAULT_ACK_REQUEST_INTERVAL = 33000; // in milliseconds
|
||||
const int ECIESX25519_ACK_REQUEST_MAX_NUM_ATTEMPTS = 3;
|
||||
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
|
||||
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_MAX_NUM_GENERATED_TAGS = 800;
|
||||
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320;
|
||||
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
|
||||
|
||||
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */
|
||||
|
@ -63,8 +57,6 @@ namespace garlic
|
|||
int GetTagSetID () const { return m_TagSetID; };
|
||||
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
|
||||
|
||||
uint32_t GetMsgID () const { return (m_TagSetID << 16) + m_NextIndex; }; // (tagsetid << 16) + N
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::Tag<64> m_SessionTagKeyData;
|
||||
|
@ -81,8 +73,8 @@ namespace garlic
|
|||
{
|
||||
public:
|
||||
|
||||
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false);
|
||||
~ReceiveRatchetTagSet () override;
|
||||
ReceiveRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session, bool isNS = false):
|
||||
m_Session (session), m_IsNS (isNS) {};
|
||||
|
||||
bool IsNS () const { return m_IsNS; };
|
||||
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session; };
|
||||
|
@ -157,7 +149,6 @@ namespace garlic
|
|||
std::shared_ptr<i2p::crypto::X25519Keys> key;
|
||||
uint8_t remote[32]; // last remote public key
|
||||
bool newKey = true;
|
||||
int GetReceiveTagSetID () const { return newKey ? (2*keyID + 1) : 2*keyID; }
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -166,32 +157,26 @@ namespace garlic
|
|||
~ECIESX25519AEADRatchetSession ();
|
||||
|
||||
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);
|
||||
|
||||
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
|
||||
i2p::data::CryptoKeyType GetRemoteStaticKeyType () const { return m_RemoteStaticKeyType; }
|
||||
void SetRemoteStaticKey (i2p::data::CryptoKeyType keyType, const uint8_t * key)
|
||||
{
|
||||
m_RemoteStaticKeyType = keyType;
|
||||
memcpy (m_RemoteStaticKey, key, 32);
|
||||
}
|
||||
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
bool CheckExpired (uint64_t ts); // true is expired
|
||||
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); }
|
||||
void CleanupReceiveNSRKeys (); // called from ReceiveRatchetTagSet at Alice's side
|
||||
|
||||
bool IsRatchets () const override { return true; };
|
||||
bool IsReadyToSend () const override { return m_State != eSessionStateNewSessionSent; };
|
||||
bool IsTerminated () const override { return m_IsTerminated; }
|
||||
uint64_t GetLastActivityTimestamp () const override { return m_LastActivityTimestamp; };
|
||||
void SetAckRequestInterval (int interval) override { m_AckRequestInterval = interval; };
|
||||
bool CleanupUnconfirmedTags () override; // return true if unaswered Ack requests, called from I2CP
|
||||
bool IsRatchets () const { return true; };
|
||||
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; };
|
||||
bool IsTerminated () const { return m_IsTerminated; }
|
||||
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -199,7 +184,6 @@ namespace garlic
|
|||
void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; };
|
||||
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);
|
||||
bool MessageConfirmed (uint32_t msgID) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -225,30 +209,20 @@ namespace garlic
|
|||
|
||||
private:
|
||||
|
||||
i2p::data::CryptoKeyType m_RemoteStaticKeyType;
|
||||
uint8_t m_RemoteStaticKey[32];
|
||||
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
|
||||
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;
|
||||
uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds)
|
||||
m_LastSentTimestamp = 0; // in milliseconds
|
||||
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::list<std::pair<uint16_t, int> > m_AckRequests; // incoming (tagsetid, index)
|
||||
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
|
||||
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
|
||||
bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false;
|
||||
std::unique_ptr<DHRatchet> m_NextReceiveRatchet, m_NextSendRatchet;
|
||||
uint8_t m_PaddingSizes[32], m_NextPaddingSize;
|
||||
|
||||
uint64_t m_LastAckRequestSendTime = 0; // milliseconds
|
||||
uint32_t m_AckRequestMsgID = 0;
|
||||
int m_AckRequestNumAttempts = 0;
|
||||
int m_AckRequestInterval = ECIESX25519_DEFAULT_ACK_REQUEST_INTERVAL; // milliseconds
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "Ed25519.h"
|
||||
|
@ -134,27 +134,22 @@ namespace crypto
|
|||
{
|
||||
BN_CTX * bnCtx = BN_CTX_new ();
|
||||
// calculate r
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
SHA512_CTX ctx;
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, expandedPrivateKey + EDDSA25519_PRIVATE_KEY_LENGTH, EDDSA25519_PRIVATE_KEY_LENGTH); // right half of expanded key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
uint8_t digest[64];
|
||||
unsigned int dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * r = DecodeBN<32> (digest); // DecodeBN<64> (digest); // for test vectors
|
||||
// calculate R
|
||||
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
|
||||
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R); // EncodePoint (Mul (B, r, bnCtx), R); // for test vectors
|
||||
// calculate S
|
||||
ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
EVP_DigestUpdate (ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * h = DecodeBN<64> (digest);
|
||||
// S = (r + h*a) % l
|
||||
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (expandedPrivateKey); // left half of expanded key
|
||||
|
@ -174,15 +169,13 @@ namespace crypto
|
|||
uint8_t T[80];
|
||||
RAND_bytes (T, 80);
|
||||
// calculate r = H*(T || publickey || data)
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, T, 80);
|
||||
EVP_DigestUpdate (ctx, publicKeyEncoded, 32);
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
SHA512_CTX ctx;
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, T, 80);
|
||||
SHA512_Update (&ctx, publicKeyEncoded, 32);
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
uint8_t digest[64];
|
||||
unsigned int dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * r = DecodeBN<64> (digest);
|
||||
BN_mod (r, r, l, bnCtx); // % l
|
||||
EncodeBN (r, digest, 32);
|
||||
|
@ -190,14 +183,11 @@ namespace crypto
|
|||
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
|
||||
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R);
|
||||
// calculate S
|
||||
ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestInit_ex (ctx, EVP_sha512(), NULL);
|
||||
EVP_DigestUpdate (ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
EVP_DigestUpdate (ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
EVP_DigestUpdate (ctx, buf, len); // data
|
||||
dl = 64;
|
||||
EVP_DigestFinal_ex (ctx, digest, &dl);
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
SHA512_Init (&ctx);
|
||||
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
|
||||
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
|
||||
SHA512_Update (&ctx, buf, len); // data
|
||||
SHA512_Final (digest, &ctx);
|
||||
BIGNUM * h = DecodeBN<64> (digest);
|
||||
// S = (r + h*a) % l
|
||||
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (privateKey);
|
||||
|
@ -467,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)
|
||||
{
|
||||
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
|
||||
*
|
||||
|
@ -84,7 +84,10 @@ namespace crypto
|
|||
EDDSAPoint GeneratePublicKey (const uint8_t * expandedPrivateKey, BN_CTX * ctx) const;
|
||||
EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const;
|
||||
void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const;
|
||||
|
||||
#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 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;
|
||||
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:
|
||||
|
||||
BIGNUM * q, * l, * d, * I;
|
||||
|
|
135
libi2pd/FS.cpp
135
libi2pd/FS.cpp
|
@ -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
|
||||
*
|
||||
|
@ -7,18 +7,13 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#if defined(MAC_OSX)
|
||||
#if !STD_FILESYSTEM
|
||||
#include <boost/system/system_error.hpp>
|
||||
#endif
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
#include <FindDirectory.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#include <windows.h>
|
||||
|
@ -30,14 +25,6 @@
|
|||
#include "Log.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
#if STD_FILESYSTEM
|
||||
#include <filesystem>
|
||||
namespace fs_lib = std::filesystem;
|
||||
#else
|
||||
#include <boost/filesystem.hpp>
|
||||
namespace fs_lib = boost::filesystem;
|
||||
#endif
|
||||
|
||||
namespace i2p {
|
||||
namespace fs {
|
||||
std::string appName = "i2pd";
|
||||
|
@ -67,17 +54,15 @@ namespace fs {
|
|||
|
||||
const std::string GetUTF8DataDir () {
|
||||
#ifdef _WIN32
|
||||
int size = MultiByteToWideChar(CP_ACP, 0,
|
||||
dataDir.c_str(), dataDir.size(), nullptr, 0);
|
||||
std::wstring utf16Str(size, L'\0');
|
||||
MultiByteToWideChar(CP_ACP, 0,
|
||||
dataDir.c_str(), dataDir.size(), &utf16Str[0], size);
|
||||
int utf8Size = WideCharToMultiByte(CP_UTF8, 0,
|
||||
utf16Str.c_str(), utf16Str.size(), nullptr, 0, nullptr, nullptr);
|
||||
std::string utf8Str(utf8Size, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0,
|
||||
utf16Str.c_str(), utf16Str.size(), &utf8Str[0], utf8Size, nullptr, nullptr);
|
||||
return utf8Str;
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
boost::filesystem::path path (dataDir);
|
||||
#else
|
||||
boost::filesystem::wpath path (dataDir);
|
||||
#endif
|
||||
auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16<wchar_t>() ) ); // convert path to UTF-8
|
||||
auto dataDirUTF8 = path.string();
|
||||
boost::filesystem::path::imbue(loc); // Return locale settings back
|
||||
return dataDirUTF8;
|
||||
#else
|
||||
return dataDir; // linux, osx, android uses UTF-8 by default
|
||||
#endif
|
||||
|
@ -106,10 +91,10 @@ namespace fs {
|
|||
}
|
||||
else
|
||||
{
|
||||
#if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
|
||||
dataDir = fs_lib::path(commonAppData).string() + "\\" + appName;
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
dataDir = boost::filesystem::path(commonAppData).string() + "\\" + appName;
|
||||
#else
|
||||
dataDir = fs_lib::wpath(commonAppData).string() + "\\" + appName;
|
||||
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
|
@ -135,14 +120,14 @@ namespace fs {
|
|||
}
|
||||
else
|
||||
{
|
||||
#if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
|
||||
auto execPath = fs_lib::path(localAppData).parent_path();
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
auto execPath = boost::filesystem::path(localAppData).parent_path();
|
||||
#else
|
||||
auto execPath = fs_lib::wpath(localAppData).parent_path();
|
||||
auto execPath = boost::filesystem::wpath(localAppData).parent_path();
|
||||
#endif
|
||||
|
||||
// if config file exists in .exe's folder use it
|
||||
if(fs_lib::exists(execPath/"i2pd.conf")) // TODO: magic string
|
||||
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
|
||||
{
|
||||
dataDir = execPath.string ();
|
||||
} else // otherwise %appdata%
|
||||
|
@ -158,10 +143,10 @@ namespace fs {
|
|||
}
|
||||
else
|
||||
{
|
||||
#if ((BOOST_VERSION >= 108500) || STD_FILESYSTEM)
|
||||
dataDir = fs_lib::path(localAppData).string() + "\\" + appName;
|
||||
#if (BOOST_VERSION >= 108500)
|
||||
dataDir = boost::filesystem::path(localAppData).string() + "\\" + appName;
|
||||
#else
|
||||
dataDir = fs_lib::wpath(localAppData).string() + "\\" + appName;
|
||||
dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -173,17 +158,18 @@ namespace fs {
|
|||
dataDir += "/Library/Application Support/" + appName;
|
||||
return;
|
||||
#elif defined(__HAIKU__)
|
||||
char home[PATH_MAX]; // /boot/home/config/settings
|
||||
if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, home, PATH_MAX) == B_OK)
|
||||
dataDir = std::string(home) + "/" + appName;
|
||||
else
|
||||
char *home = getenv("HOME");
|
||||
if (home != NULL && strlen(home) > 0) {
|
||||
dataDir = std::string(home) + "/config/settings/" + appName;
|
||||
} else {
|
||||
dataDir = "/tmp/" + appName;
|
||||
}
|
||||
return;
|
||||
#else /* other unix */
|
||||
#if defined(ANDROID)
|
||||
const char * ext = getenv("EXTERNAL_STORAGE");
|
||||
if (!ext) ext = "/sdcard";
|
||||
if (fs_lib::exists(ext))
|
||||
if (boost::filesystem::exists(ext))
|
||||
{
|
||||
dataDir = std::string (ext) + "/" + appName;
|
||||
return;
|
||||
|
@ -216,16 +202,16 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool Init() {
|
||||
if (!fs_lib::exists(dataDir))
|
||||
fs_lib::create_directory(dataDir);
|
||||
if (!boost::filesystem::exists(dataDir))
|
||||
boost::filesystem::create_directory(dataDir);
|
||||
|
||||
std::string destinations = DataDirPath("destinations");
|
||||
if (!fs_lib::exists(destinations))
|
||||
fs_lib::create_directory(destinations);
|
||||
if (!boost::filesystem::exists(destinations))
|
||||
boost::filesystem::create_directory(destinations);
|
||||
|
||||
std::string tags = DataDirPath("tags");
|
||||
if (!fs_lib::exists(tags))
|
||||
fs_lib::create_directory(tags);
|
||||
if (!boost::filesystem::exists(tags))
|
||||
boost::filesystem::create_directory(tags);
|
||||
else
|
||||
i2p::garlic::CleanUpTagsFiles ();
|
||||
|
||||
|
@ -233,13 +219,13 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool ReadDir(const std::string & path, std::vector<std::string> & files) {
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return false;
|
||||
fs_lib::directory_iterator it(path);
|
||||
fs_lib::directory_iterator end;
|
||||
boost::filesystem::directory_iterator it(path);
|
||||
boost::filesystem::directory_iterator end;
|
||||
|
||||
for ( ; it != end; it++) {
|
||||
if (!fs_lib::is_regular_file(it->status()))
|
||||
if (!boost::filesystem::is_regular_file(it->status()))
|
||||
continue;
|
||||
files.push_back(it->path().string());
|
||||
}
|
||||
|
@ -248,42 +234,29 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool Exists(const std::string & path) {
|
||||
return fs_lib::exists(path);
|
||||
return boost::filesystem::exists(path);
|
||||
}
|
||||
|
||||
uint32_t GetLastUpdateTime (const std::string & path)
|
||||
{
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return 0;
|
||||
#if STD_FILESYSTEM
|
||||
std::error_code ec;
|
||||
auto t = std::filesystem::last_write_time (path, ec);
|
||||
if (ec) return 0;
|
||||
/*#if __cplusplus >= 202002L // C++ 20 or higher
|
||||
const auto sctp = std::chrono::clock_cast<std::chrono::system_clock>(t);
|
||||
#else */ // TODO: wait until implemented
|
||||
const auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
|
||||
t - decltype(t)::clock::now() + std::chrono::system_clock::now());
|
||||
/*#endif */
|
||||
return std::chrono::system_clock::to_time_t(sctp);
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
auto t = boost::filesystem::last_write_time (path, ec);
|
||||
return ec ? 0 : t;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Remove(const std::string & path) {
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return false;
|
||||
return fs_lib::remove(path);
|
||||
return boost::filesystem::remove(path);
|
||||
}
|
||||
|
||||
bool CreateDirectory (const std::string& path)
|
||||
{
|
||||
if (fs_lib::exists(path) && fs_lib::is_directory (fs_lib::status (path)))
|
||||
if (boost::filesystem::exists(path) && boost::filesystem::is_directory (boost::filesystem::status (path)))
|
||||
return true;
|
||||
return fs_lib::create_directory(path);
|
||||
return boost::filesystem::create_directory(path);
|
||||
}
|
||||
|
||||
void HashedStorage::SetPlace(const std::string &path) {
|
||||
|
@ -291,18 +264,18 @@ namespace fs {
|
|||
}
|
||||
|
||||
bool HashedStorage::Init(const char * chars, size_t count) {
|
||||
if (!fs_lib::exists(root)) {
|
||||
fs_lib::create_directories(root);
|
||||
if (!boost::filesystem::exists(root)) {
|
||||
boost::filesystem::create_directories(root);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
|
||||
if (fs_lib::exists(p))
|
||||
if (boost::filesystem::exists(p))
|
||||
continue;
|
||||
#if TARGET_OS_SIMULATOR
|
||||
// ios simulator fs says it is case sensitive, but it is not
|
||||
boost::system::error_code ec;
|
||||
if (fs_lib::create_directory(p, ec))
|
||||
if (boost::filesystem::create_directory(p, ec))
|
||||
continue;
|
||||
switch (ec.value()) {
|
||||
case boost::system::errc::file_exists:
|
||||
|
@ -312,7 +285,7 @@ namespace fs {
|
|||
throw boost::system::system_error( ec, __func__ );
|
||||
}
|
||||
#else
|
||||
if (fs_lib::create_directory(p))
|
||||
if (boost::filesystem::create_directory(p))
|
||||
continue; /* ^ throws exception on failure */
|
||||
#endif
|
||||
return false;
|
||||
|
@ -335,9 +308,9 @@ namespace fs {
|
|||
|
||||
void HashedStorage::Remove(const std::string & ident) {
|
||||
std::string path = Path(ident);
|
||||
if (!fs_lib::exists(path))
|
||||
if (!boost::filesystem::exists(path))
|
||||
return;
|
||||
fs_lib::remove(path);
|
||||
boost::filesystem::remove(path);
|
||||
}
|
||||
|
||||
void HashedStorage::Traverse(std::vector<std::string> & files) {
|
||||
|
@ -348,12 +321,12 @@ namespace fs {
|
|||
|
||||
void HashedStorage::Iterate(FilenameVisitor v)
|
||||
{
|
||||
fs_lib::path p(root);
|
||||
fs_lib::recursive_directory_iterator it(p);
|
||||
fs_lib::recursive_directory_iterator end;
|
||||
boost::filesystem::path p(root);
|
||||
boost::filesystem::recursive_directory_iterator it(p);
|
||||
boost::filesystem::recursive_directory_iterator end;
|
||||
|
||||
for ( ; it != end; it++) {
|
||||
if (!fs_lib::is_regular_file( it->status() ))
|
||||
if (!boost::filesystem::is_regular_file( it->status() ))
|
||||
continue;
|
||||
const std::string & t = it->path().string();
|
||||
v(t);
|
||||
|
|
12
libi2pd/FS.h
12
libi2pd/FS.h
|
@ -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
|
||||
*
|
||||
|
@ -15,16 +15,6 @@
|
|||
#include <sstream>
|
||||
#include <functional>
|
||||
|
||||
#ifndef STD_FILESYSTEM
|
||||
# if (_WIN32 && __GNUG__) // MinGW GCC somehow incorrectly converts paths
|
||||
# define STD_FILESYSTEM 0
|
||||
# elif (!TARGET_OS_SIMULATOR && __has_include(<filesystem>)) // supports std::filesystem
|
||||
# define STD_FILESYSTEM 1
|
||||
# else
|
||||
# define STD_FILESYSTEM 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace i2p {
|
||||
namespace fs {
|
||||
extern std::string dirSep;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
|
@ -24,8 +25,6 @@ namespace data
|
|||
|
||||
Families::~Families ()
|
||||
{
|
||||
for (auto it : m_SigningKeys)
|
||||
if (it.second.first) EVP_PKEY_free (it.second.first);
|
||||
}
|
||||
|
||||
void Families::LoadCertificate (const std::string& filename)
|
||||
|
@ -48,29 +47,48 @@ namespace data
|
|||
cn += 3;
|
||||
char * family = strstr (cn, ".family");
|
||||
if (family) family[0] = 0;
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
if (pkey)
|
||||
{
|
||||
int curve = 0;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
char groupName[20];
|
||||
if (EVP_PKEY_get_group_name(pkey, groupName, sizeof(groupName), NULL) == 1)
|
||||
curve = OBJ_txt2nid (groupName);
|
||||
else
|
||||
curve = -1;
|
||||
#endif
|
||||
if (!curve || curve == NID_X9_62_prime256v1)
|
||||
{
|
||||
if (!m_SigningKeys.emplace (cn, std::make_pair(pkey, (int)m_SigningKeys.size () + 1)).second)
|
||||
{
|
||||
EVP_PKEY_free (pkey);
|
||||
LogPrint (eLogError, "Family: Duplicated family name ", cn);
|
||||
}
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
int keyType = EVP_PKEY_base_id (pkey);
|
||||
switch (keyType)
|
||||
{
|
||||
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);
|
||||
if (verifier && cn)
|
||||
m_SigningKeys.emplace (cn, std::make_pair(verifier, (int)m_SigningKeys.size () + 1));
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
|
@ -103,37 +121,23 @@ namespace data
|
|||
}
|
||||
|
||||
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];
|
||||
size_t len = family.length ();
|
||||
size_t len = family.length (), signatureLen = strlen (signature);
|
||||
if (len + 32 > 100)
|
||||
{
|
||||
LogPrint (eLogError, "Family: ", family, " is too long");
|
||||
return false;
|
||||
}
|
||||
auto it = m_SigningKeys.find (family);
|
||||
if (it != m_SigningKeys.end () && it->second.first)
|
||||
{
|
||||
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
auto signatureBufLen = Base64ToByteStream (signature, signatureBuf, 64);
|
||||
if (signatureBufLen == 64)
|
||||
{
|
||||
ECDSA_SIG * sig = ECDSA_SIG_new();
|
||||
ECDSA_SIG_set0 (sig, BN_bin2bn (signatureBuf, 32, NULL), BN_bin2bn (signatureBuf + 32, 32, NULL));
|
||||
uint8_t sign[72];
|
||||
uint8_t * s = sign;
|
||||
auto l = i2d_ECDSA_SIG (sig, &s);
|
||||
ECDSA_SIG_free(sig);
|
||||
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestVerifyInit (ctx, NULL, EVP_sha256(), NULL, it->second.first);
|
||||
auto ret = EVP_DigestVerify (ctx, sign, l, buf, len) == 1;
|
||||
EVP_MD_CTX_destroy (ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||
auto it = m_SigningKeys.find (family);
|
||||
if (it != m_SigningKeys.end ())
|
||||
return it->second.first->Verify (buf, len, signatureBuf);
|
||||
// TODO: process key
|
||||
return true;
|
||||
}
|
||||
|
@ -156,40 +160,34 @@ namespace data
|
|||
{
|
||||
SSL * ssl = SSL_new (ctx);
|
||||
EVP_PKEY * pkey = SSL_get_privatekey (ssl);
|
||||
int curve = 0;
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
|
||||
char groupName[20];
|
||||
if (EVP_PKEY_get_group_name(pkey, groupName, sizeof(groupName), NULL) == 1)
|
||||
curve = OBJ_txt2nid (groupName);
|
||||
else
|
||||
curve = -1;
|
||||
#endif
|
||||
if (!curve || curve == NID_X9_62_prime256v1)
|
||||
EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey);
|
||||
if (ecKey)
|
||||
{
|
||||
uint8_t buf[100], sign[72], signature[64];
|
||||
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 signingPrivateKey[32], buf[50], signature[64];
|
||||
i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32);
|
||||
i2p::crypto::ECDSAP256Signer signer (signingPrivateKey);
|
||||
size_t len = family.length ();
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
|
||||
size_t l = 72;
|
||||
EVP_MD_CTX * mdctx = EVP_MD_CTX_create ();
|
||||
EVP_DigestSignInit (mdctx, NULL, EVP_sha256(), NULL, pkey);
|
||||
EVP_DigestSign (mdctx, sign, &l, buf, len);
|
||||
EVP_MD_CTX_destroy (mdctx);
|
||||
|
||||
const uint8_t * s1 = sign;
|
||||
ECDSA_SIG * sig1 = d2i_ECDSA_SIG (NULL, &s1, l);
|
||||
const BIGNUM * r, * s;
|
||||
ECDSA_SIG_get0 (sig1, &r, &s);
|
||||
i2p::crypto::bn2buf (r, signature, 32);
|
||||
i2p::crypto::bn2buf (s, signature + 32, 32);
|
||||
ECDSA_SIG_free(sig1);
|
||||
sig = ByteStreamToBase64 (signature, 64);
|
||||
signer.Sign (buf, len, signature);
|
||||
len = Base64EncodingBufferSize (64);
|
||||
char * b64 = new char[len+1];
|
||||
len = ByteStreamToBase64 (signature, 64, b64, len);
|
||||
b64[len] = 0;
|
||||
sig = b64;
|
||||
delete[] b64;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");
|
||||
|
||||
}
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -11,9 +11,8 @@
|
|||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
#include <openssl/evp.h>
|
||||
#include "Signature.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -29,7 +28,7 @@ namespace data
|
|||
~Families ();
|
||||
void LoadCertificates ();
|
||||
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;
|
||||
|
||||
private:
|
||||
|
@ -38,7 +37,7 @@ namespace data
|
|||
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -160,7 +160,7 @@ namespace garlic
|
|||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
m_Destination->Encrypt ((uint8_t *)&elGamal, buf);
|
||||
m_IV = iv;
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 514;
|
||||
len += 514;
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ namespace garlic
|
|||
memcpy (buf, tag, 32);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(tag, 32, iv);
|
||||
m_IV = iv;
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 32;
|
||||
len += 32;
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ namespace garlic
|
|||
size_t rem = blockSize % 16;
|
||||
if (rem)
|
||||
blockSize += (16-rem); //padding
|
||||
m_Encryption.Encrypt(buf, blockSize, m_IV, buf);
|
||||
m_Encryption.Encrypt(buf, blockSize, buf);
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
|
@ -426,8 +426,7 @@ namespace garlic
|
|||
}
|
||||
|
||||
GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default
|
||||
m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0),
|
||||
m_NumRatchetInboundTags (0) // 0 means standard
|
||||
m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -498,8 +497,7 @@ namespace garlic
|
|||
buf += 4; // length
|
||||
|
||||
bool found = false;
|
||||
bool supportsRatchets = SupportsRatchets ();
|
||||
if (supportsRatchets)
|
||||
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
// try ECIESx25519 tag
|
||||
found = HandleECIESx25519TagMessage (buf, length);
|
||||
if (!found)
|
||||
|
@ -515,7 +513,8 @@ namespace garlic
|
|||
{
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
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);
|
||||
found = true;
|
||||
}
|
||||
|
@ -533,23 +532,43 @@ namespace garlic
|
|||
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
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);
|
||||
}
|
||||
else if (supportsRatchets)
|
||||
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
{
|
||||
// otherwise ECIESx25519
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL)
|
||||
{
|
||||
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
|
||||
if (session->HandleNextMessage (buf, length, nullptr, 0))
|
||||
m_LastIncomingSessionTimestamp = ts;
|
||||
else
|
||||
if (!session->HandleNextMessage (buf, length, nullptr, 0))
|
||||
{
|
||||
// try to generate more tags for last tagset
|
||||
if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS))
|
||||
{
|
||||
uint64_t missingTag; memcpy (&missingTag, buf, 8);
|
||||
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
|
||||
LogPrint (eLogWarning, "Garlic: Trying to generate more ECIES-X25519-AEAD-Ratchet tags");
|
||||
for (int i = 0; i < maxTags; i++)
|
||||
{
|
||||
auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset);
|
||||
if (!nextTag)
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
|
||||
break;
|
||||
}
|
||||
if (nextTag == missingTag)
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated");
|
||||
if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index))
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) m_LastTagset = nullptr;
|
||||
}
|
||||
if (!found)
|
||||
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: Incoming sessions come too often");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Failed to decrypt message");
|
||||
|
@ -564,7 +583,9 @@ namespace garlic
|
|||
auto it = m_ECIESx25519Tags.find (tag);
|
||||
if (it != m_ECIESx25519Tags.end ())
|
||||
{
|
||||
if (!it->second.tagset || !it->second.tagset->HandleNextMessage (buf, len, it->second.index))
|
||||
if (it->second.tagset && it->second.tagset->HandleNextMessage (buf, len, it->second.index))
|
||||
m_LastTagset = it->second.tagset;
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||
m_ECIESx25519Tags.erase (it);
|
||||
return true;
|
||||
|
@ -745,12 +766,10 @@ namespace garlic
|
|||
}
|
||||
|
||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet,
|
||||
bool requestNewIfNotFound)
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
||||
{
|
||||
if (destination->GetEncryptionType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
|
||||
{
|
||||
if (SupportsEncryptionType (destination->GetEncryptionType ()))
|
||||
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
|
||||
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
|
||||
{
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
uint8_t staticKey[32];
|
||||
|
@ -762,22 +781,18 @@ namespace garlic
|
|||
if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ()))
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: Session restarted");
|
||||
requestNewIfNotFound = true; // it's not a new session
|
||||
session = nullptr;
|
||||
}
|
||||
}
|
||||
if (!session && requestNewIfNotFound)
|
||||
if (!session)
|
||||
{
|
||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this, true);
|
||||
session->SetRemoteStaticKey (destination->GetEncryptionType (), staticKey);
|
||||
session->SetRemoteStaticKey (staticKey);
|
||||
}
|
||||
if (session && destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // NS or NSR
|
||||
if (destination->IsDestination ())
|
||||
session->SetDestination (destination->GetIdentHash ()); // TODO: remove
|
||||
return session;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Non-supported encryption type ", destination->GetEncryptionType ());
|
||||
}
|
||||
else
|
||||
{
|
||||
ElGamalAESSessionPtr session;
|
||||
|
@ -878,6 +893,8 @@ namespace garlic
|
|||
}
|
||||
if (numExpiredTags > 0)
|
||||
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ());
|
||||
if (m_LastTagset && m_LastTagset->IsExpired (ts))
|
||||
m_LastTagset = nullptr;
|
||||
}
|
||||
|
||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
||||
|
@ -911,7 +928,7 @@ namespace garlic
|
|||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::SetLeaseSetUpdated (bool post)
|
||||
void GarlicDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
|
@ -1004,8 +1021,7 @@ namespace garlic
|
|||
i2p::fs::Remove (it);
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len,
|
||||
ECIESX25519AEADRatchetSession * from)
|
||||
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len)
|
||||
{
|
||||
const uint8_t * buf1 = buf;
|
||||
uint8_t flag = buf[0]; buf++; // flag
|
||||
|
@ -1015,7 +1031,9 @@ namespace garlic
|
|||
case eGarlicDeliveryTypeDestination:
|
||||
LogPrint (eLogDebug, "Garlic: Type destination");
|
||||
buf += 32; // TODO: check destination
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
// no break here
|
||||
case eGarlicDeliveryTypeLocal:
|
||||
{
|
||||
|
@ -1025,7 +1043,7 @@ namespace garlic
|
|||
buf += 4; // expiration
|
||||
ptrdiff_t offset = buf - buf1;
|
||||
if (offset <= (int)len)
|
||||
HandleCloveI2NPMessage (typeID, buf, len - offset, msgID, from);
|
||||
HandleCloveI2NPMessage (typeID, buf, len - offset, msgID);
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Clove is too long");
|
||||
break;
|
||||
|
@ -1109,17 +1127,5 @@ namespace garlic
|
|||
m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE];
|
||||
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
|
||||
*
|
||||
|
@ -52,7 +52,6 @@ namespace garlic
|
|||
const int OUTGOING_TAGS_CONFIRMATION_TIMEOUT = 10; // 10 seconds
|
||||
const int LEASESET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
|
||||
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>
|
||||
{
|
||||
|
@ -110,13 +109,12 @@ namespace garlic
|
|||
GarlicRoutingSession ();
|
||||
virtual ~GarlicRoutingSession ();
|
||||
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0;
|
||||
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession and ECIESX25519AEADRatchetSession
|
||||
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
|
||||
virtual bool MessageConfirmed (uint32_t msgID);
|
||||
virtual bool IsRatchets () const { return false; };
|
||||
virtual bool IsReadyToSend () const { return true; };
|
||||
virtual bool IsTerminated () const { return !GetOwner (); };
|
||||
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
|
||||
virtual void SetAckRequestInterval (int interval) {}; // in milliseconds, override in ECIESX25519AEADRatchetSession
|
||||
|
||||
void SetLeaseSetUpdated ()
|
||||
{
|
||||
|
@ -206,7 +204,6 @@ namespace garlic
|
|||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
i2p::data::Tag<16> m_IV;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -237,18 +234,12 @@ namespace garlic
|
|||
int GetNumTags () const { return m_NumTags; };
|
||||
void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; };
|
||||
int GetNumRatchetInboundTags () const { return m_NumRatchetInboundTags; };
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
bool attachLeaseSet, bool requestNewIfNotFound = true);
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||
void CleanupExpiredTags ();
|
||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||
std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
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 AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
|
||||
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);
|
||||
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
||||
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 ();
|
||||
|
||||
virtual void ProcessGarlicMessage (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<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:
|
||||
|
||||
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
|
||||
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 bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload,
|
||||
size_t len, uint32_t msgID, ECIESX25519AEADRatchetSession * from) = 0;
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0;
|
||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
|
@ -286,7 +272,6 @@ namespace garlic
|
|||
|
||||
private:
|
||||
|
||||
bool SupportsRatchets () const { return GetRatchetsHighestCryptoType () > 0; }
|
||||
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
@ -299,17 +284,14 @@ namespace garlic
|
|||
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
|
||||
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
|
||||
uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet
|
||||
uint64_t m_LastIncomingSessionTimestamp; // in milliseconds
|
||||
// incoming
|
||||
int m_NumRatchetInboundTags;
|
||||
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
|
||||
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
|
||||
ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for
|
||||
// DeliveryStatus
|
||||
std::mutex m_DeliveryStatusSessionsMutex;
|
||||
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||
// encryption
|
||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
||||
|
||||
public:
|
||||
|
||||
|
|
152
libi2pd/HTTP.cpp
152
libi2pd/HTTP.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
|
||||
*
|
||||
|
@ -10,7 +10,6 @@
|
|||
#include <utility>
|
||||
#include <stdio.h>
|
||||
#include <ctime>
|
||||
#include <charconv>
|
||||
#include "util.h"
|
||||
#include "Base.h"
|
||||
#include "HTTP.h"
|
||||
|
@ -19,68 +18,58 @@ namespace i2p
|
|||
{
|
||||
namespace http
|
||||
{
|
||||
// list of valid HTTP methods
|
||||
static constexpr std::array<std::string_view, 16> HTTP_METHODS =
|
||||
{
|
||||
const std::vector<std::string> HTTP_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
|
||||
};
|
||||
|
||||
// list of valid HTTP versions
|
||||
static constexpr std::array<std::string_view, 2> HTTP_VERSIONS =
|
||||
{
|
||||
const std::vector<std::string> HTTP_VERSIONS = {
|
||||
"HTTP/1.0", "HTTP/1.1"
|
||||
};
|
||||
|
||||
static constexpr std::array<const char *, 7> weekdays =
|
||||
{
|
||||
const std::vector<const char *> weekdays = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
|
||||
static constexpr std::array<const char *, 12> months =
|
||||
{
|
||||
const std::vector<const char *> months = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void strsplit(std::string_view line, std::vector<std::string_view> &tokens, char delim, std::size_t limit = 0)
|
||||
{
|
||||
size_t count = 1, pos;
|
||||
while ((pos = line.find (delim)) != line.npos)
|
||||
{
|
||||
void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) {
|
||||
std::size_t count = 0;
|
||||
std::stringstream ss(line);
|
||||
std::string token;
|
||||
while (1) {
|
||||
count++;
|
||||
if (limit > 0 && count >= limit) delim = '\n'; // reset delimiter
|
||||
tokens.push_back (line.substr (0, pos));
|
||||
line = line.substr (pos + 1);
|
||||
if (limit > 0 && count >= limit)
|
||||
delim = '\n'; /* reset delimiter */
|
||||
if (!std::getline(ss, token, delim))
|
||||
break;
|
||||
tokens.push_back(token);
|
||||
}
|
||||
if (!line.empty ()) tokens.push_back (line);
|
||||
}
|
||||
|
||||
static std::pair<std::string, std::string> parse_header_line(std::string_view line)
|
||||
static std::pair<std::string, std::string> parse_header_line(const std::string& line)
|
||||
{
|
||||
std::size_t pos = 0;
|
||||
std::size_t len = 1; /*: */
|
||||
std::size_t max = line.length();
|
||||
if ((pos = line.find(':', pos)) == std::string::npos)
|
||||
return std::pair{"", ""}; // no ':' found
|
||||
return std::make_pair("", ""); // no ':' found
|
||||
if (pos + 1 < max) // ':' at the end of header is valid
|
||||
{
|
||||
while ((pos + len) < max && isspace(line.at(pos + len)))
|
||||
len++;
|
||||
if (len == 1)
|
||||
return std::pair{"", ""}; // no following space, but something else
|
||||
return std::make_pair("", ""); // no following space, but something else
|
||||
}
|
||||
return std::pair{std::string (line.substr(0, pos)), std::string (line.substr(pos + len))};
|
||||
return std::make_pair(line.substr(0, pos), line.substr(pos + len));
|
||||
}
|
||||
|
||||
void gen_rfc7231_date(std::string & out) {
|
||||
|
@ -94,18 +83,15 @@ namespace http
|
|||
out = buf;
|
||||
}
|
||||
|
||||
bool URL::parse(const char *str, std::size_t len)
|
||||
{
|
||||
return parse({str, len ? len : strlen(str)});
|
||||
bool URL::parse(const char *str, std::size_t len) {
|
||||
std::string url(str, len ? len : strlen(str));
|
||||
return parse(url);
|
||||
}
|
||||
|
||||
bool URL::parse(std::string_view url)
|
||||
{
|
||||
if (url.empty ()) return false;
|
||||
bool URL::parse(const std::string& url) {
|
||||
std::size_t pos_p = 0; /* < current parse position */
|
||||
std::size_t pos_c = 0; /* < work position */
|
||||
if(url.at(0) != '/' || pos_p > 0)
|
||||
{
|
||||
if(url.at(0) != '/' || pos_p > 0) {
|
||||
std::size_t pos_s = 0;
|
||||
|
||||
/* schema */
|
||||
|
@ -155,7 +141,7 @@ namespace http
|
|||
/* port[/path] */
|
||||
pos_p = pos_c + 1;
|
||||
pos_c = url.find('/', pos_p);
|
||||
std::string_view port_str = (pos_c == std::string::npos)
|
||||
std::string port_str = (pos_c == std::string::npos)
|
||||
? url.substr(pos_p, std::string::npos)
|
||||
: url.substr(pos_p, pos_c - pos_p);
|
||||
/* stoi throws exception on failure, we don't need it */
|
||||
|
@ -209,9 +195,8 @@ namespace http
|
|||
return true;
|
||||
}
|
||||
|
||||
bool URL::parse_query(std::map<std::string, std::string> & params)
|
||||
{
|
||||
std::vector<std::string_view> tokens;
|
||||
bool URL::parse_query(std::map<std::string, std::string> & params) {
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(query, tokens, '&');
|
||||
|
||||
params.clear();
|
||||
|
@ -268,7 +253,7 @@ namespace http
|
|||
return host.rfind(".i2p") == ( host.size() - 4 );
|
||||
}
|
||||
|
||||
void HTTPMsg::add_header(const char *name, const std::string & value, bool replace) {
|
||||
void HTTPMsg::add_header(const char *name, std::string & value, bool replace) {
|
||||
add_header(name, value.c_str(), replace);
|
||||
}
|
||||
|
||||
|
@ -287,13 +272,12 @@ namespace http
|
|||
headers.erase(name);
|
||||
}
|
||||
|
||||
int HTTPReq::parse(const char *buf, size_t len)
|
||||
{
|
||||
return parse({buf, len});
|
||||
int HTTPReq::parse(const char *buf, size_t len) {
|
||||
std::string str(buf, len);
|
||||
return parse(str);
|
||||
}
|
||||
|
||||
int HTTPReq::parse(std::string_view str)
|
||||
{
|
||||
int HTTPReq::parse(const std::string& str) {
|
||||
enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
|
||||
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
|
||||
std::size_t eol = 0, pos = 0;
|
||||
|
@ -302,14 +286,11 @@ namespace http
|
|||
if (eoh == std::string::npos)
|
||||
return 0; /* str not contains complete request */
|
||||
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos)
|
||||
{
|
||||
if (expect == REQ_LINE)
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string_view> tokens;
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
|
||||
if (expect == REQ_LINE) {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(line, tokens, ' ');
|
||||
|
||||
if (tokens.size() != 3)
|
||||
return -1;
|
||||
if (!is_http_method(tokens[0]))
|
||||
|
@ -326,18 +307,18 @@ namespace http
|
|||
}
|
||||
else
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
auto p = parse_header_line(line);
|
||||
if (p.first.length () > 0)
|
||||
headers.push_back (p);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
pos = eol + CRLF.length();
|
||||
pos = eol + strlen(CRLF);
|
||||
if (pos >= eoh)
|
||||
break;
|
||||
}
|
||||
return eoh + HTTP_EOH.length();
|
||||
return eoh + strlen(HTTP_EOH);
|
||||
}
|
||||
|
||||
void HTTPReq::write(std::ostream & o)
|
||||
|
@ -381,7 +362,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)
|
||||
if (it.first == name)
|
||||
|
@ -389,7 +370,7 @@ namespace http
|
|||
return "";
|
||||
}
|
||||
|
||||
size_t HTTPReq::GetNumHeaders (std::string_view name) const
|
||||
size_t HTTPReq::GetNumHeaders (const std::string& name) const
|
||||
{
|
||||
size_t num = 0;
|
||||
for (auto& it : headers)
|
||||
|
@ -432,13 +413,12 @@ namespace http
|
|||
return length;
|
||||
}
|
||||
|
||||
int HTTPRes::parse(const char *buf, size_t len)
|
||||
{
|
||||
return parse({buf,len});
|
||||
int HTTPRes::parse(const char *buf, size_t len) {
|
||||
std::string str(buf, len);
|
||||
return parse(str);
|
||||
}
|
||||
|
||||
int HTTPRes::parse(std::string_view str)
|
||||
{
|
||||
int HTTPRes::parse(const std::string& str) {
|
||||
enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
|
||||
std::size_t eoh = str.find(HTTP_EOH); /* request head size */
|
||||
std::size_t eol = 0, pos = 0;
|
||||
|
@ -446,41 +426,35 @@ namespace http
|
|||
if (eoh == std::string::npos)
|
||||
return 0; /* str not contains complete request */
|
||||
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos)
|
||||
{
|
||||
if (expect == RES_LINE)
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string_view> tokens;
|
||||
while ((eol = str.find(CRLF, pos)) != std::string::npos) {
|
||||
if (expect == RES_LINE) {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
std::vector<std::string> tokens;
|
||||
strsplit(line, tokens, ' ', 3);
|
||||
if (tokens.size() != 3)
|
||||
return -1;
|
||||
if (!is_http_version(tokens[0]))
|
||||
return -1;
|
||||
auto res = std::from_chars(tokens[1].data (), tokens[1].data() + tokens[1].size(), code);
|
||||
if (res.ec != std::errc())
|
||||
return -1;
|
||||
code = atoi(tokens[1].c_str());
|
||||
if (code < 100 || code >= 600)
|
||||
return -1;
|
||||
/* all ok */
|
||||
version = tokens[0];
|
||||
status = tokens[2];
|
||||
expect = HEADER_LINE;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view line = str.substr(pos, eol - pos);
|
||||
} else {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
auto p = parse_header_line(line);
|
||||
if (p.first.length () > 0)
|
||||
headers.insert (p);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
pos = eol + CRLF.length();
|
||||
pos = eol + strlen(CRLF);
|
||||
if (pos >= eoh)
|
||||
break;
|
||||
}
|
||||
return eoh + HTTP_EOH.length();
|
||||
return eoh + strlen(HTTP_EOH);
|
||||
}
|
||||
|
||||
std::string HTTPRes::to_string() {
|
||||
|
@ -505,11 +479,9 @@ namespace http
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
std::string_view HTTPCodeToStatus(int code)
|
||||
{
|
||||
std::string_view ptr;
|
||||
switch (code)
|
||||
{
|
||||
const char * HTTPCodeToStatus(int code) {
|
||||
const char *ptr;
|
||||
switch (code) {
|
||||
case 105: ptr = "Name Not Resolved"; break;
|
||||
/* success */
|
||||
case 200: ptr = "OK"; break;
|
||||
|
@ -536,14 +508,14 @@ namespace http
|
|||
return ptr;
|
||||
}
|
||||
|
||||
std::string UrlDecode(std::string_view data, bool allow_null)
|
||||
std::string UrlDecode(const std::string& data, bool allow_null)
|
||||
{
|
||||
std::string decoded(data);
|
||||
size_t pos = 0;
|
||||
while ((pos = decoded.find('%', pos)) != std::string::npos)
|
||||
{
|
||||
char c = std::stol(decoded.substr(pos + 1, 2), nullptr, 16);
|
||||
if (!c && !allow_null)
|
||||
char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
|
||||
if (c == '\0' && !allow_null)
|
||||
{
|
||||
pos += 3;
|
||||
continue;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -14,15 +14,16 @@
|
|||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
constexpr std::string_view CRLF = "\r\n"; /**< HTTP line terminator */
|
||||
constexpr std::string_view HTTP_EOH = "\r\n\r\n"; /**< HTTP end-of-headers mark */
|
||||
const char CRLF[] = "\r\n"; /**< HTTP line terminator */
|
||||
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
|
||||
{
|
||||
|
@ -44,7 +45,7 @@ namespace http
|
|||
* @return true on success, false on invalid url
|
||||
*/
|
||||
bool parse (const char *str, std::size_t len = 0);
|
||||
bool parse (std::string_view url);
|
||||
bool parse (const std::string& url);
|
||||
|
||||
/**
|
||||
* @brief Parse query part of url to key/value map
|
||||
|
@ -68,7 +69,7 @@ namespace http
|
|||
{
|
||||
std::map<std::string, std::string> headers;
|
||||
|
||||
void add_header(const char *name, const std::string & value, bool replace = false);
|
||||
void add_header(const char *name, std::string & value, bool replace = false);
|
||||
void add_header(const char *name, const char *value, bool replace = false);
|
||||
void del_header(const char *name);
|
||||
|
||||
|
@ -91,7 +92,7 @@ namespace http
|
|||
* @note Positive return value is a size of header
|
||||
*/
|
||||
int parse(const char *buf, size_t len);
|
||||
int parse(std::string_view buf);
|
||||
int parse(const std::string& buf);
|
||||
|
||||
/** @brief Serialize HTTP request to string */
|
||||
std::string to_string();
|
||||
|
@ -101,8 +102,8 @@ namespace http
|
|||
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) { RemoveHeader (name, ""); };
|
||||
std::string GetHeader (std::string_view name) const;
|
||||
size_t GetNumHeaders (std::string_view name) const;
|
||||
std::string GetHeader (const std::string& name) const;
|
||||
size_t GetNumHeaders (const std::string& name) const;
|
||||
size_t GetNumHeaders () const { return headers.size (); };
|
||||
};
|
||||
|
||||
|
@ -127,7 +128,7 @@ namespace http
|
|||
* @note Positive return value is a size of header
|
||||
*/
|
||||
int parse(const char *buf, size_t len);
|
||||
int parse(const std::string_view buf);
|
||||
int parse(const std::string& buf);
|
||||
|
||||
/**
|
||||
* @brief Serialize HTTP response to string
|
||||
|
@ -152,7 +153,7 @@ namespace http
|
|||
* @param code HTTP code [100, 599]
|
||||
* @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
|
||||
|
@ -160,7 +161,7 @@ namespace http
|
|||
* @param null If set to true - decode also %00 sequence, otherwise - skip
|
||||
* @return Decoded string
|
||||
*/
|
||||
std::string UrlDecode(std::string_view data, bool null = false);
|
||||
std::string UrlDecode(const std::string& data, bool null = false);
|
||||
|
||||
/**
|
||||
* @brief Merge HTTP response content with Transfer-Encoding: chunked
|
||||
|
|
|
@ -10,15 +10,20 @@
|
|||
#include <atomic>
|
||||
#include "Base.h"
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "Tunnel.h"
|
||||
#include "TransitTunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "Garlic.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "version.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage ()
|
||||
|
@ -371,6 +376,337 @@ namespace i2p
|
|||
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)
|
||||
{
|
||||
auto msg = NewI2NPTunnelMessage (false);
|
||||
|
@ -466,6 +802,41 @@ namespace i2p
|
|||
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)
|
||||
{
|
||||
if (msg)
|
||||
|
@ -561,8 +932,14 @@ namespace i2p
|
|||
void I2NPMessagesHandler::Flush ()
|
||||
{
|
||||
if (!m_TunnelMsgs.empty ())
|
||||
{
|
||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
|
||||
m_TunnelMsgs.clear ();
|
||||
}
|
||||
if (!m_TunnelGatewayMsgs.empty ())
|
||||
{
|
||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
|
||||
m_TunnelGatewayMsgs.clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -13,7 +13,6 @@
|
|||
#include <string.h>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include "Crypto.h"
|
||||
#include "I2PEndian.h"
|
||||
|
@ -108,6 +107,7 @@ namespace i2p
|
|||
|
||||
enum I2NPMessageType
|
||||
{
|
||||
eI2NPDummyMsg = 0,
|
||||
eI2NPDatabaseStore = 1,
|
||||
eI2NPDatabaseLookup = 2,
|
||||
eI2NPDatabaseSearchReply = 3,
|
||||
|
@ -155,7 +155,6 @@ namespace tunnel
|
|||
const unsigned int I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_FACTOR = 3; // multiples of RTT
|
||||
const unsigned int I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MIN = 200000; // in microseconds
|
||||
const unsigned int I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX = 2000000; // in microseconds
|
||||
const unsigned int I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_TRANSIT = 4000000; // in microseconds
|
||||
const unsigned int I2NP_MESSAGE_EXPIRATION_TIMEOUT = 8000; // in milliseconds (as initial RTT)
|
||||
const unsigned int I2NP_MESSAGE_CLOCK_SKEW = 60*1000; // 1 minute in milliseconds
|
||||
|
||||
|
@ -317,6 +316,7 @@ namespace tunnel
|
|||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
size_t GetI2NPMessageLength (const uint8_t * msg, size_t len);
|
||||
void HandleTunnelBuildI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
class I2NPMessagesHandler
|
||||
|
@ -329,7 +329,7 @@ namespace tunnel
|
|||
|
||||
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
|
||||
*
|
||||
|
@ -10,7 +10,6 @@
|
|||
#include "I2PEndian.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -28,15 +27,18 @@ namespace data
|
|||
|
||||
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < DEFAULT_IDENTITY_SIZE) return 0; // buffer too small, don't overflow
|
||||
memcpy (this, buf, DEFAULT_IDENTITY_SIZE);
|
||||
if ( len < DEFAULT_IDENTITY_SIZE ) {
|
||||
// buffer too small, don't overflow
|
||||
return 0;
|
||||
}
|
||||
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
|
||||
return DEFAULT_IDENTITY_SIZE;
|
||||
}
|
||||
|
||||
IdentHash Identity::Hash () const
|
||||
{
|
||||
IdentHash hash;
|
||||
SHA256((const uint8_t *)this, DEFAULT_IDENTITY_SIZE, hash);
|
||||
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
@ -120,17 +122,6 @@ namespace data
|
|||
memcpy (m_StandardIdentity.signingKey, signingKey, i2p::crypto::GOSTR3410_512_PUBLIC_KEY_LENGTH);
|
||||
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);
|
||||
cryptoType = 0xFF; // crypto key is not used
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)type, " is not supported");
|
||||
}
|
||||
|
@ -143,14 +134,11 @@ namespace data
|
|||
htobe16buf (m_ExtendedBuffer + 2, cryptoType);
|
||||
if (excessLen && excessBuf)
|
||||
{
|
||||
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE)
|
||||
if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4)
|
||||
{
|
||||
auto newBuf = new uint8_t[m_ExtendedLen];
|
||||
memcpy (newBuf, m_ExtendedBuffer, 4);
|
||||
memcpy (newBuf + 4, excessBuf, excessLen);
|
||||
m_ExtendedBufferPtr = newBuf;
|
||||
LogPrint (eLogError, "Identity: Unexpected excessive signing key len ", excessLen);
|
||||
excessLen = MAX_EXTENDED_BUFFER_SIZE - 4;
|
||||
}
|
||||
else
|
||||
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
|
||||
delete[] excessBuf;
|
||||
}
|
||||
|
@ -199,8 +187,6 @@ namespace data
|
|||
|
||||
IdentityEx::~IdentityEx ()
|
||||
{
|
||||
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE)
|
||||
delete[] m_ExtendedBufferPtr;
|
||||
}
|
||||
|
||||
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
|
||||
|
@ -208,30 +194,12 @@ namespace data
|
|||
memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
||||
m_IdentHash = other.m_IdentHash;
|
||||
|
||||
size_t oldLen = m_ExtendedLen;
|
||||
m_ExtendedLen = other.m_ExtendedLen;
|
||||
if (m_ExtendedLen > 0)
|
||||
{
|
||||
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE)
|
||||
{
|
||||
if (oldLen > MAX_EXTENDED_BUFFER_SIZE)
|
||||
{
|
||||
if (m_ExtendedLen > oldLen)
|
||||
{
|
||||
delete[] m_ExtendedBufferPtr;
|
||||
m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen];
|
||||
}
|
||||
}
|
||||
else
|
||||
m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen];
|
||||
memcpy (m_ExtendedBufferPtr, other.m_ExtendedBufferPtr, m_ExtendedLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oldLen > MAX_EXTENDED_BUFFER_SIZE) delete[] m_ExtendedBufferPtr;
|
||||
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE;
|
||||
memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen);
|
||||
}
|
||||
}
|
||||
m_Verifier = nullptr;
|
||||
CreateVerifier ();
|
||||
|
||||
|
@ -259,27 +227,12 @@ namespace data
|
|||
}
|
||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
||||
|
||||
size_t oldLen = m_ExtendedLen;
|
||||
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
|
||||
if (m_ExtendedLen)
|
||||
{
|
||||
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
|
||||
{
|
||||
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE)
|
||||
{
|
||||
if (oldLen > MAX_EXTENDED_BUFFER_SIZE)
|
||||
{
|
||||
if (m_ExtendedLen > oldLen)
|
||||
{
|
||||
delete[] m_ExtendedBufferPtr;
|
||||
m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen];
|
||||
}
|
||||
}
|
||||
else
|
||||
m_ExtendedBufferPtr = new uint8_t[m_ExtendedLen];
|
||||
memcpy (m_ExtendedBufferPtr, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
|
||||
}
|
||||
else
|
||||
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE;
|
||||
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
|
||||
}
|
||||
else
|
||||
|
@ -305,28 +258,27 @@ namespace data
|
|||
if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else
|
||||
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
||||
if (m_ExtendedLen > 0)
|
||||
{
|
||||
if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE)
|
||||
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBufferPtr, m_ExtendedLen);
|
||||
else
|
||||
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
|
||||
}
|
||||
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
|
||||
auto len = Base64ToByteStream (s, buf.data(), buf.size ());
|
||||
const size_t slen = s.length();
|
||||
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);
|
||||
}
|
||||
|
||||
std::string IdentityEx::ToBase64 () const
|
||||
{
|
||||
const size_t bufLen = GetFullLen();
|
||||
const size_t strLen = Base64EncodingBufferSize(bufLen);
|
||||
std::vector<uint8_t> buf(bufLen);
|
||||
std::vector<char> str(strLen);
|
||||
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
|
||||
|
@ -339,7 +291,7 @@ namespace data
|
|||
const uint8_t * IdentityEx::GetSigningPublicKeyBuffer () const
|
||||
{
|
||||
auto keyLen = GetSigningPublicKeyLen ();
|
||||
if (keyLen > 128) return nullptr; // P521 or PQ
|
||||
if (keyLen > 128) return nullptr; // P521
|
||||
return m_StandardIdentity.signingKey + 128 - keyLen;
|
||||
}
|
||||
|
||||
|
@ -366,7 +318,7 @@ namespace data
|
|||
SigningKeyType IdentityEx::GetSigningKeyType () const
|
||||
{
|
||||
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 2)
|
||||
return bufbe16toh (m_ExtendedLen <= MAX_EXTENDED_BUFFER_SIZE ? m_ExtendedBuffer : m_ExtendedBufferPtr); // signing key
|
||||
return bufbe16toh (m_ExtendedBuffer); // signing key
|
||||
return SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
}
|
||||
|
||||
|
@ -379,7 +331,7 @@ namespace data
|
|||
CryptoKeyType IdentityEx::GetCryptoKeyType () const
|
||||
{
|
||||
if (m_StandardIdentity.certificate[0] == CERTIFICATE_TYPE_KEY && m_ExtendedLen >= 4)
|
||||
return bufbe16toh (m_ExtendedLen <= MAX_EXTENDED_BUFFER_SIZE ? m_ExtendedBuffer + 2 : m_ExtendedBufferPtr + 2); // crypto key
|
||||
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
|
||||
return CRYPTO_KEY_TYPE_ELGAMAL;
|
||||
}
|
||||
|
||||
|
@ -403,10 +355,6 @@ namespace data
|
|||
return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512);
|
||||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
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_SHA384_3072:
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
|
@ -428,18 +376,6 @@ namespace data
|
|||
auto keyLen = verifier->GetPublicKeyLen ();
|
||||
if (keyLen <= 128)
|
||||
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, 384);
|
||||
size_t excessLen = keyLen - 384;
|
||||
memcpy (signingKey + 384, m_ExtendedBufferPtr + 4, excessLen); // right after signing and crypto key types
|
||||
verifier->SetPublicKey (signingKey);
|
||||
delete[] signingKey;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// for P521
|
||||
|
@ -463,14 +399,15 @@ namespace data
|
|||
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||
return std::make_shared<i2p::crypto::ECIESGOSTR3410Encryptor>(key);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType);
|
||||
};
|
||||
|
@ -495,9 +432,7 @@ namespace data
|
|||
{
|
||||
m_Public = std::make_shared<IdentityEx>(Identity (keys));
|
||||
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
|
||||
size_t keyLen = m_Public->GetSigningPrivateKeyLen ();
|
||||
m_SigningPrivateKey.resize (keyLen);
|
||||
memcpy (m_SigningPrivateKey.data (), keys.signingPrivateKey, keyLen);
|
||||
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public->GetSigningPrivateKeyLen ());
|
||||
m_OfflineSignature.resize (0);
|
||||
m_TransientSignatureLen = 0;
|
||||
m_TransientSigningPrivateKeyLen = 0;
|
||||
|
@ -513,7 +448,7 @@ namespace data
|
|||
m_OfflineSignature = other.m_OfflineSignature;
|
||||
m_TransientSignatureLen = other.m_TransientSignatureLen;
|
||||
m_TransientSigningPrivateKeyLen = other.m_TransientSigningPrivateKeyLen;
|
||||
m_SigningPrivateKey = other.m_SigningPrivateKey;
|
||||
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_TransientSigningPrivateKeyLen > 0 ? m_TransientSigningPrivateKeyLen : m_Public->GetSigningPrivateKeyLen ());
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
return *this;
|
||||
|
@ -536,9 +471,8 @@ namespace data
|
|||
memcpy (m_PrivateKey, buf + ret, cryptoKeyLen);
|
||||
ret += cryptoKeyLen;
|
||||
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
|
||||
if (signingPrivateKeySize + ret > len) return 0; // overflow
|
||||
m_SigningPrivateKey.resize (signingPrivateKeySize);
|
||||
memcpy (m_SigningPrivateKey.data (), buf + ret, signingPrivateKeySize);
|
||||
if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow
|
||||
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
m_Signer = nullptr;
|
||||
// check if signing private key is all zeros
|
||||
|
@ -579,9 +513,8 @@ namespace data
|
|||
memcpy (m_OfflineSignature.data (), offlineInfo, offlineInfoLen);
|
||||
// override signing private key
|
||||
m_TransientSigningPrivateKeyLen = transientVerifier->GetPrivateKeyLen ();
|
||||
if (m_TransientSigningPrivateKeyLen + ret > len) return 0;
|
||||
if (m_TransientSigningPrivateKeyLen > 128) m_SigningPrivateKey.resize (m_TransientSigningPrivateKeyLen);
|
||||
memcpy (m_SigningPrivateKey.data (), buf + ret, m_TransientSigningPrivateKeyLen);
|
||||
if (m_TransientSigningPrivateKeyLen + ret > len || m_TransientSigningPrivateKeyLen > 128) return 0;
|
||||
memcpy (m_SigningPrivateKey, buf + ret, m_TransientSigningPrivateKeyLen);
|
||||
ret += m_TransientSigningPrivateKeyLen;
|
||||
CreateSigner (keyType);
|
||||
}
|
||||
|
@ -601,7 +534,7 @@ namespace data
|
|||
if (IsOfflineSignature ())
|
||||
memset (buf + ret, 0, signingPrivateKeySize);
|
||||
else
|
||||
memcpy (buf + ret, m_SigningPrivateKey.data (), signingPrivateKeySize);
|
||||
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
if (IsOfflineSignature ())
|
||||
{
|
||||
|
@ -612,24 +545,32 @@ namespace data
|
|||
ret += offlineSignatureLen;
|
||||
// transient private key
|
||||
if (ret + m_TransientSigningPrivateKeyLen > len) return 0;
|
||||
memcpy (buf + ret, m_SigningPrivateKey.data (), m_TransientSigningPrivateKeyLen);
|
||||
memcpy (buf + ret, m_SigningPrivateKey, m_TransientSigningPrivateKeyLen);
|
||||
ret += m_TransientSigningPrivateKeyLen;
|
||||
}
|
||||
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 ());
|
||||
size_t l = i2p::data::Base64ToByteStream (s, buf.data (), buf.size ());
|
||||
return FromBuffer (buf.data (), l);
|
||||
uint8_t * buf = new uint8_t[s.length ()];
|
||||
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
|
||||
size_t ret = FromBuffer (buf, l);
|
||||
delete[] buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string PrivateKeys::ToBase64 () const
|
||||
{
|
||||
std::vector<uint8_t> buf(GetFullLen ());
|
||||
size_t l = ToBuffer (buf.data (), buf.size ());
|
||||
return i2p::data::ByteStreamToBase64 (buf.data (), l);
|
||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
||||
char * str = new char[GetFullLen ()*2];
|
||||
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
|
||||
|
@ -651,13 +592,13 @@ namespace data
|
|||
{
|
||||
if (m_Signer) return;
|
||||
if (keyType == SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey.data (), m_Public->GetStandardIdentity ().signingKey));
|
||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
|
||||
else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature ())
|
||||
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey.data (), m_Public->GetStandardIdentity ().signingKey + (sizeof(Identity::signingKey) - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH))); // TODO: remove public key check
|
||||
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey + (sizeof(Identity::signingKey) - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH))); // TODO: remove public key check
|
||||
else
|
||||
{
|
||||
// public key is not required
|
||||
auto signer = CreateSigner (keyType, m_SigningPrivateKey.data ());
|
||||
auto signer = CreateSigner (keyType, m_SigningPrivateKey);
|
||||
if (signer) m_Signer.reset (signer);
|
||||
}
|
||||
}
|
||||
|
@ -692,11 +633,6 @@ namespace data
|
|||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
return new i2p::crypto::RedDSA25519Signer (priv);
|
||||
break;
|
||||
#if OPENSSL_PQ
|
||||
case SIGNING_KEY_TYPE_MLDSA44:
|
||||
return new i2p::crypto::MLDSA44Signer (priv);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
|
||||
}
|
||||
|
@ -710,7 +646,8 @@ namespace data
|
|||
|
||||
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()
|
||||
|
@ -736,14 +673,15 @@ namespace data
|
|||
return std::make_shared<i2p::crypto::ElGamalDecryptor>(key);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
|
||||
};
|
||||
|
@ -756,10 +694,8 @@ namespace data
|
|||
{
|
||||
PrivateKeys keys;
|
||||
// signature
|
||||
std::unique_ptr<i2p::crypto::Verifier> verifier (IdentityEx::CreateVerifier (type));
|
||||
std::vector<uint8_t> signingPublicKey(verifier->GetPublicKeyLen ());
|
||||
keys.m_SigningPrivateKey.resize (verifier->GetPrivateKeyLen ());
|
||||
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey.data (), signingPublicKey.data ());
|
||||
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
|
||||
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
// encryption
|
||||
uint8_t publicKey[256];
|
||||
if (isDestination)
|
||||
|
@ -767,7 +703,7 @@ namespace data
|
|||
else
|
||||
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
|
||||
// identity
|
||||
keys.m_Public = std::make_shared<IdentityEx> (isDestination ? nullptr : publicKey, signingPublicKey.data (), type, cryptoType);
|
||||
keys.m_Public = std::make_shared<IdentityEx> (isDestination ? nullptr : publicKey, signingPublicKey, type, cryptoType);
|
||||
|
||||
keys.CreateSigner ();
|
||||
return keys;
|
||||
|
@ -792,7 +728,9 @@ namespace data
|
|||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
// no break here
|
||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||
i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub);
|
||||
|
@ -806,11 +744,6 @@ namespace data
|
|||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub);
|
||||
break;
|
||||
#if OPENSSL_PQ
|
||||
case SIGNING_KEY_TYPE_MLDSA44:
|
||||
i2p::crypto::CreateMLDSA44RandomKeys (priv, pub);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
||||
i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1
|
||||
|
@ -825,12 +758,13 @@ namespace data
|
|||
i2p::crypto::GenerateElGamalKeyPair(priv, pub);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||
i2p::crypto::CreateECIESP256RandomKeys (priv, pub);
|
||||
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_MLKEM512_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD:
|
||||
case CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD:
|
||||
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
|
||||
break;
|
||||
default:
|
||||
|
@ -848,10 +782,9 @@ namespace data
|
|||
keys.m_TransientSigningPrivateKeyLen = verifier->GetPrivateKeyLen ();
|
||||
keys.m_TransientSignatureLen = verifier->GetSignatureLen ();
|
||||
keys.m_OfflineSignature.resize (pubKeyLen + m_Public->GetSignatureLen () + 6);
|
||||
keys.m_SigningPrivateKey.resize (verifier->GetPrivateKeyLen ());
|
||||
htobe32buf (keys.m_OfflineSignature.data (), expires); // expires
|
||||
htobe16buf (keys.m_OfflineSignature.data () + 4, type); // type
|
||||
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey.data (), keys.m_OfflineSignature.data () + 6); // public key
|
||||
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key
|
||||
Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature
|
||||
// recreate signer
|
||||
keys.m_Signer = 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
|
||||
*
|
||||
|
@ -12,19 +12,14 @@
|
|||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "Base.h"
|
||||
#include "Signature.h"
|
||||
#include "CryptoKey.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
class CryptoKeyEncryptor;
|
||||
class CryptoKeyDecryptor;
|
||||
}
|
||||
namespace data
|
||||
{
|
||||
typedef Tag<32> IdentHash;
|
||||
|
@ -59,8 +54,6 @@ namespace data
|
|||
Identity& operator=(const Keys& keys);
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
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 ();
|
||||
|
@ -70,9 +63,8 @@ namespace data
|
|||
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_X25519_AEAD = 4;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM512_X25519_AEAD = 5;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM768_X25519_AEAD = 6;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_MLKEM1024_X25519_AEAD = 7;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
|
||||
|
||||
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
|
||||
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_SHA512_4096 = 6;
|
||||
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_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_MLDSA44 = 12;
|
||||
|
||||
typedef uint16_t SigningKeyType;
|
||||
typedef uint16_t CryptoKeyType;
|
||||
|
@ -108,7 +99,7 @@ namespace data
|
|||
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
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;
|
||||
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
|
||||
|
||||
|
@ -142,11 +133,7 @@ namespace data
|
|||
IdentHash m_IdentHash;
|
||||
std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
||||
size_t m_ExtendedLen;
|
||||
union
|
||||
{
|
||||
uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE];
|
||||
uint8_t * m_ExtendedBufferPtr;
|
||||
};
|
||||
};
|
||||
|
||||
size_t GetIdentityBufferLen (const uint8_t * buf, size_t len); // return actual identity length in buffer
|
||||
|
@ -164,7 +151,7 @@ namespace data
|
|||
|
||||
std::shared_ptr<const IdentityEx> GetPublic () const { return m_Public; };
|
||||
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
|
||||
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey.data (); };
|
||||
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
|
||||
size_t GetSignatureLen () const; // might not match identity
|
||||
bool IsOfflineSignature () const { return m_TransientSignatureLen > 0; };
|
||||
uint8_t * GetPadding();
|
||||
|
@ -175,7 +162,7 @@ namespace data
|
|||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
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::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
|
||||
|
@ -200,7 +187,7 @@ namespace data
|
|||
|
||||
std::shared_ptr<IdentityEx> m_Public;
|
||||
uint8_t m_PrivateKey[256];
|
||||
std::vector<uint8_t> m_SigningPrivateKey;
|
||||
uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes
|
||||
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
|
||||
std::vector<uint8_t> m_OfflineSignature; // non zero length, if applicable
|
||||
size_t m_TransientSignatureLen = 0;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -14,7 +14,6 @@
|
|||
#include "Timestamp.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "Tunnel.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -36,7 +35,7 @@ namespace data
|
|||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
void LeaseSet::Update (const uint8_t * buf, size_t len, std::shared_ptr<LocalDestination> dest, bool verifySignature)
|
||||
void LeaseSet::Update (const uint8_t * buf, size_t len, bool verifySignature)
|
||||
{
|
||||
SetBuffer (buf, len);
|
||||
ReadFromBuffer (false, verifySignature);
|
||||
|
@ -281,29 +280,28 @@ namespace data
|
|||
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen));
|
||||
}
|
||||
|
||||
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len,
|
||||
bool storeLeases, std::shared_ptr<LocalDestination> dest, CryptoKeyType preferredCrypto):
|
||||
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
|
||||
LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto)
|
||||
{
|
||||
SetBuffer (buf, len);
|
||||
if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
ReadFromBufferEncrypted (buf, len, nullptr, dest, nullptr);
|
||||
ReadFromBufferEncrypted (buf, len, nullptr, nullptr);
|
||||
else
|
||||
ReadFromBuffer (buf, len, dest);
|
||||
ReadFromBuffer (buf, len);
|
||||
}
|
||||
|
||||
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
|
||||
std::shared_ptr<LocalDestination> dest, const uint8_t * secret, CryptoKeyType preferredCrypto):
|
||||
const uint8_t * secret, CryptoKeyType preferredCrypto):
|
||||
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto)
|
||||
{
|
||||
ReadFromBufferEncrypted (buf, len, key, dest, secret);
|
||||
ReadFromBufferEncrypted (buf, len, key, secret);
|
||||
}
|
||||
|
||||
void LeaseSet2::Update (const uint8_t * buf, size_t len, std::shared_ptr<LocalDestination> dest, bool verifySignature)
|
||||
void LeaseSet2::Update (const uint8_t * buf, size_t len, bool verifySignature)
|
||||
{
|
||||
SetBuffer (buf, len);
|
||||
if (GetStoreType () != NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
ReadFromBuffer (buf, len, dest, false, verifySignature);
|
||||
ReadFromBuffer (buf, len, false, verifySignature);
|
||||
// TODO: implement encrypted
|
||||
}
|
||||
|
||||
|
@ -313,8 +311,7 @@ namespace data
|
|||
return ExtractPublishedTimestamp (buf, len, expiration) > m_PublishedTimestamp;
|
||||
}
|
||||
|
||||
void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len, std::shared_ptr<LocalDestination> dest,
|
||||
bool readIdentity, bool verifySignature)
|
||||
void LeaseSet2::ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity, bool verifySignature)
|
||||
{
|
||||
// standard LS2 header
|
||||
std::shared_ptr<const IdentityEx> identity;
|
||||
|
@ -352,7 +349,7 @@ namespace data
|
|||
switch (m_StoreType)
|
||||
{
|
||||
case NETDB_STORE_TYPE_STANDARD_LEASESET2:
|
||||
s = ReadStandardLS2TypeSpecificPart (buf + offset, len - offset, dest);
|
||||
s = ReadStandardLS2TypeSpecificPart (buf + offset, len - offset);
|
||||
break;
|
||||
case NETDB_STORE_TYPE_META_LEASESET2:
|
||||
s = ReadMetaLS2TypeSpecificPart (buf + offset, len - offset);
|
||||
|
@ -394,8 +391,7 @@ namespace data
|
|||
return verified;
|
||||
}
|
||||
|
||||
size_t LeaseSet2::ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len,
|
||||
std::shared_ptr<LocalDestination> dest)
|
||||
size_t LeaseSet2::ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len)
|
||||
{
|
||||
size_t offset = 0;
|
||||
// properties
|
||||
|
@ -403,7 +399,6 @@ namespace data
|
|||
offset += propertiesLen; // skip for now. TODO: implement properties
|
||||
// key sections
|
||||
CryptoKeyType preferredKeyType = m_EncryptionType;
|
||||
m_EncryptionType = 0;
|
||||
bool preferredKeyFound = false;
|
||||
if (offset + 1 > len) return 0;
|
||||
int numKeySections = buf[offset]; offset++;
|
||||
|
@ -415,40 +410,21 @@ namespace data
|
|||
if (offset + encryptionKeyLen > len) return 0;
|
||||
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
|
||||
{
|
||||
// we pick max key type 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) &&
|
||||
(!dest || dest->SupportsEncryptionType (keyType)))
|
||||
{
|
||||
// we pick first valid key if preferred not found
|
||||
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
|
||||
if (encryptor)
|
||||
if (encryptor && (!m_Encryptor || keyType == preferredKeyType))
|
||||
{
|
||||
m_Encryptor = encryptor; // TODO: atomic
|
||||
m_EncryptionType = keyType;
|
||||
if (keyType == preferredKeyType) preferredKeyFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += encryptionKeyLen;
|
||||
}
|
||||
// leases
|
||||
if (offset + 1 > len) return 0;
|
||||
int numLeases = buf[offset]; offset++;
|
||||
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 ())
|
||||
{
|
||||
UpdateLeasesBegin ();
|
||||
|
@ -459,11 +435,6 @@ namespace data
|
|||
lease.tunnelGateway = buf + offset; offset += 32; // gateway
|
||||
lease.tunnelID = bufbe32toh (buf + offset); offset += 4; // tunnel ID
|
||||
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);
|
||||
}
|
||||
UpdateLeasesEnd ();
|
||||
|
@ -502,8 +473,7 @@ namespace data
|
|||
return offset;
|
||||
}
|
||||
|
||||
void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len,
|
||||
std::shared_ptr<const BlindedPublicKey> key, std::shared_ptr<LocalDestination> dest, const uint8_t * secret)
|
||||
void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret)
|
||||
{
|
||||
size_t offset = 0;
|
||||
// blinded key
|
||||
|
@ -606,7 +576,7 @@ namespace data
|
|||
m_StoreType = innerPlainText[0];
|
||||
SetBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1);
|
||||
// parse and verify Layer 2
|
||||
ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1, dest);
|
||||
ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "LeaseSet2: Unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet");
|
||||
|
@ -758,41 +728,25 @@ namespace data
|
|||
memset (m_Buffer + offset, 0, signingKeyLen);
|
||||
offset += signingKeyLen;
|
||||
// num leases
|
||||
auto numLeasesPos = offset;
|
||||
m_Buffer[offset] = num;
|
||||
offset++;
|
||||
// leases
|
||||
m_Leases = m_Buffer + offset;
|
||||
auto currentTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
int skipped = 0;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
|
||||
ts *= 1000; // in milliseconds
|
||||
if (ts <= currentTime)
|
||||
{
|
||||
// already expired, skip
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
|
||||
// make sure leaseset is newer than previous, but adding some time to expiration date
|
||||
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
|
||||
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
|
||||
offset += 32; // gateway id
|
||||
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
|
||||
offset += 4; // tunnel id
|
||||
uint64_t ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
|
||||
ts *= 1000; // in milliseconds
|
||||
if (ts > m_ExpirationTime) m_ExpirationTime = ts;
|
||||
// make sure leaseset is newer than previous, but adding some time to expiration date
|
||||
ts += (currentTime - tunnels[i]->GetCreationTime ()*1000LL)*2/i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; // up to 2 secs
|
||||
htobe64buf (m_Buffer + offset, ts);
|
||||
offset += 8; // end date
|
||||
}
|
||||
if (skipped > 0)
|
||||
{
|
||||
// adjust num leases
|
||||
if (skipped > num) skipped = num;
|
||||
num -= skipped;
|
||||
m_BufferLen -= skipped*LEASE_SIZE;
|
||||
m_Buffer[numLeasesPos] = num;
|
||||
}
|
||||
// we don't sign it yet. must be signed later on
|
||||
}
|
||||
|
||||
|
@ -853,8 +807,8 @@ namespace data
|
|||
}
|
||||
|
||||
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||
const EncryptionKeys& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
bool isPublic, uint64_t publishedTimestamp, bool isPublishedEncrypted):
|
||||
const KeySections& encryptionKeys, const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
bool isPublic, bool isPublishedEncrypted):
|
||||
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
|
||||
{
|
||||
auto identity = keys.GetPublic ();
|
||||
|
@ -863,7 +817,7 @@ namespace data
|
|||
if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES;
|
||||
size_t keySectionsLen = 0;
|
||||
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*/ +
|
||||
1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen ();
|
||||
uint16_t flags = 0;
|
||||
|
@ -883,7 +837,8 @@ namespace data
|
|||
m_Buffer[0] = storeType;
|
||||
// LS2 header
|
||||
auto offset = identity->ToBuffer (m_Buffer + 1, m_BufferLen) + 1;
|
||||
htobe32buf (m_Buffer + offset, publishedTimestamp); offset += 4; // published timestamp (seconds)
|
||||
auto timestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
|
||||
uint8_t * expiresBuf = m_Buffer + offset; offset += 2; // expires, fill later
|
||||
htobe16buf (m_Buffer + offset, flags); offset += 2; // flags
|
||||
if (keys.IsOfflineSignature ())
|
||||
|
@ -898,50 +853,35 @@ namespace data
|
|||
m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key
|
||||
for (const auto& it: encryptionKeys)
|
||||
{
|
||||
htobe16buf (m_Buffer + offset, it->keyType); offset += 2; // key type
|
||||
htobe16buf (m_Buffer + offset, it->pub.size()); offset += 2; // key len
|
||||
memcpy (m_Buffer + offset, it->pub.data(), it->pub.size()); offset += it->pub.size(); // key
|
||||
htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type
|
||||
htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len
|
||||
memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key
|
||||
}
|
||||
// leases
|
||||
uint32_t expirationTime = 0; // in seconds
|
||||
int skipped = 0; auto numLeasesPos = offset;
|
||||
m_Buffer[offset] = num; offset++; // num leases
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
auto ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration
|
||||
if (ts <= publishedTimestamp)
|
||||
{
|
||||
// already expired, skip
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
if (ts > expirationTime) expirationTime = ts;
|
||||
memcpy (m_Buffer + offset, tunnels[i]->GetNextIdentHash (), 32);
|
||||
offset += 32; // gateway id
|
||||
htobe32buf (m_Buffer + offset, tunnels[i]->GetNextTunnelID ());
|
||||
offset += 4; // tunnel id
|
||||
auto ts = tunnels[i]->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // in seconds, 1 minute before expiration
|
||||
if (ts > expirationTime) expirationTime = ts;
|
||||
htobe32buf (m_Buffer + offset, ts);
|
||||
offset += 4; // end date
|
||||
}
|
||||
if (skipped > 0)
|
||||
{
|
||||
// adjust num leases
|
||||
if (skipped > num) skipped = num;
|
||||
num -= skipped;
|
||||
m_BufferLen -= skipped*LEASE2_SIZE;
|
||||
m_Buffer[numLeasesPos] = num;
|
||||
}
|
||||
// update expiration
|
||||
if (expirationTime)
|
||||
{
|
||||
SetExpirationTime (expirationTime*1000LL);
|
||||
auto expires = (int)expirationTime - publishedTimestamp;
|
||||
auto expires = (int)expirationTime - timestamp;
|
||||
htobe16buf (expiresBuf, expires > 0 ? expires : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no tunnels or withdraw
|
||||
SetExpirationTime (publishedTimestamp*1000LL);
|
||||
SetExpirationTime (timestamp*1000LL);
|
||||
memset (expiresBuf, 0, 2); // expires immeditely
|
||||
}
|
||||
// sign
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -12,14 +12,12 @@
|
|||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "Identity.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Blinding.h"
|
||||
#include "CryptoKey.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -59,15 +57,11 @@ namespace data
|
|||
};
|
||||
|
||||
typedef std::function<bool(const Lease & l)> LeaseInspectFunc;
|
||||
#if OPENSSL_PQ
|
||||
const size_t MAX_LS_BUFFER_SIZE = 8192;
|
||||
#else
|
||||
const size_t MAX_LS_BUFFER_SIZE = 4096;
|
||||
#endif
|
||||
|
||||
const size_t MAX_LS_BUFFER_SIZE = 3072;
|
||||
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
|
||||
const size_t LEASE2_SIZE = 40; // 32 + 4 + 4
|
||||
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;
|
||||
class LeaseSet: public RoutingDestination
|
||||
|
@ -76,7 +70,7 @@ namespace data
|
|||
|
||||
LeaseSet (const uint8_t * buf, size_t len, bool storeLeases = true);
|
||||
virtual ~LeaseSet () { delete[] m_EncryptionKey; delete[] m_Buffer; };
|
||||
virtual void Update (const uint8_t * buf, size_t len, std::shared_ptr<LocalDestination> dest, bool verifySignature);
|
||||
virtual void Update (const uint8_t * buf, size_t len, bool verifySignature = true);
|
||||
virtual bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
void PopulateLeases (); // from buffer
|
||||
|
||||
|
@ -155,42 +149,38 @@ namespace data
|
|||
public:
|
||||
|
||||
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,
|
||||
std::shared_ptr<LocalDestination> dest = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
|
||||
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
|
||||
std::shared_ptr<LocalDestination> dest = nullptr, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // store type 5, called from local netdb only
|
||||
uint8_t GetStoreType () const override { return m_StoreType; };
|
||||
uint32_t GetPublishedTimestamp () const override { return m_PublishedTimestamp; };
|
||||
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_ELGAMAL); // store type 5, called from local netdb only
|
||||
uint8_t GetStoreType () const { return m_StoreType; };
|
||||
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
bool IsPublishedEncrypted () const override { return m_IsPublishedEncrypted; };
|
||||
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const override { return m_TransientVerifier; };
|
||||
void Update (const uint8_t * buf, size_t len, std::shared_ptr<LocalDestination> dest, bool verifySignature) override;
|
||||
bool IsNewer (const uint8_t * buf, size_t len) const override;
|
||||
bool IsPublishedEncrypted () const { return m_IsPublishedEncrypted; };
|
||||
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; };
|
||||
void Update (const uint8_t * buf, size_t len, bool verifySignature);
|
||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||
|
||||
// implements RoutingDestination
|
||||
void Encrypt (const uint8_t * data, uint8_t * encrypted) const override;
|
||||
CryptoKeyType GetEncryptionType () const override { return m_EncryptionType; };
|
||||
void Encrypt (const uint8_t * data, uint8_t * encrypted) const;
|
||||
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
|
||||
|
||||
private:
|
||||
|
||||
void ReadFromBuffer (const uint8_t * buf, size_t len, std::shared_ptr<LocalDestination> dest,
|
||||
bool readIdentity = true, bool verifySignature = true);
|
||||
void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
|
||||
std::shared_ptr<LocalDestination> dest, const uint8_t * secret);
|
||||
size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len, std::shared_ptr<LocalDestination> dest);
|
||||
void ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity = true, bool verifySignature = true);
|
||||
void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret);
|
||||
size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len);
|
||||
size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len);
|
||||
|
||||
template<typename Verifier>
|
||||
bool VerifySignature (Verifier& verifier, const uint8_t * buf, size_t len, size_t signatureOffset);
|
||||
|
||||
uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const override;
|
||||
uint64_t ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const;
|
||||
uint64_t ExtractPublishedTimestamp (const uint8_t * buf, size_t len, uint64_t& expiration) const;
|
||||
size_t ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const; // subcredential is subcredential + timestamp, return length of autData without flag
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_StoreType;
|
||||
uint32_t m_PublishedTimestamp = 0; // seconds
|
||||
uint32_t m_PublishedTimestamp = 0;
|
||||
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
|
||||
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
|
||||
CryptoKeyType m_EncryptionType;
|
||||
|
@ -256,13 +246,17 @@ namespace data
|
|||
{
|
||||
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,
|
||||
const EncryptionKeys& encryptionKeys,
|
||||
const KeySections& encryptionKeys,
|
||||
const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels,
|
||||
bool isPublic, uint64_t publishedTimestamp,
|
||||
bool isPublishedEncrypted = false);
|
||||
bool isPublic, bool isPublishedEncrypted = false);
|
||||
|
||||
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
|
||||
|
||||
|
|
|
@ -172,6 +172,16 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept
|
|||
s << std::forward<TValue>(arg);
|
||||
}
|
||||
|
||||
#if (__cplusplus < 201703L) // below C++ 17
|
||||
/** internal usage only -- folding args array to single string */
|
||||
template<typename TValue, typename... TArgs>
|
||||
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
|
||||
{
|
||||
LogPrint (s, std::forward<TValue>(arg));
|
||||
LogPrint (s, std::forward<TArgs>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create log message and send it to queue
|
||||
* @param level Message level (eLogError, eLogInfo, ...)
|
||||
|
@ -184,7 +194,13 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
|
|||
|
||||
// fold message to single string
|
||||
std::stringstream ss;
|
||||
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
(LogPrint (ss, std::forward<TArgs>(args)), ...);
|
||||
#else
|
||||
LogPrint (ss, std::forward<TArgs>(args)...);
|
||||
#endif
|
||||
|
||||
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), std::move(ss).str());
|
||||
msg->tid = std::this_thread::get_id();
|
||||
i2p::log::Logger().Append(msg);
|
||||
|
@ -201,7 +217,11 @@ void ThrowFatal (TArgs&&... args) noexcept
|
|||
if (!f) return;
|
||||
// fold message to single string
|
||||
std::stringstream ss("");
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
(LogPrint (ss, std::forward<TArgs>(args)), ...);
|
||||
#else
|
||||
LogPrint (ss, std::forward<TArgs>(args)...);
|
||||
#endif
|
||||
f (ss.str ());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -42,29 +42,28 @@ namespace transport
|
|||
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);
|
||||
// h = SHA256(h || epub)
|
||||
MixHash (epub, 32);
|
||||
// x25519 between pub and priv
|
||||
uint8_t inputKeyMaterial[32];
|
||||
if (!priv.Agree (pub, inputKeyMaterial)) return false;
|
||||
priv.Agree (pub, 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
|
||||
|
||||
|
@ -75,35 +74,33 @@ namespace transport
|
|||
|
||||
// x25519 between remote pub and ephemaral priv
|
||||
uint8_t inputKeyMaterial[32];
|
||||
if (!m_EphemeralKeys->Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
||||
m_EphemeralKeys->Agree (GetRemotePub (), 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];
|
||||
if (!i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial)) return false;
|
||||
i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial);
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::KDF3Bob ()
|
||||
void NTCP2Establisher::KDF3Bob ()
|
||||
{
|
||||
uint8_t inputKeyMaterial[32];
|
||||
if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial)) return false;
|
||||
m_EphemeralKeys->Agree (m_RemoteStaticKey, inputKeyMaterial);
|
||||
MixKey (inputKeyMaterial);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCP2Establisher::CreateEphemeralKey ()
|
||||
|
@ -111,19 +108,20 @@ namespace transport
|
|||
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::CreateSessionRequestMessage (std::mt19937& rng)
|
||||
void NTCP2Establisher::CreateSessionRequestMessage ()
|
||||
{
|
||||
// create buffer and fill padding
|
||||
auto paddingLength = rng () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
|
||||
auto paddingLength = rand () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes
|
||||
m_SessionRequestBufferLen = paddingLength + 64;
|
||||
RAND_bytes (m_SessionRequestBuffer + 64, paddingLength);
|
||||
// encrypt X
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
encryption.SetKey (m_RemoteIdentHash);
|
||||
encryption.Encrypt (GetPub (), 32, m_IV, m_SessionRequestBuffer); // X
|
||||
memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated
|
||||
encryption.SetIV (m_IV);
|
||||
encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X
|
||||
encryption.GetIV (m_IV); // save IV for SessionCreated
|
||||
// encryption key for next block
|
||||
if (!KDF1Alice ()) return false;
|
||||
KDF1Alice ();
|
||||
// fill options
|
||||
uint8_t options[32]; // actual options size is 16 bytes
|
||||
memset (options, 0, 16);
|
||||
|
@ -145,40 +143,36 @@ namespace transport
|
|||
// 2 bytes reserved
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsA, rounded to seconds
|
||||
// 4 bytes reserved
|
||||
// encrypt options
|
||||
if (!Encrypt (options, m_SessionRequestBuffer + 32, 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest failed to encrypt options");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// sign and encrypt options, use m_H as AD
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::CreateSessionCreatedMessage (std::mt19937& rng)
|
||||
void NTCP2Establisher::CreateSessionCreatedMessage ()
|
||||
{
|
||||
auto paddingLen = rng () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
|
||||
auto paddingLen = rand () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64);
|
||||
m_SessionCreatedBufferLen = paddingLen + 64;
|
||||
RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen);
|
||||
// encrypt Y
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
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)
|
||||
if (!KDF2Bob ()) return false;
|
||||
KDF2Bob ();
|
||||
uint8_t options[16];
|
||||
memset (options, 0, 16);
|
||||
htobe16buf (options + 2, paddingLen); // padLen
|
||||
htobe32buf (options + 8, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); // tsB, rounded to seconds
|
||||
// encrypt options
|
||||
if (!Encrypt (options, m_SessionCreatedBuffer + 32, 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated failed to encrypt options");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// sign and encrypt options, use m_H as AD
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
|
||||
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart1 ()
|
||||
void NTCP2Establisher::CreateSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||
{
|
||||
// update AD
|
||||
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
||||
|
@ -186,31 +180,21 @@ namespace transport
|
|||
if (paddingLength > 0)
|
||||
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
||||
|
||||
// part1 48 bytes, n = 1
|
||||
if (!Encrypt (i2p::context.GetNTCP2StaticPublicKey (), m_SessionConfirmedBuffer, 32))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part1");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// part1 48 bytes
|
||||
i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::CreateSessionConfirmedMessagePart2 ()
|
||||
void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
|
||||
{
|
||||
// part 2
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
// encrypt m3p2, it must be filled in SessionRequest
|
||||
if (!KDF3Alice ()) return false; // MixKey, n = 0
|
||||
KDF3Alice ();
|
||||
uint8_t * m3p2 = m_SessionConfirmedBuffer + 48;
|
||||
if (!Encrypt (m3p2, m3p2, m3p2Len - 16))
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed failed to encrypt part2");
|
||||
return false;
|
||||
}
|
||||
i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt
|
||||
// update h again
|
||||
MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew)
|
||||
|
@ -219,17 +203,15 @@ namespace transport
|
|||
// decrypt X
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
decryption.SetKey (i2p::context.GetIdentHash ());
|
||||
decryption.Decrypt (m_SessionRequestBuffer, 32, i2p::context.GetNTCP2IV (), GetRemotePub ());
|
||||
memcpy (m_IV, m_SessionRequestBuffer + 16, 16); // save last block as IV for SessionCreated
|
||||
decryption.SetIV (i2p::context.GetNTCP2IV ());
|
||||
decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ());
|
||||
decryption.GetIV (m_IV); // save IV for SessionCreated
|
||||
// decryption key for next block
|
||||
if (!KDF1Bob ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest KDF failed");
|
||||
return false;
|
||||
}
|
||||
// verify MAC and decrypt options block (32 bytes)
|
||||
uint8_t options[16];
|
||||
if (Decrypt (m_SessionRequestBuffer + 32, options, 16))
|
||||
KDF1Bob ();
|
||||
// verify MAC and decrypt options block (32 bytes), use m_H as AD
|
||||
uint8_t nonce[12], options[16];
|
||||
memset (nonce, 0, 12); // set nonce to zero
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt
|
||||
{
|
||||
// options
|
||||
if (options[0] && options[0] != i2p::context.GetNetID ())
|
||||
|
@ -277,16 +259,15 @@ namespace transport
|
|||
// decrypt Y
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
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)
|
||||
if (!KDF2Alice ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionCreated KDF failed");
|
||||
return false;
|
||||
}
|
||||
KDF2Alice ();
|
||||
// decrypt and verify MAC
|
||||
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
|
||||
paddingLen = bufbe16toh(payload + 2);
|
||||
|
@ -307,7 +288,7 @@ namespace transport
|
|||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 ()
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce)
|
||||
{
|
||||
// update AD
|
||||
MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload
|
||||
|
@ -315,8 +296,7 @@ namespace transport
|
|||
if (paddingLength > 0)
|
||||
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
|
||||
|
||||
// decrypt S, n = 1
|
||||
if (!Decrypt (m_SessionConfirmedBuffer, m_RemoteStaticKey, 32))
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
|
||||
return false;
|
||||
|
@ -324,17 +304,13 @@ namespace transport
|
|||
return true;
|
||||
}
|
||||
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf)
|
||||
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf)
|
||||
{
|
||||
// update AD again
|
||||
MixHash (m_SessionConfirmedBuffer, 48);
|
||||
|
||||
if (!KDF3Bob ()) // MixKey, n = 0
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 KDF failed");
|
||||
return false;
|
||||
}
|
||||
if (Decrypt (m_SessionConfirmedBuffer + 48, m3p2Buf, m3p2Len - 16))
|
||||
KDF3Bob ();
|
||||
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
|
||||
// calculate new h again for KDF data
|
||||
MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
|
||||
else
|
||||
|
@ -351,7 +327,6 @@ namespace transport
|
|||
m_Server (server), m_Socket (m_Server.GetService ()),
|
||||
m_IsEstablished (false), m_IsTerminated (false),
|
||||
m_Establisher (new NTCP2Establisher),
|
||||
m_SendKey (nullptr), m_ReceiveKey (nullptr),
|
||||
#if OPENSSL_SIPHASH
|
||||
m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
|
||||
#else
|
||||
|
@ -374,7 +349,7 @@ namespace transport
|
|||
LogPrint (eLogWarning, "NTCP2: Missing NTCP2 address");
|
||||
}
|
||||
m_NextRouterInfoResendTime = i2p::util::GetSecondsSinceEpoch () + NTCP2_ROUTERINFO_RESEND_INTERVAL +
|
||||
m_Server.GetRng ()() % NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
|
||||
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
|
||||
}
|
||||
|
||||
NTCP2Session::~NTCP2Session ()
|
||||
|
@ -400,8 +375,6 @@ namespace transport
|
|||
m_Socket.close ();
|
||||
transports.PeerDisconnected (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)
|
||||
it->Drop ();
|
||||
m_SendQueue.clear ();
|
||||
|
@ -431,14 +404,14 @@ namespace transport
|
|||
|
||||
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 ()
|
||||
{
|
||||
m_IsEstablished = true;
|
||||
m_Establisher.reset (nullptr);
|
||||
SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT + m_Server.GetRng ()() % NTCP2_TERMINATION_TIMEOUT_VARIANCE);
|
||||
SetTerminationTimeout (NTCP2_TERMINATION_TIMEOUT);
|
||||
SendQueue ();
|
||||
transports.PeerConnected (shared_from_this ());
|
||||
}
|
||||
|
@ -491,12 +464,7 @@ namespace transport
|
|||
|
||||
void NTCP2Session::SendSessionRequest ()
|
||||
{
|
||||
if (!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;
|
||||
}
|
||||
m_Establisher->CreateSessionRequestMessage ();
|
||||
// send message
|
||||
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 (),
|
||||
|
@ -521,6 +489,7 @@ namespace transport
|
|||
|
||||
void NTCP2Session::HandleSessionRequestReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionRequest read error: ", ecode.message ());
|
||||
|
@ -528,18 +497,7 @@ namespace transport
|
|||
}
|
||||
else
|
||||
{
|
||||
m_Establisher->CreateEphemeralKey ();
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this (), bytes_transferred] ()
|
||||
{
|
||||
s->ProcessSessionRequest (bytes_transferred);;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::ProcessSessionRequest (size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: SessionRequest received ", len);
|
||||
LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred);
|
||||
uint16_t paddingLen = 0;
|
||||
bool clockSkew = false;
|
||||
if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew))
|
||||
|
@ -548,7 +506,7 @@ namespace transport
|
|||
{
|
||||
// we don't care about padding, send SessionCreated and close session
|
||||
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)
|
||||
{
|
||||
|
@ -560,14 +518,15 @@ namespace transport
|
|||
else
|
||||
{
|
||||
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
|
||||
SendSessionCreated ();
|
||||
}
|
||||
else
|
||||
ReadSomethingAndTerminate (); // probing resistance
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
|
@ -578,23 +537,12 @@ namespace transport
|
|||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
s->SendSessionCreated ();
|
||||
});
|
||||
}
|
||||
SendSessionCreated ();
|
||||
}
|
||||
|
||||
void NTCP2Session::SendSessionCreated ()
|
||||
{
|
||||
if (!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;
|
||||
}
|
||||
m_Establisher->CreateSessionCreatedMessage ();
|
||||
// send message
|
||||
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 (),
|
||||
|
@ -611,17 +559,7 @@ namespace transport
|
|||
else
|
||||
{
|
||||
m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval;
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this (), bytes_transferred] ()
|
||||
{
|
||||
s->ProcessSessionCreated (bytes_transferred);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::ProcessSessionCreated (size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: SessionCreated received ", len);
|
||||
LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred);
|
||||
uint16_t paddingLen = 0;
|
||||
if (m_Establisher->ProcessSessionCreatedMessage (paddingLen))
|
||||
{
|
||||
|
@ -635,7 +573,7 @@ namespace transport
|
|||
else
|
||||
{
|
||||
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
|
||||
|
@ -645,7 +583,8 @@ namespace transport
|
|||
{
|
||||
if (GetRemoteIdentity ())
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -659,27 +598,17 @@ namespace transport
|
|||
else
|
||||
{
|
||||
m_Establisher->m_SessionCreatedBufferLen += bytes_transferred;
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
s->SendSessionConfirmed ();
|
||||
});
|
||||
SendSessionConfirmed ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::SendSessionConfirmed ()
|
||||
{
|
||||
if (!m_Establisher->CreateSessionConfirmedMessagePart1 ())
|
||||
{
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce); // set nonce to 1
|
||||
m_Establisher->CreateSessionConfirmedMessagePart1 (nonce);
|
||||
memset (nonce, 0, 12); // set nonce back to 0
|
||||
m_Establisher->CreateSessionConfirmedMessagePart2 (nonce);
|
||||
// send message
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
|
@ -731,7 +660,6 @@ namespace transport
|
|||
|
||||
void NTCP2Session::HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
(void) bytes_transferred;
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP2: SessionConfirmed read error: ", ecode.message ());
|
||||
|
@ -740,56 +668,17 @@ namespace transport
|
|||
else
|
||||
{
|
||||
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");
|
||||
// part 1
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 ())
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (1, nonce);
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
|
||||
{
|
||||
// part 2
|
||||
auto buf = std::make_shared<std::vector<uint8_t> > (m_Establisher->m3p2Len - 16); // -MAC
|
||||
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (buf->data ())) // TODO:handle in establisher thread
|
||||
std::vector<uint8_t> buf(m_Establisher->m3p2Len - 16); // -MAC
|
||||
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 ();
|
||||
// Bob data phase keys
|
||||
m_SendKey = m_Kba;
|
||||
|
@ -797,9 +686,23 @@ namespace transport
|
|||
SetSipKeys (m_Sipkeysba, m_Sipkeysab);
|
||||
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 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)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Unexpected RouterInfo size ", size, " in SessionConfirmed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
// TODO: check flag
|
||||
i2p::data::RouterInfo ri (buf->data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
|
||||
if (ri.IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
|
||||
|
@ -821,33 +724,8 @@ namespace transport
|
|||
SendTerminationAndTerminate (eNTCP2Message3Error);
|
||||
return;
|
||||
}
|
||||
// update RouterInfo in netdb
|
||||
auto ri1 = i2p::data::netdb.AddRouterInfo (ri.GetBuffer (), ri.GetBufferLen ()); // ri1 points to one from netdb now
|
||||
if (!ri1)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Couldn't update RouterInfo from SessionConfirmed in netdb");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOlder = false;
|
||||
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
|
||||
{
|
||||
// received RouterInfo is older than one in netdb
|
||||
isOlder = true;
|
||||
if (ri1->HasProfile ())
|
||||
{
|
||||
auto profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
|
||||
if (profile && profile->IsDuplicated ())
|
||||
{
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri1->GetNTCP2V4Address () :
|
||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri1->GetYggdrasilAddress () : ri1->GetNTCP2V6Address ());
|
||||
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri.GetNTCP2V4Address () :
|
||||
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri.GetYggdrasilAddress () : ri.GetNTCP2V6Address ());
|
||||
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP2: Wrong static key in SessionConfirmed");
|
||||
|
@ -859,21 +737,16 @@ 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 (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
|
||||
{
|
||||
if (isOlder) // older router?
|
||||
i2p::data::UpdateRouterProfile (ri1->GetIdentHash (),
|
||||
[](std::shared_ptr<i2p::data::RouterProfile> profile)
|
||||
{
|
||||
if (profile) profile->Duplicated (); // mark router as duplicated in profile
|
||||
});
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||
SendTerminationAndTerminate (eNTCP2Banned);
|
||||
LogPrint (eLogError, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
// TODO: process options block
|
||||
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice
|
||||
// TODO: process options
|
||||
|
||||
// ready to communicate
|
||||
SetRemoteIdentity (ri1->GetRouterIdentity ());
|
||||
auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already
|
||||
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ());
|
||||
if (m_Server.AddNTCP2Session (shared_from_this (), true))
|
||||
{
|
||||
Established ();
|
||||
|
@ -882,6 +755,13 @@ namespace transport
|
|||
else
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey)
|
||||
{
|
||||
|
@ -908,17 +788,14 @@ namespace transport
|
|||
void NTCP2Session::ClientLogin ()
|
||||
{
|
||||
m_Establisher->CreateEphemeralKey ();
|
||||
boost::asio::post (m_Server.GetEstablisherService (),
|
||||
[s = shared_from_this ()] ()
|
||||
{
|
||||
s->SendSessionRequest ();
|
||||
});
|
||||
SendSessionRequest ();
|
||||
}
|
||||
|
||||
void NTCP2Session::ServerLogin ()
|
||||
{
|
||||
SetTerminationTimeout (NTCP2_ESTABLISH_TIMEOUT);
|
||||
SetLastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ());
|
||||
m_Establisher->CreateEphemeralKey ();
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (),
|
||||
std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
|
@ -1009,7 +886,7 @@ namespace transport
|
|||
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred + 2);
|
||||
uint8_t nonce[12];
|
||||
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");
|
||||
ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16);
|
||||
|
@ -1062,19 +939,7 @@ namespace transport
|
|||
case eNTCP2BlkRouterInfo:
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP2: RouterInfo flag=", (int)frame[offset]);
|
||||
if (size <= i2p::data::MAX_RI_BUFFER_SIZE + 1)
|
||||
{
|
||||
auto newRi = i2p::data::netdb.AddRouterInfo (frame + offset + 1, size - 1);
|
||||
if (newRi)
|
||||
{
|
||||
auto remoteIdentity = GetRemoteIdentity ();
|
||||
if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ())
|
||||
// peer's RouterInfo update
|
||||
SetRemoteIdentity (newRi->GetIdentity ());
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP2: RouterInfo block is too long ", size);
|
||||
i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, frame + offset, size));
|
||||
break;
|
||||
}
|
||||
case eNTCP2BlkI2NPMessage:
|
||||
|
@ -1198,7 +1063,7 @@ namespace transport
|
|||
}
|
||||
uint8_t nonce[12];
|
||||
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
|
||||
|
||||
// send buffers
|
||||
|
@ -1229,7 +1094,7 @@ namespace transport
|
|||
// encrypt
|
||||
uint8_t nonce[12];
|
||||
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);
|
||||
// send
|
||||
m_IsSending = true;
|
||||
|
@ -1256,7 +1121,7 @@ namespace transport
|
|||
if (GetLastActivityTimestamp () > m_NextRouterInfoResendTime)
|
||||
{
|
||||
m_NextRouterInfoResendTime += NTCP2_ROUTERINFO_RESEND_INTERVAL +
|
||||
m_Server.GetRng ()() % NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
|
||||
rand ()%NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD;
|
||||
SendRouterInfo ();
|
||||
}
|
||||
else
|
||||
|
@ -1309,7 +1174,7 @@ namespace transport
|
|||
void NTCP2Session::MoveSendQueue (std::shared_ptr<NTCP2Session> other)
|
||||
{
|
||||
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 ();
|
||||
for (auto it: m_SendQueue)
|
||||
if (!it->IsExpired (ts))
|
||||
|
@ -1318,7 +1183,7 @@ namespace transport
|
|||
it->Drop ();
|
||||
m_SendQueue.clear ();
|
||||
if (!msgs.empty ())
|
||||
other->SendI2NPMessages (msgs);
|
||||
other->PostI2NPMessages (msgs);
|
||||
}
|
||||
|
||||
size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len)
|
||||
|
@ -1396,58 +1261,23 @@ namespace transport
|
|||
void NTCP2Session::SendTerminationAndTerminate (NTCP2TerminationReason 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::ReadSomethingAndTerminate ()
|
||||
void NTCP2Session::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
size_t len = m_Server.GetRng ()() % NTCP2_SESSION_REQUEST_MAX_SIZE;
|
||||
if (len > 0 && m_Establisher)
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, len), boost::asio::transfer_all (),
|
||||
[s = shared_from_this()](const boost::system::error_code& ecode, size_t bytes_transferred)
|
||||
{
|
||||
s->Terminate ();
|
||||
});
|
||||
else
|
||||
boost::asio::post (m_Server.GetService (), std::bind (&NTCP2Session::Terminate, shared_from_this ()));
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::PostI2NPMessages, shared_from_this (), msgs));
|
||||
}
|
||||
|
||||
void NTCP2Session::SendI2NPMessages (std::list<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
if (m_IsTerminated || msgs.empty ())
|
||||
{
|
||||
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;
|
||||
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;
|
||||
if (isSemiFull)
|
||||
{
|
||||
for (auto it: msgs)
|
||||
if (it->onDrop)
|
||||
if (isSemiFull && it->onDrop)
|
||||
it->Drop (); // drop earlier because we can handle it
|
||||
else
|
||||
m_SendQueue.push_back (std::move (it));
|
||||
}
|
||||
else
|
||||
m_SendQueue.splice (m_SendQueue.end (), msgs);
|
||||
|
||||
if (!m_IsSending && m_IsEstablished)
|
||||
SendQueue ();
|
||||
|
@ -1463,19 +1293,12 @@ namespace transport
|
|||
void NTCP2Session::SendLocalRouterInfo (bool update)
|
||||
{
|
||||
if (update || !IsOutgoing ()) // we send it in SessionConfirmed for outgoing session
|
||||
boost::asio::post (m_Server.GetService (), 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;
|
||||
m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
|
||||
}
|
||||
|
||||
NTCP2Server::NTCP2Server ():
|
||||
RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()),
|
||||
m_ProxyType(eNoProxy), m_Resolver(GetService ()),
|
||||
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
|
||||
m_ProxyType(eNoProxy), m_Resolver(GetService ())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1486,7 +1309,6 @@ namespace transport
|
|||
|
||||
void NTCP2Server::Start ()
|
||||
{
|
||||
m_EstablisherService.Start ();
|
||||
if (!IsRunning ())
|
||||
{
|
||||
StartIOService ();
|
||||
|
@ -1494,13 +1316,14 @@ namespace transport
|
|||
{
|
||||
LogPrint(eLogInfo, "NTCP2: Using proxy to connect to peers");
|
||||
// TODO: resolve proxy until it is resolved
|
||||
boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort));
|
||||
boost::system::error_code e;
|
||||
auto itr = m_Resolver.resolve(m_ProxyAddress, std::to_string(m_ProxyPort), e);
|
||||
auto itr = m_Resolver.resolve(q, e);
|
||||
if(e)
|
||||
LogPrint(eLogCritical, "NTCP2: Failed to resolve proxy ", e.message());
|
||||
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)
|
||||
LogPrint(eLogDebug, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint);
|
||||
}
|
||||
|
@ -1581,7 +1404,6 @@ namespace transport
|
|||
|
||||
void NTCP2Server::Stop ()
|
||||
{
|
||||
m_EstablisherService.Stop ();
|
||||
{
|
||||
// we have to copy it because Terminate changes m_NTCP2Sessions
|
||||
auto ntcpSessions = m_NTCP2Sessions;
|
||||
|
@ -1661,7 +1483,7 @@ namespace transport
|
|||
}
|
||||
LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint (),
|
||||
" (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")");
|
||||
boost::asio::post (GetService (), [this, conn]()
|
||||
GetService ().post([this, conn]()
|
||||
{
|
||||
if (this->AddNTCP2Session (conn))
|
||||
{
|
||||
|
@ -1819,8 +1641,7 @@ namespace transport
|
|||
|
||||
void NTCP2Server::ScheduleTermination ()
|
||||
{
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(
|
||||
NTCP2_TERMINATION_CHECK_TIMEOUT + m_Rng () % NTCP2_TERMINATION_CHECK_TIMEOUT_VARIANCE));
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP2_TERMINATION_CHECK_TIMEOUT));
|
||||
m_TerminationTimer.async_wait (std::bind (&NTCP2Server::HandleTerminationTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
@ -1882,7 +1703,7 @@ namespace transport
|
|||
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
|
||||
return;
|
||||
}
|
||||
boost::asio::post (GetService(), [this, conn]()
|
||||
GetService().post([this, conn]()
|
||||
{
|
||||
if (this->AddNTCP2Session (conn))
|
||||
{
|
||||
|
@ -1966,7 +1787,7 @@ namespace transport
|
|||
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",
|
||||
[readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred)
|
||||
{
|
||||
|
@ -1980,12 +1801,13 @@ namespace transport
|
|||
{
|
||||
readbuff->commit(transferred);
|
||||
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)
|
||||
{
|
||||
timer->cancel();
|
||||
conn->ClientLogin();
|
||||
delete readbuff;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -1995,6 +1817,7 @@ namespace transport
|
|||
LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response");
|
||||
timer->cancel();
|
||||
conn->Terminate();
|
||||
delete readbuff;
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
@ -2017,17 +1840,5 @@ namespace transport
|
|||
else
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -14,7 +14,6 @@
|
|||
#include <list>
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <boost/asio.hpp>
|
||||
|
@ -36,10 +35,8 @@ namespace transport
|
|||
|
||||
const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds
|
||||
const int NTCP2_TERMINATION_TIMEOUT = 115; // 2 minutes - 5 seconds
|
||||
const int NTCP2_TERMINATION_TIMEOUT_VARIANCE = 10; // 10 seconds
|
||||
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 28; // 28 seconds
|
||||
const int NTCP2_TERMINATION_CHECK_TIMEOUT_VARIANCE = 5; // 5 seconds
|
||||
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds
|
||||
const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds
|
||||
const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes
|
||||
|
@ -91,29 +88,30 @@ namespace transport
|
|||
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
|
||||
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
|
||||
|
||||
const uint8_t * GetK () const { return m_CK + 32; };
|
||||
const uint8_t * GetCK () const { return m_CK; };
|
||||
const uint8_t * GetH () const { return m_H; };
|
||||
|
||||
bool KDF1Alice ();
|
||||
bool KDF1Bob ();
|
||||
bool KDF2Alice ();
|
||||
bool KDF2Bob ();
|
||||
bool KDF3Alice (); // for SessionConfirmed part 2
|
||||
bool KDF3Bob ();
|
||||
void KDF1Alice ();
|
||||
void KDF1Bob ();
|
||||
void KDF2Alice ();
|
||||
void KDF2Bob ();
|
||||
void KDF3Alice (); // for SessionConfirmed part 2
|
||||
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
|
||||
bool KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
|
||||
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
|
||||
void CreateEphemeralKey ();
|
||||
|
||||
bool CreateSessionRequestMessage (std::mt19937& rng);
|
||||
bool CreateSessionCreatedMessage (std::mt19937& rng);
|
||||
bool CreateSessionConfirmedMessagePart1 ();
|
||||
bool CreateSessionConfirmedMessagePart2 ();
|
||||
void CreateSessionRequestMessage ();
|
||||
void CreateSessionCreatedMessage ();
|
||||
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
|
||||
|
||||
bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew);
|
||||
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
|
||||
bool ProcessSessionConfirmedMessagePart1 ();
|
||||
bool ProcessSessionConfirmedMessagePart2 (uint8_t * m3p2Buf);
|
||||
bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce);
|
||||
bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
|
||||
|
||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
|
||||
|
@ -146,14 +144,13 @@ namespace transport
|
|||
void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
||||
|
||||
bool IsEstablished () const override { return m_IsEstablished; };
|
||||
i2p::data::RouterInfo::SupportedTransports GetTransportType () const override;
|
||||
bool IsTerminated () const { return m_IsTerminated; };
|
||||
|
||||
void ClientLogin (); // Alice
|
||||
void ServerLogin (); // Bob
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@ -172,16 +169,12 @@ namespace transport
|
|||
|
||||
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 ProcessSessionRequest (size_t len);
|
||||
void HandleSessionRequestPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void ProcessSessionCreated (size_t len);
|
||||
void HandleSessionCreatedPaddingReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleSessionConfirmedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void ProcessSessionConfirmed ();
|
||||
void EstablishSessionAfterSessionConfirmed (std::shared_ptr<std::vector<uint8_t> > buf, size_t size);
|
||||
|
||||
// data
|
||||
void ReceiveLength ();
|
||||
|
@ -200,8 +193,7 @@ namespace transport
|
|||
void SendRouterInfo ();
|
||||
void SendTermination (NTCP2TerminationReason reason);
|
||||
void SendTerminationAndTerminate (NTCP2TerminationReason reason);
|
||||
void ReadSomethingAndTerminate ();
|
||||
void PostI2NPMessages ();
|
||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -235,27 +227,12 @@ namespace transport
|
|||
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
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];
|
||||
int m_NextPaddingSize;
|
||||
};
|
||||
|
||||
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:
|
||||
|
||||
enum ProxyType
|
||||
|
@ -270,14 +247,7 @@ namespace transport
|
|||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
auto& GetService () { return GetIOService (); };
|
||||
auto& GetEstablisherService () { return m_EstablisherService.GetService (); };
|
||||
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);
|
||||
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
|
||||
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
|
||||
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
|
||||
|
@ -315,12 +285,7 @@ namespace transport
|
|||
uint16_t m_ProxyPort;
|
||||
boost::asio::ip::tcp::resolver m_Resolver;
|
||||
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::mt19937 m_Rng;
|
||||
EstablisherService m_EstablisherService;
|
||||
i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor;
|
||||
i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor;
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -10,6 +10,7 @@
|
|||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <boost/asio.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
|
@ -39,7 +40,7 @@ namespace data
|
|||
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr),
|
||||
m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true),
|
||||
m_LastExploratorySelectionUpdateTime (0), m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL)
|
||||
m_LastExploratorySelectionUpdateTime (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -68,7 +69,7 @@ namespace data
|
|||
{
|
||||
Reseed ();
|
||||
}
|
||||
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false, false))
|
||||
else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false))
|
||||
Reseed (); // we don't have a router we can connect to. Trying to reseed
|
||||
|
||||
auto it = m_RouterInfos.find (i2p::context.GetIdentHash ());
|
||||
|
@ -118,22 +119,19 @@ namespace data
|
|||
i2p::util::SetThreadName("NetDB");
|
||||
|
||||
uint64_t lastManage = 0;
|
||||
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (),
|
||||
lastObsoleteProfilesCleanup = lastProfilesCleanup, lastApplyingProfileUpdates = lastProfilesCleanup;
|
||||
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0, applyingProfileUpdatesVariance = 0;
|
||||
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup;
|
||||
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0;
|
||||
|
||||
std::list<std::shared_ptr<const I2NPMessage> > msgs;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_Queue.Wait (1,0)) // 1 sec
|
||||
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
|
||||
if (msg)
|
||||
{
|
||||
m_Queue.GetWholeQueue (msgs);
|
||||
while (!msgs.empty ())
|
||||
int numMsgs = 0;
|
||||
while (msg)
|
||||
{
|
||||
auto msg = msgs.front (); msgs.pop_front ();
|
||||
if (!msg) continue;
|
||||
LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ());
|
||||
switch (msg->GetTypeID ())
|
||||
{
|
||||
|
@ -143,10 +141,17 @@ namespace data
|
|||
case eI2NPDatabaseLookup:
|
||||
HandleDatabaseLookupMsg (msg);
|
||||
break;
|
||||
case eI2NPDummyMsg:
|
||||
// plain RouterInfo from NTCP2 with flags for now
|
||||
HandleNTCP2RouterInfoMsg (msg);
|
||||
break;
|
||||
default: // WTF?
|
||||
LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ());
|
||||
//i2p::HandleI2NPMessage (msg);
|
||||
}
|
||||
if (numMsgs > 100) break;
|
||||
msg = m_Queue.Get ();
|
||||
numMsgs++;
|
||||
}
|
||||
}
|
||||
if (!m_IsRunning) break;
|
||||
|
@ -181,7 +186,7 @@ namespace data
|
|||
LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk");
|
||||
}
|
||||
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)
|
||||
|
@ -197,20 +202,7 @@ namespace data
|
|||
else
|
||||
LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk");
|
||||
lastObsoleteProfilesCleanup = mts;
|
||||
obsoleteProfilesCleanVariance = m_Rng () % 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;
|
||||
obsoleteProfilesCleanVariance = rand () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE;
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
|
@ -294,7 +286,6 @@ namespace data
|
|||
}
|
||||
else
|
||||
{
|
||||
r->CancelBufferToDelete (); // since an update received
|
||||
if (CheckLogLevel (eLogDebug))
|
||||
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
|
||||
updated = false;
|
||||
|
@ -308,8 +299,7 @@ namespace data
|
|||
{
|
||||
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
isValid = mts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL > r->GetTimestamp () && // from future
|
||||
(mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL || // too old
|
||||
context.GetUptime () < NETDB_CHECK_FOR_EXPIRATION_UPTIME/10); // enough uptime
|
||||
mts < r->GetTimestamp () + NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; // too old
|
||||
}
|
||||
if (isValid)
|
||||
{
|
||||
|
@ -361,7 +351,7 @@ namespace data
|
|||
{
|
||||
if(it->second->GetExpirationTime() < expires)
|
||||
{
|
||||
it->second->Update (buf, len, nullptr, false); // signature is verified already
|
||||
it->second->Update (buf, len, false); // signature is verified already
|
||||
if (CheckLogLevel (eLogInfo))
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet updated: ", ident.ToBase32());
|
||||
updated = true;
|
||||
|
@ -398,7 +388,8 @@ namespace data
|
|||
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
|
||||
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
|
||||
if (CheckLogLevel (eLogInfo))
|
||||
|
@ -493,7 +484,7 @@ namespace data
|
|||
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
|
||||
{
|
||||
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 ih = ri.GetIdentHash();
|
||||
|
@ -516,7 +507,7 @@ namespace data
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
@ -571,7 +562,7 @@ namespace data
|
|||
while(n > 0)
|
||||
{
|
||||
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;
|
||||
for (const auto & it : m_RouterInfos) {
|
||||
if(i >= idx) // are we at the random start point?
|
||||
|
@ -651,80 +642,70 @@ namespace data
|
|||
if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour
|
||||
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;
|
||||
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::string> removeFromDisk;
|
||||
|
||||
auto own = i2p::context.GetSharedRouterInfo ();
|
||||
for (auto [ident, r]: m_RouterInfos)
|
||||
for (auto& it: m_RouterInfos)
|
||||
{
|
||||
if (!r || r == own) continue; // skip own
|
||||
if (r->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete
|
||||
if (!it.second || it.second == own) continue; // skip own
|
||||
std::string ident = it.second->GetIdentHashBase64();
|
||||
if (it.second->IsUpdated ())
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
|
||||
r->DeleteBuffer ();
|
||||
}
|
||||
if (r->IsUpdated ())
|
||||
{
|
||||
if (r->GetBuffer () && !r->IsUnreachable ())
|
||||
if (it.second->GetBuffer ())
|
||||
{
|
||||
// we have something to save
|
||||
std::shared_ptr<RouterInfo::Buffer> buffer;
|
||||
{
|
||||
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))
|
||||
r->ScheduleBufferToDelete ();
|
||||
if (buffer)
|
||||
saveToDisk.emplace_back(ident.ToBase64 (), buffer);
|
||||
if (buffer && !it.second->IsUnreachable ()) // don't save bad router
|
||||
saveToDisk.push_back(std::make_pair(ident, buffer));
|
||||
it.second->SetUnreachable (false);
|
||||
}
|
||||
r->SetUpdated (false);
|
||||
it.second->SetUpdated (false);
|
||||
updatedCount++;
|
||||
continue;
|
||||
}
|
||||
else if (r->GetBuffer () && ts > r->GetTimestamp () + NETDB_MIN_EXPIRATION_TIMEOUT*1000LL)
|
||||
// since update was long time ago we assume that router is not connected anymore
|
||||
r->ScheduleBufferToDelete ();
|
||||
|
||||
if (r->HasProfile () && r->GetProfile ()->IsUnreachable ())
|
||||
r->SetUnreachable (true);
|
||||
if (it.second->GetProfile ()->IsUnreachable ())
|
||||
it.second->SetUnreachable (true);
|
||||
// make router reachable back if too few routers or floodfills
|
||||
if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline ||
|
||||
(r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
r->SetUnreachable (false);
|
||||
if (!r->IsUnreachable ())
|
||||
if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate ||
|
||||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
it.second->SetUnreachable (false);
|
||||
if (!it.second->IsUnreachable ())
|
||||
{
|
||||
// find & mark expired routers
|
||||
if (!r->GetCompatibleTransports (true)) // non reachable by any transport
|
||||
r->SetUnreachable (true);
|
||||
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ())
|
||||
if (!it.second->GetCompatibleTransports (true)) // non reachable by any transport
|
||||
it.second->SetUnreachable (true);
|
||||
else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (r->GetTimestamp () - ts)/1000LL, " seconds");
|
||||
r->SetUnreachable (true);
|
||||
LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds");
|
||||
it.second->SetUnreachable (true);
|
||||
}
|
||||
else if (checkForExpiration)
|
||||
{
|
||||
if (ts > r->GetTimestamp () + expirationTimeout)
|
||||
r->SetUnreachable (true);
|
||||
else if ((ts > r->GetTimestamp () + expirationTimeout/2) && // more than half of expiration
|
||||
total > NETDB_NUM_ROUTERS_THRESHOLD && !r->IsHighBandwidth() && // low bandwidth
|
||||
!r->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill
|
||||
(CreateRoutingKey (ident) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits
|
||||
r->SetUnreachable (true);
|
||||
if (ts > it.second->GetTimestamp () + expirationTimeout)
|
||||
it.second->SetUnreachable (true);
|
||||
else if ((ts > it.second->GetTimestamp () + expirationTimeout/2) && // more than half of expiration
|
||||
total > NETDB_NUM_ROUTERS_THRESHOLD && !it.second->IsHighBandwidth() && // low bandwidth
|
||||
!it.second->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill
|
||||
(CreateRoutingKey (it.second->GetIdentHash ()) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits
|
||||
it.second->SetUnreachable (true);
|
||||
}
|
||||
}
|
||||
// make router reachable back if connected now or trusted router
|
||||
if (r->IsUnreachable () && (i2p::transport::transports.IsConnected (ident) ||
|
||||
i2p::transport::transports.IsTrustedRouter (ident)))
|
||||
r->SetUnreachable (false);
|
||||
// make router reachable back if connected now
|
||||
if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ()))
|
||||
it.second->SetUnreachable (false);
|
||||
|
||||
if (r->IsUnreachable ())
|
||||
if (it.second->IsUnreachable ())
|
||||
{
|
||||
if (r->IsFloodfill ()) deletedFloodfillsCount++;
|
||||
if (it.second->IsFloodfill ()) deletedFloodfillsCount++;
|
||||
// delete RI file
|
||||
removeFromDisk.emplace_back (ident.ToBase64());
|
||||
removeFromDisk.push_back (ident);
|
||||
deletedCount++;
|
||||
if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false;
|
||||
}
|
||||
|
@ -782,8 +763,7 @@ namespace data
|
|||
|
||||
void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct)
|
||||
{
|
||||
if (direct && (i2p::transport::transports.RoutesRestricted () || i2p::context.IsLimitedConnectivity ()))
|
||||
direct = false; // always use tunnels for restricted routes or limited connectivity
|
||||
if (direct && i2p::transport::transports.RoutesRestricted ()) direct = false; // always use tunnels for restricted routes
|
||||
if (m_Requests)
|
||||
m_Requests->PostRequestDestination (destination, requestComplete, direct);
|
||||
else
|
||||
|
@ -951,13 +931,14 @@ namespace data
|
|||
LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored");
|
||||
return;
|
||||
}
|
||||
std::string key;
|
||||
if (CheckLogLevel (eLogInfo))
|
||||
key = i2p::data::ByteStreamToBase64 (buf, 32);
|
||||
char key[48];
|
||||
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||
key[l] = 0;
|
||||
|
||||
IdentHash replyIdent(buf + 32);
|
||||
uint8_t flag = buf[64];
|
||||
|
||||
|
||||
LogPrint (eLogDebug, "NetDb: DatabaseLookup for ", key, " received flags=", (int)flag);
|
||||
uint8_t lookupType = flag & DATABASE_LOOKUP_TYPE_FLAGS_MASK;
|
||||
const uint8_t * excluded = buf + 65;
|
||||
|
@ -1152,18 +1133,15 @@ namespace data
|
|||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith,
|
||||
bool reverse, bool endpoint, bool clientTunnel) const
|
||||
bool reverse, bool endpoint) const
|
||||
{
|
||||
bool checkIsReal = clientTunnel && i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD && // too low rate
|
||||
context.GetUptime () > NETDB_CHECK_FOR_EXPIRATION_UPTIME; // after 10 minutes uptime
|
||||
return GetRandomRouter (
|
||||
[compatibleWith, reverse, endpoint, clientTunnel, checkIsReal](std::shared_ptr<const RouterInfo> router)->bool
|
||||
[compatibleWith, reverse, endpoint](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router != compatibleWith &&
|
||||
(reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)):
|
||||
router->IsReachableFrom (*compatibleWith)) && !router->IsNAT2NATOnly (*compatibleWith) &&
|
||||
router->IsECIES () && !router->IsHighCongestion (clientTunnel) &&
|
||||
(!checkIsReal || router->GetProfile ()->IsReal ()) &&
|
||||
router->IsECIES () && !router->IsHighCongestion (false) &&
|
||||
(!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse)
|
||||
});
|
||||
}
|
||||
|
@ -1336,8 +1314,12 @@ namespace data
|
|||
{
|
||||
// update selection
|
||||
m_ExploratorySelection.clear ();
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
std::vector<std::shared_ptr<const RouterInfo> > eligible;
|
||||
eligible.reserve (m_RouterInfos.size ());
|
||||
#else
|
||||
auto& eligible = m_ExploratorySelection;
|
||||
#endif
|
||||
{
|
||||
// collect eligible from current netdb
|
||||
bool checkIsReal = i2p::tunnel::tunnels.GetPreciseTunnelCreationSuccessRate () < NETDB_TUNNEL_CREATION_RATE_THRESHOLD; // too low rate
|
||||
|
@ -1347,13 +1329,22 @@ namespace data
|
|||
(!checkIsReal || (it.second->HasProfile () && it.second->GetProfile ()->IsReal ())))
|
||||
eligible.push_back (it.second);
|
||||
}
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
|
||||
{
|
||||
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
|
||||
std::swap (m_ExploratorySelection, eligible);
|
||||
#else
|
||||
if (m_ExploratorySelection.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
|
||||
{
|
||||
// reduce number of eligible to max selection size
|
||||
std::shuffle (m_ExploratorySelection.begin(), m_ExploratorySelection.end(), std::mt19937(ts));
|
||||
m_ExploratorySelection.resize (NETDB_MAX_EXPLORATORY_SELECTION_SIZE);
|
||||
}
|
||||
#endif
|
||||
m_LastExploratorySelectionUpdateTime = ts;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -16,7 +16,6 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <random>
|
||||
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
|
@ -40,8 +39,7 @@ namespace data
|
|||
{
|
||||
const int NETDB_MIN_ROUTERS = 90;
|
||||
const int NETDB_MIN_FLOODFILLS = 5;
|
||||
const int NETDB_MIN_TRANSPORTS = 10 ; // otherwise assume offline
|
||||
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1500;
|
||||
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200;
|
||||
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
|
||||
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
|
||||
const int NETDB_CHECK_FOR_EXPIRATION_UPTIME = 600; // 10 minutes, in seconds
|
||||
|
@ -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_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_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_EXPLORATORY_SELECTION_SIZE = 500;
|
||||
const int NETDB_EXPLORATORY_SELECTION_UPDATE_INTERVAL = 82; // in seconds. for floodfill
|
||||
|
@ -90,7 +87,7 @@ namespace data
|
|||
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true);
|
||||
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint, bool clientTunnel) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse, bool endpoint) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::unordered_set<IdentHash>& excluded) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::unordered_set<IdentHash>& excluded) const;
|
||||
|
@ -130,12 +127,12 @@ namespace data
|
|||
}
|
||||
bool PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
|
||||
std::shared_ptr<RouterInfo::Address> NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); };
|
||||
RouterInfo::AddressesPtr NewRouterInfoAddresses ()
|
||||
boost::shared_ptr<RouterInfo::Addresses> NewRouterInfoAddresses ()
|
||||
{
|
||||
return RouterInfo::AddressesPtr{m_RouterInfoAddressVectorsPool.AcquireMt (),
|
||||
return boost::shared_ptr<RouterInfo::Addresses>(m_RouterInfoAddressVectorsPool.AcquireMt (),
|
||||
std::bind <void (i2p::util::MemoryPoolMt<RouterInfo::Addresses>::*)(RouterInfo::Addresses *)>
|
||||
(&i2p::util::MemoryPoolMt<RouterInfo::Addresses>::ReleaseMt,
|
||||
&m_RouterInfoAddressVectorsPool, std::placeholders::_1)};
|
||||
&m_RouterInfoAddressVectorsPool, std::placeholders::_1));
|
||||
};
|
||||
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
|
||||
std::shared_ptr<IdentityEx> NewIdentity (const uint8_t * buf, size_t len) { return m_IdentitiesPool.AcquireSharedMt (buf, len); };
|
||||
|
@ -187,11 +184,10 @@ namespace data
|
|||
std::shared_ptr<NetDbRequests> m_Requests;
|
||||
|
||||
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;
|
||||
uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds
|
||||
std::mt19937 m_Rng;
|
||||
|
||||
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
|
||||
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
|
||||
*
|
||||
|
@ -20,10 +20,8 @@ namespace i2p
|
|||
namespace data
|
||||
{
|
||||
RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct):
|
||||
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct),
|
||||
m_IsActive (true), m_IsSentDirectly (false),
|
||||
m_CreationTime (i2p::util::GetMillisecondsSinceEpoch ()),
|
||||
m_LastRequestTime (0), m_NumAttempts (0)
|
||||
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), m_IsActive (true),
|
||||
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_LastRequestTime (0), m_NumAttempts (0)
|
||||
{
|
||||
if (i2p::context.IsFloodfill ())
|
||||
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);
|
||||
if(router)
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_NumAttempts++;
|
||||
m_IsSentDirectly = false;
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -58,8 +55,7 @@ namespace data
|
|||
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
|
||||
m_ExcludedPeers.insert (floodfill);
|
||||
m_NumAttempts++;
|
||||
m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
m_IsSentDirectly = true;
|
||||
m_LastRequestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -183,7 +179,7 @@ namespace data
|
|||
|
||||
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;
|
||||
auto it = m_RequestedDestinations.find (ident);
|
||||
|
@ -214,7 +210,7 @@ namespace data
|
|||
|
||||
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 ();)
|
||||
{
|
||||
auto& dest = it->second;
|
||||
|
@ -226,8 +222,7 @@ namespace data
|
|||
bool done = false;
|
||||
if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME)
|
||||
{
|
||||
if (ts > dest->GetLastRequestTime () + (dest->IsSentDirectly () ? MIN_DIRECT_REQUEST_TIME : MIN_REQUEST_TIME))
|
||||
// try next floodfill if no response after min interval
|
||||
if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval
|
||||
done = !SendNextRequest (dest);
|
||||
}
|
||||
else // request is expired
|
||||
|
@ -272,7 +267,7 @@ namespace data
|
|||
{
|
||||
if (dest->IsActive ())
|
||||
{
|
||||
boost::asio::post (s->GetIOService (), [s, dest]()
|
||||
s->GetIOService ().post ([s, dest]()
|
||||
{
|
||||
if (dest->IsActive ()) s->SendNextRequest (dest);
|
||||
});
|
||||
|
@ -333,8 +328,7 @@ namespace data
|
|||
|
||||
void NetDbRequests::ScheduleManageRequests ()
|
||||
{
|
||||
m_ManageRequestsTimer.expires_from_now (boost::posix_time::milliseconds(MANAGE_REQUESTS_INTERVAL +
|
||||
m_Rng () % MANAGE_REQUESTS_INTERVAL_VARIANCE));
|
||||
m_ManageRequestsTimer.expires_from_now (boost::posix_time::seconds(MANAGE_REQUESTS_INTERVAL));
|
||||
m_ManageRequestsTimer.async_wait (std::bind (&NetDbRequests::HandleManageRequestsTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
@ -351,7 +345,7 @@ namespace data
|
|||
|
||||
void NetDbRequests::PostDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
boost::asio::post (GetIOService (), [this, msg]()
|
||||
GetIOService ().post ([this, msg]()
|
||||
{
|
||||
HandleDatabaseSearchReplyMsg (msg);
|
||||
});
|
||||
|
@ -360,12 +354,11 @@ namespace data
|
|||
void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
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
|
||||
if (CheckLogLevel (eLogInfo))
|
||||
key = i2p::data::ByteStreamToBase64 (buf, 32);
|
||||
LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num);
|
||||
|
||||
IdentHash ident (buf);
|
||||
bool isExploratory = false;
|
||||
auto dest = FindRequest (ident);
|
||||
|
@ -438,7 +431,7 @@ namespace data
|
|||
void NetDbRequests::PostRequestDestination (const IdentHash& destination,
|
||||
const RequestedDestination::RequestComplete& requestComplete, bool direct)
|
||||
{
|
||||
boost::asio::post (GetIOService (), [this, destination, requestComplete, direct]()
|
||||
GetIOService ().post ([this, 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
|
||||
*
|
||||
|
@ -24,17 +24,15 @@ namespace i2p
|
|||
namespace data
|
||||
{
|
||||
const int MAX_NUM_REQUEST_ATTEMPTS = 5;
|
||||
const uint64_t MANAGE_REQUESTS_INTERVAL = 400; // in milliseconds
|
||||
const uint64_t MANAGE_REQUESTS_INTERVAL_VARIANCE = 300; // in milliseconds
|
||||
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 + MANAGE_REQUESTS_INTERVAL_VARIANCE);
|
||||
const uint64_t MIN_DIRECT_REQUEST_TIME = 600; // in milliseconds
|
||||
const uint64_t MANAGE_REQUESTS_INTERVAL = 1; // in seconds
|
||||
const uint64_t MIN_REQUEST_TIME = 5; // in seconds
|
||||
const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL);
|
||||
const uint64_t EXPLORATORY_REQUEST_INTERVAL = 55; // 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_VARIANCE = 540; // in milliseconds
|
||||
const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30000; // in milliseconds
|
||||
const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40000; // in milliseconds
|
||||
const uint64_t MAX_EXPLORATORY_REQUEST_TIME = 30; // in seconds
|
||||
const uint64_t REQUEST_CACHE_TIME = MAX_REQUEST_TIME + 40; // in seconds
|
||||
const uint64_t REQUESTED_DESTINATIONS_POOL_CLEANUP_INTERVAL = 191; // in seconds
|
||||
|
||||
class RequestedDestination
|
||||
|
@ -53,7 +51,6 @@ namespace data
|
|||
bool IsExploratory () const { return m_IsExploratory; };
|
||||
bool IsDirect () const { return m_IsDirect; };
|
||||
bool IsActive () const { return m_IsActive; };
|
||||
bool IsSentDirectly () const { return m_IsSentDirectly; };
|
||||
bool IsExcluded (const IdentHash& ident) const;
|
||||
uint64_t GetCreationTime () const { return m_CreationTime; };
|
||||
uint64_t GetLastRequestTime () const { return m_LastRequestTime; };
|
||||
|
@ -72,9 +69,9 @@ namespace data
|
|||
private:
|
||||
|
||||
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;
|
||||
uint64_t m_CreationTime, m_LastRequestTime; // in milliseconds
|
||||
uint64_t m_CreationTime, m_LastRequestTime; // in seconds
|
||||
std::list<RequestComplete> m_RequestComplete;
|
||||
int m_NumAttempts;
|
||||
};
|
||||
|
@ -118,9 +115,9 @@ namespace data
|
|||
|
||||
private:
|
||||
|
||||
i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool;
|
||||
std::unordered_map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
|
||||
std::list<IdentHash> m_DiscoveredRouterHashes;
|
||||
i2p::util::MemoryPoolMt<RequestedDestination> m_RequestedDestinationsPool;
|
||||
boost::asio::deadline_timer m_ManageRequestsTimer, m_ExploratoryTimer,
|
||||
m_CleanupTimer, m_DiscoveredRoutersTimer;
|
||||
std::mt19937 m_Rng;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue