Merge pull request #1347 from PurpleI2P/openssl

2.25.0
This commit is contained in:
orignal 2019-05-09 11:38:44 -04:00 committed by GitHub
commit f176f1909b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1209 additions and 503 deletions

View File

@ -1,6 +1,23 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.25.0] - 2019-05-09
### Added
- Create, publish and handle encrypted LeaseSet2
- Support of b33 addresses
- RedDSA key blinding
- .b32.i2p addresses in jump links
- ntcp2.addressv6 parameter
### Changed
- Allow HTTP headers without value
- Set data directory from external storage path for Android
- addresshelper support is configurable per tunnel
- gradlew script for android build
### Fixed
- Deletion of expired encrypted LeaseSet2 on floodfills
- ipv6 fallback address
- SSU incoming packets routing
## [2.24.0] - 2019-03-21 ## [2.24.0] - 2019-03-21
### Added ### Added
- Support of transient keys for LeaseSet2 - Support of transient keys for LeaseSet2

View File

@ -19,10 +19,7 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6 else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
NEEDED_CXXFLAGS += -std=c++0x NEEDED_CXXFLAGS += -std=c++0x
else ifeq ($(shell expr match ${CXXVER} "[5-7]\.[0-9]"),3) # gcc >= 5.0 else ifeq ($(shell expr match ${CXXVER} "[5-9]"),1) # gcc >= 5
NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "[7-8]"),1) # gcc 7 ubuntu or gcc 8 arch
NEEDED_CXXFLAGS += -std=c++11 NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic LDLIBS = -latomic
else # not supported else # not supported

View File

@ -1,3 +1,6 @@
![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)
![GitHub](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)
i2pd i2pd
==== ====
@ -38,8 +41,12 @@ Resources
Installing Installing
---------- ----------
The easiest way to install i2pd is by using The easiest way to install i2pd is by using precompiled packages and binaries.
[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest). You can fetch most of them on [release](https://github.com/PurpleI2P/i2pd/releases/latest) page.
Please see [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/install/) for more info.
Building
--------
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
i2pd from source on your OS. i2pd from source on your OS.
@ -54,10 +61,10 @@ Build instructions:
**Supported systems:** **Supported systems:**
* GNU/Linux x86/x64 - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) * GNU/Linux - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd) * Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd)
* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) * Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* CentOS / Fedora - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) * CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/) * Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/)
* FreeBSD * FreeBSD
* Android * Android

View File

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

View File

@ -29,8 +29,8 @@ android {
applicationId "org.purplei2p.i2pd" applicationId "org.purplei2p.i2pd"
targetSdkVersion 28 targetSdkVersion 28
minSdkVersion 14 minSdkVersion 14
versionCode 2240 versionCode 2250
versionName "2.24.0" versionName "2.25.0"
ndk { ndk {
abiFilters 'armeabi-v7a' abiFilters 'armeabi-v7a'
abiFilters 'x86' abiFilters 'x86'

View File

@ -65,6 +65,8 @@ namespace android
} }
} }
*/ */
std::string dataDir = "";
DaemonAndroidImpl::DaemonAndroidImpl () DaemonAndroidImpl::DaemonAndroidImpl ()
//: //:
/*mutex(nullptr), */ /*mutex(nullptr), */
@ -85,7 +87,7 @@ namespace android
//m_IsRunning=false; //m_IsRunning=false;
// make sure assets are ready before proceed // make sure assets are ready before proceed
i2p::fs::DetectDataDir("", false); i2p::fs::DetectDataDir(dataDir, false);
int numAttempts = 0; int numAttempts = 0;
do do
{ {
@ -203,5 +205,10 @@ namespace android
{ {
daemon.stop(); daemon.stop();
} }
void SetDataDir(std::string jdataDir)
{
dataDir = jdataDir;
}
} }
} }

View File

@ -42,6 +42,8 @@ namespace android
// stops the daemon // stops the daemon
void stop(); void stop();
// set datadir received from jni
void SetDataDir(std::string jdataDir);
/* /*
class Worker : public QObject class Worker : public QObject
{ {

View File

@ -5,7 +5,7 @@
#include "Transports.h" #include "Transports.h"
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
(JNIEnv * env, jclass clazz) { (JNIEnv *env, jclass clazz) {
#if defined(__arm__) #if defined(__arm__)
#if defined(__ARM_ARCH_7A__) #if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__) #if defined(__ARM_NEON__)
@ -42,27 +42,53 @@ JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
} }
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
(JNIEnv * env, jclass clazz) { (JNIEnv *env, jclass clazz) {
return env->NewStringUTF(i2p::android::start().c_str()); return env->NewStringUTF(i2p::android::start().c_str());
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
(JNIEnv * env, jclass clazz) { (JNIEnv *env, jclass clazz) {
i2p::android::stop(); i2p::android::stop();
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels
(JNIEnv * env, jclass clazz) { (JNIEnv *env, jclass clazz) {
i2p::context.SetAcceptsTunnels (false); i2p::context.SetAcceptsTunnels (false);
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
(JNIEnv * env, jclass clazz) { (JNIEnv *env, jclass clazz) {
i2p::context.SetAcceptsTunnels (true); i2p::context.SetAcceptsTunnels (true);
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected) { (JNIEnv *env, jclass clazz, jboolean isConnected) {
bool isConnectedBool = (bool) isConnected; bool isConnectedBool = (bool) isConnected;
i2p::transport::transports.SetOnline (isConnectedBool); i2p::transport::transports.SetOnline (isConnectedBool);
} }
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir) {
/*
// Method 1: convert UTF-16 jstring to std::string (https://stackoverflow.com/a/41820336)
const jclass stringClass = env->GetObjectClass(jdataDir);
const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jdataDir, getBytes, env->NewStringUTF("UTF-8"));
size_t length = (size_t) env->GetArrayLength(stringJbytes);
jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);
std::string dataDir = std::string((char *)pBytes, length);
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
env->DeleteLocalRef(stringJbytes);
env->DeleteLocalRef(stringClass); */
// Method 2: get string chars and make char array.
auto dataDir = env->GetStringUTFChars(jdataDir, NULL);
env->ReleaseStringUTFChars(jdataDir, dataDir);
// Set DataDir
i2p::android::SetDataDir(dataDir);
}

View File

