Compare commits

...

190 Commits

Author SHA1 Message Date
a0d6c654cc 2.30.0
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2020-02-25 20:08:50 +03:00
5115c27e72 2.30.0 2020-02-25 11:15:30 -05:00
d09c3ccb2d Merge pull request #1474 from rex4539/dead-strip
Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS
2020-02-24 18:16:20 +00:00
5c308026ac Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS 2020-02-21 11:12:00 +02:00
91919c6d64 check if both sides are ECIESx25519 2020-02-20 21:07:45 -05:00
7168738835 check ctx for null 2020-02-20 21:05:07 -05:00
9c9b723cf5 delete expired ECIESX25519AEADRatchet sessions and tags 2020-02-20 15:44:09 -05:00
50450923df don't add extra , to result string 2020-02-19 20:51:32 -05:00
f392edd66c single thread SAM by default 2020-02-19 13:27:28 -05:00
24b48e5d50 reseeds update 2020-02-18 17:45:04 -05:00
47f384a0e0 postpone SAM destination termination 2020-02-17 15:14:35 -05:00
88594887f9 fixed qt build 2020-02-16 17:44:36 -05:00
32e2f0b1fa correct termination of streaming destination 2020-02-15 16:30:10 -05:00
09ed57ad42 select preferred crypto from LeaseSet2 2020-02-12 11:09:20 -05:00
53a6162b0c generate more receive tags when needed 2020-02-09 17:19:42 -05:00
694d851cdb Symmetric Key Ratchet 2020-02-08 21:51:02 -05:00
8e53c30a00 correct calls sequence for tag and index 2020-02-07 22:08:55 -05:00
63e807b0b4 fixed crash on stop 2020-02-06 10:53:45 -05:00
012f22cc47 create session tags for ECIESX25519 2020-02-05 15:48:51 -05:00
9d891ab5dd single thread mode for SAM 2020-02-04 15:31:04 -05:00
d0e78be867 moved io_service away from ClientDestination 2020-02-04 14:17:23 -05:00
cbedebc9dd change minimal MTU size 2020-02-04 13:32:16 -05:00
969f9aa436 common RuunableBase with private inheritance 2020-02-04 11:48:56 -05:00
b982be5ff5 handle existing session message 2020-02-03 16:21:07 -05:00
2d154ee640 move RunnableService away from LeaseSetDestination 2020-02-02 18:58:58 -05:00
49810eb153 common RunnableService 2020-02-02 17:05:30 -05:00
85b88b8749 second x25519 for new session reply 2020-01-30 19:30:30 -05:00
239c8b5172 destination delivery instructions 2020-01-30 11:48:32 -05:00
8c800dc178 save aepk from new session message 2020-01-29 21:57:10 -05:00
cdd068d99a correct message size 2020-01-29 19:27:38 -05:00
48fa10b080 incoming ECIESX25519AEADRatchet messages hanler 2020-01-29 15:54:11 -05:00
a1dbec0fcb handle new session reply 2020-01-29 12:54:26 -05:00
abe668f1c3 fixed build error 2020-01-28 10:31:35 -05:00
77440c235d replaced map by unordered_map 2020-01-28 10:03:51 -05:00
fd1ee48dbe datetime and padding blocks 2020-01-23 14:26:40 -05:00
205e807b66 reset keys 2020-01-22 21:42:30 -05:00
34295adb05 attach LeaseSet clove 2020-01-22 14:26:47 -05:00
7c212bef63 add new session to the list after reply received 2020-01-22 11:27:47 -05:00
76f95644b7 fixed #1461. Use openssl's HKDF for 1.1.1 anf higher 2020-01-22 09:59:08 -05:00
928b90d5bc fixed #1461. Use openssl's HKDF for 1.1.1 anf higher 2020-01-22 09:50:50 -05:00
09c6c2a4f3 decode aepk and bepk back 2020-01-21 21:09:19 -05:00
2b2bd733e9 correct sharedkey for new outgoing session 2020-01-21 19:13:23 -05:00
0d2d7e5e71 fixed Elligator tests 2020-01-21 17:53:48 -05:00
6142e93252 session tag for ECIESx25519 sessions 2020-01-21 14:40:23 -05:00
ccec3376ba try another ephemeral keys if elligator encoding failes 2020-01-21 12:19:20 -05:00
f497a74ec4 set random two highest bits 2020-01-21 12:18:31 -05:00
0e666e7d6a encoding fail test 2020-01-21 10:53:11 -05:00
f498fabd27 fix for openssl 1.1 2020-01-21 10:52:51 -05:00
8b49a55442 ratchet tagsets 2020-01-20 15:17:38 -05:00
a26eb942a9 Merge pull request #1458 from neheb/jjjjj
replace random_shuffle with shuffle
2020-01-20 07:16:00 -05:00
eabcafa516 replace random_shuffle with shuffle
random_shuffle is gone with C++17.

Found and fixed with clang-tidy.
2020-01-19 18:09:04 -08:00
6cc388c1bc use HKDF for MixKey 2020-01-18 14:43:36 -05:00
62e39ddfbd new session reply 2020-01-17 14:11:15 -05:00
80373623cd create payload 2020-01-17 11:21:41 -05:00
451c3945f0 create new ECIESX25519AEADRatchet session if not found 2020-01-16 19:33:00 -05:00
00cb15d9b4 fixed tyypo 2020-01-16 18:03:51 -05:00
67dd59125e new outgoing ECIESX25519AEADRatchet session 2020-01-16 16:34:13 -05:00
b6800dd125 lookup ECIESX25519AEADRatchet session by static key 2020-01-16 15:45:22 -05:00
dc9da69509 derive ECIESX25519AEADRatchetSession from GarlicRoutingSession 2020-01-16 14:59:19 -05:00
d7d964bf57 GarlicRoutingSession/ElGamalAESSession split 2020-01-16 14:31:01 -05:00
bcfe44db54 handle tunnel delivery instructioin for ECIESx25519 2020-01-16 12:47:08 -05:00
376bf6ba72 correct message size for ECIESx25519 2020-01-15 19:22:42 -05:00
f651baab25 ECIESX25519AEADRatchetSession added 2020-01-15 15:13:43 -05:00
61752e2aab correct ciphertext length 2020-01-13 22:37:31 -05:00
b7d3fd959e Merge pull request #1454 from rex4539/dead-strip
Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS
2020-01-13 09:17:05 -05:00
7ac05f8487 Pass -dead_strip -dead_strip_dylibs -bind_at_load on macOS 2020-01-13 14:47:15 +02:00
4f70822b13 always persist crypto keys for public destinations 2020-01-12 10:03:30 -05:00
0007f304d0 don't pass from to HandleI2NPMessage 2020-01-07 15:20:55 -05:00
4afef91359 invoke HandleCloveI2NPMessage 2020-01-06 16:14:41 -05:00
815b6db0bf HandleCloveI2NPMessage 2020-01-06 15:31:20 -05:00
433d3bf582 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2020-01-06 14:38:13 -05:00
a335841509 pass msgID to HandleDeliveryStatus 2020-01-06 14:37:40 -05:00
26ad793d82 use unscoped storage (fixes usage on android 10)
Signed-off-by: r4sas <r4sas@i2pmail.org>
2020-01-05 23:04:08 +00:00
5337aa10f7 check AES tag first 2020-01-02 13:30:54 -05:00
9f79bdae9b encryptor for ECIES-X25519-AEAD-Ratchet 2019-12-19 15:59:15 -05:00
db84be2488 use HKDF for NTCP2 key derivation data phase 2019-12-18 20:48:30 -05:00
599ec62bb0 use HKDF for NTCP2 key derivation data phase 2019-12-18 20:45:47 -05:00
19a88300c6 decrypt payload section 2019-12-18 14:44:02 -05:00
b5d55e1ffb decrypt flags/static section 2019-12-17 16:34:47 -05:00
521fb83e38 initial code for ECIES-X25519-AEAD-Ratchet KDF 2019-12-17 16:18:40 -05:00
553d59c32b decryptor for ECIES-X25519-AEAD-Ratchet 2019-12-11 13:38:36 -05:00
9ed58e5186 encode with highY 2019-12-10 14:10:12 -05:00
36eaaa748c handle case when encoded key is (p-1)/2 2019-12-10 13:40:04 -05:00
4d7b86ca26 elligator test added 2019-12-10 13:20:23 -05:00
5faf84c732 correct conversion from Little Endian 2019-12-10 12:51:39 -05:00
d7b819267f check a for 0 in Legendre 2019-12-10 10:53:39 -05:00
7417867d0f implemented Legendre 2019-12-10 10:45:08 -05:00
8d74905257 0.9.44 2019-12-10 10:44:19 -05:00
c38298c06e Elligator decode 2019-12-09 16:11:46 -05:00
3100d587d1 use d%q 2019-12-09 13:23:17 -05:00
ba849d0300 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2019-12-06 20:29:29 -05:00
95df3e4b39 encode key 2019-12-06 20:29:03 -05:00
72492e33a0 appveyor: drop msys2 overwrite 2019-12-07 01:02:23 +00:00
934f1269f5 appveyor: replace deprecated --force in msys2 2019-12-07 00:59:33 +00:00
e6956d9bb0 calculate constants 2019-12-06 14:54:15 -05:00
2877900233 use 486662 for A 2019-12-05 16:13:59 -05:00
df1aa52e08 Elligator's encode 2019-12-05 16:03:11 -05:00
5fa2485a7d removed reseed.i2p.net.in 2019-12-04 17:27:16 -05:00
29f0e10411 Elligator added 2019-12-04 15:37:24 -05:00
39300a5bbf removed reseed.i2p.net.in 2019-11-28 17:49:36 -05:00
0abb871f3f close socket after if accept failed 2019-11-20 13:00:50 -05:00
704fca969f handle accept errors 2019-11-20 12:05:32 -05:00
95debf8c80 update mingw build script
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2019-11-19 14:29:55 +03:00
dd94b77b2a use GetTickCount dll pointer, add USE_WINXP to makefile
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2019-11-19 13:51:14 +03:00
6cfe4fa580 handle sending errors 2019-11-18 14:13:31 -05:00
515c086099 Use GetProcAddress for inet_pton. Fixed build error 2019-11-12 15:06:04 -05:00
34ce06ac17 some cleanup 2019-11-12 14:19:14 -05:00
a104c9881e some cleanup 2019-11-12 11:57:34 -05:00
c3e3c091cc correct implementation of GetMTUWindows for WindowsXP 2019-11-12 11:35:59 -05:00
651240113c mark RI as unreachable if all connections failed 2019-11-12 10:03:33 -05:00
77189bf8e9 start over if an active session got disconnected 2019-11-12 09:38:22 -05:00
60fd3a4542 fixed #1434 use memset inster bzero 2019-11-12 07:17:57 -05:00
c66f9c8d6d reset connection attempts before reconnect 2019-11-12 06:46:08 -05:00
569088eaca 2.29.0 2019-10-21 12:02:43 -04:00
a7e8dd04fe 2.29.0 2019-10-21 11:50:59 -04:00
dfdd76a1bb fixed #1429. Don't use monotonic timer for Win32 2019-10-15 10:32:29 -04:00
28aac6f93b fix bogus date in changelogs
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2019-10-07 21:18:46 +03:00
c2f47119ce fixed #1424. Check if .b32.i2p address string is valid 2019-09-23 13:42:15 -04:00
d6b1d0d4fb remove incoming session from pending list when established 2019-09-22 21:01:34 -04:00
03a861745b removed CloseSession 2019-09-20 20:09:25 -04:00
9a7aed20e9 handle error for SessionConfrimed send 2019-09-19 16:54:23 -04:00
b7f17d4cb1 client auth flag for B33 address 2019-09-06 11:02:19 -04:00
2497c3d187 Merge pull request #1421 from nonlinear-chaos-order-etc/openssl
Fixes three upnp issues, probably including: probably fixed #1419
2019-09-05 15:51:11 -04:00
f7a084969a fixed #1387 2019-09-06 03:21:26 +08:00
2900bc26a5 fixed #1388 : took code from 736c95a870 and fixed it as https://github.com/PurpleI2P/i2pd/issues/1388#issuecomment-528495918 tells 2019-09-06 02:58:28 +08:00
2334c56a96 Merge pull request #1414 from nonlinear-chaos-order-etc/openssl
qt: fixed logging to window in release builds
2019-08-27 12:40:19 -04:00
90a5d02bf6 2.28.0 2019-08-27 10:17:32 -04:00
81d9626da9 qt: fixed logging to window in release builds 2019-08-27 21:56:36 +08:00
44a2549b81 2.28.0 2019-08-27 09:46:54 -04:00
a2b8d468bc Merge pull request #1412 from nonlinear-chaos-order-etc/openssl
qt: disabled broken UPnP + small qt debugging tweaks
2019-08-27 08:53:44 -04:00
d523f0cadd gitignored autosave files by qtcreator 2019-08-27 19:31:57 +08:00
99116ff097 qt: disabled upnp for now - until upnp fixed 2019-08-27 19:31:28 +08:00
3939ca9eb4 enabled default logging debug option for qt debug builds 2019-08-27 17:51:55 +08:00
b5aa67b491 tweaked debug logging in i2pd_qt 2019-08-27 17:10:53 +08:00
e42efec220 correct outet plain text length in case of authKeys 2019-08-26 07:35:11 -04:00
9d06aa2f6a pass authSalt or epk 2019-08-25 20:51:15 -04:00
80765a797b correct outer cipher text len 2019-08-25 19:14:53 -04:00
0b5509a1ed correct authClients offset 2019-08-25 14:54:43 -04:00
478d7b4a83 Merge pull request #1410 from nonlinear-chaos-order-etc/openssl
android various fixes and improvements
2019-08-24 07:31:12 -04:00
9d3b38141a android various fixes and improv 2019-08-24 19:13:10 +08:00
ab3a4d902e Merge pull request #1409 from nonlinear-chaos-order-etc/openssl
android - show battery optimiz. os dialog menu item added; translated all battery stuff into Russian
2019-08-24 07:03:15 -04:00
5eab5f2437 show battery optimiz. menu item now hidden if not supported by os 2019-08-24 18:00:11 +08:00
80f632c19a show battery optimiz. menu item added; translated all battery stuff into Russian 2019-08-24 17:50:30 +08:00
6e4f18543d added *.local to android/.gitignore 2019-08-24 17:04:04 +08:00
54586c9076 Merge pull request #1408 from docker-and-co/fix-dockerfile-non-existend-deps
fix dockerfile: remove unmet dependencies
2019-08-23 11:02:00 -04:00
351c899807 cleanup incoming streams on stop 2019-08-23 10:00:49 -04:00
fe45d431d7 fix dockerfile: remove unmet dependencies 2019-08-23 16:20:09 +03:00
488c2f6d05 bump SDK version 2019-08-22 09:45:49 -04:00
75ab0909b3 Merge pull request #1405 from nonlinear-chaos-order-etc/openssl
various Android stuff. Fixed #1400
2019-08-22 07:15:36 -04:00
8f82d563c1 various Android stuff. Fixed #1400 2019-08-22 10:00:50 +08:00
9bbce5dba6 fixed typo 2019-08-21 20:26:19 -04:00
099adab9ed Update README.md
update head badges links, add snapcraft badge
2019-08-16 21:45:44 +03:00
c8cbf425ac check and send netid for NTCP2 and SSU 2019-08-13 14:55:18 -04:00
ad9c11cd92 correct parsing of addreses containing # 2019-08-10 22:16:26 -04:00
3872c2a3f5 use published encrypted instead orig type 2019-08-07 16:18:00 -04:00
e6a09b49c9 published encrypted flag 2019-08-07 15:43:03 -04:00
db107602bd handle messages with \r\n 2019-08-02 13:48:39 -04:00
a6558a61a7 Recognize RedDSA_SHA512_Ed25519 signature type 2019-08-02 12:54:24 -04:00
254d2b82b3 fixed #1393. store streams by recvStreamID 2019-07-26 14:23:21 -04:00
97d9795fc9 pass encrepted LeaseSet auth keys 2019-07-16 16:31:17 -04:00
54071b0e5d set and handle unpublished LeaseSet flag 2019-07-16 11:48:30 -04:00
925e8316c7 read i2cp.leaseSetAuthType, i2cp.leaseSetClient.dh.nnn and i2cp.leaseSetClient.psk.nnn from tunnel config 2019-07-12 20:58:17 -04:00
99e1b74023 create encrypted LeaseSet2 with authentication 2019-07-12 15:40:59 -04:00
7d68ccca53 create encrypted LeaseSet2 with authentication 2019-07-12 15:37:32 -04:00
a090114066 send data message wih raw type fpr raw datagrams 2019-07-10 13:31:49 -04:00
a204841abb handle RAW SEND 2019-07-10 13:30:31 -04:00
cc451809cc send/receive raw datagrams through the SAM 2019-07-10 11:32:56 -04:00
a605e4bab6 send and recieve raw datagrams 2019-07-09 21:33:55 -04:00
3f0534134d check for malformed b33 2019-07-04 13:05:39 -04:00
3acfb129cd 2.27.0 2019-07-03 12:38:55 -04:00
6ccef66920 call shutdown before close 2019-06-26 10:47:16 -04:00
e9fa4e94a6 Merge pull request #1374 from rszibele/openssl
BOB: fix status command.
2019-06-25 18:28:43 -04:00
fecc0c4640 don't call destructor twice 2019-06-25 16:37:06 -04:00
b759294975 BOB: fix status command. 2019-06-25 19:18:40 +02:00
a23e845c03 BOB: improve comment and remove error log in list command 2019-06-25 19:04:27 +02:00
cb8373e487 BOB: status response now correctly starts with "OK DATA". 2019-06-25 17:59:44 +02:00
8e919ddc8e use monotonic clock for uptime 2019-06-19 11:43:04 -04:00
832a9ab6b5 don't set random NTCP2 port if already set 2019-06-18 15:47:58 -04:00
13732ac333 fix #1363 try connect in SSU's thread 2019-06-14 15:43:03 -04:00
3e932a55f4 fixed typo 2019-06-11 15:09:10 -04:00
74e8610ec9 DH auth for encrypted LeaseSet2 2019-06-11 10:40:53 -04:00
089a60ded6 Merge pull request #1367 from rszibele/openssl
fix build on BSDs, as "isset" is a defined macro.
2019-06-10 19:10:34 -04:00
c8eeefe194 fix build on BSDs, as "isset" is a defined macro. 2019-06-10 23:04:59 +02:00
85eeba14c1 Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl 2019-06-08 21:24:16 -04:00
f6f45eab39 flood encrypted LeaseSet2 with store hash 2019-06-08 21:23:25 -04:00
a74065f775 [qt] dont build UnixDaemon.cpp 2019-06-09 01:04:43 +03:00
48d02f7e09 [qt] update headers, fix ChaCha source name, remove duplicates 2019-06-08 22:24:11 +03:00
e60549f8df [qt] fix build 2019-06-08 21:16:20 +03:00
41f4f4713e handle i2cp.leaseSetPrivKey 2019-06-07 14:51:08 -04:00
213a292fd5 correct offsets for auth data 2019-06-07 11:59:48 -04:00
91 changed files with 3289 additions and 1191 deletions

View File

@ -1,6 +1,56 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.30.0] - 2020-02-25
### Added
- Single threaded SAM
- Experimental support of ECIES-X25519-AEAD-Ratchet crypto type
### Changed
- Minimal MTU size is 1280 for ipv6
- Use unordered_map instead map for destination's sessions and tags list
- Use std::shuffle instead std::random_shuffle
- SAM is single threaded by default
- Reseeds list
### Fixed
- Correct termination of streaming destination
- Extra ',' in RouterInfo response in I2PControl
- SAM crash on session termination
- Storage for Android 10
## [2.29.0] - 2019-10-21
### Added
- Client auth flag for b33 address
### Changed
- Remove incoming NTCP2 session from pending list when established
- Handle errors for NTCP2 SessionConfrimed send
### Fixed
- Failure to start on Windows XP
- SAM crash if invalid lookup address
- Possible crash when UPnP enabled on shutdown
## [2.28.0] - 2019-08-27
### Added
- RAW datagrams in SAM
- Publishing encrypted LeaseSet2 with DH or PSH authentication
- Ability to disable battery optimization for Android
- Transport Network ID Check
### Changed
- Set and handle published encrypted flag for LeaseSet2
### Fixed
- ReceiveID changes in the same stream
- "\r\n" command terminator in SAM
- Addressbook lines with signatures
## [2.27.0] - 2019-07-03
### Added
- Support of PSK and DH authentication for encrypted LeaseSet2
### Changed
- Uptime is based on monotonic timer
### Fixed
- BOB status command response
- Correct NTCP2 port if NTCP is disabled
- Flood encrypted LeaseSet2 with store hash
## [2.26.0] - 2019-06-07
### Added
- HTTP method "PROPFIND"

View File

@ -37,6 +37,10 @@ ifeq ($(USE_WIN32_APP), yes)
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif
ifeq ($(USE_WINXP_FLAGS), yes)
CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
ifeq ($(USE_AESNI),1)
CPU_FLAGS += -maes

View File

@ -2,6 +2,9 @@ CXX = clang++
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX
INCFLAGS = -I/usr/local/include
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
LDFLAGS += -Wl,-dead_strip
LDFLAGS += -Wl,-dead_strip_dylibs
LDFLAGS += -Wl,-bind_at_load
ifeq ($(USE_STATIC),yes)
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread

View File

