Compare commits
190 Commits
Author | SHA1 | Date | |
---|---|---|---|
a0d6c654cc | |||
5115c27e72 | |||
d09c3ccb2d | |||
5c308026ac | |||
91919c6d64 | |||
7168738835 | |||
9c9b723cf5 | |||
50450923df | |||
f392edd66c | |||
24b48e5d50 | |||
47f384a0e0 | |||
88594887f9 | |||
32e2f0b1fa | |||
09ed57ad42 | |||
53a6162b0c | |||
694d851cdb | |||
8e53c30a00 | |||
63e807b0b4 | |||
012f22cc47 | |||
9d891ab5dd | |||
d0e78be867 | |||
cbedebc9dd | |||
969f9aa436 | |||
b982be5ff5 | |||
2d154ee640 | |||
49810eb153 | |||
85b88b8749 | |||
239c8b5172 | |||
8c800dc178 | |||
cdd068d99a | |||
48fa10b080 | |||
a1dbec0fcb | |||
abe668f1c3 | |||
77440c235d | |||
fd1ee48dbe | |||
205e807b66 | |||
34295adb05 | |||
7c212bef63 | |||
76f95644b7 | |||
928b90d5bc | |||
09c6c2a4f3 | |||
2b2bd733e9 | |||
0d2d7e5e71 | |||
6142e93252 | |||
ccec3376ba | |||
f497a74ec4 | |||
0e666e7d6a | |||
f498fabd27 | |||
8b49a55442 | |||
a26eb942a9 | |||
eabcafa516 | |||
6cc388c1bc | |||
62e39ddfbd | |||
80373623cd | |||
451c3945f0 | |||
00cb15d9b4 | |||
67dd59125e | |||
b6800dd125 | |||
dc9da69509 | |||
d7d964bf57 | |||
bcfe44db54 | |||
376bf6ba72 | |||
f651baab25 | |||
61752e2aab | |||
b7d3fd959e | |||
7ac05f8487 | |||
4f70822b13 | |||
0007f304d0 | |||
4afef91359 | |||
815b6db0bf | |||
433d3bf582 | |||
a335841509 | |||
26ad793d82 | |||
5337aa10f7 | |||
9f79bdae9b | |||
db84be2488 | |||
599ec62bb0 | |||
19a88300c6 | |||
b5d55e1ffb | |||
521fb83e38 | |||
553d59c32b | |||
9ed58e5186 | |||
36eaaa748c | |||
4d7b86ca26 | |||
5faf84c732 | |||
d7b819267f | |||
7417867d0f | |||
8d74905257 | |||
c38298c06e | |||
3100d587d1 | |||
ba849d0300 | |||
95df3e4b39 | |||
72492e33a0 | |||
934f1269f5 | |||
e6956d9bb0 | |||
2877900233 | |||
df1aa52e08 | |||
5fa2485a7d | |||
29f0e10411 | |||
39300a5bbf | |||
0abb871f3f | |||
704fca969f | |||
95debf8c80 | |||
dd94b77b2a | |||
6cfe4fa580 | |||
515c086099 | |||
34ce06ac17 | |||
a104c9881e | |||
c3e3c091cc | |||
651240113c | |||
77189bf8e9 | |||
60fd3a4542 | |||
c66f9c8d6d | |||
569088eaca | |||
a7e8dd04fe | |||
dfdd76a1bb | |||
28aac6f93b | |||
c2f47119ce | |||
d6b1d0d4fb | |||
03a861745b | |||
9a7aed20e9 | |||
b7f17d4cb1 | |||
2497c3d187 | |||
f7a084969a | |||
2900bc26a5 | |||
2334c56a96 | |||
90a5d02bf6 | |||
81d9626da9 | |||
44a2549b81 | |||
a2b8d468bc | |||
d523f0cadd | |||
99116ff097 | |||
3939ca9eb4 | |||
b5aa67b491 | |||
e42efec220 | |||
9d06aa2f6a | |||
80765a797b | |||
0b5509a1ed | |||
478d7b4a83 | |||
9d3b38141a | |||
ab3a4d902e | |||
5eab5f2437 | |||
80f632c19a | |||
6e4f18543d | |||
54586c9076 | |||
351c899807 | |||
fe45d431d7 | |||
488c2f6d05 | |||
75ab0909b3 | |||
8f82d563c1 | |||
9bbce5dba6 | |||
099adab9ed | |||
c8cbf425ac | |||
ad9c11cd92 | |||
3872c2a3f5 | |||
e6a09b49c9 | |||
db107602bd | |||
a6558a61a7 | |||
254d2b82b3 | |||
97d9795fc9 | |||
54071b0e5d | |||
925e8316c7 | |||
99e1b74023 | |||
7d68ccca53 | |||
a090114066 | |||
a204841abb | |||
cc451809cc | |||
a605e4bab6 | |||
3f0534134d | |||
3acfb129cd | |||
6ccef66920 | |||
e9fa4e94a6 | |||
fecc0c4640 | |||
b759294975 | |||
a23e845c03 | |||
cb8373e487 | |||
8e919ddc8e | |||
832a9ab6b5 | |||
13732ac333 | |||
3e932a55f4 | |||
74e8610ec9 | |||
089a60ded6 | |||
c8eeefe194 | |||
85eeba14c1 | |||
f6f45eab39 | |||
a74065f775 | |||
48d02f7e09 | |||
e60549f8df | |||
41f4f4713e | |||
213a292fd5 |
50
ChangeLog
50
ChangeLog
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||