@ -30,6 +30,9 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected); (JNIEnv * env, jclass clazz, jboolean isConnected);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -2,7 +2,9 @@ package org.purplei2p.i2pd;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import android.os.Environment;
import android.util.Log; import android.util.Log;
import org.purplei2p.i2pd.R; import org.purplei2p.i2pd.R;
public class DaemonSingleton { public class DaemonSingleton {
@ -80,6 +82,7 @@ public class DaemonSingleton {
} }
try { try {
synchronized (DaemonSingleton.this) { synchronized (DaemonSingleton.this) {
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
daemonStartResult = I2PD_JNI.startDaemon(); daemonStartResult = I2PD_JNI.startDaemon();
if("ok".equals(daemonStartResult)){ if("ok".equals(daemonStartResult)){
setState(State.startedOkay); setState(State.startedOkay);

View File

@ -18,6 +18,8 @@ public class I2PD_JNI {
public static native void onNetworkStateChanged(boolean isConnected); public static native void onNetworkStateChanged(boolean isConnected);
public static native void setDataDir(String jdataDir);
public static void loadLibraries() { public static void loadLibraries() {
//System.loadLibrary("c++_shared"); //System.loadLibrary("c++_shared");
System.loadLibrary("i2pd"); System.loadLibrary("i2pd");

View File

@ -1,8 +1,8 @@
#APP_ABI := all APP_ABI := all
#APP_ABI := armeabi-v7a x86 #APP_ABI += x86
#APP_ABI := x86 #APP_ABI += x86_64
#APP_ABI := x86_64 #APP_ABI += armeabi-v7a
APP_ABI := armeabi-v7a #APP_ABI += arm64-v8a
#can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there. #can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there.
APP_PLATFORM := android-14 APP_PLATFORM := android-14

View File

@ -1,4 +1,4 @@
version: 2.24.0.{build} version: 2.25.0.{build}
pull_requests: pull_requests:
do_not_increment_build_number: true do_not_increment_build_number: true
branches: branches:

View File

@ -0,0 +1,2 @@
archive
i2pd_*_android_binary.zip

View File

@ -0,0 +1,45 @@
#!/bin/bash
# Copyright (c) 2013-2017, 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
GITDESC=$(git describe --tags)
declare -A ABILIST=(
["armeabi-v7a"]="armv7l"
["arm64-v8a"]="aarch64"
["x86"]="x86"
["x86_64"]="x86_64"
)
# Remove old files and archives
if [ -d archive ]; then
rm -r archive
fi
if [ -f i2pd_*_android_binary.zip ]; then
rm i2pd_*_android_binary.zip
fi
# Prepare files for package
mkdir archive
for ABI in "${!ABILIST[@]}"; do
if [ -f ../../android_binary_only/libs/${ABI}/i2pd ]; then
cp ../../android_binary_only/libs/${ABI}/i2pd archive/i2pd-${ABILIST[$ABI]}
fi
done
cp i2pd archive/i2pd
cp -rH ../../android/assets/* archive/
# Compress files
cd archive
zip -r6 ../i2pd_${GITDESC}_android_binary.zip .
# Remove temporary folder
cd ..
rm -r archive

View File

@ -0,0 +1,33 @@
#!/bin/sh
# Copyright (c) 2013-2019, 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
#
# That script written for use with Termux.
# https://stackoverflow.com/a/246128
SOURCE="${0}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
arch=$(uname -m)
screenfind=$(which screen)
if [ -z $screenfind ]; then
echo "Can't find 'screen' installed. That script needs it!";
exit 1;
fi
if [ -z i2pd-$arch ]; then
echo "Can't find i2pd binary for your archtecture.";
exit 1;
fi
screen -AmdS i2pd ./i2pd-$arch --datadir=$DIR

View File

@ -24,7 +24,7 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
# 1. install deps, clone and build. # 1. install deps, clone and build.
# 2. strip binaries. # 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory. # 3. Purge all dependencies and other unrelated packages, including build directory.
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool boost-dev build-base openssl-dev openssl git \ RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl git \
&& mkdir -p /tmp/build \ && mkdir -p /tmp/build \
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \ && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
&& cd i2pd \ && cd i2pd \

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.24.0 Version: 2.25.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -110,6 +110,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Thu May 9 2019 orignal <i2porignal@yandex.ru> - 2.25.0
- update to 2.25.0
* Thu Mar 21 2019 orignal <i2porignal@yandex.ru> - 2.24.0 * Thu Mar 21 2019 orignal <i2porignal@yandex.ru> - 2.24.0
- update to 2.24.0 - update to 2.24.0

View File

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.24.0 Version: 2.25.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -108,6 +108,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Thu May 9 2019 orignal <i2porignal@yandex.ru> - 2.25.0
- update to 2.25.0
* Thu Mar 21 2019 orignal <i2porignal@yandex.ru> - 2.24.0 * Thu Mar 21 2019 orignal <i2porignal@yandex.ru> - 2.24.0
- update to 2.24.0 - update to 2.24.0

View File

@ -154,14 +154,24 @@ namespace i2p
i2p::context.SetSupportsV6 (ipv6); i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4); i2p::context.SetSupportsV4 (ipv4);
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
i2p::context.PublishNTCPAddress (ntcp, !ipv6);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2) if (ntcp2)
{ {
bool published; i2p::config::GetOption("ntcp2.published", published); bool published; i2p::config::GetOption("ntcp2.published", published);
if (published) if (published)
{ {
uint16_t port; i2p::config::GetOption("ntcp2.port", port); uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
i2p::context.PublishNTCP2Address (port, true); // publish if (!ntcp && !ntcp2port) ntcp2port = port; // use standard port
i2p::context.PublishNTCP2Address (ntcp2port, true); // publish
if (ipv6)
{
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
auto addr = boost::asio::ip::address_v6::from_string (ipv6Addr);
if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ())
i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured
}
} }
else else
i2p::context.PublishNTCP2Address (port, false); // unpublish i2p::context.PublishNTCP2Address (port, false); // unpublish
@ -256,7 +266,7 @@ namespace i2p
pos = comma + 1; pos = comma + 1;
} }
while (comma != std::string::npos); while (comma != std::string::npos);
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routesrs"); LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routers");
i2p::transport::transports.RestrictRoutesToRouters(idents); i2p::transport::transports.RestrictRoutesToRouters(idents);
restricted = idents.size() > 0; restricted = idents.size() > 0;
} }

View File

@ -358,7 +358,7 @@ namespace http {
{ {
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<p class='content'>\r\n"; s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<p class='content'>\r\n";
for(auto& it: dest->GetLeaseSets ()) for(auto& it: dest->GetLeaseSets ())
s << it.second->GetIdentHash ().ToBase32 () << " " << (int)it.second->GetStoreType () << "<br>\r\n"; s << it.first.ToBase32 () << " " << (int)it.second->GetStoreType () << "<br>\r\n";
s << "</p>\r\n</div>\r\n"; s << "</p>\r\n</div>\r\n";
} else } else
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n"; s << "<b>LeaseSets:</b> <i>0</i><br>\r\n";

7
debian/changelog vendored
View File

@ -1,7 +1,12 @@
i2pd (2.25.0-1) unstable; urgency=medium
* updated to version 2.25.0/0.9.40
-- orignal <orignal@i2pmail.org> Thu, 9 May 2019 16:00:00 +0000
i2pd (2.24.0-1) unstable; urgency=medium i2pd (2.24.0-1) unstable; urgency=medium
* updated to version 2.24.0/0.9.39 * updated to version 2.24.0/0.9.39
* update docs, dirs, install, links files
-- orignal <orignal@i2pmail.org> Thu, 21 Mar 2019 16:00:00 +0000 -- orignal <orignal@i2pmail.org> Thu, 21 Mar 2019 16:00:00 +0000

View File

@ -9,6 +9,7 @@
* *
*/ */
#include "I2PEndian.h"
#include "ChaCha20.h" #include "ChaCha20.h"
#if !OPENSSL_AEAD_CHACHA20_POLY1305 #if !OPENSSL_AEAD_CHACHA20_POLY1305
@ -89,14 +90,14 @@ void Chacha20Init (Chacha20State& state, const uint8_t * nonce, const uint8_t *
for (size_t i = 0; i < 8; i++) for (size_t i = 0; i < 8; i++)
state.data[4 + i] = chacha::u8t32le(key + i * 4); state.data[4 + i] = chacha::u8t32le(key + i * 4);
state.data[12] = counter; state.data[12] = htole32 (counter);
for (size_t i = 0; i < 3; i++) for (size_t i = 0; i < 3; i++)
state.data[13 + i] = chacha::u8t32le(nonce + i * 4); state.data[13 + i] = chacha::u8t32le(nonce + i * 4);
} }
void Chacha20SetCounter (Chacha20State& state, uint32_t counter) void Chacha20SetCounter (Chacha20State& state, uint32_t counter)
{ {
state.data[12] = counter; state.data[12] = htole32 (counter);
state.offset = 0; state.offset = 0;
} }

View File

@ -73,7 +73,7 @@ namespace config {
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)") ("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabalistic backoff with ntcp sessions (default: use system limit)") ("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)")
("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)") ("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)")
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)") ("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)")
; ;
@ -153,8 +153,8 @@ namespace config {
("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address") ("i2pcontrol.address", value<std::string>()->default_value("127.0.0.1"), "I2PCP listen address")
("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port") ("i2pcontrol.port", value<uint16_t>()->default_value(7650), "I2PCP listen port")
("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password") ("i2pcontrol.password", value<std::string>()->default_value("itoopie"), "I2PCP access password")
("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate") ("i2pcontrol.cert", value<std::string>()->default_value("i2pcontrol.crt.pem"), "I2PCP connection certificate")
("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key") ("i2pcontrol.key", value<std::string>()->default_value("i2pcontrol.key.pem"), "I2PCP connection certificate key")
; ;
bool upnp_default = false; bool upnp_default = false;
@ -164,7 +164,7 @@ namespace config {
options_description upnp("UPnP options"); options_description upnp("UPnP options");
upnp.add_options() upnp.add_options()
("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding") ("upnp.enabled", value<bool>()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding")
("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list") ("upnp.name", value<std::string>()->default_value("I2Pd"), "Name i2pd appears in UPnP forwarding list")
; ;
options_description precomputation("Precomputation options"); options_description precomputation("Precomputation options");
@ -239,6 +239,7 @@ namespace config {
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)") ("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value<bool>()->default_value(false), "Publish NTCP2 (default: disabled)") ("ntcp2.published", value<bool>()->default_value(false), "Publish NTCP2 (default: disabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") ("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to bind NTCP2 on")
; ;
options_description nettime("Time sync options"); options_description nettime("Time sync options");
@ -256,7 +257,7 @@ namespace config {
options_description persist("Network information persisting options"); options_description persist("Network information persisting options");
persist.add_options() persist.add_options()
("persist.profiles", value<bool>()->default_value(true), "Persist peer profiles (default: true)") ("persist.profiles", value<bool>()->default_value(true), "Persist peer profiles (default: true)")
("persist.addressbook", value<bool>()->default_value(true), "Persist full addreses (default: true)") ("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
; ;
m_OptionsDesc m_OptionsDesc

View File

@ -32,12 +32,12 @@ namespace config {
* @param argc Cmdline arguments count, should be passed from main(). * @param argc Cmdline arguments count, should be passed from main().
* @param argv Cmdline parameters array, should be passed from main() * @param argv Cmdline parameters array, should be passed from main()
* *
* If --help is given in parameters, shows it's list with description * If --help is given in parameters, shows its list with description
* terminates the program with exitcode 0. * and terminates the program with exitcode 0.
* *
* In case of parameter misuse boost throws an exception. * In case of parameter misuse boost throws an exception.
* We internally handle type boost::program_options::unknown_option, * We internally handle type boost::program_options::unknown_option,
* and then terminate program with exitcode 1. * and then terminate the program with exitcode 1.
* *
* Other exceptions will be passed to higher level. * Other exceptions will be passed to higher level.
*/ */
@ -107,7 +107,7 @@ namespace config {
/** /**
* @brief Check is value explicitly given or default * @brief Check is value explicitly given or default
* @param name Name of checked parameter * @param name Name of checked parameter
* @return true if value set to default, false othervise * @return true if value set to default, false otherwise
*/ */
bool IsDefault(const char *name); bool IsDefault(const char *name);
} }

View File

@ -1235,7 +1235,9 @@ namespace crypto
{ {
#if OPENSSL_AEAD_CHACHA20_POLY1305 #if OPENSSL_AEAD_CHACHA20_POLY1305
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new (); EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new ();
EVP_EncryptInit_ex(ctx, EVP_chacha20 (), 0, key, nonce); uint32_t iv[4];
iv[0] = htole32 (1); memcpy (iv + 1, nonce, 12); // counter | nonce
EVP_EncryptInit_ex(ctx, EVP_chacha20 (), NULL, key, (const uint8_t *)iv);
int outlen = 0; int outlen = 0;
EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen); EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen);
EVP_EncryptFinal_ex(ctx, NULL, &outlen); EVP_EncryptFinal_ex(ctx, NULL, &outlen);

View File

@ -213,7 +213,7 @@ namespace client
return pool->Reconfigure(inLen, outLen, inQuant, outQuant); return pool->Reconfigure(inLen, outLen, inQuant, outQuant);
} }
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident) std::shared_ptr<i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
{ {
std::shared_ptr<i2p::data::LeaseSet> remoteLS; std::shared_ptr<i2p::data::LeaseSet> remoteLS;
{ {
@ -272,7 +272,8 @@ namespace client
if (!m_Pool) return nullptr; if (!m_Pool) return nullptr;
if (!m_LeaseSet) if (!m_LeaseSet)
UpdateLeaseSet (); UpdateLeaseSet ();
return GetLeaseSetMt (); auto ls = GetLeaseSetMt ();
return (ls && ls->GetInnerLeaseSet ()) ? ls->GetInnerLeaseSet () : ls; // always non-encrypted
} }
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSetMt () std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSetMt ()
@ -419,12 +420,13 @@ namespace client
case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5 case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5
{ {
auto it2 = m_LeaseSetRequests.find (key); auto it2 = m_LeaseSetRequests.find (key);
if (it2 != m_LeaseSetRequests.end () && it2->second->requestedIdentity) if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey)
{ {
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedIdentity); auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedBlindedKey);
if (ls2->IsValid ()) if (ls2->IsValid ())
{ {
m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key
m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup
leaseSet = ls2; leaseSet = ls2;
} }
} }
@ -576,7 +578,7 @@ namespace client
else else
{ {
LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ());
// Java floodfill never sends confirmantion back for unknown crypto type // Java floodfill never sends confirmation back for unknown crypto type
// assume it successive and try to verify // assume it successive and try to verify
m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT));
m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer,
@ -591,27 +593,32 @@ namespace client
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
auto ls = GetLeaseSetMt ();
if (!ls)
{
LogPrint (eLogWarning, "Destination: couldn't verify LeaseSet for ", GetIdentHash().ToBase32());
return;
}
auto s = shared_from_this (); auto s = shared_from_this ();
RequestLeaseSet (GetIdentHash (), // we must capture this for gcc 4.7 due the bug
// "this" added due to bug in gcc 4.7-4.8 RequestLeaseSet (ls->GetStoreHash (),
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet) [s, ls, this](std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{ {
if (leaseSet) if (leaseSet)
{ {
auto ls = s->GetLeaseSetMt (); if (*ls == *leaseSet)
if (ls && *ls == *leaseSet)
{ {
// we got latest LeasetSet // we got latest LeasetSet
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32()); LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", s->GetIdentHash().ToBase32());
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
return; return;
} }
else else
LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", GetIdentHash().ToBase32()); LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", s->GetIdentHash().ToBase32());
} }
else else
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32()); LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", s->GetIdentHash().ToBase32());
// we have to publish again // we have to publish again
s->Publish (); s->Publish ();
}); });
@ -636,17 +643,23 @@ namespace client
return true; return true;
} }
bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::IdentityEx> dest, RequestComplete requestComplete) bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete)
{ {
if (!m_Pool || !IsReady ()) if (!dest || !m_Pool || !IsReady ())
{ {
if (requestComplete) if (requestComplete)
m_Service.post ([requestComplete](void){requestComplete (nullptr);}); m_Service.post ([requestComplete](void){requestComplete (nullptr);});
return false; return false;
} }
i2p::data::IdentHash ident; auto storeHash = dest->GetStoreHash ();
i2p::data::LeaseSet2::CalculateStoreHash (dest, i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519, ident); // always assume type 11 auto leaseSet = FindLeaseSet (storeHash);
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), ident, requestComplete, dest)); if (leaseSet)
{
if (requestComplete)
m_Service.post ([requestComplete, leaseSet](void){requestComplete (leaseSet);});
return true;
}
m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), storeHash, requestComplete, dest));
return true; return true;
} }
@ -665,21 +678,20 @@ namespace client
}); });
} }
void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::IdentityEx> dest, bool notify) void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify)
{ {
i2p::data::IdentHash ident; if (dest)
i2p::data::LeaseSet2::CalculateStoreHash (dest, i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519, ident); // always assume type 11 CancelDestinationRequest (dest->GetStoreHash (), notify);
CancelDestinationRequest (ident, notify);
} }
void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::IdentityEx> requestedIdentity) void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey)
{ {
std::set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
if (floodfill) if (floodfill)
{ {
auto request = std::make_shared<LeaseSetRequest> (m_Service); auto request = std::make_shared<LeaseSetRequest> (m_Service);
request->requestedIdentity = requestedIdentity; // for encrypted LeaseSet2 request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2
if (requestComplete) if (requestComplete)
request->requestComplete.push_back (requestComplete); request->requestComplete.push_back (requestComplete);
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
@ -974,7 +986,7 @@ namespace client
{ {
auto s = GetSharedFromThis (); auto s = GetSharedFromThis ();
RequestDestination (dest, RequestDestination (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls) [s, streamRequestComplete, port](std::shared_ptr<const i2p::data::LeaseSet> ls)
{ {
if (ls) if (ls)
streamRequestComplete(s->CreateStream (ls, port)); streamRequestComplete(s->CreateStream (ls, port));
@ -984,6 +996,24 @@ namespace client
} }
} }
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port)
{
if (!streamRequestComplete)
{
LogPrint (eLogError, "Destination: request callback is not specified in CreateStream");
return;
}
auto s = GetSharedFromThis ();
RequestDestinationWithEncryptedLeaseSet (dest,
[s, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
streamRequestComplete(s->CreateStream (ls, port));
else
streamRequestComplete (nullptr);
});
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port) std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{ {
if (m_StreamingDestination) if (m_StreamingDestination)
@ -1097,10 +1127,13 @@ namespace client
} }
else else
{ {
// standard LS2 (type 3) assumed for now. TODO: implement others // standard LS2 (type 3) first
auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256; auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256;
leaseSet = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels); 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);
leaseSet = ls2;
} }
SetLeaseSet (leaseSet); SetLeaseSet (leaseSet);
} }

View File

@ -82,7 +82,7 @@ namespace client
std::list<RequestComplete> requestComplete; std::list<RequestComplete> requestComplete;
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel; std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel; std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
std::shared_ptr<const i2p::data::IdentityEx> requestedIdentity; // for encrypted LeaseSet2 only std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey; // for encrypted LeaseSet2 only
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls) void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
{ {
@ -108,11 +108,11 @@ namespace client
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; }; std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; }; bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident); std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::IdentityEx> dest, RequestComplete requestComplete = nullptr); bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, RequestComplete requestComplete = nullptr);
void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true); void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true);
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::IdentityEx> dest, bool notify = true); void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
// implements GarlicDestination // implements GarlicDestination
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet (); std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
@ -148,7 +148,7 @@ namespace client
void HandleDatabaseSearchReplyMessage (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 (std::shared_ptr<I2NPMessage> msg);
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::IdentityEx> requestedIdentity = nullptr); 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); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
void HandleCleanupTimer (const boost::system::error_code& ecode); void HandleCleanupTimer (const boost::system::error_code& ecode);
@ -213,6 +213,7 @@ namespace client
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const; std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
// following methods operate with default streaming destination // following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
void StopAcceptingStreams (); void StopAcceptingStreams ();

View File