@ -1,5 +1,6 @@
![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)
![GitHub](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)
[![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)](https://github.com/PurpleI2P/i2pd/releases/latest)
[![Snapcraft release](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd)
[![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
i2pd
====
@ -66,6 +67,7 @@ Build instructions:
* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/)
* Snap - [![Snap Status](https://build.snapcraft.io/badge/PurpleI2P/i2pd-snap.svg)](https://build.snapcraft.io/user/PurpleI2P/i2pd-snap)
* FreeBSD
* Android
* iOS

View File

@ -13,10 +13,6 @@
#include "Win32App.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define ID_ABOUT 2000
#define ID_EXIT 2001
#define ID_CONSOLE 2002
@ -39,6 +35,9 @@ namespace i2p
namespace win32
{
static DWORD GracefulShutdownEndtime = 0;
typedef DWORD (* IPN)();
IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount");
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
{
@ -53,7 +52,7 @@ namespace win32
ID_ACCEPT_TRANSIT, "Accept &transit");
else
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs");
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
else
@ -161,7 +160,7 @@ namespace win32
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
if (GracefulShutdownEndtime != 0)
{
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCount()) / 1000;
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000;
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
}
else
@ -239,7 +238,7 @@ namespace win32
i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000;
i2p::util::DaemonWin32::Instance ().isGraceful = true;
return 0;
}

View File

@ -1,5 +1,5 @@
#define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.26.0"
#define I2Pd_ver "2.30.0"
#define I2Pd_Publisher "PurpleI2P"
[Setup]

4
android/.gitignore vendored
View File

@ -12,5 +12,5 @@ local.properties
build.sh
android.iml
build
*.iml
*.local

View File

@ -9,12 +9,14 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:requestLegacyExternalStorage="true"
>
<receiver android:name=".NetworkStateChangeReceiver">
<intent-filter>

View File

@ -5,7 +5,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:3.4.2'
}
}
@ -16,21 +16,22 @@ repositories {
maven {
url 'https://maven.google.com'
}
google()
}
dependencies {
implementation 'com.android.support:support-compat:28.0.0'
implementation 'androidx.core:core:1.0.2'
}
android {
compileSdkVersion 28
compileSdkVersion 29
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "org.purplei2p.i2pd"
targetSdkVersion 28
targetSdkVersion 29
minSdkVersion 14
versionCode 2260
versionName "2.26.0"
versionCode 2300
versionName "2.30.0"
ndk {
abiFilters 'armeabi-v7a'
abiFilters 'x86'
@ -81,4 +82,8 @@ android {
path './jni/Android.mk'
}
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}

View File

@ -1 +1,3 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.parallel=true

View File

@ -1,6 +1,6 @@
#Thu Mar 14 18:21:08 MSK 2019
#Tue Aug 20 14:39:08 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

View File

@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-28
target=android-29

View File

@ -3,14 +3,19 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".I2PDActivity">
<item
android:id="@+id/action_graceful_stop"
android:title="@string/action_graceful_stop"
android:orderInCategory="98"
/>
<item
android:id="@+id/action_stop"
android:title="@string/action_stop"
android:orderInCategory="99"
/>
<group android:id="@+id/group_i2pd_control" >
<item
android:id="@+id/action_stop"
android:orderInCategory="99"
android:title="@string/action_stop" />
<item
android:id="@+id/action_graceful_stop"
android:orderInCategory="98"
android:title="@string/action_graceful_stop" />
</group>
<group android:id="@+id/group_various" >
<item
android:id="@+id/action_battery_otimizations"
android:title="@string/menu_item_battery_optimizations_str" />
</group>
</menu>

View File

@ -17,4 +17,12 @@
<string name="remaining">осталось</string>
<string name="title_activity_i2_pdperms_asker_prompt">Запрос</string>
<string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string>
<string name="menu_item_battery_optimizations_str">Оптимизации аккумулятора</string>
<string name="battery_optimizations_enabled">Оптимизации аккумулятора включены</string>
<string name="device_does_not_support_disabling_battery_optimizations">Ваша версия Андроид не поддерживает отключение оптимизаций аккумулятора</string>
<string name="battery_optimizations_enabled_explained">Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\nРекомендуется отключить эти оптимизации.</string>
<string name="battery_optimizations_enabled_dialog" >Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\n\nВам сейчас будет предложено разрешить отключение этих оптимизаций.</string>
<string name="continue_str">Продолжить</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Ваша версия Андроид не поддерживает показ диалога об оптимизациях аккумулятора для приложений.</string>
<string name="shutdown_canceled">Плановая остановка отменена</string>
</resources>

View File

@ -17,4 +17,12 @@
<string name="remaining">remaining</string>
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string>
<string name="permDenied">SD card write permission denied, you need to allow this to continue</string>
<string name="battery_optimizations_enabled">Battery optimizations enabled</string>
<string name="battery_optimizations_enabled_explained">Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\nIt is recommended to allow disabling those battery optimizations.</string>
<string name="battery_optimizations_enabled_dialog" >Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\n\nYou will now be asked to allow to disable those.</string>
<string name="continue_str">Continue</string>
<string name="device_does_not_support_disabling_battery_optimizations">Your Android version does not support opting out of battery optimizations</string>
<string name="menu_item_battery_optimizations_str">Battery Optimizations</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Your Android OS version does not support showing the dialog for battery optimizations for applications.</string>
<string name="shutdown_canceled">Planned shutdown canceled</string>
</resources>

View File

@ -1,6 +1,5 @@
package org.purplei2p.i2pd;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@ -11,10 +10,9 @@ import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
public class ForegroundService extends Service {
private static final String TAG="FgService";
@ -112,14 +110,15 @@ public class ForegroundService extends Service {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
String channelId = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? createNotificationChannel() : "";
String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : "";
// Set the info for the views that show in the notification panel.
Notification notification = new NotificationCompat.Builder(this, channelId)
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setOngoing(true)
.setSmallIcon(R.drawable.itoopie_notification_icon) // the status icon
.setPriority(Notification.PRIORITY_DEFAULT)
.setCategory(Notification.CATEGORY_SERVICE)
.setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon
if(Build.VERSION.SDK_INT >= 16) builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
if(Build.VERSION.SDK_INT >= 21) builder = builder.setCategory(Notification.CATEGORY_SERVICE);
Notification notification = builder
.setTicker(text) // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle(getText(R.string.app_name)) // the label of the entry
@ -141,9 +140,10 @@ public class ForegroundService extends Service {
//chan.setLightColor(Color.PURPLE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager service = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
service.createNotificationChannel(chan);
if(service!=null)service.createNotificationChannel(chan);
else Log.e(TAG, "error: NOTIFICATION_SERVICE is null");
return channelId;
}
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
}

View File

@ -14,32 +14,44 @@ import java.util.Timer;
import java.util.TimerTask;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
// For future package update checking
import org.purplei2p.i2pd.BuildConfig;
import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS;
public class I2PDActivity extends Activity {
private static final String TAG = "i2pdActvt";
private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
public static final String PACKAGE_URI_SCHEME = "package:";
private TextView textView;
private boolean assetsCopied;
@ -53,32 +65,27 @@ public class I2PDActivity extends Activity {
public void daemonStateUpdate()
{
processAssets();
runOnUiThread(new Runnable(){
@Override
public void run() {
try {
if(textView==null) return;
Throwable tr = daemon.getLastThrowable();
if(tr!=null) {
textView.setText(throwableToString(tr));
return;
}
DaemonSingleton.State state = daemon.getState();
textView.setText(
String.valueOf(getText(state.getStatusStringResourceId()))+
(DaemonSingleton.State.startFailed.equals(state) ? ": "+daemon.getDaemonStartResult() : "")+
(DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? ": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining) : "")
);
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
}
});
runOnUiThread(() -> {
try {
if(textView==null) return;
Throwable tr = daemon.getLastThrowable();
if(tr!=null) {
textView.setText(throwableToString(tr));
return;
}
DaemonSingleton.State state = daemon.getState();
String startResultStr = DaemonSingleton.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
String graceStr = DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
});
}
};
private static volatile long graceStartedMillis;
private static final Object graceStartedMillis_LOCK=new Object();
private Menu optionsMenu;
private static String formatGraceTimeRemaining() {
long remainingSeconds;
@ -92,6 +99,7 @@ public class I2PDActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
textView = new TextView(this);
@ -121,6 +129,8 @@ public class I2PDActivity extends Activity {
}
rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
}
openBatteryOptimizationDialogIfNeeded();
}
@Override
@ -128,7 +138,7 @@ public class I2PDActivity extends Activity {
super.onDestroy();
textView = null;
daemon.removeStateChangeListener(daemonStateUpdatedListener);
//cancelGracefulStop();
//cancelGracefulStop0();
try{
doUnbindService();
}catch(Throwable tr){
@ -137,24 +147,20 @@ public class I2PDActivity extends Activity {
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
switch (requestCode)
{
case MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE:
{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.e(TAG, "Memory permission granted");
else
Log.e(TAG, "Memory permission declined");
// TODO: terminate
return;
}
default: ;
}
if (requestCode == MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.e(TAG, "WR_EXT_STORAGE perm granted");
else {
Log.e(TAG, "WR_EXT_STORAGE perm declined, stopping i2pd");
i2pdStop();
//TODO must work w/o this perm, ask orignal
}
}
}
private static void cancelGracefulStop() {
private void cancelGracefulStop0() {
Timer gracefulQuitTimer = getGracefulQuitTimer();
if(gracefulQuitTimer!=null) {
gracefulQuitTimer.cancel();
@ -225,11 +231,17 @@ public class I2PDActivity extends Activity {
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.options_main, menu);
menu.findItem(R.id.action_battery_otimizations).setVisible(isBatteryOptimizationsOpenOsDialogApiAvailable());
this.optionsMenu = menu;
return true;
}
private boolean isBatteryOptimizationsOpenOsDialogApiAvailable() {
return android.os.Build.VERSION.SDK_INT >= 23;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
@ -240,37 +252,43 @@ public class I2PDActivity extends Activity {
i2pdStop();
return true;
case R.id.action_graceful_stop:
if (getGracefulQuitTimer()!= null)
{
item.setTitle(R.string.action_graceful_stop);
i2pdCancelGracefulStop ();
synchronized (graceStartedMillis_LOCK) {
if (getGracefulQuitTimer() != null)
cancelGracefulStop();
else
i2pdGracefulStop();
}
else
{
item.setTitle(R.string.action_cancel_graceful_stop);
i2pdGracefulStop();
}
return true;
return true;
case R.id.action_battery_otimizations:
onActionBatteryOptimizations();
return true;
}
return super.onOptionsItemSelected(item);
}
private void i2pdStop() {
cancelGracefulStop();
new Thread(new Runnable(){
@Override
public void run() {
Log.d(TAG, "stopping");
try{
daemon.stopDaemon();
}catch (Throwable tr) {
Log.e(TAG, "", tr);
}
private void onActionBatteryOptimizations() {
if (isBatteryOptimizationsOpenOsDialogApiAvailable()) {
try {
startActivity(new Intent(ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
} catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_DIALOG_ActvtNotFound", e);
Toast.makeText(this, R.string.os_version_does_not_support_battery_optimizations_show_os_dialog_api, Toast.LENGTH_SHORT).show();
}
}
}
},"stop").start();
private void i2pdStop() {
cancelGracefulStop0();
new Thread(() -> {
Log.d(TAG, "stopping");
try {
daemon.stopDaemon();
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
quit(); //TODO make menu items for starting i2pd. On my Android, I need to reboot the OS to restart i2pd.
},"stop").start();
}
private static volatile Timer gracefulQuitTimer;
@ -288,55 +306,45 @@ public class I2PDActivity extends Activity {
}
Toast.makeText(this, R.string.graceful_stop_is_in_progress,
Toast.LENGTH_SHORT).show();
new Thread(new Runnable(){
@Override
public void run() {
try {
Log.d(TAG, "grac stopping");
if(daemon.isStartedOkay()) {
daemon.stopAcceptingTunnels();
long gracefulStopAtMillis;
synchronized (graceStartedMillis_LOCK) {
graceStartedMillis = System.currentTimeMillis();
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
}
rescheduleGraceStop(null,gracefulStopAtMillis);
} else {
i2pdStop();
}
} catch(Throwable tr) {
Log.e(TAG,"",tr);
}
}
},"gracInit").start();
new Thread(() -> {
try {
Log.d(TAG, "grac stopping");
if(daemon.isStartedOkay()) {
daemon.stopAcceptingTunnels();
long gracefulStopAtMillis;
synchronized (graceStartedMillis_LOCK) {
graceStartedMillis = System.currentTimeMillis();
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
}
rescheduleGraceStop(null,gracefulStopAtMillis);
} else {
i2pdStop();
}
} catch(Throwable tr) {
Log.e(TAG,"",tr);
}
},"gracInit").start();
}
private void i2pdCancelGracefulStop()
private void cancelGracefulStop()
{
cancelGracefulStop();
Toast.makeText(this, R.string.startedOkay, Toast.LENGTH_SHORT).show();
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Log.d(TAG, "grac stopping cancel");
if(daemon.isStartedOkay())
daemon.startAcceptingTunnels();
else
i2pdStop();
cancelGracefulStop0();
new Thread(() -> {
try
{
Log.d(TAG, "canceling grac stop");
if(daemon.isStartedOkay()) {
daemon.startAcceptingTunnels();
runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show());
}
catch(Throwable tr)
{
Log.e(TAG,"",tr);
}
}
},"gracCancel").start();
else
i2pdStop();
}
catch(Throwable tr)
{
Log.e(TAG,"",tr);
}
},"gracCancel").start();
}
private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) {
@ -364,8 +372,19 @@ public class I2PDActivity extends Activity {
return gracefulQuitTimer;
}
private static void setGracefulQuitTimer(Timer gracefulQuitTimer) {
private void setGracefulQuitTimer(Timer gracefulQuitTimer) {
I2PDActivity.gracefulQuitTimer = gracefulQuitTimer;
runOnUiThread(()-> {
Menu menu = optionsMenu;
if (menu != null) {
MenuItem item = menu.findItem(R.id.action_graceful_stop);
if (item != null) {
synchronized (graceStartedMillis_LOCK) {
item.setTitle(getGracefulQuitTimer() != null ? R.string.action_cancel_graceful_stop : R.string.action_graceful_stop);
}
}
}
});
}
/**
@ -388,19 +407,22 @@ public class I2PDActivity extends Activity {
// to a file. That doesn't appear to be the case. If the returned array is
// null or has 0 length, we assume the path is to a file. This means empty
// directories will get turned into files.
if (contents == null || contents.length == 0)
throw new IOException();
if (contents == null || contents.length == 0) {
copyFileAsset(path);
return;
}
// Make the directory.
File dir = new File(i2pdpath, path);
dir.mkdirs();
boolean result = dir.mkdirs();
Log.d(TAG, "dir.mkdirs() returned " + result);
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + "/" + entry);
copyAsset(path + '/' + entry);
}
} catch (IOException e) {
copyFileAsset(path);
Log.e(TAG, "ex ignored for path='" + path + "'", e);
}
}
@ -413,63 +435,89 @@ public class I2PDActivity extends Activity {
*/
private void copyFileAsset(String path) {
File file = new File(i2pdpath, path);
if(!file.exists()) try {
InputStream in = getAssets().open(path);
OutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
if(!file.exists()) {
try {
try (InputStream in = getAssets().open(path) ) {
try (OutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
}
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
out.close();
in.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
for (File child : fileOrDirectory.listFiles()) {
deleteRecursive(child);
File[] files = fileOrDirectory.listFiles();
if(files!=null) {
for (File child : files) {
deleteRecursive(child);
}
}
}
fileOrDirectory.delete();
boolean deleteResult = fileOrDirectory.delete();
if(!deleteResult)Log.e(TAG, "fileOrDirectory.delete() returned "+deleteResult+", absolute path='"+fileOrDirectory.getAbsolutePath()+"'");
}
private void processAssets() {
if (!assetsCopied) try {
assetsCopied = true; // prevent from running on every state update
File holderfile = new File(i2pdpath, "assets.ready");
File holderFile = new File(i2pdpath, "assets.ready");
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder();
if (holderfile.exists()) try { // if holder file exists, read assets version string
BufferedReader br = new BufferedReader(new FileReader(holderfile));
String line;
if (holderFile.exists()) {
try { // if holder file exists, read assets version string
FileReader fileReader = new FileReader(holderFile);
while ((line = br.readLine()) != null) {
text.append(line);
}
br.close();
}
catch (IOException e) {
Log.e(TAG, "", e);
}
try {
BufferedReader br = new BufferedReader(fileReader);
try {
String line;
while ((line = br.readLine()) != null) {
text.append(line);
}
}finally {
try{
br.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} finally {
try{
fileReader.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
// if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName)) try {
holderfile.delete();
File certpath = new File(i2pdpath, "certificates");
deleteRecursive(certpath);
boolean deleteResult = holderFile.delete();
if(!deleteResult)Log.e(TAG, "holderFile.delete() returned "+deleteResult+", absolute path='"+holderFile.getAbsolutePath()+"'");
File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
}
catch (Throwable tr) {
Log.e(TAG, "", tr);
}
// copy assets. If processed file exists, it won't be overwrited
// copy assets. If processed file exists, it won't be overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
@ -478,14 +526,95 @@ public class I2PDActivity extends Activity {
copyAsset("tunnels.conf");
// update holder file about successful copying
FileWriter writer = new FileWriter(holderfile);
writer.append(versionName);
writer.flush();
writer.close();
FileWriter writer = new FileWriter(holderFile);
try {
writer.append(versionName);
} finally {
try{
writer.close();
}catch (IOException e){
Log.e(TAG,"on writer close", e);
}
}
}
catch (Throwable tr)
{
Log.e(TAG,"copy assets",tr);
Log.e(TAG,"on assets copying", tr);
}
}
@SuppressLint("BatteryLife")
private void openBatteryOptimizationDialogIfNeeded() {
boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true);
Log.i(TAG,"BATT_OPTIM_questionEnabled=="+questionEnabled);
if (!isKnownIgnoringBatteryOptimizations()
&& android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
&& questionEnabled) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.battery_optimizations_enabled);
builder.setMessage(R.string.battery_optimizations_enabled_dialog);
builder.setPositiveButton(R.string.continue_str, (dialog, which) -> {
try {
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse(PACKAGE_URI_SCHEME + getPackageName())));
} catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_ActvtNotFound", e);
Toast.makeText(this, R.string.device_does_not_support_disabling_battery_optimizations, Toast.LENGTH_SHORT).show();
}
});
builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
}
private void setNeverAskForBatteryOptimizationsAgain() {
getPreferences().edit().putBoolean(getBatteryOptimizationPreferenceKey(), false).apply();
}
protected boolean isKnownIgnoringBatteryOptimizations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm == null) {
Log.i(TAG, "BATT_OPTIM: POWER_SERVICE==null");
return false;
}
boolean ignoring = pm.isIgnoringBatteryOptimizations(getPackageName());
Log.i(TAG, "BATT_OPTIM: ignoring==" + ignoring);
return ignoring;
} else {
Log.i(TAG, "BATT_OPTIM: old sdk version=="+Build.VERSION.SDK_INT);
return false;
}
}
protected SharedPreferences getPreferences() {
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
private String getBatteryOptimizationPreferenceKey() {
@SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
return "show_battery_optimization" + (device == null ? "" : device);
}
private void quit() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAndRemoveTask();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
finishAffinity();
} else {
//moveTaskToBack(true);
finish();
}
}catch (Throwable tr) {
Log.e(TAG, "", tr);
}
try{
daemon.stopDaemon();
}catch (Throwable tr) {
Log.e(TAG, "", tr);
}
System.exit(0);
}
}

View File

@ -1,4 +1,4 @@
version: 2.26.0.{build}
version: 2.30.0.{build}
pull_requests:
do_not_increment_build_number: true
branches:
@ -18,9 +18,9 @@ environment:
install:
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
- if "%MSYSTEM%" == "MINGW64" (
c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc"

View File

@ -82,6 +82,8 @@ set (LIBI2PD_SRC
"${LIBI2PD_SRC_DIR}/Ed25519.cpp"
"${LIBI2PD_SRC_DIR}/NTCP2.cpp"
"${LIBI2PD_SRC_DIR}/Blinding.cpp"
"${LIBI2PD_SRC_DIR}/Elligator.cpp"
"${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp"
)
if (WITH_WEBSOCKETS)

View File

@ -54,6 +54,14 @@ set bitness=64
call :BUILDING
echo.
REM building for WinXP
set "WD=C:\msys64-xp\usr\bin\"
set MSYSTEM=MINGW32
set bitness=32
set "xSH=%WD%bash -lc"
call :BUILDING_XP
echo.
del README.txt >> nul
echo Build complete...
@ -71,5 +79,11 @@ echo Build AESNI...
%xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_aesni_%tag%.log 2>&1
echo Build without extensions...
%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build/build_win%bitness%_%tag%.log 2>&1
goto EOF
:BUILDING_XP
%xSH% "make clean" >> nul
echo Building i2pd %tag% for winxp...
%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build/build_winxp_%tag%.log 2>&1
:EOF

View File

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu
aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC
WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255
bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls
LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d
roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr
omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H
uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV
Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA
3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo
dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ
HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv
TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl
/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV
exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe
jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl
1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T
5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa
0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME
Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge
vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S
DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O
lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs
cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA
FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG
1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/
4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI
flpzWXkFM2D36OUaubfe9YY=
-----END CERTIFICATE-----

View File

@ -1,34 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIF3DCCA8SgAwIBAgIQPxUlcrbHX/xdyJ09E36rJzANBgkqhkiG9w0BAQsFADB3
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEgMB4GA1UEAwwX
cmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwHhcNMTgxMjA3MTYzNDIxWhcNMjgxMjA3
MTYzNDIxWjB3MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhY
MR4wHAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEg
MB4GA1UEAwwXcmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQC912NDk6x85uqB4gyQQcded0RVrbWehWOanDRv7kC3
92jeziPbeMtqrLsfU1MdDtQiGijpNkQ/IIitPw+6vJAIh82gyOUZvsn2XOyb/Fz0
Fu8OrDghwl39yK8kwtqCFw3VAgafgKxz2oRge9mxFBECi50vYEPIBwNhr4yc/opu
wWUmzmRyX4gD7vKmRU6ZTwX4LXnwdl+5VbW3updcZKsDuTnKvC9FGhDRR9kIk2G9
43sLN263nCYPykP7DaB1cUdi1vDEMw5dot+eu16qTIbuypEvYNvbB/9FyCQllm1h
vBbSku3IYpcnRPmoeyhoR/MmCySRbK5R4SrSsVD1YBpwxgn0Q4+fzEgFzT9P4oez
HkDGKVP2HdgmXx9j36fEqqvjqzRleWDwEWwIZVRLCFO+hhhT3JAjnNGJTWv1SQGB
8tz9nyYTJuhvyHE/CO5owFeCdeOGMq2KPge9w34T+mvewTEEhGU8yRAt8Xp8s5Y9
RCUGvuQ79+edRtj7FJg7yVB8pAQ+VB9msNQvzrTnPYC9Wo7chJhBiraMiIabzIhC
f34Gg9lkX1N0dVND5rnZWwzBM6JhNG1iZZCRHVPnXdZRixUlqmFpCP/eekshksj/
6UP/WeGA6X4HyEsC6QEf7eMhcHYjyyTzYagKrwCHg77fmIjF8rmpP2LqWSQW8bDD
uQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0OBBkEF3Jlc2VlZGkycG5l
dGluQG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQCWpXs6iuTy/w2R7q7Ua6vl
JYZwbQ+czk5ydzkBgcNkMMMNRT7sZR9xYvV+ftiL4bFQP/3ZJyo7cYz2Q6+M3oAm
YDcZWBkLUVihSlMxhWwmeFTKV2EL+bzwY1V/cy7wgukKnFIes75dLP/v25jgjdlw
Xe6R+fQM0EoHeVzzrWk/qYp6oEwtQXfZnUu/Bf45hRnnHBzzh1wCql41vbEs3Niq
+SVwY1wLT0yC1L8HqjCLX1/L5PAXxbvEGzwnXSkLKK4bPxdmVDZvS9uzXrWmTbNi
HpKIFnOif16zSgyeaOM7HETIJuVzgooUMtt+Vsr1VGdtm6K7I9J5C+rX/ckU8oaX
UjmzhWXudN0VTslogsKUCV6xG2CskeE3wnuT8HYXz9NMw6c/kIGH4hY7LcfU8Teu
QjSy2RRvy6InmZNV5sY9lzzO6romEycSoUlpCa3Ltb/5KKoYZFTsXr8suqJk89lC
e+TVMHqOZdLK/usqQDcafLypHpw9SH2Tg4jrzV/zLqacbjx6bZD5IrpY0Gf7BXg/
pikwyA9c490G6ZcWrSEP8bzh6LL2rA2AwxaeJJNVyLHCSLrn/7DezM5J/qhd90Qg
kcZGJrUOCSWl6mDvUZn5XiQ955XwOnZQ+wsM85B3CVX22x5bp0SYWHCQBPnthPwP
Q5DD3jExbpwG5n35HEcHYw==
-----END CERTIFICATE-----

View File

@ -36,8 +36,8 @@ RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib
&& cd /usr/local/bin \
&& strip i2pd \
&& rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \
boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
libtool g++ gcc pkgconfig
# 2. Adding required libraries to run i2pd to ensure it will run.

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.26.0
Version: 2.30.0
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@ -110,6 +110,18 @@ getent passwd i2pd >/dev/null || \
%changelog
* Tue Feb 25 2020 orignal <i2porignal@yandex.ru> - 2.30.0
- update to 2.30.0
* Mon Oct 21 2019 orignal <i2porignal@yandex.ru> - 2.29.0
- update to 2.29.0
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
- update to 2.28.0
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
- update to 2.27.0
* Fri Jun 7 2019 orignal <i2porignal@yandex.ru> - 2.26.0
- update to 2.26.0

View File

@ -1,5 +1,5 @@
Name: i2pd
Version: 2.26.0
Version: 2.30.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@ -108,6 +108,18 @@ getent passwd i2pd >/dev/null || \
%changelog
* Tue Feb 25 2020 orignal <i2porignal@yandex.ru> - 2.30.0
- update to 2.30.0
* Mon Oct 21 2019 orignal <i2porignal@yandex.ru> - 2.29.0
- update to 2.29.0
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
- update to 2.28.0
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
- update to 2.27.0
* Fri Jun 7 2019 orignal <i2porignal@yandex.ru> - 2.26.0
- update to 2.26.0
@ -124,7 +136,7 @@ getent passwd i2pd >/dev/null || \
- update to 2.22.0
- add support of tunnelsdir option
* Thu Oct 22 2018 orignal <i2porignal@yandex.ru> - 2.21.1
* Mon Oct 22 2018 orignal <i2porignal@yandex.ru> - 2.21.1
- update to 2.21.1
* Thu Oct 4 2018 orignal <i2porignal@yandex.ru> - 2.21.0

View File

@ -356,7 +356,7 @@ namespace http {
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
if (dest->IsEncryptedLeaseSet ())
{
i2p::data::BlindedPublicKey blinded (dest->GetIdentity ());
i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ());
s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type='checkbox' id='slide-b33'/>\r\n<p class='content'>\r\n";
s << blinded.ToB33 () << ".b32.i2p<br>\r\n";
s << "</p>\r\n</div>\r\n";

View File

@ -394,13 +394,15 @@ namespace client
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
bool first = true;
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
if (it1 != m_RouterInfoHandlers.end ())
{
if (it != params.begin ()) results << ",";
if (!first) results << ",";
else first = false;
(this->*(it1->second))(results);
}
else

View File

@ -79,43 +79,59 @@ namespace transport
void UPnP::Discover ()
{
#if MINIUPNPC_API_VERSION >= 14
int nerror = 0;
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
#elif ( MINIUPNPC_API_VERSION >= 8 || defined(UPNPDISCOVER_SUCCESS) )
int nerror = 0;
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
bool isError;
int err;
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNPDISCOVER_SUCCESS;
#if (MINIUPNPC_API_VERSION >= 14)
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err);
#else
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0);
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, &err);
#endif
isError = err != UPNPDISCOVER_SUCCESS;
#else // MINIUPNPC_API_VERSION >= 8
err = 0;
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
isError = m_Devlist == NULL;
#endif // MINIUPNPC_API_VERSION >= 8
{
// notify satrting thread
// notify starting thread
std::unique_lock<std::mutex> l(m_StartedMutex);
m_Started.notify_all ();
}
int r;
r = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
if (r == 1)
if (isError)
{
r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(r != UPNPCOMMAND_SUCCESS)
LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
return;
}
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED)
{
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress() returned ", r);
LogPrint (eLogError, "UPnP: unable to get external address: error ", err);
return;
}
else
{
LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0])
{
LogPrint (eLogError, "UPnP: GetExternalIPAddress() failed.");
LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
return;
}
}
}
else
{
LogPrint (eLogError, "UPnP: GetValidIGD() failed.");
LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
return;
}
@ -126,6 +142,20 @@ namespace transport
PortMapping ();
}
int UPnP::CheckMapping (const char* port, const char* type)
{
int err = UPNPCOMMAND_SUCCESS;
#if (MINIUPNPC_API_VERSION >= 10)
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL, NULL);
#elif ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL);
#else
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL);
#endif
return err;
}
void UPnP::PortMapping ()
{
const auto& a = context.GetRouterInfo().GetAddresses();
@ -134,13 +164,47 @@ namespace transport
if (!address->host.is_v6 () && address->port)
TryPortMapping (address);
}
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
PortMapping ();
});
}
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
int err = UPNPCOMMAND_SUCCESS;
// check for existing mapping
err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err != UPNPCOMMAND_SUCCESS) // if mapping not found
{
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL);
#else
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL);
#endif
if (err != UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: port forwarding to ", m_NetworkAddr, ":", strPort, " failed: return code ", err);
return;
}
else
{
LogPrint (eLogInfo, "UPnP: port successfully forwarded (", m_externalIPAddress ,":", strPort, " type ", strType, " -> ", m_NetworkAddr ,":", strPort ,")");
return;
}
}
else
{
LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
return;
}
}
void UPnP::CloseMapping ()
@ -153,53 +217,42 @@ namespace transport
}
}
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int r;
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
#ifdef UPNPDISCOVER_SUCCESS
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
#else
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0);
#endif
if (r!=UPNPCOMMAND_SUCCESS)
{
LogPrint (eLogError, "UPnP: AddPortMapping (", m_NetworkAddr, ":", strPort, ") failed with code ", r);
return;
}
else
{
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort, " type ", strType, " -> ", m_externalIPAddress ,":", strPort ,")");
return;
}
}
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
if(!m_upnpUrlsInitialized) {
return;
}
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int r = 0;
r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r);
int err = UPNPCOMMAND_SUCCESS;
err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err == UPNPCOMMAND_SUCCESS)
{
err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err);
}
}
void UPnP::Close ()
{
freeUPNPDevlist (m_Devlist);
m_Devlist = 0;
FreeUPNPUrls (&m_upnpUrls);
}
if(m_upnpUrlsInitialized){
FreeUPNPUrls (&m_upnpUrls);
m_upnpUrlsInitialized=false;
}
}
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{
switch (address->transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
return "TCP";
break;
return "TCP";
break;
case i2p::data::RouterInfo::eTransportSSU:
default:
return "UDP";
return "UDP";
}
}
}