|
||||

|
||||
[](https://github.com/PurpleI2P/i2pd/releases/latest)
|
||||
[](https://snapcraft.io/i2pd)
|
||||
[](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
|
||||
|
||||
i2pd
|
||||
====
|
||||
@ -66,6 +67,7 @@ Build instructions:
|
||||
* Mac OS X - [](https://travis-ci.org/PurpleI2P/i2pd)
|
||||
* CentOS / Fedora / Mageia - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||
* Docker image - [](https://hub.docker.com/r/meeh/i2pd/builds/)
|
||||
* Snap - [](https://build.snapcraft.io/user/PurpleI2P/i2pd-snap)
|
||||
* FreeBSD
|
||||
* Android
|
||||
* iOS
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
4
android/.gitignore
vendored
@ -12,5 +12,5 @@ local.properties
|
||||
build.sh
|
||||
android.iml
|
||||
build
|
||||
|
||||
|
||||
*.iml
|
||||
*.local
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
@ -1 +1,3 @@
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
org.gradle.parallel=true
|
@ -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
|
||||
|
@ -11,4 +11,4 @@
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-28
|
||||
target=android-29
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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-----
|
@ -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-----
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
147
daemon/UPnP.cpp
147
daemon/UPnP.cpp
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
26
debian/changelog
vendored
@ -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
|
||||
|
||||
|
@ -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 ());
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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/,"
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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 ();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
522
libi2pd/ECIESX25519AEADRatchetSession.cpp
Normal file
522
libi2pd/ECIESX25519AEADRatchetSession.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
127
libi2pd/ECIESX25519AEADRatchetSession.h
Normal file
127
libi2pd/ECIESX25519AEADRatchetSession.h
Normal 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
|
@ -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
207
libi2pd/Elligator.cpp
Normal 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
39
libi2pd/Elligator.h
Normal 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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
138
libi2pd/Garlic.h
138
libi2pd/Garlic.h
@ -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:
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 ());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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++)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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"); }
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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, ¶ms), m_Owner (owner), m_Identity (identity)
|
||||
RunnableService ("I2CP"), LeaseSetDestination (GetIOService (), isPublic, ¶ms),
|
||||
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 ());
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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, ¶ms);
|
||||
auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms);
|
||||
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");
|
||||
|
@ -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;
|
||||
|
4
qt/i2pd_qt/.gitignore
vendored
4
qt/i2pd_qt/.gitignore
vendored
@ -6,4 +6,6 @@ i2pd_qt
|
||||
Makefile*
|
||||
*.stash
|
||||
object_script.*
|
||||
i2pd_qt_plugin_import.cpp
|
||||
i2pd_qt_plugin_import.cpp
|
||||
i2pd_qt.pro.autosave*
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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 "";
|
||||
|
@ -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
85
tests/test-elligator.cpp
Normal 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));
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user