@ -121,8 +121,8 @@ namespace crypto
return passed; return passed;
} }
void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded,
uint8_t * signature) const const uint8_t * buf, size_t len, uint8_t * signature) const
{ {
BN_CTX * bnCtx = BN_CTX_new (); BN_CTX * bnCtx = BN_CTX_new ();
// calculate r // calculate r
@ -153,6 +153,44 @@ namespace crypto
BN_CTX_free (bnCtx); BN_CTX_free (bnCtx);
} }
void Ed25519::SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded,
const uint8_t * buf, size_t len, uint8_t * signature) const
{
BN_CTX * bnCtx = BN_CTX_new ();
// T = 80 random bytes
uint8_t T[80];
RAND_bytes (T, 80);
// calculate r = H*(T || publickey || data)
SHA512_CTX ctx;
SHA512_Init (&ctx);
SHA512_Update (&ctx, T, 80);
SHA512_Update (&ctx, publicKeyEncoded, 32);
SHA512_Update (&ctx, buf, len); // data
uint8_t digest[64];
SHA512_Final (digest, &ctx);
BIGNUM * r = DecodeBN<64> (digest);
BN_mod (r, r, l, bnCtx); // % l
EncodeBN (r, digest, 32);
// calculate R
uint8_t R[EDDSA25519_SIGNATURE_LENGTH/2]; // we must use separate buffer because signature might be inside buf
EncodePoint (Normalize (MulB (digest, bnCtx), bnCtx), R);
// calculate S
SHA512_Init (&ctx);
SHA512_Update (&ctx, R, EDDSA25519_SIGNATURE_LENGTH/2); // R
SHA512_Update (&ctx, publicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key
SHA512_Update (&ctx, buf, len); // data
SHA512_Final (digest, &ctx);
BIGNUM * h = DecodeBN<64> (digest);
// S = (r + h*a) % l
BIGNUM * a = DecodeBN<EDDSA25519_PRIVATE_KEY_LENGTH> (privateKey);
BN_mod_mul (h, h, a, l, bnCtx); // %l
BN_mod_add (h, h, r, l, bnCtx); // %l
memcpy (signature, R, EDDSA25519_SIGNATURE_LENGTH/2);
EncodeBN (h, signature + EDDSA25519_SIGNATURE_LENGTH/2, EDDSA25519_SIGNATURE_LENGTH/2); // S
BN_free (r); BN_free (h); BN_free (a);
BN_CTX_free (bnCtx);
}
EDDSAPoint Ed25519::Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const EDDSAPoint Ed25519::Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const
{ {
// x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2) // x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2)
@ -506,6 +544,24 @@ namespace crypto
BN_CTX_free (ctx); BN_CTX_free (ctx);
} }
void Ed25519::BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub)
{
BN_CTX * ctx = BN_CTX_new ();
// calculate alpha = seed mod l
BIGNUM * alpha = DecodeBN<64> (seed); // seed is in Little Endian
BN_mod (alpha, alpha, l, ctx); // % l
BIGNUM * p = DecodeBN<32> (priv); // priv is in Little Endian
BN_add (alpha, alpha, p); // alpha = alpha + priv
// a' = BLIND_PRIVKEY(a, alpha) = (a + alpha) mod L
BN_mod (alpha, alpha, l, ctx); // % l
EncodeBN (alpha, blindedPriv, 32);
// A' = DERIVE_PUBLIC(a')
auto A1 = MulB (blindedPriv, ctx);
EncodePublicKey (A1, blindedPub, ctx);
BN_free (alpha); BN_free (p);
BN_CTX_free (ctx);
}
void Ed25519::ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey) void Ed25519::ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey)
{ {
SHA512 (key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey); SHA512 (key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey);
@ -514,6 +570,18 @@ namespace crypto
expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit expandedKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit
} }
void Ed25519::CreateRedDSAPrivateKey (uint8_t * priv)
{
uint8_t seed[32];
RAND_bytes (seed, 32);
BIGNUM * p = DecodeBN<32> (seed);
BN_CTX * ctx = BN_CTX_new ();
BN_mod (p, p, l, ctx); // % l
EncodeBN (p, priv, 32);
BN_CTX_free (ctx);
BN_free (p);
}
static std::unique_ptr<Ed25519> g_Ed25519; static std::unique_ptr<Ed25519> g_Ed25519;
std::unique_ptr<Ed25519>& GetEd25519 () std::unique_ptr<Ed25519>& GetEd25519 ()
{ {

View File

@ -81,11 +81,14 @@ namespace crypto
void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const;
#endif #endif
void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32
bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const; bool Verify (const EDDSAPoint& publicKey, const uint8_t * digest, const uint8_t * signature) const;
void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const; void Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const;
void SignRedDSA (const uint8_t * privateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, uint8_t * signature) const;
static void ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey); // key - 32 bytes, expandedKey - 64 bytes static void ExpandPrivateKey (const uint8_t * key, uint8_t * expandedKey); // key - 32 bytes, expandedKey - 64 bytes
void CreateRedDSAPrivateKey (uint8_t * priv); // priv is 32 bytes
private: private:

View File

@ -66,7 +66,7 @@ namespace fs {
/** @brief Returns current application name, default 'i2pd' */ /** @brief Returns current application name, default 'i2pd' */
const std::string & GetAppName (); const std::string & GetAppName ();
/** @brief Set applicaton name, affects autodetection of datadir */ /** @brief Set application name, affects autodetection of datadir */
void SetAppName (const std::string& name); void SetAppName (const std::string& name);
/** @brief Returns datadir path */ /** @brief Returns datadir path */

View File

@ -578,7 +578,7 @@ namespace garlic
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
else else
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
if (tunnel) // we have send it through an outbound tunnel if (tunnel) // we have sent it through an outbound tunnel
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
else else
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");

View File

@ -55,12 +55,16 @@ namespace http {
static std::pair<std::string, std::string> parse_header_line(const std::string& line) static std::pair<std::string, std::string> parse_header_line(const std::string& line)
{ {
std::size_t pos = 0; std::size_t pos = 0;
std::size_t len = 2; /* strlen(": ") */ std::size_t len = 1; /*: */
std::size_t max = line.length(); std::size_t max = line.length();
if ((pos = line.find(": ", pos)) == std::string::npos) if ((pos = line.find(':', pos)) == std::string::npos)
return std::make_pair("", ""); return std::make_pair("", ""); // no ':' found
if (pos + 1 < max) // ':' at the end of header is valid
{
while ((pos + len) < max && isspace(line.at(pos + len))) while ((pos + len) < max && isspace(line.at(pos + len)))
len++; len++;
if (len == 1) return std::make_pair("", ""); // no following space, but something else
}
return std::make_pair(line.substr(0, pos), line.substr(pos + len)); return std::make_pair(line.substr(0, pos), line.substr(pos + len));
} }

View File

@ -279,7 +279,7 @@ namespace i2p
if (!leaseSet) return nullptr; if (!leaseSet) return nullptr;
auto m = NewI2NPShortMessage (); auto m = NewI2NPShortMessage ();
uint8_t * payload = m->GetPayload (); uint8_t * payload = m->GetPayload ();
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32); memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetStoreHash (), 32);
payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // LeaseSet or LeaseSet2 payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // LeaseSet or LeaseSet2
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
size_t size = DATABASE_STORE_HEADER_SIZE; size_t size = DATABASE_STORE_HEADER_SIZE;

View File

@ -338,12 +338,13 @@ namespace data
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
return new i2p::crypto::ECDSAP521Verifier (); return new i2p::crypto::ECDSAP521Verifier ();
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
return new i2p::crypto::EDDSA25519Verifier (); return new i2p::crypto::EDDSA25519Verifier ();
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256:
return new i2p::crypto::GOSTR3410_256_Verifier (i2p::crypto::eGOSTR3410CryptoProA); return new i2p::crypto::GOSTR3410_256_Verifier (i2p::crypto::eGOSTR3410CryptoProA);
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512); return new i2p::crypto::GOSTR3410_512_Verifier (i2p::crypto::eGOSTR3410TC26A512);
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
return new i2p::crypto::RedDSA25519Verifier ();
case SIGNING_KEY_TYPE_RSA_SHA256_2048: case SIGNING_KEY_TYPE_RSA_SHA256_2048:
case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096: case SIGNING_KEY_TYPE_RSA_SHA512_4096:
@ -591,38 +592,52 @@ namespace data
void PrivateKeys::CreateSigner (SigningKeyType keyType) const void PrivateKeys::CreateSigner (SigningKeyType keyType) const
{ {
if (m_Signer) return; if (m_Signer) return;
if (keyType == SIGNING_KEY_TYPE_DSA_SHA1)
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
else if (keyType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 && !IsOfflineSignature ())
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check
else
{
// public key is not required
auto signer = CreateSigner (keyType, m_SigningPrivateKey);
if (signer) m_Signer.reset (signer);
}
}
i2p::crypto::Signer * PrivateKeys::CreateSigner (SigningKeyType keyType, const uint8_t * priv)
{
switch (keyType) switch (keyType)
{ {
case SIGNING_KEY_TYPE_DSA_SHA1:
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
break;
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey)); return new i2p::crypto::ECDSAP256Signer (priv);
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey)); return new i2p::crypto::ECDSAP384Signer (priv);
break; break;
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey)); return new i2p::crypto::ECDSAP521Signer (priv);
break; break;
case SIGNING_KEY_TYPE_RSA_SHA256_2048: case SIGNING_KEY_TYPE_RSA_SHA256_2048:
case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA384_3072:
case SIGNING_KEY_TYPE_RSA_SHA512_4096: case SIGNING_KEY_TYPE_RSA_SHA512_4096:
LogPrint (eLogError, "Identity: RSA signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported"); LogPrint (eLogError, "Identity: RSA signing key type ", (int)keyType, " is not supported");
break; break;
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: return new i2p::crypto::EDDSA25519Signer (priv, nullptr);
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, IsOfflineSignature () ? nullptr: m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH)); // TODO: remove public key check
break; break;
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256:
m_Signer.reset (new i2p::crypto::GOSTR3410_256_Signer (i2p::crypto::eGOSTR3410CryptoProA, m_SigningPrivateKey)); return new i2p::crypto::GOSTR3410_256_Signer (i2p::crypto::eGOSTR3410CryptoProA, priv);
break; break;
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
m_Signer.reset (new i2p::crypto::GOSTR3410_512_Signer (i2p::crypto::eGOSTR3410TC26A512, m_SigningPrivateKey)); return new i2p::crypto::GOSTR3410_512_Signer (i2p::crypto::eGOSTR3410TC26A512, priv);
break;
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
return new i2p::crypto::RedDSA25519Signer (priv);
break; break;
default: default:
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported"); LogPrint (eLogError, "Identity: Signing key type ", (int)keyType, " is not supported");
} }
return nullptr;
} }
size_t PrivateKeys::GetSignatureLen () const size_t PrivateKeys::GetSignatureLen () const
@ -704,7 +719,6 @@ namespace data
LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA");
// no break here // no break here
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub);
break; break;
case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256:
@ -713,6 +727,9 @@ namespace data
case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512:
i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, priv, pub); i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, priv, pub);
break; break;
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub);
break;
default: default:
LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1 i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1

View File

@ -163,6 +163,7 @@ namespace data
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL); static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub); static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub);
static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long
static i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv);
// offline keys // offline keys
PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const; PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const;

View File