View File

@ -19,20 +19,31 @@ namespace i2p
{
namespace transport
{
const int UPNP_RESPONSE_TIMEOUT = 2000; // in milliseconds
enum
{
UPNP_IGD_NONE = 0,
UPNP_IGD_VALID_CONNECTED = 1,
UPNP_IGD_VALID_NOT_CONNECTED = 2,
UPNP_IGD_INVALID = 3
};
class UPnP
{
public:
public:
UPnP ();
~UPnP ();
void Close ();
void Close ();
void Start ();
void Stop ();
void Start ();
void Stop ();
private:
private:
void Discover ();
int CheckMapping (const char* port, const char* type);
void PortMapping ();
void TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
void CloseMapping ();
@ -41,23 +52,22 @@ namespace transport
void Run ();
std::string GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address);
private:
private:
bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
std::unique_ptr<std::thread> m_Thread;
std::condition_variable m_Started;
std::mutex m_StartedMutex;
boost::asio::io_service m_Service;
boost::asio::deadline_timer m_Timer;
struct UPNPUrls m_upnpUrls;
struct IGDdatas m_upnpData;
bool m_upnpUrlsInitialized=false;
struct UPNPUrls m_upnpUrls;
struct IGDdatas m_upnpData;
// For miniupnpc
char * m_MulticastIf = 0;
char * m_Minissdpdpath = 0;
struct UPNPDev * m_Devlist = 0;
char m_NetworkAddr[64];
char m_externalIPAddress[40];
// For miniupnpc
struct UPNPDev * m_Devlist = 0;
char m_NetworkAddr[64];
char m_externalIPAddress[40];
};
}
}
@ -65,14 +75,15 @@ namespace transport
#else // USE_UPNP
namespace i2p {
namespace transport {
/* class stub */
class UPnP {
public:
UPnP () {};
~UPnP () {};
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
void Stop () {};
};
/* class stub */
class UPnP {
public:
UPnP () {};
~UPnP () {};
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
void Stop () {};
};
}
}
#endif // USE_UPNP

26
debian/changelog vendored
View File

@ -1,3 +1,27 @@
i2pd (2.30.0-1) unstable; urgency=medium
* updated to version 2.30.0/0.9.45
-- orignal <orignal@i2pmail.org> Tue, 25 Feb 2020 16:00:00 +0000
i2pd (2.29.0-1) unstable; urgency=medium
* updated to version 2.29.0/0.9.43
-- orignal <orignal@i2pmail.org> Mon, 21 Oct 2019 16:00:00 +0000
i2pd (2.28.0-1) unstable; urgency=medium
* updated to version 2.28.0/0.9.42
-- orignal <orignal@i2pmail.org> Tue, 27 Aug 2019 16:00:00 +0000
i2pd (2.27.0-1) unstable; urgency=medium
* updated to version 2.27.0/0.9.41
-- orignal <orignal@i2pmail.org> Wed, 3 Jul 2019 16:00:00 +0000
i2pd (2.26.0-1) unstable; urgency=medium
* updated to version 2.26.0
@ -36,7 +60,7 @@ i2pd (2.21.1-1) unstable; urgency=medium
* updated to version 2.21.1
-- orignal <orignal@i2pmail.org> Thu, 22 Oct 2018 16:00:00 +0000
-- orignal <orignal@i2pmail.org> Mon, 22 Oct 2018 16:00:00 +0000
i2pd (2.21.0-1) unstable; urgency=medium

View File

@ -124,8 +124,14 @@ namespace data
return publicKeyLength;
}
//----------------------------------------------------------
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity)
const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01;
const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth):
m_IsClientAuth (clientAuth)
{
if (!identity) return;
auto len = identity->GetSigningPublicKeyLen ();
@ -135,16 +141,22 @@ namespace data
m_BlindedSigType = m_SigType;
}
BlindedPublicKey::BlindedPublicKey (const std::string& 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.c_str (), b33.length (), addr, 40);
if (l < 32)
{
LogPrint (eLogError, "Blinding: malformed b33 ", b33);
return;
}
uint32_t checksum = crc32 (0, addr + 3, l - 3);
// checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
uint8_t flag = addr[0];
uint8_t flags = addr[0];
size_t offset = 1;
if (flag & 0x01) // two bytes signatures
if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures
{
m_SigType = bufbe16toh (addr + offset); offset += 2;
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2;
@ -154,6 +166,8 @@ namespace data
m_SigType = addr[offset]; offset++;
m_BlindedSigType = addr[offset]; offset++;
}
m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG;
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType));
if (blindedVerifier)
{
@ -174,7 +188,9 @@ namespace data
{
if (m_PublicKey.size () > 32) return ""; // assume 25519
uint8_t addr[35]; char str[60]; // TODO: define actual length
addr[0] = 0; // flags
uint8_t flags = 0;
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
addr[0] = flags; // flags
addr[1] = m_SigType; // sig type
addr[2] = m_BlindedSigType; // blinded sig type
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ());

View File

@ -14,7 +14,7 @@ namespace data
{
public:
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity);
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
std::string ToB33 () const;
@ -22,6 +22,7 @@ namespace data
size_t GetPublicKeyLen () const { return m_PublicKey.size (); };
SigningKeyType GetSigType () const { return m_SigType; };
SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; };
bool IsValid () const { return GetSigType (); }; // signature type 0 means invalid
void GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const; // 32 bytes
size_t GetBlindedKey (const char * date, uint8_t * blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length
@ -38,6 +39,7 @@ namespace data
std::vector<uint8_t> m_PublicKey;
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
bool m_IsClientAuth = false;
};
}
}

View File

@ -131,6 +131,7 @@ namespace config {
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
("sam.singlethread", value<bool>()->default_value(true), "Sessions run in the SAM bridge's thread")
;
options_description bob("BOB options");
@ -190,10 +191,10 @@ namespace config {
"https://reseed.i2p-projekt.de/,"
"https://i2p.mooo.com/netDb/,"
"https://netdb.i2p2.no/,"
"https://reseed.i2p2.no/,"
"https://reseed2.i2p2.no/,"
// "https://us.reseed.i2p2.no:444/," // mamoth's shit
// "https://uk.reseed.i2p2.no:444/," // mamoth's shit
"https://reseed.i2p.net.in/,"
"https://download.xxlspeed.com/,"
"https://reseed-fr.i2pd.xyz/,"
"https://reseed.memcpy.io/,"
"https://reseed.onion.im/,"

View File

@ -296,11 +296,20 @@ namespace crypto
#if OPENSSL_X25519
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey
if (pub)
memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey
else
{
size_t len = 32;
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
}
#else
m_Ctx = BN_CTX_new ();
memcpy (m_PrivateKey, priv, 32);
memcpy (m_PublicKey, pub, 32);
m_Ctx = BN_CTX_new ();
if (pub)
memcpy (m_PublicKey, pub, 32);
else
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
#endif
}
@ -358,6 +367,18 @@ namespace crypto
#endif
}
void X25519Keys::SetPrivateKey (const uint8_t * priv)
{
#if OPENSSL_X25519
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
if (m_Pkey) EVP_PKEY_free (m_Pkey);
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
#else
memcpy (m_PrivateKey, priv, 32);
#endif
}
// ElGamal
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding)
{
@ -1250,18 +1271,29 @@ namespace crypto
#endif
}
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out)
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
uint8_t * out, size_t outLen)
{
#if OPENSSL_HKDF
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, NULL);
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr);
EVP_PKEY_derive_init (pctx);
EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256());
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
if (key && keyLen)
{
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
}
else
{
// zerolen
EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
uint8_t tempKey[32]; unsigned int len;
HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len);
EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len);
}
if (info.length () > 0)
EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ());
size_t outlen = 64;
EVP_PKEY_derive (pctx, out, &outlen);
EVP_PKEY_derive (pctx, out, &outLen);
EVP_PKEY_CTX_free (pctx);
#else
uint8_t prk[32]; unsigned int len;
@ -1269,8 +1301,11 @@ namespace crypto
auto l = info.length ();
memcpy (out, info.c_str (), l); out[l] = 0x01;
HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len);
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
if (outLen > 32) // 64
{
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
}
#endif
}

View File

@ -27,8 +27,8 @@
# define X509_getm_notAfter X509_get_notAfter
#else
# define LEGACY_OPENSSL 0
# define OPENSSL_HKDF 1
# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
# define OPENSSL_HKDF 1
# define OPENSSL_EDDSA 1
# define OPENSSL_X25519 1
# define OPENSSL_SIPHASH 1
@ -74,12 +74,13 @@ namespace crypto
public:
X25519Keys ();
X25519Keys (const uint8_t * priv, const uint8_t * pub); // for RouterContext
X25519Keys (const uint8_t * priv, const uint8_t * pub); // if pub is null, derive from priv
~X25519Keys ();
void GenerateKeys ();
const uint8_t * GetPublicKey () const { return m_PublicKey; };
void GetPrivateKey (uint8_t * priv) const;
void SetPrivateKey (const uint8_t * priv); // wihout calculating public
void Agree (const uint8_t * pub, uint8_t * shared);
private:
@ -296,7 +297,7 @@ namespace crypto
// HKDF
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out); // salt - 32, out - 64, info <= 32
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
// init and terminate
void InitCrypto (bool precomputation);

View File

@ -14,6 +14,7 @@ namespace crypto
void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding)
{
if (!ctx) return;
ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding);
}
@ -24,6 +25,7 @@ namespace crypto
bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding)
{
if (!ctx) return false;
return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding);
}
@ -146,6 +148,34 @@ namespace crypto
BN_free (x); BN_free (y);
}
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub)
{
memcpy (m_PublicKey, pub, 32);
}
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool)
{
memcpy (pub, m_PublicKey, 32);
}
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv)
{
m_StaticKeys.SetPrivateKey (priv);
}
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding)
{
m_StaticKeys.Agree (epub, sharedSecret);
return true;
}
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub)
{
X25519Keys k;
k.GenerateKeys ();
k.GetPrivateKey (priv);
memcpy (pub, k.GetPublicKey (), 32);
}
}
}

View File

@ -116,6 +116,39 @@ namespace crypto
};
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub);
// ECIES-X25519-AEAD-Ratchet
class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor
{
public:
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub);
~ECIESX25519AEADRatchetEncryptor () {};
void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool);
// copies m_PublicKey to pub
private:
uint8_t m_PublicKey[32];
};
class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor
{
public:
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv);
~ECIESX25519AEADRatchetDecryptor () {};
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding);
// agree with static and return in sharedSecret (32 bytes)
size_t GetPublicKeyLen () const { return 32; };
private:
X25519Keys m_StaticKeys;
};
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
}
}

View File

@ -12,10 +12,8 @@ namespace i2p
namespace datagram
{
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner.get()),
m_Receiver (nullptr)
m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr)
{
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
}
DatagramDestination::~DatagramDestination ()
@ -28,14 +26,15 @@ namespace datagram
auto owner = m_Owner;
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
uint8_t * buf = v.data();
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE);
auto localIdentity = m_Owner->GetIdentity ();
auto identityLen = localIdentity->ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen;
auto signatureLen = m_Identity.GetSignatureLen ();
auto signatureLen = localIdentity->GetSignatureLen ();
uint8_t * buf1 = signature + signatureLen;
size_t headerLen = identityLen + signatureLen;
memcpy (buf1, payload, len);
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
if (localIdentity->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{
uint8_t hash[32];
SHA256(buf1, len, hash);
@ -49,7 +48,13 @@ namespace datagram
session->SendMsg(msg);
}
void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{
auto msg = CreateDataMessage (payload, len, fromPort, toPort, true); // raw
auto session = ObtainSession(identity);
session->SendMsg(msg);
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
{
i2p::data::IdentityEx identity;
@ -82,6 +87,14 @@ namespace datagram
LogPrint (eLogWarning, "Datagram signature verification failed");
}
void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (m_RawReceiver)
m_RawReceiver (fromPort, toPort, buf, len);
else
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
}
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
{
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
@ -92,18 +105,24 @@ namespace datagram
return r;
}
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
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)
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
{
if (isRaw)
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen);
else
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
else
LogPrint (eLogWarning, "Datagram: decompression failed");
}
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw)
{
auto msg = NewI2NPMessage ();
uint8_t * buf = msg->GetPayload ();
@ -114,7 +133,7 @@ namespace datagram
htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, fromPort); // source port
htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // 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);
}
@ -170,7 +189,7 @@ namespace datagram
return nullptr;
}
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent),

View File

@ -37,7 +37,7 @@ namespace datagram
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
{
public:
DatagramSession(i2p::client::ClientDestination * localDestination, const i2p::data::IdentHash & remoteIdent);
DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, const i2p::data::IdentHash & remoteIdent);
void Start ();
void Stop ();
@ -81,7 +81,7 @@ namespace datagram
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
private:
i2p::client::ClientDestination * m_LocalDestination;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDestination;
i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
@ -99,22 +99,28 @@ namespace datagram
class DatagramDestination
{
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver;
public:
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; };
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver () { m_RawReceiver = nullptr; };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
// clean up stale sessions
@ -124,17 +130,19 @@ namespace datagram
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw = false);
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);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
Receiver FindReceiver(uint16_t port);
private:
i2p::client::ClientDestination * m_Owner;
i2p::data::IdentityEx m_Identity;
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default
RawReceiver m_RawReceiver; // default
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
std::mutex m_ReceiversMutex;

View File

@ -7,17 +7,17 @@
#include "Timestamp.h"
#include "NetDb.hpp"
#include "Destination.h"
#include "util.h"
namespace i2p
{
namespace client
{
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic),
m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service,
bool isPublic, const std::map<std::string, std::string> * params):
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service),
m_LeaseSetType (DEFAULT_LEASESET_TYPE)
m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE)
{
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
@ -70,6 +70,29 @@ namespace client
it = params->find (I2CP_PARAM_LEASESET_TYPE);
if (it != params->end ())
m_LeaseSetType = std::stoi(it->second);
if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{
// authentication for encrypted LeaseSet
it = params->find (I2CP_PARAM_LEASESET_AUTH_TYPE);
if (it != params->end ())
{
auto authType = std::stoi (it->second);
if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
m_AuthType = authType;
else
LogPrint (eLogError, "Destination: Unknown auth type ", authType);
}
}
it = params->find (I2CP_PARAM_LEASESET_PRIV_KEY);
if (it != params->end ())
{
m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>());
if (m_LeaseSetPrivKey->FromBase64 (it->second) != 32)
{
LogPrint(eLogError, "Destination: invalid value i2cp.leaseSetPrivKey ", it->second);
m_LeaseSetPrivKey.reset (nullptr);
}
}
}
}
catch (std::exception & ex)
@ -100,77 +123,36 @@ namespace client
LeaseSetDestination::~LeaseSetDestination ()
{
if (m_IsRunning)
Stop ();
if (m_Pool)
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
for (auto& it: m_LeaseSetRequests)
it.second->Complete (nullptr);
}
void LeaseSetDestination::Run ()
void LeaseSetDestination::Start ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Destination: runtime exception: ", ex.what ());
}
}
if (m_Nickname.empty ())
m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname
LoadTags ();
m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true);
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
}
bool LeaseSetDestination::Start ()
void LeaseSetDestination::Stop ()
{
if (!m_IsRunning)
m_CleanupTimer.cancel ();
m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
if (m_Pool)
{
if (m_Nickname.empty ())
m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname
LoadTags ();
m_IsRunning = true;
m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true);
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
shared_from_this (), std::placeholders::_1));
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
return true;
m_Pool->SetLocalDestination (nullptr);
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
}
else
return false;
}
bool LeaseSetDestination::Stop ()
{
if (m_IsRunning)
{
m_CleanupTimer.cancel ();
m_PublishConfirmationTimer.cancel ();
m_PublishVerificationTimer.cancel ();
m_IsRunning = false;
if (m_Pool)
{
m_Pool->SetLocalDestination (nullptr);
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
}
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = 0;
}
SaveTags ();
CleanUp (); // GarlicDestination
return true;
}
else
return false;
SaveTags ();
CleanUp (); // GarlicDestination
}
bool LeaseSetDestination::Reconfigure(std::map<std::string, std::string> params)
@ -330,30 +312,38 @@ namespace client
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
}
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
{
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
}
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
{
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
switch (typeID)
{
case eI2NPData:
HandleDataMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
HandleDataMessage (payload, len);
break;
case eI2NPDeliveryStatus:
// we assume tunnel tests non-encrypted
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
break;
case eI2NPDatabaseStore:
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
HandleDatabaseStoreMessage (payload, len);
break;
case eI2NPDatabaseSearchReply:
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
HandleDatabaseSearchReplyMessage (payload, len);
break;
default:
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
return false;
}
return true;
}
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
@ -398,7 +388,7 @@ 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); // LeaseSet2
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetEncryptionType ()); // LeaseSet2
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
@ -422,7 +412,7 @@ namespace client
auto it2 = m_LeaseSetRequests.find (key);
if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey)
{
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedBlindedKey);
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr, GetEncryptionType ());
if (ls2->IsValid ())
{
m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
@ -488,9 +478,8 @@ namespace client
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
}
void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
if (msgID == m_PublishReplyToken)
{
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
@ -502,7 +491,7 @@ namespace client
shared_from_this (), std::placeholders::_1));
}
else
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
}
void LeaseSetDestination::SetLeaseSetUpdated ()
@ -833,10 +822,12 @@ namespace client
}
}
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (service, isPublic, params),
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
m_DatagramDestination (nullptr), m_RefCounter (0),
m_ReadyChecker(GetService())
m_ReadyChecker(service)
{
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
@ -849,21 +840,54 @@ namespace client
if (it != params->end ())
m_EncryptionKeyType = std::stoi(it->second);
}
if (isPublic && m_EncryptionKeyType == GetIdentity ()->GetCryptoKeyType ()) // TODO: presist key type
memset (m_EncryptionPrivateKey, 0, 256);
memset (m_EncryptionPublicKey, 0, 256);
if (isPublic)
PersistTemporaryKeys ();
else
i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey);
if (isPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
// extract streaming params
if (params)
try
{
if (params)
{
// extract streaming params
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{
// authentication for encrypted LeaseSet
auto authType = GetAuthType ();
if (authType > 0)
{
m_AuthKeys = std::make_shared<std::vector<i2p::data::AuthPublicKey> >();
if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH)
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params);
else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params);
else
LogPrint (eLogError, "Destination: Unexpected auth type ", authType);
if (m_AuthKeys->size ())
LogPrint (eLogInfo, "Destination: ", m_AuthKeys->size (), " auth keys read");
else
{
LogPrint (eLogError, "Destination: No auth keys read for auth type ", authType);
m_AuthKeys = nullptr;
}
}
}
}
}
catch (std::exception & ex)
{
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second);
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
}
}
@ -871,43 +895,33 @@ namespace client
{
}
bool ClientDestination::Start ()
void ClientDestination::Start ()
{
if (LeaseSetDestination::Start ())
{
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Start ();
return true;
}
else
return false;
LeaseSetDestination::Start ();
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
m_StreamingDestination->Start ();
for (auto& it: m_StreamingDestinationsByPorts)
it.second->Start ();
}
bool ClientDestination::Stop ()
void ClientDestination::Stop ()
{
if (LeaseSetDestination::Stop ())
LeaseSetDestination::Stop ();
m_ReadyChecker.cancel();
m_StreamingDestination->Stop ();
//m_StreamingDestination->SetOwner (nullptr);
m_StreamingDestination = nullptr;
for (auto& it: m_StreamingDestinationsByPorts)
{
m_ReadyChecker.cancel();
m_StreamingDestination->Stop ();
//m_StreamingDestination->SetOwner (nullptr);
m_StreamingDestination = nullptr;
for (auto& it: m_StreamingDestinationsByPorts)
{
it.second->Stop ();
//it.second->SetOwner (nullptr);
}
m_StreamingDestinationsByPorts.clear ();
if (m_DatagramDestination)
{
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
}
return true;
it.second->Stop ();
//it.second->SetOwner (nullptr);
}
m_StreamingDestinationsByPorts.clear ();
if (m_DatagramDestination)
{
delete m_DatagramDestination;
m_DatagramDestination = nullptr;
}
else
return false;
}
#ifdef I2LUA
@ -967,6 +981,13 @@ namespace client
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]);
}
@ -1105,8 +1126,8 @@ namespace client
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
memset (m_EncryptionPrivateKey, 0, 256);
memset (m_EncryptionPublicKey, 0, 256);
i2p::data::PrivateKeys::GenerateCryptoKeyPair (GetIdentity ()->GetCryptoKeyType (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
// TODO:: persist crypto key type
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
if (f1) {
f1.write ((char *)m_EncryptionPublicKey, 256);
@ -1129,10 +1150,11 @@ namespace client
{
// standard LS2 (type 3) first
auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256;
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, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys);
m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels, IsPublic (), isPublishedEncrypted);
if (isPublishedEncrypted) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
leaseSet = ls2;
}
SetLeaseSet (leaseSet);
@ -1151,5 +1173,53 @@ namespace client
LogPrint (eLogError, "Destinations: decryptor is not set");
return false;
}
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
{
for (auto it: *params)
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
{
auto pos = it.second.find (':');
if (pos != std::string::npos)
{
i2p::data::AuthPublicKey pubKey;
if (pubKey.FromBase64 (it.second.substr (pos+1)))
m_AuthKeys->push_back (pubKey);
else
LogPrint (eLogError, "Destination: Unexpected auth key ", it.second.substr (pos+1));
}
}
}
RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
RunnableService ("Destination"),
ClientDestination (GetIOService (), keys, isPublic, params)
{
}
RunnableClientDestination::~RunnableClientDestination ()
{
if (IsRunning ())
Stop ();
}
void RunnableClientDestination::Start ()
{
if (!IsRunning ())
{
ClientDestination::Start ();
StartIOService ();
}
}
void RunnableClientDestination::Stop ()
{
if (IsRunning ())
{
ClientDestination::Stop ();
StopIOService ();
}
}
}
}

View File

@ -20,6 +20,7 @@
#include "NetDb.hpp"
#include "Streaming.h"
#include "Datagram.h"
#include "util.h"
namespace i2p
{
@ -55,7 +56,11 @@ namespace client
const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";
const int DEFAULT_LEASESET_TYPE = 1;
const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType";
const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType";
const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn
const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
// latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
@ -94,18 +99,17 @@ namespace client
public:
LeaseSetDestination (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; };
boost::asio::io_service& GetService () { return m_Service; };
virtual bool Start ();
virtual bool Stop ();
virtual void Start ();
virtual void Stop ();
/** i2cp reconfigure */
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
bool IsRunning () const { return m_IsRunning; };
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
@ -117,7 +121,6 @@ namespace client
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
@ -127,9 +130,15 @@ namespace client
protected:
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
int GetAuthType () const { return m_AuthType; };
bool IsPublic () const { return m_IsPublic; };
virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
@ -137,7 +146,6 @@ namespace client
private:
void Run ();
void UpdateLeaseSet ();
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt ();
void Publish ();
@ -146,7 +154,7 @@ namespace client
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (uint32_t msgID);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
@ -156,9 +164,7 @@ namespace client
private:
volatile bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service& m_Service;
mutable std::mutex m_RemoteLeaseSetsMutex;
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
@ -174,7 +180,8 @@ namespace client
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
m_PublishDelayTimer, m_CleanupTimer;
std::string m_Nickname;
int m_LeaseSetType;
int m_LeaseSetType, m_AuthType;
std::unique_ptr<i2p::data::Tag<32> > m_LeaseSetPrivKey; // non-null if presented
public:
@ -182,6 +189,7 @@ namespace client
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
bool IsEncryptedLeaseSet () const { return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; };
bool IsPerClientAuth () const { return m_AuthType > 0; };
};
class ClientDestination: public LeaseSetDestination
@ -195,11 +203,12 @@ namespace client
void Ready(ReadyPromise & p);
#endif
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~ClientDestination ();
virtual bool Start ();
virtual bool Stop ();
void Start ();
void Stop ();
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
@ -229,6 +238,8 @@ namespace client
// implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
i2p::data::CryptoKeyType GetEncryptionType () const { return m_EncryptionKeyType; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
protected:
@ -246,6 +257,9 @@ namespace client
void ScheduleCheckForReady(ReadyPromise * p);
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
#endif
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
private:
i2p::data::PrivateKeys m_Keys;
@ -261,11 +275,25 @@ namespace client
boost::asio::deadline_timer m_ReadyChecker;
std::shared_ptr<std::vector<i2p::data::AuthPublicKey> > m_AuthKeys; // we don't need them for I2CP
public:
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
};
class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination
{
public:
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
~RunnableClientDestination ();
void Start ();
void Stop ();
};
}
}

View File

@ -0,0 +1,522 @@
#include <string.h>
#include <openssl/sha.h>
#include "Log.h"
#include "Crypto.h"
#include "Elligator.h"
#include "Tag.h"
#include "I2PEndian.h"
#include "Timestamp.h"
#include "ECIESX25519AEADRatchetSession.h"
namespace i2p
{
namespace garlic
{
void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k)
{
// DH_INITIALIZE(rootKey, k)
uint8_t keydata[64];
i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
// nextRootKey = keydata[0:31]
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf);
// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32);
m_NextSymmKeyIndex = 0;
}
void RatchetTagSet::NextSessionTagRatchet ()
{
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32);
m_NextIndex = 0;
}
uint64_t RatchetTagSet::GetNextSessionTag ()
{
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
m_NextIndex++;
return m_KeyData.GetTag ();
}
const uint8_t * RatchetTagSet::GetSymmKey (int index)
{
// TODO: store intermediate keys
if (m_NextSymmKeyIndex > 0 && index == m_NextSymmKeyIndex)
{
i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK);
m_NextSymmKeyIndex++;
}
else
CalculateSymmKeyCK (index);
return m_CurrentSymmKeyCK + 32;
}
void RatchetTagSet::CalculateSymmKeyCK (int index)
{
i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
for (int i = 0; i < index; i++)
i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); // keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
m_NextSymmKeyIndex = index + 1;
}
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner):
GarlicRoutingSession (owner, true)
{
ResetKeys ();
}
ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession ()
{
}
void ECIESX25519AEADRatchetSession::ResetKeys ()
{
// TODO : use precalculated hashes
static const char protocolName[41] = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"; // 40 bytes
SHA256 ((const uint8_t *)protocolName, 40, m_H);
memcpy (m_CK, m_H, 32);
SHA256 (m_H, 32, m_H);
}
void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (m_H, &ctx);
}
void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce)
{
memset (nonce, 0, 4);
htole64buf (nonce + 4, seqn);
}
bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf)
{
for (int i = 0; i < 10; i++)
{
m_EphemeralKeys.GenerateKeys ();
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf))
return true; // success
}
return false;
}
uint64_t ECIESX25519AEADRatchetSession::CreateNewSessionTag () const
{
uint8_t tagsetKey[32];
i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
// Session Tag Ratchet
RatchetTagSet tagsetNsr;
tagsetNsr.DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
tagsetNsr.NextSessionTagRatchet ();
return tagsetNsr.GetNextSessionTag ();
}
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
// we are Bob
// KDF1
MixHash (GetOwner ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || bpk)
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
{
LogPrint (eLogError, "Garlic: Can't decode elligator");
return false;
}
buf += 32; len -= 32;
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32];
GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr); // x25519(bsk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// decrypt flags/static
uint8_t nonce[12], fs[32];
CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
return false;
}
MixHash (buf, 48); // h = SHA256(h || ciphertext)
buf += 48; len -= 48; // 32 data + 16 poly
// decrypt payload
std::vector<uint8_t> payload (len - 16);
// KDF2 for payload
bool isStatic = !i2p::data::Tag<32> (fs).IsZero ();
if (isStatic)
{
// static key, fs is apk
memcpy (m_RemoteStaticKey, fs, 32);
GetOwner ()->Decrypt (fs, sharedSecret, nullptr); // x25519(bsk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
}
else // all zeros flags
CreateNonce (1, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
return false;
}
if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionReceived;
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
HandlePayload (payload.data (), len - 16);
return true;
}
void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len)
{
size_t offset = 0;
while (offset < len)
{
uint8_t blk = buf[offset];
offset++;
auto size = bufbe16toh (buf + offset);
offset += 2;
LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size);
if (size > len)
{
LogPrint (eLogError, "Garlic: Unexpected block length ", size);
break;
}
switch (blk)
{
case eECIESx25519BlkGalicClove:
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size);
break;
case eECIESx25519BlkDateTime:
LogPrint (eLogDebug, "Garlic: datetime");
break;
case eECIESx25519BlkOptions:
LogPrint (eLogDebug, "Garlic: options");
break;
case eECIESx25519BlkPadding:
LogPrint (eLogDebug, "Garlic: padding");
break;
default:
LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk);
}
offset += size;
}
}
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
ResetKeys ();
// we are Alice, bpk is m_RemoteStaticKey
size_t offset = 0;
if (!GenerateEphemeralKeysAndEncode (out + offset))
{
LogPrint (eLogError, "Garlic: Can't encode elligator");
return false;
}
offset += 32;
// KDF1
MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk)
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt static key section
uint8_t nonce[12];
CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed ");
return false;
}
MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
offset += 48;
// KDF2
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr); // x25519 (ask, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false;
}
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionSent;
if (GetOwner ())
GetOwner ()->AddECIESx25519SessionTag (0, CreateNewSessionTag (), shared_from_this ());
return true;
}
bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
// we are Bob
uint64_t tag = CreateNewSessionTag ();
size_t offset = 0;
memcpy (out + offset, &tag, 8);
offset += 8;
if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk
{
LogPrint (eLogError, "Garlic: Can't encode elligator");
return false;
}
offset += 32;
// KDF for Reply Key Section
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
uint8_t nonce[12];
CreateNonce (0, nonce);
// calulate hash for zero length
if (!i2p::crypto::AEADChaCha20Poly1305 (sharedSecret /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
{
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
return false;
}
MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
out += 16;
// KDF for payload
uint8_t keydata[64];
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
// k_ab = keydata[0:31], k_ba = keydata[32:63]
m_ReceiveTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
m_ReceiveTagset.NextSessionTagRatchet ();
m_SendTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_SendTagset.NextSessionTagRatchet ();
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, keydata, nonce, out + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false;
}
m_State = eSessionStateEstablished;
return true;
}
bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len)
{
// we are Alice
LogPrint (eLogDebug, "Garlic: reply received");
const uint8_t * tag = buf;
buf += 8; len -= 8; // tag
uint8_t bepk[32]; // Bob's ephemeral key
if (!i2p::crypto::GetElligator ()->Decode (buf, bepk))
{
LogPrint (eLogError, "Garlic: Can't decode elligator");
return false;
}
buf += 32; len -= 32;
// KDF for Reply Key Section
MixHash (tag, 8); // h = SHA256(h || tag)
MixHash (bepk, 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32];
m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr); // x25519 (ask, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
uint8_t nonce[12];
CreateNonce (0, nonce);
// calulate hash for zero length
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anyting */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
{
LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed");
return false;
}
MixHash (buf, 16); // h = SHA256(h || ciphertext)
buf += 16; len -= 16;
// KDF for payload
uint8_t keydata[64];
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
// k_ab = keydata[0:31], k_ba = keydata[32:63]
m_SendTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
m_SendTagset.NextSessionTagRatchet ();
m_ReceiveTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
m_ReceiveTagset.NextSessionTagRatchet ();
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
// decrypt payload
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
return false;
}
m_State = eSessionStateEstablished;
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
HandlePayload (payload.data (), len - 16);
return true;
}
bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
uint8_t nonce[12];
auto index = m_SendTagset.GetNextIndex ();
CreateNonce (index, nonce); // tag's index
uint64_t tag = m_SendTagset.GetNextSessionTag ();
memcpy (out, &tag, 8);
// ad = The session tag, 8 bytes
// ciphertext = ENCRYPT(k, n, payload, ad)
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, m_SendTagset.GetSymmKey (index), nonce, out + 8, outLen - 8, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false;
}
return true;
}
bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index)
{
uint8_t nonce[12];
CreateNonce (index, nonce); // tag's index
len -= 8; // tag
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, m_ReceiveTagset.GetSymmKey (index), nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
return false;
}
HandlePayload (payload.data (), len - 16);
if (m_NumReceiveTags > 0)m_NumReceiveTags--;
if (m_NumReceiveTags <= GetOwner ()->GetNumTags ()*2/3)
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
return true;
}
bool ECIESX25519AEADRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len, int index)
{
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
switch (m_State)
{
case eSessionStateEstablished:
return HandleExistingSessionMessage (buf, len, index);
case eSessionStateNew:
return HandleNewIncomingSession (buf, len);
case eSessionStateNewSessionSent:
return HandleNewOutgoingSessionReply (buf, len);
default:
return false;
}
return true;
}
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
auto payload = CreatePayload (msg);
size_t len = payload.size ();
switch (m_State)
{
case eSessionStateEstablished:
if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 24;
break;
case eSessionStateNew:
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 96;
break;
case eSessionStateNewSessionReceived:
if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 72;
break;
default:
return nullptr;
}
htobe32buf (m->GetPayload (), len);
m->len += len + 4;
m->FillI2NPMessageHeader (eI2NPGarlic);
return m;
}
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg)
{
size_t payloadLen = 7; // datatime
if (msg && m_Destination)
payloadLen += msg->GetPayloadLength () + 13 + 32;
auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ());
if (leaseSet)
payloadLen += leaseSet->GetPayloadLength () + 13;
uint8_t paddingSize;
RAND_bytes (&paddingSize, 1);
paddingSize &= 0x0F; paddingSize++; // 1 - 16
payloadLen += paddingSize + 3;
std::vector<uint8_t> v(payloadLen);
size_t offset = 0;
// DateTime
v[offset] = eECIESx25519BlkDateTime; offset++;
htobe16buf (v.data () + offset, 4); offset += 2;
htobe32buf (v.data () + offset, i2p::util::GetSecondsSinceEpoch ()); offset += 4;
// LeaseSet
if (leaseSet)
offset += CreateGarlicClove (leaseSet, v.data () + offset, payloadLen - offset);
// msg
if (msg && m_Destination)
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true);
// padding
v[offset] = eECIESx25519BlkPadding; offset++;
htobe16buf (v.data () + offset, paddingSize); offset += 2;
memset (v.data () + offset, 0, paddingSize); offset += paddingSize;
return v;
}
size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination)
{
if (!msg) return 0;
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
if (isDestination) cloveSize += 32;
if ((int)len < cloveSize + 3) return 0;
buf[0] = eECIESx25519BlkGalicClove; // clove type
htobe16buf (buf + 1, cloveSize); // size
buf += 3;
if (isDestination)
{
*buf = (eGarlicDeliveryTypeDestination << 5);
memcpy (buf + 1, *m_Destination, 32); buf += 32;
}
else
*buf = 0;
buf++; // flag and delivery instructions
*buf = msg->GetTypeID (); // I2NP msg type
htobe32buf (buf + 1, msg->GetMsgID ()); // msgID
htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds
memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ());
return cloveSize + 3;
}
void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (int numTags)
{
for (int i = 0; i < numTags; i++)
{
auto index = m_ReceiveTagset.GetNextIndex ();
uint64_t tag = m_ReceiveTagset.GetNextSessionTag ();
GetOwner ()->AddECIESx25519SessionTag (index, tag, shared_from_this ());
}
m_NumReceiveTags += numTags;
}
}
}

View File

@ -0,0 +1,127 @@
#ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__
#define ECIES_X25519_AEAD_RATCHET_SESSION_H__
#include <string.h>
#include <inttypes.h>
#include <functional>
#include <memory>
#include <vector>
#include "Identity.h"
#include "Crypto.h"
#include "Garlic.h"
namespace i2p
{
namespace garlic
{
class RatchetTagSet
{
public:
void DHInitialize (const uint8_t * rootKey, const uint8_t * k);
void NextSessionTagRatchet ();
uint64_t GetNextSessionTag ();
int GetNextIndex () const { return m_NextIndex; };
const uint8_t * GetSymmKey (int index);
private:
void CalculateSymmKeyCK (int index);
private:
union
{
uint64_t ll[8];
uint8_t buf[64];
const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31]
const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63]
uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39]
} m_KeyData;
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64];
int m_NextIndex, m_NextSymmKeyIndex;
};
enum ECIESx25519BlockType
{
eECIESx25519BlkDateTime = 0,
eECIESx25519BlkSessionID = 1,
eECIESx25519BlkTermination = 4,
eECIESx25519BlkOptions = 5,
eECIESx25519BlkNextSessionKey = 7,
eECIESx25519BlkGalicClove = 11,
eECIESx25519BlkPadding = 254
};
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second of inactivity we should restart after
const int ECIESX25519_EXPIRATION_TIMEOUT = 600; // in seconds
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
{
enum SessionState
{
eSessionStateNew =0,
eSessionStateNewSessionReceived,
eSessionStateNewSessionSent,
eSessionStateEstablished
};
public:
ECIESX25519AEADRatchetSession (GarlicDestination * owner);
~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (const uint8_t * buf, size_t len, int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
{
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
}
bool IsExpired (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT; }
bool CanBeRestarted (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_RESTART_TIMEOUT; }
private:
void ResetKeys ();
void MixHash (const uint8_t * buf, size_t len);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
uint64_t CreateNewSessionTag () const;
bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
bool HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len);
bool HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index);
void HandlePayload (const uint8_t * buf, size_t len);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination = false);
void GenerateMoreReceiveTags (int numTags);
private:
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
uint8_t m_Aepk[32]; // Alice's ephemeral keys TODO: for incoming only
i2p::crypto::X25519Keys m_EphemeralKeys;
SessionState m_State = eSessionStateNew;
uint64_t m_LastActivityTimestamp = 0; // incoming
RatchetTagSet m_SendTagset, m_ReceiveTagset;
int m_NumReceiveTags = 0;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
};
}
}
#endif

View File

@ -31,7 +31,7 @@ namespace crypto
BN_mod_inverse (tmp, tmp, q, ctx);
BN_set_word (d, 121665);
BN_set_negative (d, 1);
BN_mul (d, d, tmp, ctx);
BN_mod_mul (d, d, tmp, q, ctx);
// 2^((q-1)/4)
I = BN_new ();

207
libi2pd/Elligator.cpp Normal file
View File

@ -0,0 +1,207 @@
#include <openssl/rand.h>
#include "Crypto.h"
#include "Elligator.h"
namespace i2p
{
namespace crypto
{
Elligator2::Elligator2 ()
{
// TODO: share with Ed22519
p = BN_new ();
// 2^255-19
BN_set_bit (p, 255); // 2^255
BN_sub_word (p, 19);
p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8
p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2
p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4
A = BN_new (); BN_set_word (A, 486662);
nA = BN_new (); BN_sub (nA, p, A);
BN_CTX * ctx = BN_CTX_new ();
// calculate sqrt(-1)
sqrtn1 = BN_new ();
BN_set_word (sqrtn1, 2);
BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
u = BN_new (); BN_set_word (u, 2);
iu = BN_new (); BN_mod_inverse (iu, u, p, ctx);
BN_CTX_free (ctx);
}
Elligator2::~Elligator2 ()
{
BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14);
BN_free (sqrtn1); BN_free (A); BN_free (nA);
BN_free (u); BN_free (iu);
}
bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const
{
bool ret = true;
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
uint8_t key1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
{
key1[i] = key[31 - i];
key1[31 - i] = key[i];
}
BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x);
BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A
BN_sub (xA, p, xA); // p - (x + A)
BIGNUM * uxxA = BN_CTX_get (ctx); // u*x*xA
BN_mod_mul (uxxA, u, x, p, ctx);
BN_mod_mul (uxxA, uxxA, xA, p, ctx);
if (Legendre (uxxA, ctx) != -1)
{
uint8_t randByte = 0; // random highest bits and high y
if (random)
{
RAND_bytes (&randByte, 1);
highY = randByte & 0x01;
}
BIGNUM * r = BN_CTX_get (ctx);
if (highY)
{
BN_mod_inverse (r, x, p, ctx);
BN_mod_mul (r, r, xA, p, ctx);
}
else
{
BN_mod_inverse (r, xA, p, ctx);
BN_mod_mul (r, r, x, p, ctx);
}
BN_mod_mul (r, r, iu, p, ctx);
SquareRoot (r, r, ctx);
bn2buf (r, encoded, 32);
if (random)
encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte
for (size_t i = 0; i < 16; i++) // To Little Endian
{
uint8_t tmp = encoded[i];
encoded[i] = encoded[31 - i];
encoded[31 - i] = tmp;
}
}
else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
return ret;
}
bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const
{
bool ret = true;
BN_CTX * ctx = BN_CTX_new ();
BN_CTX_start (ctx);
uint8_t encoded1[32];
for (size_t i = 0; i < 16; i++) // from Little Endian
{
encoded1[i] = encoded[31 - i];
encoded1[31 - i] = encoded[i];
}
encoded1[0] &= 0x3F; // drop two highest bits
BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r);
if (BN_cmp (r, p12) <= 0) // r < (p-1)/2
{
// v = -A/(1+u*r^2)
BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx);
BN_mod_mul (v, v, u, p, ctx);
BN_add_word (v, 1);
BN_mod_inverse (v, v, p, ctx);
BN_mod_mul (v, v, nA, p, ctx);
BIGNUM * vpA = BN_CTX_get (ctx);
BN_add (vpA, v, A); // v + A
// t = v^3+A*v^2+v = v^2*(v+A)+v
BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx);
BN_mod_mul (t, t, vpA, p, ctx);
BN_mod_add (t, t, v, p, ctx);
int legendre = Legendre (t, ctx);
BIGNUM * x = BN_CTX_get (ctx);
if (legendre == 1)
BN_copy (x, v);
else
{
BN_sub (x, p, v);
BN_mod_sub (x, x, A, p, ctx);
}
bn2buf (x, key, 32);
for (size_t i = 0; i < 16; i++) // To Little Endian
{
uint8_t tmp = key[i];
key[i] = key[31 - i];
key[31 - i] = tmp;
}
}
else
ret = false;
BN_CTX_end (ctx);
BN_CTX_free (ctx);
return ret;
}
void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const
{
BIGNUM * t = BN_CTX_get (ctx);
BN_mod_exp (t, x, p14, p, ctx); // t = x^((p-1)/4)
BN_mod_exp (r, x, p38, p, ctx); // r = x^((p+3)/8)
BN_add_word (t, 1);
if (!BN_cmp (t, p))
BN_mod_mul (r, r, sqrtn1, p, ctx);
if (BN_cmp (r, p12) > 0) // r > (p-1)/2
BN_sub (r, p, r);
}
int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const
{
// assume a < p, so don't check for a % p = 0, but a = 0 only
if (BN_is_zero(a)) return 0;
BIGNUM * r = BN_CTX_get (ctx);
BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
if (BN_is_word(r, 1))
return 1;
else if (BN_is_zero(r))
return 0;
return -1;
}
static std::unique_ptr<Elligator2> g_Elligator;
std::unique_ptr<Elligator2>& GetElligator ()
{
if (!g_Elligator)
{
auto el = new Elligator2();
if (!g_Elligator) // make sure it was not created already
g_Elligator.reset (el);
else
delete el;
}
return g_Elligator;
}
}
}