@ -1,6 +1,7 @@
#include <string.h> #include <string.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include <openssl/hmac.h> #include <openssl/hmac.h>
#include <zlib.h> // for crc32
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Crypto.h" #include "Crypto.h"
#include "Ed25519.h" #include "Ed25519.h"
@ -253,8 +254,147 @@ namespace data
memcpy (m_Buffer, buf, len); memcpy (m_Buffer, buf, len);
} }
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, SigningKeyType blindedKeyType):
m_BlindedSigType (blindedKeyType)
{
if (!identity) return;
auto len = identity->GetSigningPublicKeyLen ();
m_PublicKey.resize (len);
memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len);
m_SigType = identity->GetSigningKeyType ();
}
BlindedPublicKey::BlindedPublicKey (const std::string& b33)
{
uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40);
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];
size_t offset = 1;
if (flag & 0x01) // two bytes signatures
{
m_SigType = bufbe16toh (addr + offset); offset += 2;
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2;
}
else // one byte sig
{
m_SigType = addr[offset]; offset++;
m_BlindedSigType = addr[offset]; offset++;
}
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType));
if (blindedVerifier)
{
auto len = blindedVerifier->GetPublicKeyLen ();
if (offset + len <= l)
{
m_PublicKey.resize (len);
memcpy (m_PublicKey.data (), addr + offset, len);
}
else
LogPrint (eLogError, "LeaseSet2: public key in b33 address is too short for signature type ", (int)m_SigType);
}
else
LogPrint (eLogError, "LeaseSet2: unknown signature type ", (int)m_SigType, " in b33");
}
std::string BlindedPublicKey::ToB33 () const
{
if (m_PublicKey.size () > 32) return ""; // assume 25519
uint8_t addr[35]; char str[60]; // TODO: define actual length
addr[0] = 0; // flags
addr[1] = m_SigType; // sig type
addr[2] = m_BlindedSigType; // blinded sig type
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ());
uint32_t checksum = crc32 (0, addr + 3, m_PublicKey.size ());
// checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
auto l = ByteStreamToBase32 (addr, m_PublicKey.size () + 3, str, 60);
return std::string (str, str + l);
}
void BlindedPublicKey::GetCredential (uint8_t * credential) const
{
// A = destination's signing public key
// stA = signature type of A, 2 bytes big endian
uint16_t stA = htobe16 (GetSigType ());
// stA1 = signature type of blinded A, 2 bytes big endian
uint16_t stA1 = htobe16 (GetBlindedSigType ());
// credential = H("credential", A || stA || stA1)
H ("credential", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, credential);
}
void BlindedPublicKey::GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const
{
uint8_t credential[32];
GetCredential (credential);
// subcredential = H("subcredential", credential || blindedPublicKey)
H ("subcredential", { {credential, 32}, {blinded, len} }, subcredential);
}
void BlindedPublicKey::GenerateAlpha (const char * date, uint8_t * seed) const
{
uint16_t stA = htobe16 (GetSigType ()), stA1 = htobe16 (GetBlindedSigType ());
uint8_t salt[32];
//seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64)
H ("I2PGenerateAlpha", { {GetPublicKey (), GetPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt);
i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed);
}
void BlindedPublicKey::GetBlindedKey (const char * date, uint8_t * blindedKey) const
{
uint8_t seed[64];
GenerateAlpha (date, seed);
i2p::crypto::GetEd25519 ()->BlindPublicKey (GetPublicKey (), seed, blindedKey);
}
void BlindedPublicKey::BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const
{
uint8_t seed[64];
GenerateAlpha (date, seed);
i2p::crypto::GetEd25519 ()->BlindPrivateKey (priv, seed, blindedPriv, blindedPub);
}
void BlindedPublicKey::H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, p.c_str (), p.length ());
for (const auto& it: bufs)
SHA256_Update (&ctx, it.first, it.second);
SHA256_Final (hash, &ctx);
}
i2p::data::IdentHash BlindedPublicKey::GetStoreHash (const char * date) const
{
i2p::data::IdentHash hash;
if (m_BlindedSigType == i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 ||
m_BlindedSigType == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
{
uint8_t blinded[32];
if (date)
GetBlindedKey (date, blinded);
else
{
char currentDate[9];
i2p::util::GetCurrentDate (currentDate);
GetBlindedKey (currentDate, blinded);
}
auto stA1 = htobe16 (m_BlindedSigType);
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, (const uint8_t *)&stA1, 2);
SHA256_Update (&ctx, blinded, 32);
SHA256_Final ((uint8_t *)hash, &ctx);
}
else
LogPrint (eLogError, "LeaseSet2: blinded key type ", (int)m_BlindedSigType, " is not supported");
return hash;
}
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases): LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases):
LeaseSet (storeLeases), m_StoreType (storeType) LeaseSet (storeLeases), m_StoreType (storeType), m_OrigStoreType (storeType)
{ {
SetBuffer (buf, len); SetBuffer (buf, len);
if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
@ -263,10 +403,10 @@ namespace data
ReadFromBuffer (buf, len); ReadFromBuffer (buf, len);
} }
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const IdentityEx> identity): LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key):
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_OrigStoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{ {
ReadFromBufferEncrypted (buf, len, identity); ReadFromBufferEncrypted (buf, len, key);
} }
void LeaseSet2::Update (const uint8_t * buf, size_t len, bool verifySignature) void LeaseSet2::Update (const uint8_t * buf, size_t len, bool verifySignature)
@ -423,7 +563,7 @@ namespace data
return offset; return offset;
} }
void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const IdentityEx> identity) void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key)
{ {
size_t offset = 0; size_t offset = 0;
// blinded key // blinded key
@ -463,33 +603,26 @@ namespace data
VerifySignature (blindedVerifier, buf, len, offset); VerifySignature (blindedVerifier, buf, len, offset);
SetIsValid (verified); SetIsValid (verified);
// handle ciphertext // handle ciphertext
if (verified && identity && lenOuterCiphertext >= 32) if (verified && key && lenOuterCiphertext >= 32)
{ {
SetIsValid (false); // we must verify it again in Layer 2 SetIsValid (false); // we must verify it again in Layer 2
if (blindedKeyType == i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519) if (blindedKeyType == i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519)
{ {
// verify blinding // verify blinding
char date[9]; char date[9];
i2p::util::GetCurrentDate (date); i2p::util::GetDateString (m_PublishedTimestamp, date);
uint8_t blinded[32]; uint8_t blinded[32];
BlindPublicKey (identity, date, blindedKeyType, blinded); key->GetBlindedKey (date, blinded);
if (memcmp (blindedPublicKey, blinded, 32)) if (memcmp (blindedPublicKey, blinded, 32))
{ {
LogPrint (eLogError, "LeaseSet2: blinded public key doesn't match"); LogPrint (eLogError, "LeaseSet2: blinded public key doesn't match");
return; return;
} }
} }
// credentials
uint8_t credential[32], subcredential[36];
// A = destination's signing public key
// stA = signature type of A, 2 bytes big endian
uint16_t stA = htobe16 (identity->GetSigningKeyType ());
// credential = H("credential", A || stA || stA1)
H ("credential", { {identity->GetSigningPublicKeyBuffer (), identity->GetSigningPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {stA1, 2} }, credential);
// subcredential = H("subcredential", credential || blindedPublicKey)
H ("subcredential", { {credential, 32}, {blindedPublicKey, blindedKeyLen} }, subcredential);
// outer key // outer key
// outerInput = subcredential || publishedTimestamp // outerInput = subcredential || publishedTimestamp
uint8_t subcredential[36];
key->GetSubcredential (blindedPublicKey, blindedKeyLen, subcredential);
memcpy (subcredential + 32, publishedTimestamp, 4); memcpy (subcredential + 32, publishedTimestamp, 4);
// outerSalt = outerCiphertext[0:32] // outerSalt = outerCiphertext[0:32]
// keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44) // keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
@ -502,6 +635,7 @@ namespace data
std::vector<uint8_t> outerPlainText (lenOuterPlaintext); std::vector<uint8_t> outerPlainText (lenOuterPlaintext);
i2p::crypto::ChaCha20 (outerCiphertext + 32, lenOuterPlaintext, keys, keys + 32, outerPlainText.data ()); i2p::crypto::ChaCha20 (outerCiphertext + 32, lenOuterPlaintext, keys, keys + 32, outerPlainText.data ());
// inner key // inner key
// innerInput = authCookie || subcredential || publishedTimestamp, TODO: non-empty authCookie
// innerSalt = innerCiphertext[0:32] // innerSalt = innerCiphertext[0:32]
// keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44) // keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44)
// skip 1 byte flags // skip 1 byte flags
@ -521,50 +655,10 @@ namespace data
ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1); ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1);
} }
else else
LogPrint (eLogError, "LeaseSet2: unxpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet"); LogPrint (eLogError, "LeaseSet2: unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet");
} }
} }
void LeaseSet2::H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, p.c_str (), p.length ());
for (const auto& it: bufs)
SHA256_Update (&ctx, it.first, it.second);
SHA256_Final (hash, &ctx);
}
void LeaseSet2::BlindPublicKey (std::shared_ptr<const IdentityEx> identity, const char * date, SigningKeyType blindedKeyType, uint8_t * blindedKey)
{
uint16_t stA = htobe16 (identity->GetSigningKeyType ()), stA1 = htobe16 (blindedKeyType);
uint8_t salt[32], seed[64];
//seed = HKDF(H("I2PGenerateAlpha", keydata), datestring || secret, "i2pblinding1", 64)
H ("I2PGenerateAlpha", { {identity->GetSigningPublicKeyBuffer (), identity->GetSigningPublicKeyLen ()}, {(const uint8_t *)&stA, 2}, {(const uint8_t *)&stA1, 2} }, salt);
i2p::crypto::HKDF (salt, (const uint8_t *)date, 8, "i2pblinding1", seed);
i2p::crypto::GetEd25519 ()->BlindPublicKey (identity->GetSigningPublicKeyBuffer (), seed, blindedKey);
}
void LeaseSet2::CalculateStoreHash (std::shared_ptr<const IdentityEx> identity, SigningKeyType blindedKeyType, i2p::data::IdentHash& hash)
{
if (blindedKeyType != i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 &&
blindedKeyType != SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
{
LogPrint (eLogError, "LeaseSet2: blinded key type ", (int)blindedKeyType, " is not supported");
return;
}
char date[9];
i2p::util::GetCurrentDate (date);
uint8_t blinded[32];
BlindPublicKey (identity, date, blindedKeyType, blinded);
auto stA1 = htobe16 (blindedKeyType);
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, (const uint8_t *)&stA1, 2);
SHA256_Update (&ctx, blinded, 32);
SHA256_Final ((uint8_t *)hash, &ctx);
}
void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const
{ {
auto encryptor = m_Encryptor; // TODO: atomic auto encryptor = m_Encryptor; // TODO: atomic
@ -763,5 +857,77 @@ namespace data
memcpy (m_Buffer + 1, buf, len); memcpy (m_Buffer + 1, buf, len);
m_Buffer[0] = storeType; m_Buffer[0] = storeType;
} }
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType):
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*/;
m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
BlindedPublicKey blindedKey (ls->GetIdentity ());
auto timestamp = i2p::util::GetSecondsSinceEpoch ();
char date[9];
i2p::util::GetDateString (timestamp, date);
uint8_t blindedPriv[32], blindedPub[32];
blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKeyType, blindedPriv));
auto offset = 1;
htobe16buf (m_Buffer + offset, blindedKeyType); offset += 2; // Blinded Public Key Sig Type
memcpy (m_Buffer + offset, blindedPub, 32); offset += 32; // Blinded Public Key
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds
auto expirationTime = ls->GetExpirationTime ()/1000LL;
if (expirationTime > nextMidnight) expirationTime = nextMidnight;
SetExpirationTime (expirationTime*1000LL);
htobe16buf (m_Buffer + offset, expirationTime > timestamp ? expirationTime - timestamp : 0); offset += 2; // expires
uint16_t flags = 0;
htobe16buf (m_Buffer + offset, flags); offset += 2; // flags
htobe16buf (m_Buffer + offset, lenOuterCiphertext); offset += 2; // lenOuterCiphertext
// outerChipherText
// Layer 1
uint8_t subcredential[36];
blindedKey.GetSubcredential (blindedPub, 32, subcredential);
htobe32buf (subcredential + 32, timestamp); // outerInput = subcredential || publishedTimestamp
// keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44)
uint8_t keys1[64]; // 44 bytes actual data
RAND_bytes (m_Buffer + offset, 32); // outerSalt = CSRNG(32)
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
// 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);
offset += 32; // innerSalt
m_Buffer[offset] = ls->GetStoreType ();
memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ());
i2p::crypto::ChaCha20 (m_Buffer + offset, lenInnerPlaintext, keys2, keys2 + 32, m_Buffer + offset); // encrypt Layer 2
offset += lenInnerPlaintext;
i2p::crypto::ChaCha20 (outerPlainText, lenOuterPlaintext, keys1, keys1 + 32, outerPlainText); // encrypt Layer 1
// signature
blindedSigner->Sign (m_Buffer, offset, m_Buffer + offset);
// store hash
m_StoreHash = blindedKey.GetStoreHash (date);
}
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len):
LocalLeaseSet2 (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, identity, buf, len)
{
// fill inner LeaseSet2
auto blindedKey = std::make_shared<BlindedPublicKey>(identity);
i2p::data::LeaseSet2 ls (buf, len, blindedKey); // inner layer
if (ls.IsValid ())
{
m_InnerLeaseSet = std::make_shared<LocalLeaseSet2>(ls.GetStoreType (), identity, ls.GetBuffer (), ls.GetBufferLen ());
m_StoreHash = blindedKey->GetStoreHash ();
}
else
LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer");
}
} }
} }

View File

@ -26,7 +26,7 @@ namespace data
IdentHash tunnelGateway; IdentHash tunnelGateway;
uint32_t tunnelID; uint32_t tunnelID;
uint64_t endDate; // 0 means invalid uint64_t endDate; // 0 means invalid
bool isUpdated; // trasient bool isUpdated; // transient
/* return true if this lease expires within t millisecond + fudge factor */ /* return true if this lease expires within t millisecond + fudge factor */
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const { bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const {
auto expire = i2p::util::GetMillisecondsSinceEpoch (); auto expire = i2p::util::GetMillisecondsSinceEpoch ();
@ -78,6 +78,7 @@ namespace data
bool operator== (const LeaseSet& other) const bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); }; { 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 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 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 std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
@ -128,26 +129,55 @@ namespace data
const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001; const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001;
class BlindedPublicKey // for encrypted LS2
{
public:
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519);
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
std::string ToB33 () const;
const uint8_t * GetPublicKey () const { return m_PublicKey.data (); };
size_t GetPublicKeyLen () const { return m_PublicKey.size (); };
SigningKeyType GetSigType () const { return m_SigType; };
SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; };
void GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const; // 32 bytes
void GetBlindedKey (const char * date, uint8_t * blindedKey) const; // blinded key 32 bytes, date is 8 chars "YYYYMMDD"
void BlindPrivateKey (const uint8_t * priv, const char * date, uint8_t * blindedPriv, uint8_t * blindedPub) const; // blinded key 32 bytes, date is 8 chars "YYYYMMDD"
i2p::data::IdentHash GetStoreHash (const char * date = nullptr) const; // date is 8 chars "YYYYMMDD", use current if null
private:
void GetCredential (uint8_t * credential) const; // 32 bytes
void GenerateAlpha (const char * date, uint8_t * seed) const; // 64 bytes, date is 8 chars "YYYYMMDD"
void H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash) const;
private:
std::vector<uint8_t> m_PublicKey;
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
};
class LeaseSet2: public LeaseSet class LeaseSet2: public LeaseSet
{ {
public: public:
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true); 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 IdentityEx> identity); // store type 5, called from local netdb only LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key); // store type 5, called from local netdb only
uint8_t GetStoreType () const { return m_StoreType; }; uint8_t GetStoreType () const { return m_StoreType; };
uint8_t GetOrigStoreType () const { return m_OrigStoreType; };
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; }; uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; }; std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; };
void Update (const uint8_t * buf, size_t len, bool verifySignature); void Update (const uint8_t * buf, size_t len, bool verifySignature);
static void CalculateStoreHash (std::shared_ptr<const IdentityEx> identity, SigningKeyType blindedKeyType, i2p::data::IdentHash& hash);
// implements RoutingDestination // implements RoutingDestination
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
private: private:
void ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity = true, bool verifySignature = true); void ReadFromBuffer (const uint8_t * buf, size_t len, bool readIdentity = true, bool verifySignature = true);
void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const IdentityEx> identity); void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key);
size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len); size_t ReadStandardLS2TypeSpecificPart (const uint8_t * buf, size_t len);
size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len); size_t ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len);
@ -156,13 +186,9 @@ namespace data
uint64_t ExtractTimestamp (const uint8_t * buf, size_t len) const; uint64_t ExtractTimestamp (const uint8_t * buf, size_t len) const;
// for encrypted LS
static void H (const std::string& p, const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * hash);
static void BlindPublicKey (std::shared_ptr<const IdentityEx> identity, const char * date, SigningKeyType blindedKeyType, uint8_t * blindedKey); // blinded key 32 bytes, date is 8 chars "YYYYMMDD"
private: private:
uint8_t m_StoreType; uint8_t m_StoreType, m_OrigStoreType;
uint32_t m_PublishedTimestamp = 0; uint32_t m_PublishedTimestamp = 0;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier; std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2 std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
@ -204,6 +230,7 @@ namespace data
uint8_t * GetLeases () { return m_Leases; }; uint8_t * GetLeases () { return m_Leases; };
const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); }; const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); };
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
bool IsExpired () const; bool IsExpired () const;
uint64_t GetExpirationTime () const { return m_ExpirationTime; }; uint64_t GetExpirationTime () const { return m_ExpirationTime; };
void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; }; void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; };
@ -211,6 +238,8 @@ namespace data
{ return GetBufferLen () == other.GetBufferLen () && !memcmp (GetBuffer (), other.GetBuffer (), GetBufferLen ()); }; { return GetBufferLen () == other.GetBufferLen () && !memcmp (GetBuffer (), other.GetBuffer (), GetBufferLen ()); };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual const IdentHash& GetStoreHash () const { return GetIdentHash (); }; // differ from ident hash for encrypted LeaseSet2
virtual std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return nullptr; }; // non-null for encrypted LeaseSet2
private: private:
@ -227,7 +256,8 @@ namespace data
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, 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);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; }; virtual ~LocalLeaseSet2 () { delete[] m_Buffer; };
uint8_t * GetBuffer () const { return m_Buffer + 1; }; uint8_t * GetBuffer () const { return m_Buffer + 1; };
@ -235,11 +265,32 @@ namespace data
uint8_t GetStoreType () const { return m_Buffer[0]; }; uint8_t GetStoreType () const { return m_Buffer[0]; };
private: protected:
LocalLeaseSet2 (std::shared_ptr<const IdentityEx> identity): LocalLeaseSet (identity, nullptr, 0), m_Buffer (nullptr), m_BufferLen(0) {}; // called from LocalEncryptedLeaseSet2
protected:
uint8_t * m_Buffer; // 1 byte store type + actual buffer uint8_t * m_Buffer; // 1 byte store type + actual buffer
size_t m_BufferLen; size_t m_BufferLen;
}; };
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 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:
IdentHash m_StoreHash;
std::shared_ptr<const LocalLeaseSet2> m_InnerLeaseSet;
};
} }
} }

View File

@ -690,7 +690,7 @@ namespace transport
} }
if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32)) if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32))
{ {
LogPrint (eLogError, "NTCP2: Static key mistmatch in SessionConfirmed"); LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed");
SendTerminationAndTerminate (eNTCP2IncorrectSParameter); SendTerminationAndTerminate (eNTCP2IncorrectSParameter);
return; return;
} }
@ -783,7 +783,7 @@ namespace transport
size_t moreBytes = m_Socket.available(ec); size_t moreBytes = m_Socket.available(ec);
if (!ec && moreBytes >= m_NextReceivedLen) if (!ec && moreBytes >= m_NextReceivedLen)
{ {
// read and process messsage immediately if avaliable // read and process message immediately if available
moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec); moreBytes = boost::asio::read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), ec);
HandleReceived (ec, moreBytes); HandleReceived (ec, moreBytes);
} }
@ -887,7 +887,7 @@ namespace transport
Terminate (); Terminate ();
} }
else else
LogPrint (eLogWarning, "NTCP2: Unexpected temination block size ", size); LogPrint (eLogWarning, "NTCP2: Unexpected termination block size ", size);
break; break;
case eNTCP2BlkPadding: case eNTCP2BlkPadding:
LogPrint (eLogDebug, "NTCP2: padding"); LogPrint (eLogDebug, "NTCP2: padding");

View File

@ -1287,7 +1287,7 @@ namespace transport
if (it.second->IsTerminationTimeoutExpired (ts)) if (it.second->IsTerminationTimeoutExpired (ts))
{ {
auto session = it.second; auto session = it.second;
// Termniate modifies m_NTCPSession, so we postpone it // Terminate modifies m_NTCPSession, so we postpone it
m_Service.post ([session] { m_Service.post ([session] {
LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds"); LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds");
session->Terminate (); session->Terminate ();

View File

@ -376,7 +376,7 @@ namespace data
} }
m_FloodfillBootstrap = ri; m_FloodfillBootstrap = ri;
ReseedFromFloodfill(*ri); ReseedFromFloodfill(*ri);
// don't try reseed servers if trying to boostrap from floodfill // don't try reseed servers if trying to bootstrap from floodfill
return; return;
} }
} }
@ -1243,7 +1243,7 @@ namespace data
{ {
if (!it->second->IsValid () || ts > it->second->GetExpirationTime () - LEASE_ENDDATE_THRESHOLD) if (!it->second->IsValid () || ts > it->second->GetExpirationTime () - LEASE_ENDDATE_THRESHOLD)
{ {
LogPrint (eLogInfo, "NetDb: LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired or invalid"); LogPrint (eLogInfo, "NetDb: LeaseSet ", it->first.ToBase64 (), " expired or invalid");
it = m_LeaseSets.erase (it); it = m_LeaseSets.erase (it);
} }
else else

View File

@ -79,7 +79,7 @@ namespace i2p
} }
if (ipv6) if (ipv6)
{ {
std::string host = "::"; std::string host = "::1";
if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only
i2p::config::GetOption("host", host); i2p::config::GetOption("host", host);
else if (!ifname.empty()) else if (!ifname.empty())
@ -106,8 +106,13 @@ namespace i2p
{ {
if (!m_NTCP2Keys) NewNTCP2Keys (); if (!m_NTCP2Keys) NewNTCP2Keys ();
UpdateNTCP2Address (true); UpdateNTCP2Address (true);
if (!ntcp) // NTCP2 should replace NTCP
{
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
PublishNTCP2Address (port, true);
}
} }
} }
void RouterContext::UpdateRouterInfo () void RouterContext::UpdateRouterInfo ()
@ -338,6 +343,51 @@ namespace i2p
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable; return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
} }
void RouterContext::PublishNTCPAddress (bool publish, bool v4only)
{
auto& addresses = m_RouterInfo.GetAddresses ();
if (publish)
{
for (const auto& addr : addresses) // v4
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
if (!v4only)
{
for (const auto& addr : addresses) // v6
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v6 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
}
}
else
{
for (auto it = addresses.begin (); it != addresses.end ();)
{
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () &&
(!v4only || (*it)->host.is_v4 ()))
{
it = addresses.erase (it);
if (v4only) break; // otherwise might be more than one address
}
else
++it;
}
}
}
void RouterContext::SetUnreachable () void RouterContext::SetUnreachable ()
{ {
// set caps // set caps
@ -347,22 +397,13 @@ namespace i2p
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
m_RouterInfo.SetCaps (caps); m_RouterInfo.SetCaps (caps);
// remove NTCP address // remove NTCP v4 address
auto& addresses = m_RouterInfo.GetAddresses (); PublishNTCPAddress (false);
for (auto it = addresses.begin (); it != addresses.end (); ++it)
{
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () &&
(*it)->host.is_v4 ())
{
addresses.erase (it);
break;
}
}
// delete previous introducers // delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses) for (auto& addr : addresses)
if (addr->ssu) if (addr->ssu)
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
// update // update
UpdateRouterInfo (); UpdateRouterInfo ();
} }
@ -377,27 +418,15 @@ namespace i2p
if (m_IsFloodfill) if (m_IsFloodfill)
caps |= i2p::data::RouterInfo::eFloodfill; caps |= i2p::data::RouterInfo::eFloodfill;
m_RouterInfo.SetCaps (caps); m_RouterInfo.SetCaps (caps);
auto& addresses = m_RouterInfo.GetAddresses ();
// insert NTCP back // insert NTCP back
bool ntcp; i2p::config::GetOption("ntcp", ntcp); bool ntcp; i2p::config::GetOption("ntcp", ntcp);
if (ntcp) { if (ntcp)
for (const auto& addr : addresses) PublishNTCPAddress (true);
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
}
// delete previous introducers // delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses) for (auto& addr : addresses)
if (addr->ssu) if (addr->ssu)
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
// update // update
UpdateRouterInfo (); UpdateRouterInfo ();
} }

View File

@ -81,6 +81,7 @@ namespace i2p
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
void PublishNTCP2Address (int port, bool publish = true); void PublishNTCP2Address (int port, bool publish = true);
void UpdateNTCP2Address (bool enable); void UpdateNTCP2Address (bool enable);
void PublishNTCPAddress (bool publish, bool v4only = true);
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const; bool IsUnreachable () const;

View File