39
libi2pd/Elligator.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef ELLIGATOR_H__
#define ELLIGATOR_H__
#include <inttypes.h>
#include <memory>
#include <openssl/bn.h>
namespace i2p
{
namespace crypto
{
class Elligator2
{
public:
Elligator2 ();
~Elligator2 ();
bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false, bool random = true) const;
bool Decode (const uint8_t * encoded, uint8_t * key) const;
private:
void SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const;
int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p
private:
BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu;
};
std::unique_ptr<Elligator2>& GetElligator ();
}
}
#endif

View File

@ -11,30 +11,22 @@
#include "Timestamp.h"
#include "Log.h"
#include "FS.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "Garlic.h"
namespace i2p
{
namespace garlic
{
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet):
m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
m_LeaseSetUpdateMsgID (0)
{
// create new session tags and session key
RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
}
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
GarlicRoutingSession::GarlicRoutingSession ():
m_Owner (nullptr), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
{
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
m_SessionTags.push_back (sessionTag);
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
}
GarlicRoutingSession::~GarlicRoutingSession ()
@ -66,88 +58,26 @@ namespace garlic
m_SharedRoutingPath = path;
}
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner,
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
GarlicRoutingSession (owner, attachLeaseSet),
m_Destination (destination), m_NumTags (numTags)
{
auto tags = new UnconfirmedTags (m_NumTags);
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++)
{
RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
}
return tags;
// create new session tags and session key
RAND_bytes (m_SessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
}
void GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
ElGamalAESSession::ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
m_NumTags(1)
{
TagsConfirmed (msgID);
if (msgID == m_LeaseSetUpdateMsgID)
{
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
m_LeaseSetUpdateMsgID = 0;
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
CleanupExpiredTags ();
memcpy (m_SessionKey, sessionKey, 32);
m_Encryption.SetKey (m_SessionKey);
m_SessionTags.push_back (sessionTag);
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
}
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
{
auto& tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
m_UnconfirmedTagsMsgs.erase (it);
}
}
bool GarlicRoutingSession::CleanupExpiredTags ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
++it;
}
CleanupUnconfirmedTags ();
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
m_LeaseSetUpdateMsgID = 0;
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
bool GarlicRoutingSession::CleanupUnconfirmedTags ()
{
bool ret = false;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (m_Owner)
m_Owner->RemoveDeliveryStatusSession (it->first);
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
else
++it;
}
return ret;
}
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
std::shared_ptr<I2NPMessage> ElGamalAESSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{
auto m = NewI2NPMessage ();
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
@ -213,10 +143,10 @@ namespace garlic
return m;
}
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
{
size_t blockSize = 0;
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
blockSize += 2;
@ -245,7 +175,7 @@ namespace garlic
return blockSize;
}
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
size_t ElGamalAESSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t msgID;
@ -255,17 +185,17 @@ namespace garlic
*numCloves = 0;
size++;
if (m_Owner)
if (GetOwner ())
{
// resubmit non-confirmed LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)
{
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
SetLeaseSetUpdateStatus (eLeaseSetUpdated);
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
}
// attach DeviveryStatus if necessary
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
if (newTags || GetLeaseSetUpdateStatus () == eLeaseSetUpdated) // new tags created or leaseset updated
{
// clove is DeliveryStatus
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
@ -279,20 +209,20 @@ namespace garlic
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
newTags = nullptr; // got acquired
}
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
GetOwner ()->DeliveryStatusSent (shared_from_this (), msgID);
}
else
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
}
// attach LeaseSet
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
if (GetLeaseSetUpdateStatus () == eLeaseSetUpdated)
{
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
m_LeaseSetUpdateMsgID = msgID;
m_LeaseSetSubmissionTime = ts;
if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous
SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
SetLeaseSetUpdateMsgID (msgID);
SetLeaseSetSubmissionTime (ts);
// clove if our leaseSet must be attached
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ());
size += CreateGarlicClove (payload + size, leaseSet, false);
(*numCloves)++;
}
@ -313,7 +243,7 @@ namespace garlic
return size;
}
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
size_t ElGamalAESSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
size_t size = 0;
@ -343,12 +273,12 @@ namespace garlic
return size;
}
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
size_t ElGamalAESSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
{
size_t size = 0;
if (m_Owner)
if (GetOwner ())
{
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel ();
if (inboundTunnel)
{
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
@ -360,14 +290,14 @@ namespace garlic
size += 4;
// create msg
auto msg = CreateDeliveryStatusMsg (msgID);
if (m_Owner)
if (GetOwner ())
{
//encrypt
uint8_t key[32], tag[32];
RAND_bytes (key, 32); // random session key
RAND_bytes (tag, 32); // random session tag
m_Owner->SubmitSessionKey (key, tag);
GarlicRoutingSession garlic (key, tag);
GetOwner ()->SubmitSessionKey (key, tag);
ElGamalAESSession garlic (key, tag);
msg = garlic.WrapSingleMessage (msg);
}
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
@ -392,6 +322,87 @@ namespace garlic
return size;
}
ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags ()
{
auto tags = new UnconfirmedTags (m_NumTags);
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
for (int i = 0; i < m_NumTags; i++)
{
RAND_bytes (tags->sessionTags[i], 32);
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
}
return tags;
}
void ElGamalAESSession::MessageConfirmed (uint32_t msgID)
{
TagsConfirmed (msgID);
if (msgID == GetLeaseSetUpdateMsgID ())
{
SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
SetLeaseSetUpdateMsgID (0);
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
CleanupExpiredTags ();
}
void ElGamalAESSession::TagsConfirmed (uint32_t msgID)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto it = m_UnconfirmedTagsMsgs.find (msgID);
if (it != m_UnconfirmedTagsMsgs.end ())
{
auto& tags = it->second;
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
{
for (int i = 0; i < tags->numTags; i++)
m_SessionTags.push_back (tags->sessionTags[i]);
}
m_UnconfirmedTagsMsgs.erase (it);
}
}
bool ElGamalAESSession::CleanupExpiredTags ()
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
{
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
it = m_SessionTags.erase (it);
else
++it;
}
CleanupUnconfirmedTags ();
if (GetLeaseSetUpdateMsgID () && ts*1000LL > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)
{
if (GetOwner ())
GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ());
SetLeaseSetUpdateMsgID (0);
}
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
bool ElGamalAESSession::CleanupUnconfirmedTags ()
{
bool ret = false;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// delete expired unconfirmed tags
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
{
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
{
if (GetOwner ())
GetOwner ()->RemoveDeliveryStatusSession (it->first);
it = m_UnconfirmedTagsMsgs.erase (it);
ret = true;
}
else
++it;
}
return ret;
}
GarlicDestination::GarlicDestination (): m_NumTags (32) // 32 tags by default
{
m_Ctx = BN_CTX_new ();
@ -407,6 +418,8 @@ namespace garlic
m_Sessions.clear ();
m_DeliveryStatusSessions.clear ();
m_Tags.clear ();
m_ECIESx25519Sessions.clear ();
m_ECIESx25519Tags.clear ();
}
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
{
@ -434,6 +447,7 @@ namespace garlic
}
buf += 4; // length
auto it = m_Tags.find (SessionTag(buf));
// AES tag might be used even if encryption type is not ElGamal/AES
if (it != m_Tags.end ())
{
// tag found. Use AES
@ -452,7 +466,13 @@ namespace garlic
}
else
{
// tag not found. Use ElGamal
// tag not found. Handle depending on encryption type
if (GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)
{
HandleECIESx25519 (buf, length);
return;
}
// otherwise assume ElGamal/AES
ElGamalBlock elGamal;
if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx))
{
@ -543,7 +563,7 @@ namespace garlic
LogPrint (eLogError, "Garlic: message is too short");
break;
}
HandleI2NPMessage (buf, len - offset, from);
HandleI2NPMessage (buf, len - offset);
break;
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination");
@ -554,7 +574,7 @@ namespace garlic
LogPrint (eLogError, "Garlic: message is too short");
break;
}
HandleI2NPMessage (buf, len - offset, from);
HandleI2NPMessage (buf, len - offset);
break;
case eGarlicDeliveryTypeTunnel:
{
@ -638,21 +658,41 @@ namespace garlic
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{
GarlicRoutingSessionPtr session;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (destination->GetIdentHash ());
if (it != m_Sessions.end ())
session = it->second;
}
if (!session)
{
session = std::make_shared<GarlicRoutingSession> (this, destination,
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET &&
GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)
{
ECIESX25519AEADRatchetSessionPtr session;
uint8_t staticKey[32];
destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key
auto it = m_ECIESx25519Sessions.find (staticKey);
if (it != m_ECIESx25519Sessions.end ())
session = it->second;
if (!session)
{
session = std::make_shared<ECIESX25519AEADRatchetSession> (this);
session->SetRemoteStaticKey (staticKey);
}
session->SetDestination (destination->GetIdentHash ()); // TODO: remove
return session;
}
else
{
ElGamalAESSessionPtr session;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto it = m_Sessions.find (destination->GetIdentHash ());
if (it != m_Sessions.end ())
session = it->second;
}
if (!session)
{
session = std::make_shared<ElGamalAESSession> (this, destination,
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
std::unique_lock<std::mutex> l(m_SessionsMutex);
m_Sessions[destination->GetIdentHash ()] = session;
}
return session;
}
}
void GarlicDestination::CleanupExpiredTags ()
@ -700,6 +740,25 @@ namespace garlic
++it;
}
}
// ECIESx25519
for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();)
{
if (ts > it->second.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
it = m_ECIESx25519Tags.erase (it);
else
++it;
}
for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();)
{
if (it->second->IsExpired (ts))
{
it->second->SetOwner (nullptr);
it = m_ECIESx25519Sessions.erase (it);
}
else
++it;
}
}
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
@ -708,16 +767,15 @@ namespace garlic
m_DeliveryStatusSessions.erase (msgID);
}
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
void GarlicDestination::DeliveryStatusSent (ElGamalAESSessionPtr session, uint32_t msgID)
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
m_DeliveryStatusSessions[msgID] = session;
}
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{
uint32_t msgID = bufbe32toh (msg->GetPayload ());
GarlicRoutingSessionPtr session;
ElGamalAESSessionPtr session;
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
auto it = m_DeliveryStatusSessions.find (msgID);
@ -748,7 +806,8 @@ namespace garlic
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{
HandleDeliveryStatusMessage (msg);
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
HandleDeliveryStatusMessage (msgID);
}
void GarlicDestination::SaveTags ()
@ -821,5 +880,104 @@ namespace garlic
if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT)
i2p::fs::Remove (it);
}
void GarlicDestination::HandleECIESx25519 (const uint8_t * buf, size_t len)
{
uint64_t tag;
memcpy (&tag, buf, 8);
ECIESX25519AEADRatchetSessionPtr session;
int index = 0;
auto it = m_ECIESx25519Tags.find (tag);
if (it != m_ECIESx25519Tags.end ())
{
session = it->second.session;
index = it->second.index;
m_ECIESx25519Tags.erase (tag);
}
else
session = std::make_shared<ECIESX25519AEADRatchetSession> (this); // incoming
if (!session->HandleNextMessage (buf, len, index))
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
}
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len)
{
const uint8_t * buf1 = buf;
uint8_t flag = buf[0]; buf++; // flag
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
switch (deliveryType)
{
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination");
buf += 32; // TODO: check destination
// no break here
case eGarlicDeliveryTypeLocal:
{
LogPrint (eLogDebug, "Garlic: type local");
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration
ptrdiff_t offset = buf - buf1;
if (offset <= (int)len)
HandleCloveI2NPMessage (typeID, buf, len - offset);
else
LogPrint (eLogError, "Garlic: clove is too long");
break;
}
case eGarlicDeliveryTypeTunnel:
{
LogPrint (eLogDebug, "Garlic: type tunnel");
// gwHash and gwTunnel sequence is reverted
const uint8_t * gwHash = buf;
buf += 32;
ptrdiff_t offset = buf - buf1;
if (offset + 13 > (int)len)
{
LogPrint (eLogError, "Garlic: message is too short");
break;
}
uint32_t gwTunnel = bufbe32toh (buf); buf += 4;
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
buf += (4 + 4); // msgID + expiration
offset += 13;
if (GetTunnelPool ())
{
auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
if (tunnel)
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset));
else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
}
else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
break;
}
default:
LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType);
}
}
void GarlicDestination::AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session)
{
m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexSession{index, session, i2p::util::GetSecondsSinceEpoch ()});
}
void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session)
{
i2p::data::Tag<32> staticKeyTag (staticKey);
auto it = m_ECIESx25519Sessions.find (staticKeyTag);
if (it != m_ECIESx25519Sessions.end ())
{
if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ()))
m_ECIESx25519Sessions.erase (it);
else
{
LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists");
return;
}
}
m_ECIESx25519Sessions.emplace (staticKeyTag, session);
}
}
}

View File

@ -2,7 +2,7 @@
#define GARLIC_H__
#include <inttypes.h>
#include <map>
#include <unordered_map>
#include <list>
#include <string>
#include <thread>
@ -85,8 +85,10 @@ namespace garlic
};
class GarlicDestination;
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
class GarlicRoutingSession
{
protected:
enum LeaseSetUpdateStatus
{
eLeaseSetUpToDate = 0,
@ -95,26 +97,13 @@ namespace garlic
eLeaseSetDoNotSend
};
struct UnconfirmedTags
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags () { delete[] sessionTags; };
uint32_t msgID;
int numTags;
SessionTag * sessionTags;
uint32_t tagsCreationTime;
};
public:
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
int numTags, bool attachLeaseSet);
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~GarlicRoutingSession ();
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet);
GarlicRoutingSession ();
virtual ~GarlicRoutingSession ();
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0;
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
void SetLeaseSetUpdated ()
{
@ -127,11 +116,61 @@ namespace garlic
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
const GarlicDestination * GetOwner () const { return m_Owner; }
GarlicDestination * GetOwner () const { return m_Owner; }
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
protected:
LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; }
void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; }
uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; }
void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; }
void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; }
private:
GarlicDestination * m_Owner;
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
public:
// for HTTP only
virtual size_t GetNumOutgoingTags () const { return 0; };
};
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession>
{
struct UnconfirmedTags
{
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
~UnconfirmedTags () { delete[] sessionTags; };
uint32_t msgID;
int numTags;
SessionTag * sessionTags;
uint32_t tagsCreationTime;
};
public:
ElGamalAESSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
int numTags, bool attachLeaseSet);
ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
~ElGamalAESSession () {};
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
private:
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination);
@ -139,31 +178,32 @@ namespace garlic
void TagsConfirmed (uint32_t msgID);
UnconfirmedTags * GenerateSessionTags ();
private:
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
private:
GarlicDestination * m_Owner;
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::AESKey m_SessionKey;
std::list<SessionTag> m_SessionTags;
int m_NumTags;
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
uint32_t m_LeaseSetUpdateMsgID;
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
i2p::crypto::CBCEncryption m_Encryption;
i2p::crypto::CBCEncryption m_Encryption;
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
public:
public:
// for HTTP only
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
};
typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr;
class ECIESX25519AEADRatchetSession;
typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr;
struct ECIESX25519AEADRatchetIndexSession
{
int index;
ECIESX25519AEADRatchetSessionPtr session;
uint64_t creationTime; // seconds since epoch
};
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
class GarlicDestination: public i2p::data::LocalDestination
{
@ -174,6 +214,7 @@ namespace garlic
void CleanUp ();
void SetNumTags (int numTags) { m_NumTags = numTags; };
int GetNumTags () const { return m_NumTags; };
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID);
@ -182,7 +223,10 @@ namespace garlic
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
void DeliveryStatusSent (ElGamalAESSessionPtr session, uint32_t msgID);
void AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session);
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
@ -190,12 +234,13 @@ namespace garlic
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
protected:
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0;
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (uint32_t msgID);
void SaveTags ();
void LoadTags ();
@ -206,18 +251,23 @@ namespace garlic
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// ECIES-X25519-AEAD-Ratchet
void HandleECIESx25519 (const uint8_t * buf, size_t len);
private:
BN_CTX * m_Ctx; // incoming
// outgoing sessions
int m_NumTags;
std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions;
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
// incoming
std::map<SessionTag, std::shared_ptr<AESDecryption> > m_Tags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexSession> m_ECIESx25519Tags; // session tag -> session
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
std::unordered_map<uint32_t, ElGamalAESSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public:

View File

@ -258,12 +258,12 @@ namespace i2p
return m;
}
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{
if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
memcpy (payload + DATABASE_STORE_KEY_OFFSET, storeHash, 32);
payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // 1 for LeaseSet
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
size_t size = DATABASE_STORE_HEADER_SIZE;

View File

@ -247,7 +247,7 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);

View File

@ -417,6 +417,9 @@ namespace data
case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
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);
@ -674,6 +677,9 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break;
default:
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
};
@ -750,6 +756,9 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
break;
default:
LogPrint (eLogError, "Identity: Crypto key type ", (int)type, " is not supported");
}

View File

@ -55,6 +55,7 @@ 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_RARCHET = 4;
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
@ -215,6 +216,7 @@ namespace data
virtual bool IsDestination () const = 0; // for garlic
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
};
class LocalDestination
@ -226,6 +228,8 @@ namespace data
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override for LeaseSet
virtual const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet
};
}
}

View File

@ -251,18 +251,19 @@ namespace data
memcpy (m_Buffer, buf, len);
}
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases):
LeaseSet (storeLeases), m_StoreType (storeType), m_OrigStoreType (storeType)
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto)
{
SetBuffer (buf, len);
SetBuffer (buf, len);
if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
ReadFromBufferEncrypted (buf, len, nullptr, nullptr);
else
ReadFromBuffer (buf, len);
}
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret):
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_OrigStoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
const uint8_t * secret, CryptoKeyType preferredCrypto):
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto)
{
ReadFromBufferEncrypted (buf, len, key, secret);
}
@ -302,6 +303,12 @@ namespace data
return;
}
}
if (flags & LEASESET2_FLAG_UNPUBLISHED_LEASESET) m_IsPublic = false;
if (flags & LEASESET2_FLAG_PUBLISHED_ENCRYPTED)
{
m_IsPublishedEncrypted = true;
m_IsPublic = true;
}
// type specific part
size_t s = 0;
switch (m_StoreType)
@ -349,7 +356,8 @@ namespace data
offset += propertiesLen; // skip for now. TODO: implement properties
if (offset + 1 >= len) return 0;
// key sections
uint16_t currentKeyType = 0;
CryptoKeyType preferredKeyType = m_EncryptionType;
bool preferredKeyFound = false;
int numKeySections = buf[offset]; offset++;
for (int i = 0; i < numKeySections; i++)
{
@ -357,15 +365,15 @@ namespace data
if (offset + 2 >= len) return 0;
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
if (offset + encryptionKeyLen >= len) return 0;
if (IsStoreLeases ()) // create encryptor with leases only
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
{
// we pick first valid key, higher key type has higher priority 4-1-0
// if two keys with of the same type, pick first
// we pick first valid key if preferred not found
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
if (encryptor && (!m_Encryptor || keyType > currentKeyType))
if (encryptor && (!m_Encryptor || keyType == preferredKeyType))
{
m_Encryptor = encryptor; // TODO: atomic
currentKeyType = keyType;
m_EncryptionType = keyType;
if (keyType == preferredKeyType) preferredKeyFound = true;
}
}
offset += encryptionKeyLen;
@ -479,7 +487,7 @@ namespace data
}
else
{
LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instread ", key->GetBlindedSigType ());
LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instead ", key->GetBlindedSigType ());
return;
}
// outer key
@ -506,7 +514,7 @@ namespace data
if (authDataLen > 0)
{
memcpy (innerInput + 32, subcredential, 36);
i2p::crypto::HKDF (outerPlainText.data () + 1, innerInput, 68, "ELS2_L2K", keys);
i2p::crypto::HKDF (outerPlainText.data () + 1 + authDataLen, innerInput, 68, "ELS2_L2K", keys);
}
else
// no authData presented, innerInput = subcredential || publishedTimestamp
@ -531,40 +539,75 @@ namespace data
}
}
// helper for ExtractClientAuthData
static inline bool GetAuthCookie (const uint8_t * authClients, int numClients, const uint8_t * okm, uint8_t * authCookie)
{
// try to find clientCookie_i for clientID_i = okm[44:51]
for (int i = 0; i < numClients; i++)
{
if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i
{
// clientKey_i = okm[0:31]
// clientIV_i = okm[32:43]
i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i
return true;
}
}
return false;
}
size_t LeaseSet2::ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const
{
size_t offset = 0;
uint8_t flag = buf[offset]; offset++; // flag
if (flag & 0x01) // client auth
{
if (flag & 0x02) // PSK, bit 1 is set to 1
if (!(flag & 0x0E)) // DH, bit 1-3 all zeroes
{
const uint8_t * ephemeralPublicKey = buf + offset; offset += 32; // ephemeralPublicKey
uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients
const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients
if (offset > len)
{
LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in DH auth data");
return 0;
}
// calculate authCookie
if (secret)
{
i2p::crypto::X25519Keys ck (secret, nullptr); // derive cpk_i from csk_i
uint8_t authInput[100];
ck.Agree (ephemeralPublicKey, authInput); // sharedSecret is first 32 bytes of authInput
memcpy (authInput + 32, ck.GetPublicKey (), 32); // cpk_i
memcpy (authInput + 64, subcredential, 36);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (ephemeralPublicKey, authInput, 100, "ELS2_XCA", okm);
if (!GetAuthCookie (authClients, numClients, okm, authCookie))
LogPrint (eLogError, "LeaseSet2: Client cookie DH not found");
}
else
LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: csk_i is not provided");
}
else if (flag & 0x02) // PSK, bit 1 is set to 1
{
const uint8_t * authSalt = buf + offset; offset += 32; // authSalt
uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients
const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients
if (offset > len)
{
LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in PSK auth data");
return 0;
}
// calculate authCookie
if (secret)
{
uint8_t authInput[68];
memcpy (authInput, secret, 32);
memcpy (authInput, subcredential, 36);
memcpy (authInput + 32, subcredential, 36);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm);
// try to find clientCookie_i for clientID_i = okm[44:51]
bool found = false;
for (int i = 0; i < numClients; i++)
{
if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i
{
// clientKey_i = okm[0:31]
// clientIV_i = okm[32:43]
i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i
found = true;
break;
}
}
if (!found)
LogPrint (eLogError, "LeaseSet2: Client cookie not found");
if (!GetAuthCookie (authClients, numClients, okm, authCookie))
LogPrint (eLogError, "LeaseSet2: Client cookie PSK not found");
}
else
LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: psk_i is not provided");
@ -706,7 +749,8 @@ namespace data
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted):
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
{
auto identity = keys.GetPublic ();
@ -721,6 +765,12 @@ namespace data
flags |= LEASESET2_FLAG_OFFLINE_KEYS;
m_BufferLen += keys.GetOfflineSignature ().size ();
}
if (isPublishedEncrypted)
{
flags |= LEASESET2_FLAG_PUBLISHED_ENCRYPTED;
isPublic = true;
}
if (!isPublic) flags |= LEASESET2_FLAG_UNPUBLISHED_LEASESET;
m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = storeType;
@ -774,12 +824,22 @@ namespace data
m_Buffer[0] = storeType;
}
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType):
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys,
int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys):
LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls)
{
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1,
lenOuterCiphertext = lenOuterPlaintext + 32;
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1;
uint8_t layer1Flags = 0;
if (authKeys)
{
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) layer1Flags |= 0x01; // DH, authentication scheme 0, auth bit 1
else if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_PSK) layer1Flags |= 0x03; // PSK, authentication scheme 1, auth bit 1
if (layer1Flags)
lenOuterPlaintext += 32 + 2 + authKeys->size ()*40; // auth data len
}
size_t lenOuterCiphertext = lenOuterPlaintext + 32;
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
BlindedPublicKey blindedKey (ls->GetIdentity ());
@ -788,9 +848,9 @@ namespace data
i2p::util::GetDateString (timestamp, date);
uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max
size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKeyType, blindedPriv));
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
auto offset = 1;
htobe16buf (m_Buffer + offset, blindedKeyType); offset += 2; // Blinded Public Key Sig Type
htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type
memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds
@ -812,12 +872,26 @@ namespace data
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1);
offset += 32; // outerSalt
uint8_t * outerPlainText = m_Buffer + offset;
m_Buffer[offset] = 0; offset++; // flag
m_Buffer[offset] = layer1Flags; offset++; // layer 1 flags
// auth data
uint8_t innerInput[68]; // authCookie || subcredential || publishedTimestamp
if (layer1Flags)
{
RAND_bytes (innerInput, 32); // authCookie
CreateClientAuthData (subcredential, authType, authKeys, innerInput, m_Buffer + offset);
offset += 32 + 2 + authKeys->size ()*40; // auth clients
}
// Layer 2
// keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44)
uint8_t keys2[64]; // 44 bytes actual data
RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32)
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2);
if (layer1Flags)
{
memcpy (innerInput + 32, subcredential, 36); // + subcredential || publishedTimestamp
i2p::crypto::HKDF (m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2);
}
else
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); // no authCookie
offset += 32; // innerSalt
m_Buffer[offset] = ls->GetStoreType ();
memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ());
@ -844,6 +918,44 @@ namespace data
else
LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer");
}
void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const
{
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH)
{
i2p::crypto::X25519Keys ek;
ek.GenerateKeys (); // esk and epk
memcpy (authData, ek.GetPublicKey (), 32); authData += 32; // epk
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp
memcpy (authInput + 64, subcredential, 36);
for (auto& it: *authKeys)
{
ek.Agree (it, authInput); // sharedSecret = DH(esk, cpk_i)
memcpy (authInput + 32, it, 32);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (ek.GetPublicKey (), authInput, 100, "ELS2_XCA", okm);
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
}
}
else // assume PSK
{
uint8_t authSalt[32];
RAND_bytes (authSalt, 32);
memcpy (authData, authSalt, 32); authData += 32; // authSalt
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
uint8_t authInput[68]; // authInput = psk_i || subcredential || publishedTimestamp
memcpy (authInput + 32, subcredential, 36);
for (auto& it: *authKeys)
{
memcpy (authInput, it, 32);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm);
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
}
}
}
}
}

View File

@ -79,9 +79,9 @@ namespace data
bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint8_t GetOrigStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only
virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
virtual bool IsPublishedEncrypted () const { return false; };
// implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
@ -129,21 +129,25 @@ namespace data
const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7;
const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001;
const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002;
const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004;
class LeaseSet2: public LeaseSet
{
public:
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true);
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr); // store type 5, called from local netdb only
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
uint8_t GetStoreType () const { return m_StoreType; };
uint8_t GetOrigStoreType () const { return m_OrigStoreType; };
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
bool IsPublic () const { return m_IsPublic; };
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);
// implements RoutingDestination
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
private:
@ -160,9 +164,11 @@ namespace data
private:
uint8_t m_StoreType, m_OrigStoreType;
uint8_t m_StoreType;
uint32_t m_PublishedTimestamp = 0;
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
CryptoKeyType m_EncryptionType;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
};
@ -227,7 +233,8 @@ namespace data
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; };
@ -247,17 +254,28 @@ namespace data
size_t m_BufferLen;
};
const int ENCRYPTED_LEASESET_AUTH_TYPE_NONE = 0;
const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1;
const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2;
typedef i2p::data::Tag<32> AuthPublicKey;
class LocalEncryptedLeaseSet2: public LocalLeaseSet2
{
public:
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519);
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr);
LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
const IdentHash& GetStoreHash () const { return m_StoreHash; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; };
private:
void CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const;
private:
IdentHash m_StoreHash;

View File

@ -1,12 +1,10 @@
/*
* Copyright (c) 2013-2018, The PurpleI2P Project
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*
* Kovri go write your own code
*
*/
#include <openssl/rand.h>
@ -41,15 +39,8 @@ namespace transport
void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial)
{
// temp_key = HMAC-SHA256(ck, input_key_material)
uint8_t tempKey[32]; unsigned int len;
HMAC(EVP_sha256(), m_CK, 32, inputKeyMaterial, 32, tempKey, &len);
// ck = HMAC-SHA256(temp_key, byte(0x01))
static uint8_t one[1] = { 1 };
HMAC(EVP_sha256(), tempKey, 32, one, 1, m_CK, &len);
// derived = HMAC-SHA256(temp_key, ck || byte(0x02))
m_CK[32] = 2;
HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, m_K, &len);
i2p::crypto::HKDF (m_CK, inputKeyMaterial, 32, "", m_CK);
// ck is m_CK[0:31], k is m_CK[32:63]
}
void NTCP2Establisher::MixHash (const uint8_t * buf, size_t len)
@ -161,6 +152,7 @@ namespace transport
// fill options
uint8_t options[32]; // actual options size is 16 bytes
memset (options, 0, 16);
options[0] = i2p::context.GetNetID (); // network ID
options[1] = 2; // ver
htobe16buf (options + 2, paddingLength); // padLen
// m3p2Len
@ -180,7 +172,7 @@ namespace transport
// sign and encrypt options, use m_H as AD
uint8_t nonce[12];
memset (nonce, 0, 12); // set nonce to zero
i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt
}
void NTCP2Establisher::CreateSessionCreatedMessage ()
@ -203,7 +195,7 @@ namespace transport
// sign and encrypt options, use m_H as AD
uint8_t nonce[12];
memset (nonce, 0, 12); // set nonce to zero
i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt
}
@ -216,7 +208,7 @@ namespace transport
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
// part1 48 bytes
i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, m_H, 32, m_K, nonce, m_SessionConfirmedBuffer, 48, true); // encrypt
i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt
}
void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce)
@ -227,7 +219,7 @@ namespace transport
// encrypt m3p2, it must be filled in SessionRequest
KDF3Alice ();
uint8_t * m3p2 = m_SessionConfirmedBuffer + 48;
i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2, m3p2Len, true); // encrypt
i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt
// update h again
MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext)
}
@ -245,9 +237,14 @@ namespace transport
// verify MAC and decrypt options block (32 bytes), use m_H as AD
uint8_t nonce[12], options[16];
memset (nonce, 0, 12); // set nonce to zero
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, m_H, 32, m_K, nonce, options, 16, false)) // decrypt
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt
{
// options
if (options[0] && options[0] != i2p::context.GetNetID ())
{
LogPrint (eLogWarning, "NTCP2: SessionRequest networkID ", (int)options[0], " mismatch. Expected ", i2p::context.GetNetID ());
return false;
}
if (options[1] == 2) // ver is always 2
{
paddingLen = bufbe16toh (options + 2);
@ -295,7 +292,7 @@ namespace transport
uint8_t payload[16];
uint8_t nonce[12];
memset (nonce, 0, 12); // set nonce to zero
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, m_H, 32, m_K, nonce, payload, 16, false)) // decrypt
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, GetH (), 32, GetK (), nonce, payload, 16, false)) // decrypt
{
// options
paddingLen = bufbe16toh(payload + 2);
@ -324,7 +321,7 @@ namespace transport
if (paddingLength > 0)
MixHash (m_SessionCreatedBuffer + 64, paddingLength);
if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, m_H, 32, m_K, nonce, m_RemoteStaticKey, 32, false)) // decrypt S
if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S
{
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
return false;
@ -338,7 +335,7 @@ namespace transport
MixHash (m_SessionConfirmedBuffer, 48);
KDF3Bob ();
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
{
// caclulate new h again for KDF data
memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext
@ -396,6 +393,10 @@ namespace transport
{
m_IsTerminated = true;
m_IsEstablished = false;
boost::system::error_code ec;
m_Socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
if (ec)
LogPrint (eLogDebug, "NTCP2: Couldn't shutdown socket: ", ec.message ());
m_Socket.close ();
transports.PeerDisconnected (shared_from_this ());
m_Server.RemoveNTCP2Session (shared_from_this ());
@ -431,23 +432,17 @@ namespace transport
void NTCP2Session::KeyDerivationFunctionDataPhase ()
{
uint8_t tempKey[32]; unsigned int len;
HMAC(EVP_sha256(), m_Establisher->GetCK (), 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(ck, zerolen)
static uint8_t one[1] = { 1 };
HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Kab, &len); // k_ab = HMAC-SHA256(temp_key, byte(0x01)).
m_Kab[32] = 2;
HMAC(EVP_sha256(), tempKey, 32, m_Kab, 33, m_Kba, &len); // k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02))
static uint8_t ask[4] = { 'a', 's', 'k', 1 }, master[32];
HMAC(EVP_sha256(), tempKey, 32, ask, 4, master, &len); // ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01))
uint8_t k[64];
i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "", k); // k_ab, k_ba = HKDF(ck, zerolen)
memcpy (m_Kab, k, 32); memcpy (m_Kba, k + 32, 32);
uint8_t master[32];
i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "ask", master, 32); // ask_master = HKDF(ck, zerolen, info="ask")
uint8_t h[39];
memcpy (h, m_Establisher->GetH (), 32);
memcpy (h + 32, "siphash", 7);
HMAC(EVP_sha256(), master, 32, h, 39, tempKey, &len); // temp_key = HMAC-SHA256(ask_master, h || "siphash")
HMAC(EVP_sha256(), tempKey, 32, one, 1, master, &len); // sip_master = HMAC-SHA256(temp_key, byte(0x01))
HMAC(EVP_sha256(), master, 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(sip_master, zerolen)
HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Sipkeysab, &len); // sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)).
m_Sipkeysab[32] = 2;
HMAC(EVP_sha256(), tempKey, 32, m_Sipkeysab, 33, m_Sipkeysba, &len); // sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02))
i2p::crypto::HKDF (master, h, 39, "", master, 32); // sip_master = HKDF(ask_master, h || "siphash")
i2p::crypto::HKDF (master, nullptr, 0, "", k); // sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen)
memcpy (m_Sipkeysab, k, 32); memcpy (m_Sipkeysba, k + 32, 32);
}
@ -592,20 +587,29 @@ namespace transport
void NTCP2Session::HandleSessionConfirmedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
LogPrint (eLogDebug, "NTCP2: SessionConfirmed sent");
KeyDerivationFunctionDataPhase ();
// Alice data phase keys
m_SendKey = m_Kab;
m_ReceiveKey = m_Kba;
SetSipKeys (m_Sipkeysab, m_Sipkeysba);
memcpy (m_ReceiveIV.buf, m_Sipkeysba + 16, 8);
memcpy (m_SendIV.buf, m_Sipkeysab + 16, 8);
Established ();
ReceiveLength ();
(void) bytes_transferred;
if (ecode)
{
LogPrint (eLogWarning, "NTCP2: couldn't send SessionConfirmed message: ", ecode.message ());
Terminate ();
}
else
{
LogPrint (eLogDebug, "NTCP2: SessionConfirmed sent");
KeyDerivationFunctionDataPhase ();
// Alice data phase keys
m_SendKey = m_Kab;
m_ReceiveKey = m_Kba;
SetSipKeys (m_Sipkeysab, m_Sipkeysba);
memcpy (m_ReceiveIV.buf, m_Sipkeysba + 16, 8);
memcpy (m_SendIV.buf, m_Sipkeysab + 16, 8);
Established ();
ReceiveLength ();
// TODO: remove
// m_SendQueue.push_back (CreateDeliveryStatusMsg (1));
// SendQueue ();
// TODO: remove
// m_SendQueue.push_back (CreateDeliveryStatusMsg (1));
// SendQueue ();
}
}
void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
@ -700,7 +704,7 @@ namespace transport
// ready to communicate
auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ());
m_Server.AddNTCP2Session (shared_from_this ());
m_Server.AddNTCP2Session (shared_from_this (), true);
Established ();
ReceiveLength ();
}
@ -1016,7 +1020,9 @@ namespace transport
if (ecode)
{
LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ());
if (ecode != boost::asio::error::operation_aborted)
LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ());
Terminate ();
}
else
{
@ -1135,8 +1141,8 @@ namespace transport
}
NTCP2Server::NTCP2Server ():
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
m_TerminationTimer (m_Service)
RunnableServiceWithWork ("NTCP2"),
m_TerminationTimer (GetService ())
{
}
@ -1147,10 +1153,9 @@ namespace transport
void NTCP2Server::Start ()
{
if (!m_IsRunning)
if (!IsRunning ())
{
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&NTCP2Server::Run, this));
StartIOService ();
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
{
@ -1161,7 +1166,7 @@ namespace transport
{
try
{
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)));
m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)));
}
catch ( std::exception & ex )
{
@ -1175,7 +1180,7 @@ namespace transport
}
else if (address->host.is_v6() && context.SupportsV6 ())
{
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service));
m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ()));
try
{
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
@ -1210,38 +1215,17 @@ namespace transport
}
m_NTCP2Sessions.clear ();
if (m_IsRunning)
{
m_IsRunning = false;
if (IsRunning ())
m_TerminationTimer.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
StopIOService ();
}
void NTCP2Server::Run ()
bool NTCP2Server::AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming)
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "NTCP2: runtime exception: ", ex.what ());
}
}
}
bool NTCP2Server::AddNTCP2Session (std::shared_ptr<NTCP2Session> session)
{
if (!session || !session->GetRemoteIdentity ()) return false;
if (!session) return false;
if (incoming)
m_PendingIncomingSessions.remove (session);
if (!session->GetRemoteIdentity ()) return false;
auto& ident = session->GetRemoteIdentity ()->GetIdentHash ();
auto it = m_NTCP2Sessions.find (ident);
if (it != m_NTCP2Sessions.end ())
@ -1271,11 +1255,11 @@ namespace transport
void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn)
{
LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port);
m_Service.post([this, address, port, conn]()
GetService ().post([this, address, port, conn]()
{
if (this->AddNTCP2Session (conn))
{
auto timer = std::make_shared<boost::asio::deadline_timer>(m_Service);
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService ());
auto timeout = NTCP2_CONNECT_TIMEOUT * 5;
conn->SetTerminationTimeout(timeout * 2);
timer->expires_from_now (boost::posix_time::seconds(timeout));
@ -1321,15 +1305,21 @@ namespace transport
{
conn->ServerLogin ();
m_PendingIncomingSessions.push_back (conn);
conn = nullptr;
}
}
else
LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ());
}
else
LogPrint (eLogError, "NTCP2: Accept error ", error.message ());
if (error != boost::asio::error::operation_aborted)
{
conn = std::make_shared<NTCP2Session> (*this);
if (!conn) // connection is used, create new one
conn = std::make_shared<NTCP2Session> (*this);
else // reuse failed
conn->Close ();
m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this,
conn, std::placeholders::_1));
}
@ -1385,13 +1375,13 @@ namespace transport
// pending
for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
{
if ((*it)->IsEstablished () || (*it)->IsTerminated ())
it = m_PendingIncomingSessions.erase (it); // established or terminated
else if ((*it)->IsTerminationTimeoutExpired (ts))
if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts))
{
(*it)->Terminate ();
it = m_PendingIncomingSessions.erase (it); // expired
it = m_PendingIncomingSessions.erase (it); // etsablished of expired
}
else if ((*it)->IsTerminated ())
it = m_PendingIncomingSessions.erase (it); // already terminated
else
it++;
}

View File

@ -1,12 +1,10 @@
/*
* Copyright (c) 2013-2018, The PurpleI2P Project
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*
* Kovri go write your own code
*
*/
#ifndef NTCP2_H__
#define NTCP2_H__
@ -85,7 +83,7 @@ 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_K; };
const uint8_t * GetK () const { return m_CK + 32; };
const uint8_t * GetCK () const { return m_CK; };
const uint8_t * GetH () const { return m_H; };
@ -114,7 +112,7 @@ namespace transport
i2p::crypto::X25519Keys m_EphemeralKeys;
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/;
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/;
i2p::data::IdentHash m_RemoteIdentHash;
uint16_t m3p2Len;
@ -133,6 +131,7 @@ namespace transport
void Terminate ();
void TerminateByTimeout ();
void Done ();
void Close () { m_Socket.close (); }; // for accept
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
@ -194,7 +193,7 @@ namespace transport
std::unique_ptr<NTCP2Establisher> m_Establisher;
// data phase
uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], m_Sipkeysba[32];
uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32];
const uint8_t * m_SendKey, * m_ReceiveKey;
#if OPENSSL_SIPHASH
EVP_PKEY * m_SendSipKey, * m_ReceiveSipKey;
@ -217,7 +216,7 @@ namespace transport
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
};
class NTCP2Server
class NTCP2Server: private i2p::util::RunnableServiceWithWork
{
public:
@ -226,18 +225,16 @@ namespace transport
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session);
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
boost::asio::io_service& GetService () { return m_Service; };
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn);
private:
void Run ();
void HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
@ -249,10 +246,6 @@ namespace transport
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_TerminationTimer;
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;

View File

@ -307,10 +307,18 @@ namespace data
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
{
// TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
return true;
if (leaseSet->IsPublic ())
{
// TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet;
return true;
}
else
{
LogPrint (eLogWarning, "NetDb: Unpublished LeaseSet2 received: ", ident.ToBase32());
m_LeaseSets.erase (ident);
}
}
}
else
@ -523,9 +531,10 @@ namespace data
auto total = m_RouterInfos.size ();
uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL;
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch();
auto uptime = i2p::context.GetUptime ();
// routers don't expire if less than 90 or uptime is less than 1 hour
bool checkForExpiration = total > NETDB_MIN_ROUTERS && ts > (i2p::context.GetStartupTime () + 600)*1000LL; // 10 minutes
if (checkForExpiration && ts > (i2p::context.GetStartupTime () + 3600)*1000LL) // 1 hour
bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes
if (checkForExpiration && uptime > 3600) // 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;
@ -909,7 +918,7 @@ namespace data
else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets
{
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
replyMsg = CreateDatabaseStoreMsg (leaseSet);
replyMsg = CreateDatabaseStoreMsg (ident, leaseSet);
}
}
@ -941,7 +950,7 @@ namespace data
if (numTags)
{
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag);
replyMsg = garlic.WrapSingleMessage (replyMsg);
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
}

View File

@ -19,7 +19,7 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_StartupTime (0), m_ShareRatio (100), m_Status (eRouterStatusOK),
m_ShareRatio (100), m_Status (eRouterStatusOK),
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
{
}
@ -27,7 +27,8 @@ namespace i2p
void RouterContext::Init ()
{
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
m_StartupTime = std::chrono::steady_clock::now();
if (!Load ())
CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
@ -183,17 +184,18 @@ namespace i2p
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4only)
{
if (!m_NTCP2Keys) return;
if (!port)
{
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish) && (!v4only || address->host.is_v4 ()))
{
address->port = port;
if (!port && !address->port)
{
// select random port only if address's port is not set
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
if (port) address->port = port;
address->cost = publish ? 3 : 14;
address->ntcp2->isPublished = publish;
address->ntcp2->iv = m_NTCP2Keys->iv;
@ -432,14 +434,14 @@ namespace i2p
}
// remove NTCP or NTCP2 v4 address
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
if (ntcp)
if (ntcp)
PublishNTCPAddress (false);
else
{
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
PublishNTCP2Address (port, false, true);
}
}
// update
UpdateRouterInfo ();
}
@ -489,7 +491,7 @@ namespace i2p
void RouterContext::SetSupportsV6 (bool supportsV6)
{
if (supportsV6)
{
{
m_RouterInfo.EnableV6 ();
// insert v6 addresses if necessary
bool foundSSU = false, foundNTCP = false, foundNTCP2 = false;
@ -507,7 +509,7 @@ namespace i2p
}
else
foundNTCP = true;
}
}
port = addr->port;
}
if (!port) i2p::config::GetOption("port", port);
@ -519,7 +521,7 @@ namespace i2p
{
std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, GetIdentHash ());
}
}
}
// NTCP2
if (!foundNTCP2)
@ -528,11 +530,11 @@ namespace i2p
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
if (ntcp2 && ntcp2Published)
{
std::string ntcp2Host;
std::string ntcp2Host;
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
else
ntcp2Host = "::1";
ntcp2Host = "::1";
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
@ -544,10 +546,10 @@ namespace i2p
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
if (ntcp)
{
std::string host = "::1";
std::string host = "::1";
m_RouterInfo.AddNTCPAddress (host.c_str (), port);
}
}
}
}
else
m_RouterInfo.DisableV6 ();
@ -690,9 +692,9 @@ namespace i2p
return i2p::tunnel::tunnels.GetExploratoryPool ();
}
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len)
{
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
@ -715,7 +717,7 @@ namespace i2p
uint32_t RouterContext::GetUptime () const
{
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count ();
}
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const

View File

@ -5,6 +5,7 @@
#include <string>
#include <memory>
#include <mutex>
#include <chrono>
#include <boost/asio.hpp>
#include "Identity.h"
#include "RouterInfo.h"
@ -64,8 +65,7 @@ namespace i2p
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
i2p::crypto::X25519Keys& GetStaticKeys ();
uint32_t GetUptime () const;
uint32_t GetStartupTime () const { return m_StartupTime; };
uint32_t GetUptime () const; // in seconds
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
@ -115,12 +115,17 @@ namespace i2p
// implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
// override GarlicDestination
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
protected:
// implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented
private:
void CreateNewRouter ();
@ -136,8 +141,8 @@ namespace i2p
i2p::data::PrivateKeys m_Keys;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor;
uint64_t m_LastUpdateTime; // in seconds
bool m_AcceptsTunnels, m_IsFloodfill;
uint64_t m_StartupTime; // in seconds since epoch
bool m_AcceptsTunnels, m_IsFloodfill;
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio;
RouterStatus m_Status;

View File

@ -1,4 +1,5 @@
#include <boost/bind.hpp>
#include "version.h"
#include "Crypto.h"
#include "Log.h"
#include "Timestamp.h"
@ -729,7 +730,8 @@ namespace transport
encryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, iv, 16);
htobe16buf (buf + len + 16, encryptedLen);
uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
}
@ -750,7 +752,8 @@ namespace transport
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16);
htobe16buf (buf + len + 16, encryptedLen);
uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
}
@ -799,7 +802,8 @@ namespace transport
uint16_t encryptedLen = len - (encrypted - buf);
// assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16);
htobe16buf (buf + len + 16, encryptedLen);
uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
uint8_t digest[16];
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest);
return !memcmp (header->mac, digest, 16);

View File