@ -51,7 +51,7 @@ namespace data
void RouterInfo::Update (const uint8_t * buf, int len) void RouterInfo::Update (const uint8_t * buf, int len)
{ {
// verify signature since we have indentity already // verify signature since we have identity already
int l = len - m_RouterIdentity->GetSignatureLen (); int l = len - m_RouterIdentity->GetSignatureLen ();
if (m_RouterIdentity->Verify (buf, l, buf + l)) if (m_RouterIdentity->Verify (buf, l, buf + l))
{ {
@ -883,7 +883,7 @@ namespace data
template<typename Filter> template<typename Filter>
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
{ {
// TODO: make it more gereric using comparator // TODO: make it more generic using comparator
#if (BOOST_VERSION >= 105300) #if (BOOST_VERSION >= 105300)
auto addresses = boost::atomic_load (&m_Addresses); auto addresses = boost::atomic_load (&m_Addresses);
#else #else

View File

@ -328,7 +328,11 @@ namespace transport
{ {
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
{ {
if (session) session->FlushData (); if (session)
{
session->FlushData ();
session = nullptr;
}
auto it = sessions->find (packet->from); auto it = sessions->find (packet->from);
if (it != sessions->end ()) if (it != sessions->end ())
session = it->second; session = it->second;
@ -750,6 +754,8 @@ namespace transport
if (it.second->IsTerminationTimeoutExpired (ts)) if (it.second->IsTerminationTimeoutExpired (ts))
{ {
auto session = it.second; auto session = it.second;
if (it.first != session->GetRemoteEndpoint ())
LogPrint (eLogWarning, "SSU: remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted");
m_Service.post ([session] m_Service.post ([session]
{ {
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
@ -776,6 +782,8 @@ namespace transport
if (it.second->IsTerminationTimeoutExpired (ts)) if (it.second->IsTerminationTimeoutExpired (ts))
{ {
auto session = it.second; auto session = it.second;
if (it.first != session->GetRemoteEndpoint ())
LogPrint (eLogWarning, "SSU: remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first);
m_ServiceV6.post ([session] m_ServiceV6.post ([session]
{ {
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");

View File

@ -158,7 +158,7 @@ namespace transport
ProcessData (buf + headerSize, len - headerSize); ProcessData (buf + headerSize, len - headerSize);
break; break;
case PAYLOAD_TYPE_SESSION_REQUEST: case PAYLOAD_TYPE_SESSION_REQUEST:
ProcessSessionRequest (buf, len, senderEndpoint); // buf with header ProcessSessionRequest (buf, len); // buf with header
break; break;
case PAYLOAD_TYPE_SESSION_CREATED: case PAYLOAD_TYPE_SESSION_CREATED:
ProcessSessionCreated (buf, len); // buf with header ProcessSessionCreated (buf, len); // buf with header
@ -194,7 +194,7 @@ namespace transport
} }
} }
void SSUSession::ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) void SSUSession::ProcessSessionRequest (const uint8_t * buf, size_t len)
{ {
LogPrint (eLogDebug, "SSU message: session request"); LogPrint (eLogDebug, "SSU message: session request");
bool sendRelayTag = true; bool sendRelayTag = true;
@ -212,10 +212,9 @@ namespace transport
} }
if (headerSize >= len) if (headerSize >= len)
{ {
LogPrint (eLogError, "Session reaquest header size ", headerSize, " exceeds packet length ", len); LogPrint (eLogError, "Session request header size ", headerSize, " exceeds packet length ", len);
return; return;
} }
m_RemoteEndpoint = senderEndpoint;
if (!m_DHKeysPair) if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair (); m_DHKeysPair = transports.GetNextDHKeysPair ();
CreateAESandMacKey (buf + headerSize); CreateAESandMacKey (buf + headerSize);
@ -1097,7 +1096,7 @@ namespace transport
// intro key // intro key
if (toAddress) if (toAddress)
{ {
// send our intro key to address instead it's own // send our intro key to address instead of its own
auto addr = i2p::context.GetRouterInfo ().GetSSUAddress (); auto addr = i2p::context.GetRouterInfo ().GetSSUAddress ();
if (addr) if (addr)
memcpy (payload, addr->ssu->key, 32); // intro key memcpy (payload, addr->ssu->key, 32); // intro key

View File

@ -80,7 +80,8 @@ namespace transport
void Close (); void Close ();
void Done (); void Done ();
void Failed (); void Failed ();
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs); void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void SendPeerTest (); // Alice void SendPeerTest (); // Alice
@ -103,7 +104,7 @@ namespace transport
size_t GetSSUHeaderSize (const uint8_t * buf) const; size_t GetSSUHeaderSize (const uint8_t * buf) const;
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs); void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
void ProcessSessionRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void ProcessSessionRequest (const uint8_t * buf, size_t len);
void SendSessionRequest (); void SendSessionRequest ();
void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce); void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce);
void ProcessSessionCreated (uint8_t * buf, size_t len); void ProcessSessionCreated (uint8_t * buf, size_t len);
@ -139,7 +140,7 @@ namespace transport
friend class SSUData; // TODO: change in later friend class SSUData; // TODO: change in later
SSUServer& m_Server; SSUServer& m_Server;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; const boost::asio::ip::udp::endpoint m_RemoteEndpoint;
boost::asio::deadline_timer m_ConnectTimer; boost::asio::deadline_timer m_ConnectTimer;
bool m_IsPeerTest; bool m_IsPeerTest;
SessionState m_State; SessionState m_State;

View File

@ -7,7 +7,8 @@ namespace i2p
namespace crypto namespace crypto
{ {
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EDDSA25519Verifier::EDDSA25519Verifier () EDDSA25519Verifier::EDDSA25519Verifier ():
m_Pkey (nullptr)
{ {
m_MDCtx = EVP_MD_CTX_create (); m_MDCtx = EVP_MD_CTX_create ();
} }

View File

@ -487,6 +487,42 @@ namespace crypto
typedef GOSTR3410Signer<GOSTR3411_256_Hash> GOSTR3410_256_Signer; typedef GOSTR3410Signer<GOSTR3411_256_Hash> GOSTR3410_256_Signer;
typedef GOSTR3410Verifier<GOSTR3411_512_Hash> GOSTR3410_512_Verifier; typedef GOSTR3410Verifier<GOSTR3411_512_Hash> GOSTR3410_512_Verifier;
typedef GOSTR3410Signer<GOSTR3411_512_Hash> GOSTR3410_512_Signer; typedef GOSTR3410Signer<GOSTR3411_512_Hash> GOSTR3410_512_Signer;
// RedDSA
typedef EDDSA25519Verifier RedDSA25519Verifier;
class RedDSA25519Signer: public Signer
{
public:
RedDSA25519Signer (const uint8_t * signingPrivateKey)
{
memcpy (m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH);
BN_CTX * ctx = BN_CTX_new ();
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_PrivateKey, ctx);
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
BN_CTX_free (ctx);
}
~RedDSA25519Signer () {};
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature);
}
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation
private:
uint8_t m_PrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH];
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
};
inline void CreateRedDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
{
GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey);
RedDSA25519Signer signer (signingPrivateKey);
memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH);
}
} }
} }

View File

@ -290,7 +290,7 @@ namespace stream
LogPrint (eLogInfo, "Streaming: offline signature without identity"); LogPrint (eLogInfo, "Streaming: offline signature without identity");
return false; return false;
} }
// if we have it in LeaseSet already we don't neet parse it again // if we have it in LeaseSet already we don't need to parse it again
if (m_RemoteLeaseSet) m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); if (m_RemoteLeaseSet) m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier ();
if (m_TransientVerifier) if (m_TransientVerifier)
{ {
@ -602,7 +602,7 @@ namespace stream
size++; // NACK count size++; // NACK count
} }
size++; // resend delay size++; // resend delay
htobuf16 (packet + size, 0); // nof flags set htobuf16 (packet + size, 0); // no flags set
size += 2; // flags size += 2; // flags
htobuf16 (packet + size, 0); // no options htobuf16 (packet + size, 0); // no options
size += 2; // options size size += 2; // options size
@ -917,7 +917,12 @@ namespace stream
if (leases.empty ()) if (leases.empty ())
{ {
expired = false; expired = false;
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to request // time to request
if (m_RemoteLeaseSet->GetOrigStoreType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
else
m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ());
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
} }
if (!leases.empty ()) if (!leases.empty ())

View File

@ -182,7 +182,13 @@ namespace util
void GetCurrentDate (char * date) void GetCurrentDate (char * date)
{ {
time_t t = time (nullptr); GetDateString (GetSecondsSinceEpoch (), date);
}
void GetDateString (uint64_t timestamp, char * date)
{
using clock = std::chrono::system_clock;
auto t = clock::to_time_t (clock::time_point (std::chrono::seconds(timestamp)));
struct tm tm; struct tm tm;
#ifdef _WIN32 #ifdef _WIN32
gmtime_s(&tm, &t); gmtime_s(&tm, &t);

View File

@ -16,6 +16,7 @@ namespace util
uint64_t GetSecondsSinceEpoch (); uint64_t GetSecondsSinceEpoch ();
void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes
void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes
class NTPTimeSync class NTPTimeSync
{ {

View File

@ -295,6 +295,9 @@ namespace net
{ {
#ifdef WIN32 #ifdef WIN32
LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32"); LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32");
if(ipv6)
return boost::asio::ip::address::from_string("::1");
else
return boost::asio::ip::address::from_string("127.0.0.1"); return boost::asio::ip::address::from_string("127.0.0.1");
#else #else
int af = (ipv6 ? AF_INET6 : AF_INET); int af = (ipv6 ? AF_INET6 : AF_INET);
@ -327,7 +330,7 @@ namespace net
std::string fallback; std::string fallback;
if(ipv6) if(ipv6)
{ {
fallback = "::"; fallback = "::1";
LogPrint(eLogWarning, "NetIface: cannot find ipv6 address for interface ", ifname); LogPrint(eLogWarning, "NetIface: cannot find ipv6 address for interface ", ifname);
} else { } else {
fallback = "127.0.0.1"; fallback = "127.0.0.1";

View File

@ -7,7 +7,7 @@
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 24 #define I2PD_VERSION_MINOR 25
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #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_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 39 #define I2P_VERSION_MICRO 40
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -40,9 +40,9 @@ namespace client
void RemoveAddress (const i2p::data::IdentHash& ident); void RemoveAddress (const i2p::data::IdentHash& ident);
bool Init (); bool Init ();
int Load (std::map<std::string, i2p::data::IdentHash>& addresses); int Load (std::map<std::string, std::shared_ptr<Address> > & addresses);
int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses); int LoadLocal (std::map<std::string, std::shared_ptr<Address> >& addresses);
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses); int Save (const std::map<std::string, std::shared_ptr<Address> >& addresses);
void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified); void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified);
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified); bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified);
@ -50,7 +50,7 @@ namespace client
private: private:
int LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses); // returns -1 if can't open file, otherwise number of records int LoadFromFile (const std::string& filename, std::map<std::string, std::shared_ptr<Address> >& addresses); // returns -1 if can't open file, otherwise number of records
private: private:
@ -79,7 +79,7 @@ namespace client
{ {
if (!m_IsPersist) if (!m_IsPersist)
{ {
LogPrint(eLogDebug, "Addressbook: Persistance is disabled"); LogPrint(eLogDebug, "Addressbook: Persistence is disabled");
return nullptr; return nullptr;
} }
std::string filename = storage.Path(ident.ToBase32()); std::string filename = storage.Path(ident.ToBase32());
@ -125,7 +125,7 @@ namespace client
storage.Remove( ident.ToBase32() ); storage.Remove( ident.ToBase32() );
} }
int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map<std::string, i2p::data::IdentHash>& addresses) int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map<std::string, std::shared_ptr<Address> >& addresses)
{ {
int num = 0; int num = 0;
std::ifstream f (filename, std::ifstream::in); // in text mode std::ifstream f (filename, std::ifstream::in); // in text mode
@ -144,16 +144,14 @@ namespace client
std::string name = s.substr(0, pos++); std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos); std::string addr = s.substr(pos);
i2p::data::IdentHash ident; addresses[name] = std::make_shared<Address>(addr);
ident.FromBase32 (addr);
addresses[name] = ident;
num++; num++;
} }
} }
return num; return num;
} }
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses) int AddressBookFilesystemStorage::Load (std::map<std::string, std::shared_ptr<Address> >& addresses)
{ {
int num = LoadFromFile (indexPath, addresses); int num = LoadFromFile (indexPath, addresses);
if (num < 0) if (num < 0)
@ -167,7 +165,7 @@ namespace client
return num; return num;
} }
int AddressBookFilesystemStorage::LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses) int AddressBookFilesystemStorage::LoadLocal (std::map<std::string, std::shared_ptr<Address> >& addresses)
{ {
int num = LoadFromFile (localPath, addresses); int num = LoadFromFile (localPath, addresses);
if (num < 0) return 0; if (num < 0) return 0;
@ -175,7 +173,7 @@ namespace client
return num; return num;
} }
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses) int AddressBookFilesystemStorage::Save (const std::map<std::string, std::shared_ptr<Address> >& addresses)
{ {
if (addresses.empty()) { if (addresses.empty()) {
LogPrint(eLogWarning, "Addressbook: not saving empty addressbook"); LogPrint(eLogWarning, "Addressbook: not saving empty addressbook");
@ -190,8 +188,14 @@ namespace client
return 0; return 0;
} }
for (const auto& it: addresses) { for (const auto& it: addresses)
f << it.first << "," << it.second.ToBase32 () << std::endl; {
f << it.first << ",";
if (it.second->IsIdentHash ())
f << it.second->identHash.ToBase32 ();
else
f << it.second->blindedPublicKey->ToB33 ();
f << std::endl;
num++; num++;
} }
LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved");
@ -232,6 +236,27 @@ namespace client
} }
//--------------------------------------------------------------------- //---------------------------------------------------------------------
Address::Address (const std::string& b32)
{
if (b32.length () <= B33_ADDRESS_THRESHOLD)
{
addressType = eAddressIndentHash;
identHash.FromBase32 (b32);
}
else
{
addressType = eAddressBlindedPublicKey;
blindedPublicKey = std::make_shared<i2p::data::BlindedPublicKey>(b32);
}
}
Address::Address (const i2p::data::IdentHash& hash)
{
addressType = eAddressIndentHash;
identHash = hash;
}
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false), AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
{ {
@ -268,7 +293,7 @@ namespace client
} }
if (m_IsDownloading) if (m_IsDownloading)
{ {
LogPrint (eLogInfo, "Addressbook: subscriptions is downloading, abort"); LogPrint (eLogInfo, "Addressbook: subscriptions are downloading, abort");
for (int i = 0; i < 30; i++) for (int i = 0; i < 30; i++)
{ {
if (!m_IsDownloading) if (!m_IsDownloading)
@ -291,67 +316,70 @@ namespace client
m_Subscriptions.clear (); m_Subscriptions.clear ();
} }
bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident) std::shared_ptr<const Address> AddressBook::GetAddress (const std::string& address)
{ {
auto pos = address.find(".b32.i2p"); auto pos = address.find(".b32.i2p");
if (pos != std::string::npos) if (pos != std::string::npos)
{ return std::make_shared<const Address>(address.substr (0, pos));
Base32ToByteStream (address.c_str(), pos, ident, 32);
return true;
}
else else
{ {
pos = address.find (".i2p"); pos = address.find (".i2p");
if (pos != std::string::npos) if (pos != std::string::npos)
{ {
auto identHash = FindAddress (address); auto addr = FindAddress (address);
if (identHash) if (!addr)
{
ident = *identHash;
return true;
}
else
{
LookupAddress (address); // TODO: LookupAddress (address); // TODO:
return false; return addr;
}
} }
} }
// if not .b32 we assume full base64 address // if not .b32 we assume full base64 address
i2p::data::IdentityEx dest; i2p::data::IdentityEx dest;
if (!dest.FromBase64 (address)) if (!dest.FromBase64 (address))
return false; return nullptr;
ident = dest.GetIdentHash (); return std::make_shared<const Address>(dest.GetIdentHash ());
return true;
} }
const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address) std::shared_ptr<const Address> AddressBook::FindAddress (const std::string& address)
{ {
auto it = m_Addresses.find (address); auto it = m_Addresses.find (address);
if (it != m_Addresses.end ()) if (it != m_Addresses.end ())
return &it->second; return it->second;
return nullptr; return nullptr;
} }
void AddressBook::InsertAddress (const std::string& address, const std::string& base64) void AddressBook::InsertAddress (const std::string& address, const std::string& jump)
{ {
auto pos = jump.find(".b32.i2p");
if (pos != std::string::npos)
{
m_Addresses[address] = std::make_shared<Address>(jump.substr (0, pos));
LogPrint (eLogInfo, "Addressbook: added ", address," -> ", jump);
}
else
{
// assume base64
auto ident = std::make_shared<i2p::data::IdentityEx>(); auto ident = std::make_shared<i2p::data::IdentityEx>();
ident->FromBase64 (base64); if (ident->FromBase64 (jump))
{
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
m_Addresses[address] = ident->GetIdentHash (); m_Addresses[address] = std::make_shared<Address>(ident->GetIdentHash ());
LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ())); LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ()));
} }
else
LogPrint (eLogError, "Addressbook: malformed address ", jump);
}
}
void AddressBook::InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address) void AddressBook::InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
{ {
m_Storage->AddAddress (address); m_Storage->AddAddress (address);
} }
std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetAddress (const std::string& address) std::shared_ptr<const i2p::data::IdentityEx> AddressBook::GetFullAddress (const std::string& address)
{ {
i2p::data::IdentHash ident; auto addr = GetAddress (address);
if (!GetIdentHash (address, ident)) return nullptr; if (!addr || !addr->IsIdentHash ()) return nullptr;
return m_Storage->GetAddress (ident); return m_Storage->GetAddress (addr->identHash);
} }
void AddressBook::LoadHosts () void AddressBook::LoadHosts ()
@ -408,16 +436,17 @@ namespace client
auto it = m_Addresses.find (name); auto it = m_Addresses.find (name);
if (it != m_Addresses.end ()) // already exists ? if (it != m_Addresses.end ()) // already exists ?
{ {
if (it->second != ident->GetIdentHash ()) // address changed? if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash ()) // address changed?
{ {
it->second = ident->GetIdentHash (); it->second->identHash = ident->GetIdentHash ();
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
LogPrint (eLogInfo, "Addressbook: updated host: ", name); LogPrint (eLogInfo, "Addressbook: updated host: ", name);
} }
} }
else else
{ {
m_Addresses.insert (std::make_pair (name, ident->GetIdentHash ())); //m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ()));
m_Addresses[name] = std::make_shared<Address>(ident->GetIdentHash ()); // for gcc 4.7
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
if (is_update) if (is_update)
LogPrint (eLogInfo, "Addressbook: added new host: ", name); LogPrint (eLogInfo, "Addressbook: added new host: ", name);
@ -472,32 +501,33 @@ namespace client
void AddressBook::LoadLocal () void AddressBook::LoadLocal ()
{ {
std::map<std::string, i2p::data::IdentHash> localAddresses; std::map<std::string, std::shared_ptr<Address>> localAddresses;
m_Storage->LoadLocal (localAddresses); m_Storage->LoadLocal (localAddresses);
for (const auto& it: localAddresses) for (const auto& it: localAddresses)
{ {
if (!it.second->IsIdentHash ()) continue; // skip blinded for now
auto dot = it.first.find ('.'); auto dot = it.first.find ('.');
if (dot != std::string::npos) if (dot != std::string::npos)
{ {
auto domain = it.first.substr (dot + 1); auto domain = it.first.substr (dot + 1);
auto it1 = m_Addresses.find (domain); // find domain in our addressbook auto it1 = m_Addresses.find (domain); // find domain in our addressbook
if (it1 != m_Addresses.end ()) if (it1 != m_Addresses.end () && it1->second->IsIdentHash ())
{ {
auto dest = context.FindLocalDestination (it1->second); auto dest = context.FindLocalDestination (it1->second->identHash);
if (dest) if (dest)
{ {
// address is ours // address is ours
std::shared_ptr<AddressResolver> resolver; std::shared_ptr<AddressResolver> resolver;
auto it2 = m_Resolvers.find (it1->second); auto it2 = m_Resolvers.find (it1->second->identHash);
if (it2 != m_Resolvers.end ()) if (it2 != m_Resolvers.end ())
resolver = it2->second; // resolver exists resolver = it2->second; // resolver exists
else else
{ {
// create new resolver // create new resolver
resolver = std::make_shared<AddressResolver>(dest); resolver = std::make_shared<AddressResolver>(dest);
m_Resolvers.insert (std::make_pair(it1->second, resolver)); m_Resolvers.insert (std::make_pair(it1->second->identHash, resolver));
} }
resolver->AddAddress (it.first, it.second); resolver->AddAddress (it.first, it.second->identHash);
} }
} }
} }
@ -627,11 +657,11 @@ namespace client
void AddressBook::LookupAddress (const std::string& address) void AddressBook::LookupAddress (const std::string& address)
{ {
const i2p::data::IdentHash * ident = nullptr; std::shared_ptr<const Address> addr;
auto dot = address.find ('.'); auto dot = address.find ('.');
if (dot != std::string::npos) if (dot != std::string::npos)
ident = FindAddress (address.substr (dot + 1)); addr = FindAddress (address.substr (dot + 1));
if (!ident) if (!addr || !addr->IsIdentHash ()) // TODO:
{ {
LogPrint (eLogError, "Addressbook: Can't find domain for ", address); LogPrint (eLogError, "Addressbook: Can't find domain for ", address);
return; return;
@ -649,14 +679,14 @@ namespace client
std::unique_lock<std::mutex> l(m_LookupsMutex); std::unique_lock<std::mutex> l(m_LookupsMutex);
m_Lookups[nonce] = address; m_Lookups[nonce] = address;
} }
LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", ident->ToBase32 (), " nonce=", nonce); LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", addr->identHash.ToBase32 (), " nonce=", nonce);
size_t len = address.length () + 9; size_t len = address.length () + 9;
uint8_t * buf = new uint8_t[len]; uint8_t * buf = new uint8_t[len];
memset (buf, 0, 4); memset (buf, 0, 4);
htobe32buf (buf + 4, nonce); htobe32buf (buf + 4, nonce);
buf[8] = address.length (); buf[8] = address.length ();
memcpy (buf + 9, address.c_str (), address.length ()); memcpy (buf + 9, address.c_str (), address.length ());
datagram->SendDatagramTo (buf, len, *ident, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT); datagram->SendDatagramTo (buf, len, addr->identHash, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT);
delete[] buf; delete[] buf;
} }
} }
@ -686,7 +716,7 @@ namespace client
// TODO: verify from // TODO: verify from
i2p::data::IdentHash hash(buf + 8); i2p::data::IdentHash hash(buf + 8);
if (!hash.IsZero ()) if (!hash.IsZero ())
m_Addresses[address] = hash; m_Addresses[address] = std::make_shared<Address>(hash);
else else
LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found"); LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found");
} }
@ -708,14 +738,19 @@ namespace client
i2p::http::URL url; i2p::http::URL url;
// must be run in separate thread // must be run in separate thread
LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link); LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link);
if (!url.parse(m_Link)) { if (!url.parse(m_Link))
{
LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link); LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link);
return false; return false;
} }
if (!m_Book.GetIdentHash (url.host, m_Ident)) { auto addr = m_Book.GetAddress (url.host);
if (!addr || !addr->IsIdentHash ())
{
LogPrint (eLogError, "Addressbook: Can't resolve ", url.host); LogPrint (eLogError, "Addressbook: Can't resolve ", url.host);
return false; return false;
} }
else
m_Ident = addr->identHash;
/* this code block still needs some love */ /* this code block still needs some love */
std::condition_variable newDataReceived; std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex; std::mutex newDataReceivedMutex;