@ -86,13 +86,13 @@ namespace stream
LogPrint (eLogDebug, "Streaming: Stream deleted");
}
void Stream::Terminate ()
void Stream::Terminate () // shoudl be called from StreamingDestination::Stop only
{
m_AckSendTimer.cancel ();
m_ReceiveTimer.cancel ();
m_ResendTimer.cancel ();
//CleanUp (); /* Need to recheck - broke working on windows */
m_LocalDestination.DeleteStream (shared_from_this ());
//m_LocalDestination.DeleteStream (shared_from_this ());
}
void Stream::CleanUp ()
@ -918,7 +918,7 @@ namespace stream
{
expired = false;
// time to request
if (m_RemoteLeaseSet->GetOrigStoreType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
if (m_RemoteLeaseSet->IsPublishedEncrypted ())
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
else
@ -964,7 +964,6 @@ namespace stream
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_LastIncomingReceiveStreamID (0),
m_PendingIncomingTimer (m_Owner->GetService ())
{
}
@ -990,7 +989,10 @@ namespace stream
m_PendingIncomingStreams.clear ();
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
for (auto it: m_Streams)
it.second->Terminate ();
m_Streams.clear ();
m_IncomingStreams.clear ();
}
}
@ -1013,18 +1015,17 @@ namespace stream
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
if (receiveStreamID == m_LastIncomingReceiveStreamID)
auto it1 = m_IncomingStreams.find (receiveStreamID);
if (it1 != m_IncomingStreams.end ())
{
// already pending
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
DeletePacket (packet); // drop it, because previous should be connected
return;
}
auto incomingStream = CreateNewIncomingStream ();
auto incomingStream = CreateNewIncomingStream (receiveStreamID);
incomingStream->HandleNextPacket (packet); // SYN
auto ident = incomingStream->GetRemoteIdentity();
m_LastIncomingReceiveStreamID = receiveStreamID;
// handle saved packets if any
{
@ -1062,13 +1063,13 @@ namespace stream
else // follow on packet without SYN
{
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
for (auto& it: m_Streams)
if (it.second->GetSendStreamID () == receiveStreamID)
{
// found
it.second->HandleNextPacket (packet);
return;
}
auto it1 = m_IncomingStreams.find (receiveStreamID);
if (it1 != m_IncomingStreams.end ())
{
// found
it1->second->HandleNextPacket (packet);
return;
}
// save follow on packet
auto it = m_SavedPackets.find (receiveStreamID);
if (it != m_SavedPackets.end ())
@ -1105,11 +1106,12 @@ namespace stream
return s;
}
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID)
{
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
m_IncomingStreams[receiveStreamID] = s;
return s;
}
@ -1118,9 +1120,8 @@ namespace stream
if (stream)
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
auto it = m_Streams.find (stream->GetRecvStreamID ());
if (it != m_Streams.end ())
m_Streams.erase (it);
m_Streams.erase (stream->GetRecvStreamID ());
m_IncomingStreams.erase (stream->GetSendStreamID ());
}
}

View File

@ -180,7 +180,6 @@ namespace stream
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
/** don't call me */
void Terminate ();
private:
@ -269,8 +268,10 @@ namespace stream
void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
private:
void HandleNextPacket (Packet * packet);
std::shared_ptr<Stream> CreateNewIncomingStream ();
std::shared_ptr<Stream> CreateNewIncomingStream (uint32_t receiveStreamID);
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
private:
@ -280,8 +281,8 @@ namespace stream
bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
Acceptor m_Acceptor;
uint32_t m_LastIncomingReceiveStreamID;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN

View File

@ -71,14 +71,14 @@ public:
return std::string (str, str + l);
}
void FromBase32 (const std::string& s)
size_t FromBase32 (const std::string& s)
{
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
void FromBase64 (const std::string& s)
size_t FromBase64 (const std::string& s)
{
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
}
private:
@ -93,4 +93,16 @@ private:
} // data
} // i2p
namespace std
{
// hash for std::unordered_map
template<size_t sz> struct hash<i2p::data::Tag<sz> >
{
size_t operator()(const i2p::data::Tag<sz>& s) const
{
return s.GetLL ()[0];
}
};
}
#endif /* TAG_H__ */

View File

@ -465,6 +465,7 @@ namespace transport
}
}
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.erase (ident);
@ -503,28 +504,6 @@ namespace transport
}
}
}
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
if (!router) return;
m_Service->post (std::bind (&Transports::PostCloseSession, this, router));
}
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
{
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
if (ssuSession) // try SSU first
{
m_SSUServer->DeleteSession (ssuSession);
LogPrint (eLogDebug, "Transports: SSU session closed");
}
auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr;
if (ntcpSession) // try deleting ntcp session too
{
ntcpSession->Terminate ();
LogPrint(eLogDebug, "Transports: NTCP session closed");
}
}
void Transports::DetectExternalIP ()
{
@ -562,7 +541,10 @@ namespace transport
{
auto addr = router->GetSSUV6Address ();
if (addr)
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
m_SSUServer->GetServiceV6 ().post ([this, router, addr]
{
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
});
}
}
}
@ -666,11 +648,16 @@ namespace transport
auto it = m_Peers.find (ident);
if (it != m_Peers.end ())
{
auto before = it->second.sessions.size ();
it->second.sessions.remove (session);
if (it->second.sessions.empty ()) // TODO: why?
if (it->second.sessions.empty ())
{
if (it->second.delayedMessages.size () > 0)
{
if (before > 0) // we had an active session before
it->second.numAttempts = 0; // start over
ConnectToPeer (ident, it->second);
}
else
{
std::unique_lock<std::mutex> l(m_PeersMutex);

View File

@ -92,7 +92,6 @@ namespace transport
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
void PeerConnected (std::shared_ptr<TransportSession> session);
void PeerDisconnected (std::shared_ptr<TransportSession> session);
@ -131,7 +130,6 @@ namespace transport
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode);

View File

@ -1,5 +1,6 @@
#include <string.h>
#include "I2PEndian.h"
#include <random>
#include <thread>
#include <algorithm>
#include <vector>
@ -45,7 +46,7 @@ namespace tunnel
// shuffle records
std::vector<int> recordIndicies;
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
std::random_shuffle (recordIndicies.begin(), recordIndicies.end());
std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()()));
// create real records
uint8_t * records = msg->GetPayload () + 1;

View File

@ -1,4 +1,5 @@
#include <algorithm>
#include <random>
#include "I2PEndian.h"
#include "Crypto.h"
#include "Tunnel.h"
@ -441,7 +442,7 @@ namespace tunnel
int size = m_ExplicitPeers->size ();
std::vector<int> peerIndicies;
for (int i = 0; i < size; i++) peerIndicies.push_back(i);
std::random_shuffle (peerIndicies.begin(), peerIndicies.end());
std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()()));
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
for (int i = 0; i < numHops; i++)

View File

@ -77,7 +77,7 @@ namespace api
std::shared_ptr<i2p::client::ClientDestination> CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
const std::map<std::string, std::string> * params)
{
auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
auto localDestination = std::make_shared<i2p::client::RunnableClientDestination> (keys, isPublic, params);
localDestination->Start ();
return localDestination;
}
@ -86,7 +86,7 @@ namespace api
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
auto localDestination = std::make_shared<i2p::client::RunnableClientDestination> (keys, isPublic, params);
localDestination->Start ();
return localDestination;
}

View File

@ -22,7 +22,7 @@
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
// inet_pton exists Windows since Vista, but XP haven't that function!
// inet_pton exists Windows since Vista, but XP doesn't have that function!
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
int inet_pton_xp(int af, const char *src, void *dst)
{
@ -57,21 +57,65 @@ namespace i2p
{
namespace util
{
void RunnableService::StartIOService ()
{
if (!m_IsRunning)
{
m_IsRunning = true;
m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this)));
}
}
void RunnableService::StopIOService ()
{
if (m_IsRunning)
{
m_IsRunning = false;
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
m_Thread = nullptr;
}
}
}
void RunnableService::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, m_Name, ": runtime exception: ", ex.what ());
}
}
}
namespace net
{
#ifdef WIN32
bool IsWindowsXPorLater()
{
OSVERSIONINFO osvi;
static bool isRequested = false;
static bool isXP = false;
if (!isRequested)
{
// request
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion <= 5)
return true;
else
return false;
isXP = osvi.dwMajorVersion <= 5;
isRequested = true;
}
return isXP;
}
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
@ -202,21 +246,20 @@ namespace net
std::string localAddressUniversal = localAddress.to_string();
#endif
if (IsWindowsXPorLater())
{
#define inet_pton inet_pton_xp
}
typedef int (* IPN)(int af, const char *src, void *dst);
IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton");
if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found
if(localAddress.is_v4())
{
sockaddr_in inputAddress;
inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
return GetMTUWindowsIpv4(inputAddress, fallback);
}
else if(localAddress.is_v6())
{
sockaddr_in6 inputAddress;
inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
return GetMTUWindowsIpv6(inputAddress, fallback);
} else {
LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported");
@ -281,7 +324,7 @@ namespace net
int GetMTU(const boost::asio::ip::address& localAddress)
{
const int fallback = 576; // fallback MTU
int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU
#ifdef WIN32
return GetMTUWindows(localAddress, fallback);
@ -312,15 +355,14 @@ namespace net
if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af)
{
// match
char * addr = new char[INET6_ADDRSTRLEN];
bzero(addr, INET6_ADDRSTRLEN);
char addr[INET6_ADDRSTRLEN];
memset (addr, 0, INET6_ADDRSTRLEN);
if(af == AF_INET)
inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN);
else
inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN);
freeifaddrs(addrs);
std::string cur_ifaddr(addr);
delete[] addr;
return boost::asio::ip::address::from_string(cur_ifaddr);
}
cur = cur->ifa_next;

View File

@ -5,6 +5,7 @@
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <boost/asio.hpp>
@ -46,7 +47,7 @@ namespace util
{
auto tmp = m_Head;
m_Head = static_cast<T*>(*(void * *)m_Head); // next
delete tmp;
::operator delete ((void *)tmp);
}
}
@ -122,6 +123,43 @@ namespace util
std::mutex m_Mutex;
};
class RunnableService
{
protected:
RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {}
virtual ~RunnableService () {}
boost::asio::io_service& GetIOService () { return m_Service; }
bool IsRunning () const { return m_IsRunning; };
void StartIOService ();
void StopIOService ();
private:
void Run ();
private:
std::string m_Name;
volatile bool m_IsRunning;
std::unique_ptr<std::thread> m_Thread;
boost::asio::io_service m_Service;
};
class RunnableServiceWithWork: public RunnableService
{
protected:
RunnableServiceWithWork (const std::string& name):
RunnableService (name), m_Work (GetIOService ()) {}
private:
boost::asio::io_service::work m_Work;
};
namespace net
{
int GetMTU (const boost::asio::ip::address& localAddress);

View File

@ -7,7 +7,7 @@
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 26
#define I2PD_VERSION_MINOR 30
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@ -21,7 +21,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 40
#define I2P_VERSION_MICRO 45
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -237,17 +237,19 @@ namespace client
//---------------------------------------------------------------------
Address::Address (const std::string& b32)
Address::Address (const std::string& b32):
addressType (eAddressInvalid)
{
if (b32.length () <= B33_ADDRESS_THRESHOLD)
{
addressType = eAddressIndentHash;
identHash.FromBase32 (b32);
if (identHash.FromBase32 (b32) > 0)
addressType = eAddressIndentHash;
}
else
{
addressType = eAddressBlindedPublicKey;
blindedPublicKey = std::make_shared<i2p::data::BlindedPublicKey>(b32);
if (blindedPublicKey->IsValid ())
addressType = eAddressBlindedPublicKey;
}
}
@ -320,7 +322,10 @@ namespace client
{
auto pos = address.find(".b32.i2p");
if (pos != std::string::npos)
return std::make_shared<const Address>(address.substr (0, pos));
{
auto addr = std::make_shared<const Address>(address.substr (0, pos));
return addr->IsValid () ? addr : nullptr;
}
else
{
pos = address.find (".i2p");
@ -422,9 +427,9 @@ namespace client
std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos);
size_t pos = s.find('#');
size_t pos = addr.find('#');
if (pos != std::string::npos)
addr = addr.substr(pos); // remove comments
addr = addr.substr(0, pos); // remove comments
auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (!ident->FromBase64(addr)) {

View File

@ -33,13 +33,14 @@ namespace client
struct Address
{
enum { eAddressIndentHash, eAddressBlindedPublicKey } addressType;
enum { eAddressIndentHash, eAddressBlindedPublicKey, eAddressInvalid } addressType;
i2p::data::IdentHash identHash;
std::shared_ptr<i2p::data::BlindedPublicKey> blindedPublicKey;
Address (const std::string& b32);
Address (const i2p::data::IdentHash& hash);
bool IsIdentHash () const { return addressType == eAddressIndentHash; };
bool IsValid () const { return addressType != eAddressInvalid; };
};
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }

View File

@ -341,17 +341,17 @@ namespace client
SendReplyOK();
}
void BOBCommandSession::SendData (const char * data)
void BOBCommandSession::SendRaw (const char * data)
{
std::ostream os(&m_SendBuffer);
os << "DATA " << data << std::endl;
os << data << std::endl;
}
void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out)
{
// helper lambdas
const auto isset = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
const auto issetNum = [&isset](const int p) { return isset(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; };
const auto destReady = [](const BOBDestination * const dest) { return dest->GetLocalDestination()->IsReady(); };
const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str
@ -359,8 +359,8 @@ namespace client
// tunnel info
const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname();
const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet();
const std::string inhost = isset(currentTunnel ? m_InHost : dest->GetInHost());
const std::string outhost = isset(currentTunnel ? m_OutHost : dest->GetOutHost());
const std::string inhost = issetStr(currentTunnel ? m_InHost : dest->GetInHost());
const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost());
const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort());
const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort());
const bool keys = destExists(dest); // key must exist when destination is created
@ -370,7 +370,8 @@ namespace client
// build line
std::stringstream ss;
ss << "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " "
ss << "DATA "
<< "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " "
<< "RUNNING: " << bool_str(running) << " " << "STOPPING: " << bool_str(stopping) << " "
<< "KEYS: " << bool_str(keys) << " " << "QUIET: " << bool_str(quiet) << " "
<< "INPORT: " << inport << " " << "INHOST: " << inhost << " "
@ -654,16 +655,16 @@ namespace client
for (const auto& it: destinations)
{
BuildStatusLine(false, it.second, statusLine);
SendData (statusLine.c_str());
SendRaw(statusLine.c_str());
if(m_Nickname.compare(it.second->GetNickname()) == 0)
sentCurrent = true;
}
if(!sentCurrent && !m_Nickname.empty())
{
// add the current tunnel to the list
// add the current tunnel to the list.
// this is for the incomplete tunnel which has not been started yet.
BuildStatusLine(true, m_CurrentDestination, statusLine);
LogPrint(eLogError, statusLine);
SendData(statusLine.c_str());
SendRaw(statusLine.c_str());
}
SendReplyOK ("Listing done");
}
@ -690,21 +691,23 @@ namespace client
void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: status ", operand);
const std::string name = operand;
std::string statusLine;
if (m_Nickname == operand)
// always prefer destination
auto ptr = m_Owner.FindDestination(name);
if(ptr != nullptr)
{
// check current tunnel
BuildStatusLine(true, nullptr, statusLine);
// tunnel destination exists
BuildStatusLine(false, ptr, statusLine);
SendReplyOK(statusLine.c_str());
}
else
{
// check other
std::string name = operand;
auto ptr = m_Owner.FindDestination(name);
if(ptr != nullptr)
if(m_Nickname == name && !name.empty())
{
BuildStatusLine(false, ptr, statusLine);
// tunnel is incomplete / has not been started yet
BuildStatusLine(true, nullptr, statusLine);
SendReplyOK(statusLine.c_str());
}
else
@ -740,8 +743,8 @@ namespace client
}
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
RunnableService ("BOB"),
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
{
// command -> handler
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
@ -791,7 +794,8 @@ namespace client
BOBCommandChannel::~BOBCommandChannel ()
{
Stop ();
if (IsRunning ())
Stop ();
for (const auto& it: m_Destinations)
delete it.second;
}
@ -799,38 +803,15 @@ namespace client
void BOBCommandChannel::Start ()
{
Accept ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this));
StartIOService ();
}
void BOBCommandChannel::Stop ()
{
m_IsRunning = false;
for (auto& it: m_Destinations)
it.second->Stop ();
m_Acceptor.cancel ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void BOBCommandChannel::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "BOB: runtime exception: ", ex.what ());
}
}
StopIOService ();
}
void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest)

View File

@ -7,6 +7,7 @@
#include <map>
#include <string>
#include <boost/asio.hpp>
#include "util.h"
#include "I2PTunnel.h"
#include "I2PService.h"
#include "Identity.h"
@ -213,7 +214,7 @@ namespace client
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void SendReplyOK (const char * msg = nullptr);
void SendReplyError (const char * msg);
void SendData (const char * data);
void SendRaw (const char * data);
void BuildStatusLine(bool currentTunnel, BOBDestination *destination, std::string &out);
@ -231,7 +232,7 @@ namespace client
};
typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len);
class BOBCommandChannel
class BOBCommandChannel: private i2p::util::RunnableService
{
public:
@ -241,22 +242,18 @@ namespace client
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
boost::asio::io_service& GetService () { return GetIOService (); };
void AddDestination (const std::string& name, BOBDestination * dest);
void DeleteDestination (const std::string& name);
BOBDestination * FindDestination (const std::string& name);
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor;
std::map<std::string, BOBDestination *> m_Destinations;
std::map<std::string, BOBCommandHandler> m_CommandHandlers;

View File

@ -53,14 +53,19 @@ namespace client
// SAM
bool sam; i2p::config::GetOption("sam.enabled", sam);
if (sam) {
if (sam)
{
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread);
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
try {
m_SamBridge = new SAMBridge (samAddr, samPort);
m_SamBridge->Start ();
} catch (std::exception& e) {
try
{
m_SamBridge = new SAMBridge (samAddr, samPort, singleThread);
m_SamBridge->Start ();
}
catch (std::exception& e)
{
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
}
}
@ -305,21 +310,34 @@ namespace client
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (
boost::asio::io_service& service, bool isPublic,
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map<std::string, std::string> * params)
{
MatchedTunnelDestination * cl = new MatchedTunnelDestination(keys, name, params);
auto localDestination = std::shared_ptr<ClientDestination>(cl);
auto localDestination = std::make_shared<MatchedTunnelDestination>(keys, name, params);
AddLocalDestination (localDestination);
return localDestination;
}
void ClientContext::AddLocalDestination (std::shared_ptr<ClientDestination> localDestination)
{
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
localDestination->Start ();
return localDestination;
}
void ClientContext::DeleteLocalDestination (std::shared_ptr<ClientDestination> destination)
@ -344,14 +362,26 @@ namespace client
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
if (!it->second->IsRunning ())
it->second->Start ();
it->second->Start (); // make sure to start
return it->second;
}
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
std::unique_lock<std::mutex> l(m_DestinationsMutex);
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
localDestination->Start ();
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (boost::asio::io_service& service,
const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params)
{
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
if (it != m_Destinations.end ())
{
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
it->second->Start (); // make sure to start
return it->second;
}
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
}
@ -381,6 +411,16 @@ namespace client
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value);
}
template<typename Section>
void ClientContext::ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const
{
for (auto it: section.second)
{
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
options[it.first] = it.second.get_value ("");
}
}
template<typename Section>
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
{
@ -395,6 +435,17 @@ namespace client
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "");
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
auto authType = GetI2CPOption(section, I2CP_PARAM_LEASESET_AUTH_TYPE, 0);
if (authType != "0") // auth is set
{
options[I2CP_PARAM_LEASESET_AUTH_TYPE] = authType;
if (authType == "1") // DH
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_DH, options);
else if (authType == "2") // PSK
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_PSK, options);
}
}
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
@ -608,8 +659,8 @@ namespace client
// I2CP
std::map<std::string, std::string> options;
ReadI2CPOptions (section, options);
ReadI2CPOptions (section, options);
std::shared_ptr<ClientDestination> localDestination = nullptr;
i2p::data::PrivateKeys k;
if(!LoadPrivateKeys (k, keys, sigType, cryptoType))

View File

@ -68,8 +68,15 @@ namespace client
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
const std::map<std::string, std::string> * params = nullptr); // used by SAM only
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
const std::map<std::string, std::string> * params = nullptr);
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
const i2p::data::PrivateKeys& keys, bool isPublic = true,
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
std::shared_ptr<ClientDestination> CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map<std::string, std::string> * params = nullptr);
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
@ -95,6 +102,8 @@ namespace client
template<typename Section>
std::string GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const; // GetI2CPOption with string default value
template<typename Section>
void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const;
template<typename Section>
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const; // for tunnels
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const; // for HTTP and SOCKS proxy
@ -105,6 +114,7 @@ namespace client
void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain
void CreateNewSharedLocalDestination ();
void AddLocalDestination (std::shared_ptr<ClientDestination> localDestination);
private:

View File

@ -24,10 +24,35 @@ namespace client
{
I2CPDestination::I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
LeaseSetDestination (isPublic, &params), m_Owner (owner), m_Identity (identity)
RunnableService ("I2CP"), LeaseSetDestination (GetIOService (), isPublic, &params),
m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ())
{
}
I2CPDestination::~I2CPDestination ()
{
if (IsRunning ())
Stop ();
}
void I2CPDestination::Start ()
{
if (!IsRunning ())
{
LeaseSetDestination::Start ();
StartIOService ();
}
}
void I2CPDestination::Stop ()
{
if (IsRunning ())
{
LeaseSetDestination::Stop ();
StopIOService ();
}
}
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
{
memcpy (m_EncryptionPrivateKey, key, 256);
@ -556,7 +581,10 @@ namespace client
}
// TODO: support multiple keys
if (currentKey)
{
m_Destination->SetEncryptionPrivateKey (currentKey);
m_Destination->SetEncryptionType (currentKeyType);
}
m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ());
}

View File

@ -15,6 +15,7 @@
#include <thread>
#include <map>
#include <boost/asio.hpp>
#include "util.h"
#include "Destination.h"
namespace i2p
@ -61,19 +62,25 @@ namespace client
const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability";
class I2CPSession;
class I2CPDestination: public LeaseSetDestination
class I2CPDestination: private i2p::util::RunnableService, public LeaseSetDestination
{
public:
I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
~I2CPDestination ();
void Start ();
void Stop ();
void SetEncryptionPrivateKey (const uint8_t * key);
void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; };
void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession
void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession
void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession
// implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
i2p::data::CryptoKeyType GetEncryptionType () const { return m_EncryptionKeyType; };
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
protected:
@ -93,6 +100,7 @@ namespace client
std::shared_ptr<I2CPSession> m_Owner;
std::shared_ptr<const i2p::data::IdentityEx> m_Identity;
uint8_t m_EncryptionPrivateKey[256];
i2p::data::CryptoKeyType m_EncryptionKeyType;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor;
uint64_t m_LeaseSetExpirationTime;
};

View File

@ -8,7 +8,7 @@ namespace i2p
namespace client
{
MatchedTunnelDestination::MatchedTunnelDestination(const i2p::data::PrivateKeys & keys, const std::string & remoteName, const std::map<std::string, std::string> * params)
: ClientDestination(keys, false, params),
: RunnableClientDestination(keys, false, params),
m_RemoteName(remoteName) {}
@ -45,29 +45,19 @@ namespace client
}
bool MatchedTunnelDestination::Start()
void MatchedTunnelDestination::Start()
{
if(ClientDestination::Start())
{
m_ResolveTimer = std::make_shared<boost::asio::deadline_timer>(GetService());
GetTunnelPool()->SetCustomPeerSelector(this);
ResolveCurrentLeaseSet();
return true;
}
else
return false;
ClientDestination::Start();
m_ResolveTimer = std::make_shared<boost::asio::deadline_timer>(GetService());
GetTunnelPool()->SetCustomPeerSelector(this);
ResolveCurrentLeaseSet();
}
bool MatchedTunnelDestination::Stop()
void MatchedTunnelDestination::Stop()
{
if(ClientDestination::Stop())
{
if(m_ResolveTimer)
m_ResolveTimer->cancel();
return true;
}
else
return false;
ClientDestination::Stop();
if(m_ResolveTimer)
m_ResolveTimer->cancel();
}

View File

@ -10,12 +10,12 @@ namespace client
/**
client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination
*/
class MatchedTunnelDestination : public ClientDestination, public i2p::tunnel::ITunnelPeerSelector
class MatchedTunnelDestination : public RunnableClientDestination, public i2p::tunnel::ITunnelPeerSelector
{
public:
MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, const std::map<std::string, std::string> * params = nullptr);
bool Start();
bool Stop();
void Start();
void Stop();
bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound);
bool OnBuildResult(const i2p::tunnel::Path & peers, bool inbound, i2p::tunnel::TunnelBuildResult result);