View File

@ -13,6 +13,7 @@
#include "Identity.h" #include "Identity.h"
#include "Log.h" #include "Log.h"
#include "Destination.h" #include "Destination.h"
#include "LeaseSet.h"
namespace i2p namespace i2p
{ {
@ -28,6 +29,19 @@ namespace client
const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53; const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53;
const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54; const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54;
const size_t B33_ADDRESS_THRESHOLD = 52; // characters
struct Address
{
enum { eAddressIndentHash, eAddressBlindedPublicKey } 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; };
};
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); } inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
class AddressBookStorage // interface for storage class AddressBookStorage // interface for storage
@ -40,9 +54,9 @@ namespace client
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
virtual bool Init () = 0; virtual bool Init () = 0;
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0; virtual int Load (std::map<std::string, std::shared_ptr<Address> >& addresses) = 0;
virtual int LoadLocal (std::map<std::string, i2p::data::IdentHash>& addresses) = 0; virtual int LoadLocal (std::map<std::string, std::shared_ptr<Address> >& addresses) = 0;
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0; virtual int Save (const std::map<std::string, std::shared_ptr<Address> >& addresses) = 0;
virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0; virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0;
virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0; virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0;
@ -60,12 +74,12 @@ namespace client
void Start (); void Start ();
void StartResolvers (); void StartResolvers ();
void Stop (); void Stop ();
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident); std::shared_ptr<const Address> GetAddress (const std::string& address);
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const std::string& address); std::shared_ptr<const i2p::data::IdentityEx> GetFullAddress (const std::string& address);
const i2p::data::IdentHash * FindAddress (const std::string& address); std::shared_ptr<const Address> FindAddress (const std::string& address);
void LookupAddress (const std::string& address); void LookupAddress (const std::string& address);
void InsertAddress (const std::string& address, const std::string& base64); // for jump service void InsertAddress (const std::string& address, const std::string& jump); // for jump links
void InsertAddress (std::shared_ptr<const i2p::data::IdentityEx> address); void InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
bool LoadHostsFromStream (std::istream& f, bool is_update); bool LoadHostsFromStream (std::istream& f, bool is_update);
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified); void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
@ -93,7 +107,7 @@ namespace client
private: private:
std::mutex m_AddressBookMutex; std::mutex m_AddressBookMutex;
std::map<std::string, i2p::data::IdentHash> m_Addresses; std::map<std::string, std::shared_ptr<Address> > m_Addresses;
std::map<i2p::data::IdentHash, std::shared_ptr<AddressResolver> > m_Resolvers; // local destination->resolver std::map<i2p::data::IdentHash, std::shared_ptr<AddressResolver> > m_Resolvers; // local destination->resolver
std::mutex m_LookupsMutex; std::mutex m_LookupsMutex;
std::map<uint32_t, std::string> m_Lookups; // nonce -> address std::map<uint32_t, std::string> m_Lookups; // nonce -> address

View File

@ -72,17 +72,24 @@ namespace client
if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address
receiver->data = (uint8_t *)eol + 1; receiver->data = (uint8_t *)eol + 1;
receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
i2p::data::IdentHash ident; auto addr = context.GetAddressBook ().GetAddress (receiver->buffer);
if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident)) if (!addr)
{ {
LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found"); LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found");
return; return;
} }
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident); if (addr->IsIdentHash ())
{
auto leaseSet = GetLocalDestination ()->FindLeaseSet (addr->identHash);
if (leaseSet) if (leaseSet)
CreateConnection (receiver, leaseSet); CreateConnection (receiver, leaseSet);
else else
GetLocalDestination ()->RequestDestination (ident, GetLocalDestination ()->RequestDestination (addr->identHash,
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
this, std::placeholders::_1, receiver));
}
else
GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
this, std::placeholders::_1, receiver)); this, std::placeholders::_1, receiver));
} }
@ -540,29 +547,37 @@ namespace client
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
{ {
LogPrint (eLogDebug, "BOB: lookup ", operand); LogPrint (eLogDebug, "BOB: lookup ", operand);
i2p::data::IdentHash ident; auto addr = context.GetAddressBook ().GetAddress (operand);
if (!context.GetAddressBook ().GetIdentHash (operand, ident)) if (!addr)
{ {
SendReplyError ("Address Not found"); SendReplyError ("Address Not found");
return; return;
} }
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
auto leaseSet = localDestination->FindLeaseSet (ident); if (addr->IsIdentHash ())
if (leaseSet)
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
else
{ {
// we might have leaseset already
auto leaseSet = localDestination->FindLeaseSet (addr->identHash);
if (leaseSet)
{
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
return;
}
}
// trying to request
auto s = shared_from_this (); auto s = shared_from_this ();
localDestination->RequestDestination (ident, auto requstCallback =
[s](std::shared_ptr<i2p::data::LeaseSet> ls) [s](std::shared_ptr<i2p::data::LeaseSet> ls)
{ {
if (ls) if (ls)
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else else
s->SendReplyError ("LeaseSet Not found"); s->SendReplyError ("LeaseSet Not found");
} };
); if (addr->IsIdentHash ())
} localDestination->RequestDestination (addr->identHash, requstCallback);
else
localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
} }
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len) void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)

View File

@ -540,7 +540,8 @@ namespace client
{ {
// http proxy // http proxy
std::string outproxy = section.second.get("outproxy", ""); std::string outproxy = section.second.get("outproxy", "");
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port, outproxy, localDestination); bool addresshelper = section.second.get("addresshelper", true);
auto tun = std::make_shared<i2p::proxy::HTTPProxy>(name, address, port, outproxy, addresshelper, localDestination);
clientTunnel = tun; clientTunnel = tun;
clientEndpoint = tun->GetLocalEndpoint (); clientEndpoint = tun->GetLocalEndpoint ();
} }
@ -716,6 +717,7 @@ namespace client
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
if (httpProxyKeys.length () > 0) if (httpProxyKeys.length () > 0)
{ {
@ -732,7 +734,7 @@ namespace client
} }
try try
{ {
m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, localDestination); m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, httpOutProxyURL, httpAddresshelper, localDestination);
m_HttpProxy->Start(); m_HttpProxy->Start();
} }
catch (std::exception& e) catch (std::exception& e)

View File

@ -87,6 +87,7 @@ namespace proxy {
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock; std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
boost::asio::ip::tcp::resolver m_proxy_resolver; boost::asio::ip::tcp::resolver m_proxy_resolver;
std::string m_OutproxyUrl; std::string m_OutproxyUrl;
bool m_Addresshelper;
i2p::http::URL m_ProxyURL; i2p::http::URL m_ProxyURL;
i2p::http::URL m_RequestURL; i2p::http::URL m_RequestURL;
uint8_t m_socks_buf[255+8]; // for socks request/response uint8_t m_socks_buf[255+8]; // for socks request/response
@ -95,13 +96,15 @@ namespace proxy {
i2p::http::HTTPReq m_ClientRequest; i2p::http::HTTPReq m_ClientRequest;
i2p::http::HTTPRes m_ClientResponse; i2p::http::HTTPRes m_ClientResponse;
std::stringstream m_ClientRequestBuffer; std::stringstream m_ClientRequestBuffer;
public: public:
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) : HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
I2PServiceHandler(parent), m_sock(sock), I2PServiceHandler(parent), m_sock(sock),
m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())), m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())),
m_proxy_resolver(parent->GetService()), m_proxy_resolver(parent->GetService()),
m_OutproxyUrl(parent->GetOutproxyURL()) {} m_OutproxyUrl(parent->GetOutproxyURL()),
m_Addresshelper(parent->GetHelperSupport()) {}
~HTTPReqHandler() { Terminate(); } ~HTTPReqHandler() { Terminate(); }
void Handle () { AsyncSockRead(); } /* overload */ void Handle () { AsyncSockRead(); } /* overload */
}; };
@ -208,7 +211,7 @@ namespace proxy {
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req) void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
{ {
/* drop common headers */ /* drop common headers */
req.RemoveHeader("Referer"); req.RemoveHeader("Referrer");
req.RemoveHeader("Via"); req.RemoveHeader("Via");
req.RemoveHeader("From"); req.RemoveHeader("From");
req.RemoveHeader("Forwarded"); req.RemoveHeader("Forwarded");
@ -234,8 +237,6 @@ namespace proxy {
*/ */
bool HTTPReqHandler::HandleRequest() bool HTTPReqHandler::HandleRequest()
{ {
std::string b64;
m_req_len = m_ClientRequest.parse(m_recv_buf); m_req_len = m_ClientRequest.parse(m_recv_buf);
if (m_req_len == 0) if (m_req_len == 0)
@ -252,10 +253,10 @@ namespace proxy {
m_RequestURL.parse(m_ClientRequest.uri); m_RequestURL.parse(m_ClientRequest.uri);
bool m_Confirm; bool m_Confirm;
if (ExtractAddressHelper(m_RequestURL, b64, m_Confirm)) std::string jump;
if (ExtractAddressHelper(m_RequestURL, jump, m_Confirm))
{ {
bool addresshelper; i2p::config::GetOption("httpproxy.addresshelper", addresshelper); if (!m_Addresshelper)
if (!addresshelper)
{ {
LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected"); LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected");
GenericProxyError("Invalid request", "addresshelper is not supported"); GenericProxyError("Invalid request", "addresshelper is not supported");
@ -263,8 +264,8 @@ namespace proxy {
} }
if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
{ {
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64); i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump);
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host); LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host);
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. " ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. "
@ -276,7 +277,7 @@ namespace proxy {
{ {
std::stringstream ss; std::stringstream ss;
ss << "Host " << m_RequestURL.host << " <font color=red>already in router's addressbook</font>. " ss << "Host " << m_RequestURL.host << " <font color=red>already in router's addressbook</font>. "
<< "Click <a href=\"" << m_RequestURL.query << "?i2paddresshelper=" << b64 << "&update=true\">here</a> to update record."; << "Click <a href=\"" << m_RequestURL.query << "?i2paddresshelper=" << jump << "&update=true\">here</a> to update record.";
GenericProxyInfo("Addresshelper found", ss.str().c_str()); GenericProxyInfo("Addresshelper found", ss.str().c_str());
return true; /* request processed */ return true; /* request processed */
} }
@ -339,9 +340,8 @@ namespace proxy {
} }
} }
/* check dest_host really exists and inside I2P network */ /* check dest_host really exists and inside I2P network */
i2p::data::IdentHash identHash;
if (str_rmatch(dest_host, ".i2p")) { if (str_rmatch(dest_host, ".i2p")) {
if (!i2p::client::context.GetAddressBook ().GetIdentHash (dest_host, identHash)) { if (!i2p::client::context.GetAddressBook ().GetAddress (dest_host)) {
HostNotFound(dest_host); HostNotFound(dest_host);
return true; /* request processed */ return true; /* request processed */
} }
@ -623,9 +623,9 @@ namespace proxy {
Done (shared_from_this()); Done (shared_from_this());
} }
HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, std::shared_ptr<i2p::client::ClientDestination> localDestination): HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr<i2p::client::ClientDestination> localDestination):
TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()),
m_Name (name), m_OutproxyUrl(outproxy) m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper)
{ {
} }

View File

@ -6,12 +6,13 @@ namespace proxy {
class HTTPProxy: public i2p::client::TCPIPAcceptor class HTTPProxy: public i2p::client::TCPIPAcceptor
{ {
public: public:
HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, std::shared_ptr<i2p::client::ClientDestination> localDestination); HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr<i2p::client::ClientDestination> localDestination);
HTTPProxy(const std::string& name, const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr) : HTTPProxy(const std::string& name, const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr) :
HTTPProxy(name, address, port, "", localDestination) {} ; HTTPProxy(name, address, port, "", true, localDestination) {} ;
~HTTPProxy() {}; ~HTTPProxy() {};
std::string GetOutproxyURL() const { return m_OutproxyUrl; } std::string GetOutproxyURL() const { return m_OutproxyUrl; }
bool GetHelperSupport() { return m_Addresshelper; }
protected: protected:
// Implements TCPIPAcceptor // Implements TCPIPAcceptor
@ -21,6 +22,7 @@ namespace proxy {
private: private:
std::string m_Name; std::string m_Name;
std::string m_OutproxyUrl; std::string m_OutproxyUrl;
bool m_Addresshelper;
}; };
} // http } // http
} // i2p } // i2p

View File