View File

@ -239,6 +239,7 @@ namespace client
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol)
{
if (eol > m_Buffer && eol[-1] == '\r') eol--;
*eol = 0;
char * separator = strchr (m_Buffer, ' ');
if (separator)
@ -259,7 +260,7 @@ namespace client
ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND))
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND) || !strcmp (m_Buffer, SAM_RAW_SEND))
{
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
@ -337,8 +338,20 @@ namespace client
return;
}
SAMSessionType type = eSAMSessionTypeUnknown;
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram;
else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw;
if (type == eSAMSessionTypeUnknown)
{
// unknown style
SendI2PError("Unknown STYLE");
return;
}
std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr;
if (style == SAM_VALUE_DATAGRAM && params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end())
if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) &&
params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end())
{
// udp forward selected
boost::system::error_code e;
@ -379,16 +392,20 @@ namespace client
}
// create destination
auto session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
if (session)
{
m_SocketType = eSAMSocketTypeSession;
if (style == SAM_VALUE_DATAGRAM)
if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw)
{
session->UDPEndpoint = forward;
auto dest = session->localDestination->CreateDatagramDestination ();
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
if (type == eSAMSessionTypeDatagram)
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
else // raw
dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
if (session->localDestination->IsReady ())
@ -550,7 +567,10 @@ namespace client
{
i2p::data::IdentityEx dest;
dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
if (session->Type == eSAMSessionTypeDatagram)
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
else // raw
d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
}
else
LogPrint (eLogError, "SAM: missing datagram destination");
@ -926,16 +946,44 @@ namespace client
}
}
void SAMSocket::HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: raw datagram received ", len);
auto session = m_Owner.FindSession(m_ID);
if(session)
{
auto ep = session->UDPEndpoint;
if (ep)
// udp forward enabled
m_Owner.SendTo(buf, len, ep);
else
{
#ifdef _MSC_VER
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
#else
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
#endif
if (len < SAM_SOCKET_BUFFER_SIZE - l)
{
memcpy (m_StreamBuffer + l, buf, len);
WriteI2PData(len + l);
}
else
LogPrint (eLogWarning, "SAM: received raw datagram size ", len," exceeds buffer");
}
}
}
void SAMSocket::HandleStreamSend(const boost::system::error_code & ec)
{
m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
}
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, std::shared_ptr<ClientDestination> dest):
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
m_Bridge(parent),
localDestination (dest),
UDPEndpoint(nullptr),
Name(id)
Name(id), Type (type)
{
}
@ -952,10 +1000,10 @@ namespace client
}
}
SAMBridge::SAMBridge (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint),
SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread):
RunnableService ("SAM"), m_IsSingleThread (singleThread),
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)),
m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (GetIOService (), m_DatagramEndpoint),
m_SignatureTypes
{
{"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1},
@ -964,14 +1012,15 @@ namespace client
{"ECDSA_SHA256_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521},
{"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519},
{"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256},
{"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512}
{"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512},
{"RedDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519},
}
{
}
SAMBridge::~SAMBridge ()
{
if (m_IsRunning)
if (IsRunning ())
Stop ();
}
@ -979,14 +1028,11 @@ namespace client
{
Accept ();
ReceiveDatagram ();
m_IsRunning = true;
m_Thread = new std::thread (std::bind (&SAMBridge::Run, this));
StartIOService ();
}
void SAMBridge::Stop ()
{
m_IsRunning = false;
try
{
m_Acceptor.cancel ();
@ -996,31 +1042,13 @@ namespace client
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
for (auto& it: m_Sessions)
it.second->CloseStreams ();
m_Sessions.clear ();
m_Service.stop ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void SAMBridge::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
}
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->CloseStreams ();
m_Sessions.clear ();
}
StopIOService ();
}
void SAMBridge::Accept ()
@ -1061,15 +1089,17 @@ namespace client
Accept ();
}
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, const std::string& destination,
const std::map<std::string, std::string> * params)
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, SAMSessionType type,
const std::string& destination, const std::map<std::string, std::string> * params)
{
std::shared_ptr<ClientDestination> localDestination = nullptr;
if (destination != "")
{
i2p::data::PrivateKeys keys;
if (!keys.FromBase64 (destination)) return nullptr;
localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params);
localDestination = m_IsSingleThread ?
i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) :
i2p::client::context.CreateNewLocalDestination (keys, true, params);
}
else // transient
{
@ -1097,12 +1127,14 @@ namespace client
}
}
}
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params);
localDestination = m_IsSingleThread ?
i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) :
i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params);
}
if (localDestination)
{
localDestination->Acquire ();
auto session = std::make_shared<SAMSession>(*this, id, localDestination);
auto session = std::make_shared<SAMSession>(*this, id, type, localDestination);
std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::make_pair(id, session));
if (!ret.second)
@ -1129,6 +1161,15 @@ namespace client
session->localDestination->Release ();
session->localDestination->StopAcceptingStreams ();
session->CloseStreams ();
if (m_IsSingleThread)
{
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService ());
timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds
timer->async_wait ([timer, session](const boost::system::error_code& ecode)
{
// session's destructor is called here
});
}
}
}
@ -1193,8 +1234,12 @@ namespace client
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
if (session->Type == eSAMSessionTypeDatagram)
session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else // raw
session->localDestination->GetDatagramDestination ()->
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
}
else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");

View File

@ -9,6 +9,7 @@
#include <mutex>
#include <memory>
#include <boost/asio.hpp>
#include "util.h"
#include "Identity.h"
#include "LeaseSet.h"
#include "Streaming.h"
@ -40,12 +41,14 @@ namespace client
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
const char SAM_RAW_SEND[] = "RAW SEND";
const char SAM_DEST_GENERATE[] = "DEST GENERATE";
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n";
const char SAM_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\n";
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=KEY_NOT_FOUND NAME=%s\n";
const char SAM_PARAM_MIN[] = "MIN";
@ -111,6 +114,7 @@ namespace client
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz);
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void ProcessSessionCreate (char * buf, size_t len);
void ProcessStreamConnect (char * buf, size_t len, size_t rem);
@ -149,31 +153,40 @@ namespace client
std::shared_ptr<i2p::stream::Stream> m_Stream;
};
enum SAMSessionType
{
eSAMSessionTypeUnknown,
eSAMSessionTypeStream,
eSAMSessionTypeDatagram,
eSAMSessionTypeRaw
};
struct SAMSession
{
SAMBridge & m_Bridge;
std::shared_ptr<ClientDestination> localDestination;
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint;
std::string Name;
SAMSessionType Type;
SAMSession (SAMBridge & parent, const std::string & name, std::shared_ptr<ClientDestination> dest);
SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
~SAMSession ();
void CloseStreams ();
};
class SAMBridge
class SAMBridge: private i2p::util::RunnableService
{
public:
SAMBridge (const std::string& address, int port);
SAMBridge (const std::string& address, int port, bool singleThread);
~SAMBridge ();
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<SAMSession> CreateSession (const std::string& id, const std::string& destination, // empty string means transient
boost::asio::io_service& GetService () { return GetIOService (); };
std::shared_ptr<SAMSession> CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params);
void CloseSession (const std::string& id);
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
@ -189,8 +202,6 @@ namespace client
private:
void Run ();
void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket);
@ -199,9 +210,7 @@ namespace client
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
bool m_IsSingleThread;
boost::asio::ip::tcp::acceptor m_Acceptor;
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
boost::asio::ip::udp::socket m_DatagramSocket;

View File

@ -6,4 +6,6 @@ i2pd_qt
Makefile*
*.stash
object_script.*
i2pd_qt_plugin_import.cpp
i2pd_qt_plugin_import.cpp
i2pd_qt.pro.autosave*

View File

@ -11,6 +11,8 @@
#include <QMutexLocker>
#include <QThread>
//#define DEBUG_WITH_DEFAULT_LOGGING (1)
namespace i2p
{
namespace qt
@ -151,10 +153,16 @@ namespace qt
int result;
{
std::shared_ptr<std::iostream> logstreamptr=std::make_shared<std::stringstream>();
std::shared_ptr<std::iostream> logstreamptr=
#ifdef DEBUG_WITH_DEFAULT_LOGGING
nullptr
#else
std::make_shared<std::stringstream>()
#endif
;
//TODO move daemon init deinit to a bg thread
DaemonQTImpl daemon;
(*logstreamptr) << "Initialising the daemon..." << std::endl;
if(logstreamptr) (*logstreamptr) << "Initialising the daemon..." << std::endl;
bool daemonInitSuccess = daemon.init(argc, argv, logstreamptr);
if(!daemonInitSuccess)
{

View File

@ -8,19 +8,19 @@
<name>i2pd</name>
<summary>Invisible Internet</summary>
<description>
<p>i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.</p>
<p>I2P (Invisible Internet Protocol) is a universal anonymous network layer.
All communications over I2P are anonymous and end-to-end encrypted, participants
don't reveal their real IP addresses. </p>
<p>I2P allows people from all around the world to communicate and share information
without restrictions.</p>
<p>Features:</p>
<ul>
<li>Distributed anonymous networking framework</li>
<li>End-to-end encrypted communications</li>
<li>Small footprint, simple dependencies, fast performance</li>
<li>Rich set of APIs for developers of secure applications</li>
</ul>
<p>i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.</p>
<p>I2P (Invisible Internet Protocol) is a universal anonymous network layer.
All communications over I2P are anonymous and end-to-end encrypted, participants
don't reveal their real IP addresses. </p>
<p>I2P allows people from all around the world to communicate and share information
without restrictions.</p>
<p>Features:</p>
<ul>
<li>Distributed anonymous networking framework</li>
<li>End-to-end encrypted communications</li>
<li>Small footprint, simple dependencies, fast performance</li>
<li>Rich set of APIs for developers of secure applications</li>
</ul>
</description>
<screenshots>
<screenshot type="default">
@ -35,13 +35,17 @@
<translation type="qt" />
<releases>
<release version="2.26.0" date="2019-06-07" />
<release version="2.25.0" date="2019-05-09" />
<release version="2.24.0" date="2019-03-21" />
<release version="2.23.0" date="2019-01-21" />
<release version="2.22.0" date="2018-11-09" />
<release version="2.21.1" date="2018-10-22" />
<release version="2.21.0" date="2018-10-04" />
<release version="2.30.0" date="2020-02-25" />
<release version="2.29.0" date="2019-10-21" />
<release version="2.28.0" date="2019-08-27" />
<release version="2.27.0" date="2019-07-03" />
<release version="2.26.0" date="2019-06-07" />
<release version="2.25.0" date="2019-05-09" />
<release version="2.24.0" date="2019-03-21" />
<release version="2.23.0" date="2019-01-21" />
<release version="2.22.0" date="2018-11-09" />
<release version="2.21.1" date="2018-10-22" />
<release version="2.21.0" date="2018-10-04" />
</releases>
<content_rating type="oars-1.1" />
</component>

View File

@ -4,19 +4,30 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = i2pd_qt
TEMPLATE = app
QMAKE_CXXFLAGS *= -std=c++11 -ggdb
QMAKE_CXXFLAGS *= -std=c++11 -Wno-unused-parameter -Wno-maybe-uninitialized
DEFINES += USE_UPNP
CONFIG(debug, debug|release) {
message(Debug build)
DEFINES += DEBUG_WITH_DEFAULT_LOGGING
} else {
message(Release build)
}
SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/api.cpp \
../../libi2pd/Base.cpp \
../../libi2pd/Blinding.cpp \
../../libi2pd/BloomFilter.cpp \
../../libi2pd/ChaCha20.cpp \
../../libi2pd/Config.cpp \
../../libi2pd/CPU.cpp \
../../libi2pd/Crypto.cpp \
../../libi2pd/CryptoKey.cpp \
../../libi2pd/CryptoKey.cpp \
../../libi2pd/Datagram.cpp \
../../libi2pd/Destination.cpp \
../../libi2pd/Ed25519.cpp \
../../libi2pd/Event.cpp \
../../libi2pd/Family.cpp \
../../libi2pd/FS.cpp \
@ -31,7 +42,9 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/Log.cpp \
../../libi2pd/NetDb.cpp \
../../libi2pd/NetDbRequests.cpp \
../../libi2pd/NTCP2.cpp \
../../libi2pd/NTCPSession.cpp \
../../libi2pd/Poly1305.cpp \
../../libi2pd/Profiling.cpp \
../../libi2pd/Reseed.cpp \
../../libi2pd/RouterContext.cpp \
@ -49,9 +62,8 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/TunnelGateway.cpp \
../../libi2pd/TunnelPool.cpp \
../../libi2pd/util.cpp \
../../libi2pd/Ed25519.cpp \
../../libi2pd/Chacha20.cpp \
../../libi2pd/Poly1305.cpp \
../../libi2pd/Elligator.cpp \
../../libi2pd/ECIESX25519AEADRatchetSession.cpp \
../../libi2pd_client/AddressBook.cpp \
../../libi2pd_client/BOB.cpp \
../../libi2pd_client/ClientContext.cpp \
@ -64,42 +76,37 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd_client/SOCKS.cpp \
../../libi2pd_client/Websocket.cpp \
../../libi2pd_client/WebSocks.cpp \
../../daemon/Daemon.cpp \
../../daemon/HTTPServer.cpp \
../../daemon/I2PControl.cpp \
../../daemon/i2pd.cpp \
../../daemon/UPnP.cpp \
ClientTunnelPane.cpp \
MainWindowItems.cpp \
ServerTunnelPane.cpp \
SignatureTypeComboboxFactory.cpp \
TunnelConfig.cpp \
TunnelPane.cpp \
../../daemon/Daemon.cpp \
../../daemon/HTTPServer.cpp \
../../daemon/i2pd.cpp \
../../daemon/I2PControl.cpp \
../../daemon/UnixDaemon.cpp \
../../daemon/UPnP.cpp \
textbrowsertweaked1.cpp \
pagewithbackbutton.cpp \
widgetlock.cpp \
widgetlockregistry.cpp \
logviewermanager.cpp \
../../libi2pd/NTCP2.cpp
#qt creator does not handle this well
#SOURCES += $$files(../../libi2pd/*.cpp)
#SOURCES += $$files(../../libi2pd_client/*.cpp)
#SOURCES += $$files(../../daemon/*.cpp)
#SOURCES += $$files(./*.cpp)
SOURCES -= ../../daemon/UnixDaemon.cpp
logviewermanager.cpp
HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd/api.h \
../../libi2pd/Base.h \
../../libi2pd/Blinding.h \
../../libi2pd/BloomFilter.h \
../../libi2pd/ChaCha20.h \
../../libi2pd/Config.h \
../../libi2pd/CPU.h \
../../libi2pd/Crypto.h \
../../libi2pd/CryptoKey.h \
../../libi2pd/CryptoKey.h \
../../libi2pd/CryptoWorker.h \
../../libi2pd/Datagram.h \
../../libi2pd/Destination.h \
../../libi2pd/Ed25519.h \
../../libi2pd/Event.h \
../../libi2pd/Family.h \
../../libi2pd/FS.h \
@ -115,13 +122,16 @@ HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd/Log.h \
../../libi2pd/NetDb.hpp \
../../libi2pd/NetDbRequests.h \
../../libi2pd/NTCP2.h \
../../libi2pd/NTCPSession.h \
../../libi2pd/Poly1305.h \
../../libi2pd/Profiling.h \
../../libi2pd/Queue.h \
../../libi2pd/Reseed.h \
../../libi2pd/RouterContext.h \
../../libi2pd/RouterInfo.h \
../../libi2pd/Signature.h \
../../libi2pd/Siphash.h \
../../libi2pd/SSU.h \
../../libi2pd/SSUData.h \
../../libi2pd/SSUSession.h \
@ -139,6 +149,8 @@ HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd/TunnelPool.h \
../../libi2pd/util.h \
../../libi2pd/version.h \
../../libi2pd/Elligator.h \
../../libi2pd/ECIESX25519AEADRatchetSession.h \
../../libi2pd_client/AddressBook.h \
../../libi2pd_client/BOB.h \
../../libi2pd_client/ClientContext.h \
@ -151,6 +163,10 @@ HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd_client/SOCKS.h \
../../libi2pd_client/Websocket.h \
../../libi2pd_client/WebSocks.h \
../../daemon/Daemon.h \
../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \
../../daemon/UPnP.h \
ClientTunnelPane.h \
MainWindowItems.h \
ServerTunnelPane.h \
@ -158,16 +174,11 @@ HEADERS += DaemonQT.h mainwindow.h \
TunnelConfig.h \
TunnelPane.h \
TunnelsPageUpdateListener.h \
../../daemon/Daemon.h \
../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \
../../daemon/UPnP.h \
textbrowsertweaked1.h \
pagewithbackbutton.h \
widgetlock.h \
widgetlockregistry.h \
i2pd.rc \
i2pd.rc \
logviewermanager.h
INCLUDEPATH += ../../libi2pd
@ -199,6 +210,9 @@ macx {
LIBS += $$BOOSTROOT/lib/libboost_filesystem.a
LIBS += $$BOOSTROOT/lib/libboost_program_options.a
LIBS += $$UPNPROOT/lib/libminiupnpc.a
LIBS += -Wl,-dead_strip
LIBS += -Wl,-dead_strip_dylibs
LIBS += -Wl,-bind_at_load
}
linux:!android {

View File

@ -18,7 +18,7 @@ namespace logviewer {
QString Worker::pollAndShootATimerForInfiniteRetries() {
std::shared_ptr<std::iostream> logStream=logViewerManager.getLogStream();
assert(logStream!=nullptr);
if(!logStream)return "";
std::streamsize MAX_SZ=64*1024;
char*buf=(char*)malloc(MAX_SZ*sizeof(char));
if(buf==nullptr)return "";

View File

@ -1,6 +1,6 @@
CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -I../libi2pd/ -pthread -Wl,--unresolved-symbols=ignore-in-object-files
TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding
TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding test-elligator
all: $(TESTS) run
@ -25,6 +25,9 @@ test-aeadchacha20poly1305: ../libi2pd/Crypto.cpp ../libi2pd/ChaCha20.cpp ../libi
test-blinding: ../libi2pd/Crypto.cpp ../libi2pd/Blinding.cpp ../libi2pd/Ed25519.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Log.cpp ../libi2pd/util.cpp ../libi2pd/Identity.cpp ../libi2pd/Signature.cpp ../libi2pd/Timestamp.cpp test-blinding.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system
test-elligator: ../libi2pd/Elligator.cpp ../libi2pd/Crypto.cpp test-elligator.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system
run: $(TESTS)
@for TEST in $(TESTS); do ./$$TEST ; done

85
tests/test-elligator.cpp Normal file
View File

@ -0,0 +1,85 @@
#include <cassert>
#include <inttypes.h>
#include <string.h>
#include "Elligator.h"
const uint8_t key[32] =
{
0x33, 0x95, 0x19, 0x64, 0x00, 0x3c, 0x94, 0x08, 0x78, 0x06, 0x3c, 0xcf, 0xd0, 0x34, 0x8a, 0xf4,
0x21, 0x50, 0xca, 0x16, 0xd2, 0x64, 0x6f, 0x2c, 0x58, 0x56, 0xe8, 0x33, 0x83, 0x77, 0xd8, 0x80
};
const uint8_t encoded_key[32] =
{
0x28, 0x20, 0xb6, 0xb2, 0x41, 0xe0, 0xf6, 0x8a, 0x6c, 0x4a, 0x7f, 0xee, 0x3d, 0x97, 0x82, 0x28,
0xef, 0x3a, 0xe4, 0x55, 0x33, 0xcd, 0x41, 0x0a, 0xa9, 0x1a, 0x41, 0x53, 0x31, 0xd8, 0x61, 0x2d
};
const uint8_t encoded_key_high_y[32] =
{
0x3c, 0xfb, 0x87, 0xc4, 0x6c, 0x0b, 0x45, 0x75, 0xca, 0x81, 0x75, 0xe0, 0xed, 0x1c, 0x0a, 0xe9,
0xda, 0xe7, 0x9d, 0xb7, 0x8d, 0xf8, 0x69, 0x97, 0xc4, 0x84, 0x7b, 0x9f, 0x20, 0xb2, 0x77, 0x18
};
const uint8_t encoded1[32] =
{
0xe7, 0x35, 0x07, 0xd3, 0x8b, 0xae, 0x63, 0x99, 0x2b, 0x3f, 0x57, 0xaa, 0xc4, 0x8c, 0x0a, 0xbc,
0x14, 0x50, 0x95, 0x89, 0x28, 0x84, 0x57, 0x99, 0x5a, 0x2b, 0x4c, 0xa3, 0x49, 0x0a, 0xa2, 0x07
};
const uint8_t key1[32] =
{
0x1e, 0x8a, 0xff, 0xfe, 0xd6, 0xbf, 0x53, 0xfe, 0x27, 0x1a, 0xd5, 0x72, 0x47, 0x32, 0x62, 0xde,
0xd8, 0xfa, 0xec, 0x68, 0xe5, 0xe6, 0x7e, 0xf4, 0x5e, 0xbb, 0x82, 0xee, 0xba, 0x52, 0x60, 0x4f
};
const uint8_t encoded2[32] =
{
0x95, 0xa1, 0x60, 0x19, 0x04, 0x1d, 0xbe, 0xfe, 0xd9, 0x83, 0x20, 0x48, 0xed, 0xe1, 0x19, 0x28,
0xd9, 0x03, 0x65, 0xf2, 0x4a, 0x38, 0xaa, 0x7a, 0xef, 0x1b, 0x97, 0xe2, 0x39, 0x54, 0x10, 0x1b
};
const uint8_t key2[32] =
{
0x79, 0x4f, 0x05, 0xba, 0x3e, 0x3a, 0x72, 0x95, 0x80, 0x22, 0x46, 0x8c, 0x88, 0x98, 0x1e, 0x0b,
0xe5, 0x78, 0x2b, 0xe1, 0xe1, 0x14, 0x5c, 0xe2, 0xc3, 0xc6, 0xfd, 0xe1, 0x6d, 0xed, 0x53, 0x63
};
const uint8_t encoded3[32] =
{
0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f
};
const uint8_t key3[32] =
{
0x9c, 0xdb, 0x52, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55
};
const uint8_t failed_key[32] =
{
0xe6, 0xf6, 0x6f, 0xdf, 0x6e, 0x23, 0x0c, 0x60, 0x3c, 0x5e, 0x6e, 0x59, 0xa2, 0x54, 0xea, 0x14,
0x76, 0xa1, 0x3e, 0xb9, 0x51, 0x1b, 0x95, 0x49, 0x84, 0x67, 0x81, 0xe1, 0x2e, 0x52, 0x23, 0x0a
};
int main ()
{
uint8_t buf[32];
i2p::crypto::Elligator2 el;
// encoding tests
el.Encode (key, buf, false, false);
assert(memcmp (buf, encoded_key, 32) == 0);
el.Encode (key, buf, true, false); // with highY
assert(memcmp (buf, encoded_key_high_y, 32) == 0);
// decoding tests
el.Decode (encoded1, buf);
assert(memcmp (buf, key1, 32) == 0);
el.Decode (encoded2, buf);
assert(memcmp (buf, key2, 32) == 0);
el.Decode (encoded3, buf);
assert(memcmp (buf, key3, 32) == 0);
// encoding fails
assert (!el.Encode (failed_key, buf));
}

View File

@ -27,10 +27,13 @@ uint8_t p[32] =
int main ()
{
#if !OPENSSL_X25519
// we test it for openssl < 1.1.0
uint8_t buf[32];
BN_CTX * ctx = BN_CTX_new ();
i2p::crypto::GetEd25519 ()->ScalarMul (u, k, buf, ctx);
BN_CTX_free (ctx);
assert(memcmp (buf, p, 32) == 0);
#endif
}