@ -70,7 +70,9 @@ namespace client
void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len) void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len)
{ {
auto ls = std::make_shared<i2p::data::LocalLeaseSet2> (storeType, m_Identity, buf, len); auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ?
std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (m_Identity, buf, len):
std::make_shared<i2p::data::LocalLeaseSet2> (storeType, m_Identity, buf, len);
ls->SetExpirationTime (m_LeaseSetExpirationTime); ls->SetExpirationTime (m_LeaseSetExpirationTime);
SetLeaseSet (ls); SetLeaseSet (ls);
} }
@ -368,7 +370,7 @@ namespace client
size_t offset = identity->FromBuffer (buf, len); size_t offset = identity->FromBuffer (buf, len);
if (!offset) if (!offset)
{ {
LogPrint (eLogError, "I2CP: create session maformed identity"); LogPrint (eLogError, "I2CP: create session malformed identity");
SendSessionStatusMessage (3); // invalid SendSessionStatusMessage (3); // invalid
return; return;
} }
@ -528,21 +530,35 @@ namespace client
if (m_Destination) if (m_Destination)
{ {
uint8_t storeType = buf[offset]; offset++; // store type uint8_t storeType = buf[offset]; offset++; // store type
// TODO: parse LS2 and obtain correct private keys lengths i2p::data::LeaseSet2 ls (storeType, buf + offset, len - offset); // outer layer only for encrypted
size_t signingPrivateKeyLength = 0, encryptionPrivateKeyLength = 0; if (!ls.IsValid ())
if (storeType != i2p::data::NETDB_STORE_TYPE_META_LEASESET2) // no private keys for meta
{ {
signingPrivateKeyLength = m_Destination->GetIdentity ()->GetSigningPrivateKeyLen (); // no offline keys LogPrint (eLogError, "I2CP: invalid LeaseSet2 of type ", storeType);
encryptionPrivateKeyLength = 256; // ElGamal only
if (len < offset + signingPrivateKeyLength + encryptionPrivateKeyLength)
{
LogPrint (eLogError, "I2CP: CreateLeaseSet2 message is too short ", len);
return; return;
} }
m_Destination->SetEncryptionPrivateKey (buf + len - encryptionPrivateKeyLength); offset += ls.GetBufferLen ();
// ignore signing private key // private keys
int numPrivateKeys = buf[offset]; offset++;
uint16_t currentKeyType = 0;
const uint8_t * currentKey = nullptr;
for (int i = 0; i < numPrivateKeys; i++)
{
if (offset + 4 > len) return;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type
uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length
if (offset + keyLen > len) return;
if (keyType > currentKeyType)
{
currentKeyType = keyType;
currentKey = buf + offset;
} }
m_Destination->LeaseSet2Created (storeType, buf + offset, len - offset - signingPrivateKeyLength - encryptionPrivateKeyLength); offset += keyLen;
}
// TODO: support multiple keys
if (currentKey)
m_Destination->SetEncryptionPrivateKey (currentKey);
m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ());
} }
} }
else else
@ -603,12 +619,16 @@ namespace client
case 1: // address case 1: // address
{ {
auto name = ExtractString (buf + 11, len - 11); auto name = ExtractString (buf + 11, len - 11);
if (!i2p::client::context.GetAddressBook ().GetIdentHash (name, ident)) auto addr = i2p::client::context.GetAddressBook ().GetAddress (name);
if (!addr || !addr->IsIdentHash ())
{ {
// TODO: handle blinded addresses
LogPrint (eLogError, "I2CP: address ", name, " not found"); LogPrint (eLogError, "I2CP: address ", name, " not found");
SendHostReplyMessage (requestID, nullptr); SendHostReplyMessage (requestID, nullptr);
return; return;
} }
else
ident = addr->identHash;
break; break;
} }
default: default:

View File

@ -36,7 +36,7 @@ namespace client
const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3; const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3;
const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37; const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37;
const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4; const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4;
const uint8_t I2CP_CREATE_LEASESET2_MESSAGE = 40; const uint8_t I2CP_CREATE_LEASESET2_MESSAGE = 41;
const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5; const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5;
const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36; const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36;
const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31; const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31;

View File

@ -101,9 +101,9 @@ namespace client
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
assert(streamRequestComplete); assert(streamRequestComplete);
i2p::data::IdentHash identHash; auto address = i2p::client::context.GetAddressBook ().GetAddress (dest);
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash)) if (address)
CreateStream(streamRequestComplete, identHash, port); CreateStream(streamRequestComplete, address, port);
else else
{ {
LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest); LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest);
@ -111,27 +111,31 @@ namespace client
} }
} }
void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash & identHash, int port) void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr<const Address> address, int port)
{ {
if(m_ConnectTimeout) if(m_ConnectTimeout && !m_LocalDestination->IsReady())
{ {
if(m_LocalDestination->IsReady()) AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) {
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
else
{
AddReadyCallback([this, streamRequestComplete, identHash, port] (const boost::system::error_code & ec) {
if(ec) if(ec)
{ {
LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message()); LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message());
streamRequestComplete(nullptr); streamRequestComplete(nullptr);
} }
else else
this->m_LocalDestination->CreateStream(streamRequestComplete, identHash, port); { if (address->IsIdentHash ())
this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port);
else
this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port);
}
}); });
} }
}
else else
m_LocalDestination->CreateStream(streamRequestComplete, identHash, port); {
if (address->IsIdentHash ())
m_LocalDestination->CreateStream (streamRequestComplete, address->identHash, port);
else
m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port);
}
} }
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream) TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)

View File

@ -8,6 +8,7 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Destination.h" #include "Destination.h"
#include "Identity.h" #include "Identity.h"
#include "AddressBook.h"
namespace i2p namespace i2p
{ {
@ -49,7 +50,7 @@ namespace client
m_LocalDestination = dest; m_LocalDestination = dest;
} }
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0); void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
void CreateStream(StreamRequestComplete complete, const i2p::data::IdentHash & ident, int port); void CreateStream(StreamRequestComplete complete, std::shared_ptr<const Address> address, int port);
inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); } inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }
virtual void Start () = 0; virtual void Start () = 0;

View File

@ -393,15 +393,15 @@ namespace client
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler> class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
{ {
public: public:
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination, I2PClientTunnelHandler (I2PClientTunnel * parent, std::shared_ptr<const Address> address,
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket): int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
I2PServiceHandler(parent), m_DestinationIdentHash(destination), I2PServiceHandler(parent), m_Address(address),
m_DestinationPort (destinationPort), m_Socket(socket) {}; m_DestinationPort (destinationPort), m_Socket(socket) {};
void Handle(); void Handle();
void Terminate(); void Terminate();
private: private:
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream); void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
i2p::data::IdentHash m_DestinationIdentHash; std::shared_ptr<const Address> m_Address;
int m_DestinationPort; int m_DestinationPort;
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket; std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
}; };
@ -410,7 +410,7 @@ namespace client
{ {
GetOwner()->CreateStream ( GetOwner()->CreateStream (
std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1),
m_DestinationIdentHash, m_DestinationPort); m_Address, m_DestinationPort);
} }
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream) void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
@ -445,43 +445,39 @@ namespace client
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort): const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort) m_DestinationPort (destinationPort)
{ {
} }
void I2PClientTunnel::Start () void I2PClientTunnel::Start ()
{ {
TCPIPAcceptor::Start (); TCPIPAcceptor::Start ();
GetIdentHash(); GetAddress ();
} }
void I2PClientTunnel::Stop () void I2PClientTunnel::Stop ()
{ {
TCPIPAcceptor::Stop(); TCPIPAcceptor::Stop();
auto *originalIdentHash = m_DestinationIdentHash; m_Address = nullptr;
m_DestinationIdentHash = nullptr;
delete originalIdentHash;
} }
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */ /* HACK: maybe we should create a caching IdentHash provider in AddressBook */
const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash () std::shared_ptr<const Address> I2PClientTunnel::GetAddress ()
{ {
if (!m_DestinationIdentHash) if (!m_Address)
{ {
i2p::data::IdentHash identHash; m_Address = i2p::client::context.GetAddressBook ().GetAddress (m_Destination);
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash)) if (!m_Address)
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
else
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found"); LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
} }
return m_DestinationIdentHash; return m_Address;
} }
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{ {
const i2p::data::IdentHash *identHash = GetIdentHash(); auto address = GetAddress ();
if (identHash) if (address)
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, m_DestinationPort, socket); return std::make_shared<I2PClientTunnelHandler>(this, address, m_DestinationPort, socket);
else else
return nullptr; return nullptr;
} }
@ -814,9 +810,9 @@ namespace client
void I2PUDPClientTunnel::TryResolving() { void I2PUDPClientTunnel::TryResolving() {
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
i2p::data::IdentHash * h = new i2p::data::IdentHash;
while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *h) && !m_cancel_resolve) std::shared_ptr<const Address> addr;
while(!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
{ {
LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest); LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
@ -826,7 +822,13 @@ namespace client
LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled"); LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled");
return; return;
} }
m_RemoteIdent = h; if (!addr || !addr->IsIdentHash ())
{
LogPrint(eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
return;
}
m_RemoteIdent = new i2p::data::IdentHash;
*m_RemoteIdent = addr->identHash;
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
} }

View File

@ -13,6 +13,7 @@
#include "Datagram.h" #include "Datagram.h"
#include "Streaming.h" #include "Streaming.h"
#include "I2PService.h" #include "I2PService.h"
#include "AddressBook.h"
namespace i2p namespace i2p
{ {
@ -129,11 +130,11 @@ namespace client
const char* GetName() { return m_Name.c_str (); } const char* GetName() { return m_Name.c_str (); }
private: private:
const i2p::data::IdentHash * GetIdentHash (); std::shared_ptr<const Address> GetAddress ();
private: private:
std::string m_Name, m_Destination; std::string m_Name, m_Destination;
const i2p::data::IdentHash * m_DestinationIdentHash; std::shared_ptr<const Address> m_Address;
int m_DestinationPort; int m_DestinationPort;
}; };

View File

@ -14,13 +14,13 @@ namespace client
void MatchedTunnelDestination::ResolveCurrentLeaseSet() void MatchedTunnelDestination::ResolveCurrentLeaseSet()
{ {
if(i2p::client::context.GetAddressBook().GetIdentHash(m_RemoteName, m_RemoteIdent)) auto addr = i2p::client::context.GetAddressBook().GetAddress (m_RemoteName);
if(addr && addr->IsIdentHash ())
{ {
m_RemoteIdent = addr->identHash;
auto ls = FindLeaseSet(m_RemoteIdent); auto ls = FindLeaseSet(m_RemoteIdent);
if(ls) if(ls)
{
HandleFoundCurrentLeaseSet(ls); HandleFoundCurrentLeaseSet(ls);
}
else else
RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1)); RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1));
} }

View File

@ -467,7 +467,7 @@ namespace client
size_t l = dest->FromBase64(destination); size_t l = dest->FromBase64(destination);
if (l > 0) if (l > 0)
{ {
context.GetAddressBook().InsertAddress(dest); context.GetAddressBook().InsertFullAddress(dest);
auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash()); auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash());
if (leaseSet) if (leaseSet)
Connect(leaseSet); Connect(leaseSet);
@ -479,7 +479,7 @@ namespace client
} }
} }
else else
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true);
} }
else else
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
@ -610,22 +610,29 @@ namespace client
ExtractParams (buf, params); ExtractParams (buf, params);
std::string& name = params[SAM_PARAM_NAME]; std::string& name = params[SAM_PARAM_NAME];
std::shared_ptr<const i2p::data::IdentityEx> identity; std::shared_ptr<const i2p::data::IdentityEx> identity;
i2p::data::IdentHash ident; std::shared_ptr<const Address> addr;
auto session = m_Owner.FindSession(m_ID); auto session = m_Owner.FindSession(m_ID);
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination; auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination;
if (name == "ME") if (name == "ME")
SendNamingLookupReply (dest->GetIdentity ()); SendNamingLookupReply (dest->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr) else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr)
SendNamingLookupReply (identity); SendNamingLookupReply (identity);
else if (context.GetAddressBook ().GetIdentHash (name, ident)) else if ((addr = context.GetAddressBook ().GetAddress (name)))
{ {
auto leaseSet = dest->FindLeaseSet (ident); if (addr->IsIdentHash ())
{
auto leaseSet = dest->FindLeaseSet (addr->identHash);
if (leaseSet) if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ()); SendNamingLookupReply (leaseSet->GetIdentity ());
else else
dest->RequestDestination (ident, dest->RequestDestination (addr->identHash,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, ident)); shared_from_this (), std::placeholders::_1, name));
}
else
dest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
shared_from_this (), std::placeholders::_1, name));
} }
else else
{ {
@ -650,22 +657,20 @@ namespace client
SendMessageReply (m_Buffer, len, true); SendMessageReply (m_Buffer, len, true);
} }
void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident) void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name)
{ {
if (leaseSet) if (leaseSet)
{ {
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ()); context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ());
SendNamingLookupReply (leaseSet->GetIdentity ()); SendNamingLookupReply (leaseSet->GetIdentity ());
} }
else else
{ {
LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found"); LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", name, " not found");
#ifdef _MSC_VER #ifdef _MSC_VER
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
context.GetAddressBook ().ToAddress (ident).c_str());
#else #else
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
context.GetAddressBook ().ToAddress (ident).c_str());
#endif #endif
SendMessageReply (m_Buffer, len, false); SendMessageReply (m_Buffer, len, false);
} }
@ -844,7 +849,7 @@ namespace client
m_SocketType = eSAMSocketTypeStream; m_SocketType = eSAMSocketTypeStream;
m_IsAccepting = false; m_IsAccepting = false;
m_Stream = stream; m_Stream = stream;
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ());
auto session = m_Owner.FindSession (m_ID); auto session = m_Owner.FindSession (m_ID);
if (session) if (session)
{ {

View File

@ -35,6 +35,7 @@ namespace client
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
const char SAM_STREAM_STATUS_INVALID_KEY[] = "STREAM STATUS RESULT=INVALID_KEY\n";
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
@ -123,7 +124,7 @@ namespace client
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote); void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet); void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity); void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident); void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk (); void SendSessionCreateReplyOk ();

View File

@ -35,6 +35,7 @@
<translation type="qt" /> <translation type="qt" />
<releases> <releases>
<release version="2.25.0" date="2019-05-09" />
<release version="2.24.0" date="2019-03-21" /> <release version="2.24.0" date="2019-03-21" />
<release version="2.23.0" date="2019-01-21" /> <release version="2.23.0" date="2019-01-21" />
<release version="2.22.0" date="2018-11-09" /> <release version="2.22.0" date="2018-11-09" />