diff --git a/ChangeLog b/ChangeLog index fbb1fb16..08f78224 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,23 @@ # for this file format description, # 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 ### Added - Support of transient keys for LeaseSet2 diff --git a/Makefile.linux b/Makefile.linux index cb376e77..d1ccf143 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -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 else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6 NEEDED_CXXFLAGS += -std=c++0x -else ifeq ($(shell expr match ${CXXVER} "[5-7]\.[0-9]"),3) # gcc >= 5.0 - NEEDED_CXXFLAGS += -std=c++11 - LDLIBS = -latomic -else ifeq ($(shell expr match ${CXXVER} "[7-8]"),1) # gcc 7 ubuntu or gcc 8 arch +else ifeq ($(shell expr match ${CXXVER} "[5-9]"),1) # gcc >= 5 NEEDED_CXXFLAGS += -std=c++11 LDLIBS = -latomic else # not supported diff --git a/README.md b/README.md index 8fc8393a..540e3bed 100644 --- a/README.md +++ b/README.md @@ -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 ==== @@ -38,8 +41,12 @@ Resources Installing ---------- -The easiest way to install i2pd is by using -[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest). +The easiest way to install i2pd is by using precompiled packages and binaries. +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 i2pd from source on your OS. @@ -54,11 +61,11 @@ Build instructions: **Supported systems:** -* GNU/Linux x86/x64 - [![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) -* 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/) -* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/) +* 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) +* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) +* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) +* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/) * FreeBSD * Android * iOS diff --git a/Win32/installer.iss b/Win32/installer.iss index b042a6b1..b215b91c 100644 --- a/Win32/installer.iss +++ b/Win32/installer.iss @@ -1,5 +1,5 @@ #define I2Pd_AppName "i2pd" -#define I2Pd_ver "2.24.0" +#define I2Pd_ver "2.25.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff --git a/android/build.gradle b/android/build.gradle index df13977e..e68c160f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -29,8 +29,8 @@ android { applicationId "org.purplei2p.i2pd" targetSdkVersion 28 minSdkVersion 14 - versionCode 2240 - versionName "2.24.0" + versionCode 2250 + versionName "2.25.0" ndk { abiFilters 'armeabi-v7a' abiFilters 'x86' diff --git a/android/jni/DaemonAndroid.cpp b/android/jni/DaemonAndroid.cpp index 7ff83d1f..c3a1b805 100644 --- a/android/jni/DaemonAndroid.cpp +++ b/android/jni/DaemonAndroid.cpp @@ -65,6 +65,8 @@ namespace android } } */ + std::string dataDir = ""; + DaemonAndroidImpl::DaemonAndroidImpl () //: /*mutex(nullptr), */ @@ -85,7 +87,7 @@ namespace android //m_IsRunning=false; // make sure assets are ready before proceed - i2p::fs::DetectDataDir("", false); + i2p::fs::DetectDataDir(dataDir, false); int numAttempts = 0; do { @@ -203,5 +205,10 @@ namespace android { daemon.stop(); } + + void SetDataDir(std::string jdataDir) + { + dataDir = jdataDir; + } } } diff --git a/android/jni/DaemonAndroid.h b/android/jni/DaemonAndroid.h index 396a4578..64bf64fd 100644 --- a/android/jni/DaemonAndroid.h +++ b/android/jni/DaemonAndroid.h @@ -42,6 +42,8 @@ namespace android // stops the daemon void stop(); + // set datadir received from jni + void SetDataDir(std::string jdataDir); /* class Worker : public QObject { diff --git a/android/jni/i2pd_android.cpp b/android/jni/i2pd_android.cpp index c258733f..da908648 100755 --- a/android/jni/i2pd_android.cpp +++ b/android/jni/i2pd_android.cpp @@ -5,7 +5,7 @@ #include "Transports.h" JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith - (JNIEnv * env, jclass clazz) { + (JNIEnv *env, jclass clazz) { #if defined(__arm__) #if defined(__ARM_ARCH_7A__) #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 - (JNIEnv * env, jclass clazz) { + (JNIEnv *env, jclass clazz) { return env->NewStringUTF(i2p::android::start().c_str()); } JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon - (JNIEnv * env, jclass clazz) { + (JNIEnv *env, jclass clazz) { i2p::android::stop(); } JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels - (JNIEnv * env, jclass clazz) { + (JNIEnv *env, jclass clazz) { i2p::context.SetAcceptsTunnels (false); } JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels - (JNIEnv * env, jclass clazz) { + (JNIEnv *env, jclass clazz) { i2p::context.SetAcceptsTunnels (true); } 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; 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); +} diff --git a/android/jni/org_purplei2p_i2pd_I2PD_JNI.h b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h index 28b9118b..6939a153 100644 --- a/android/jni/org_purplei2p_i2pd_I2PD_JNI.h +++ b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h @@ -30,6 +30,9 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged (JNIEnv * env, jclass clazz, jboolean isConnected); +JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir + (JNIEnv *env, jclass clazz, jstring jdataDir); + #ifdef __cplusplus } #endif diff --git a/android/src/org/purplei2p/i2pd/DaemonSingleton.java b/android/src/org/purplei2p/i2pd/DaemonSingleton.java index fa487b67..5f806024 100644 --- a/android/src/org/purplei2p/i2pd/DaemonSingleton.java +++ b/android/src/org/purplei2p/i2pd/DaemonSingleton.java @@ -2,7 +2,9 @@ package org.purplei2p.i2pd; import java.util.HashSet; import java.util.Set; +import android.os.Environment; import android.util.Log; + import org.purplei2p.i2pd.R; public class DaemonSingleton { @@ -80,6 +82,7 @@ public class DaemonSingleton { } try { synchronized (DaemonSingleton.this) { + I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd"); daemonStartResult = I2PD_JNI.startDaemon(); if("ok".equals(daemonStartResult)){ setState(State.startedOkay); diff --git a/android/src/org/purplei2p/i2pd/I2PD_JNI.java b/android/src/org/purplei2p/i2pd/I2PD_JNI.java index a929b703..31a8a4eb 100644 --- a/android/src/org/purplei2p/i2pd/I2PD_JNI.java +++ b/android/src/org/purplei2p/i2pd/I2PD_JNI.java @@ -18,6 +18,8 @@ public class I2PD_JNI { public static native void onNetworkStateChanged(boolean isConnected); + public static native void setDataDir(String jdataDir); + public static void loadLibraries() { //System.loadLibrary("c++_shared"); System.loadLibrary("i2pd"); diff --git a/android_binary_only/jni/Application.mk b/android_binary_only/jni/Application.mk index 9b437f6a..655bf430 100755 --- a/android_binary_only/jni/Application.mk +++ b/android_binary_only/jni/Application.mk @@ -1,8 +1,8 @@ -#APP_ABI := all -#APP_ABI := armeabi-v7a x86 -#APP_ABI := x86 -#APP_ABI := x86_64 -APP_ABI := armeabi-v7a +APP_ABI := all +#APP_ABI += x86 +#APP_ABI += x86_64 +#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. APP_PLATFORM := android-14 diff --git a/appveyor.yml b/appveyor.yml index e9a9a40e..a7cf22c3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.24.0.{build} +version: 2.25.0.{build} pull_requests: do_not_increment_build_number: true branches: diff --git a/contrib/android_binary_pack/.gitignore b/contrib/android_binary_pack/.gitignore new file mode 100644 index 00000000..bad5f807 --- /dev/null +++ b/contrib/android_binary_pack/.gitignore @@ -0,0 +1,2 @@ +archive +i2pd_*_android_binary.zip diff --git a/contrib/android_binary_pack/build-archive b/contrib/android_binary_pack/build-archive new file mode 100755 index 00000000..bb56cace --- /dev/null +++ b/contrib/android_binary_pack/build-archive @@ -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 diff --git a/contrib/android_binary_pack/i2pd b/contrib/android_binary_pack/i2pd new file mode 100755 index 00000000..aeaae804 --- /dev/null +++ b/contrib/android_binary_pack/i2pd @@ -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 diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index 22990995..e0c4d26e 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -24,7 +24,7 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \ # 1. install deps, clone and build. # 2. strip binaries. # 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 \ && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \ && cd i2pd \ diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index 24f990a7..ffc87519 100644 --- a/contrib/rpm/i2pd-git.spec +++ b/contrib/rpm/i2pd-git.spec @@ -1,7 +1,7 @@ %define git_hash %(git rev-parse HEAD | cut -c -7) Name: i2pd-git -Version: 2.24.0 +Version: 2.25.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -110,6 +110,9 @@ getent passwd i2pd >/dev/null || \ %changelog +* Thu May 9 2019 orignal - 2.25.0 +- update to 2.25.0 + * Thu Mar 21 2019 orignal - 2.24.0 - update to 2.24.0 diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index 39afff3b..7932629f 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -1,5 +1,5 @@ Name: i2pd -Version: 2.24.0 +Version: 2.25.0 Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -108,6 +108,9 @@ getent passwd i2pd >/dev/null || \ %changelog +* Thu May 9 2019 orignal - 2.25.0 +- update to 2.25.0 + * Thu Mar 21 2019 orignal - 2.24.0 - update to 2.24.0 diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 8e0804d1..0ea45bb7 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -154,14 +154,24 @@ namespace i2p i2p::context.SetSupportsV6 (ipv6); i2p::context.SetSupportsV4 (ipv4); + bool ntcp; i2p::config::GetOption("ntcp", ntcp); + i2p::context.PublishNTCPAddress (ntcp, !ipv6); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) { bool published; i2p::config::GetOption("ntcp2.published", published); if (published) { - uint16_t port; i2p::config::GetOption("ntcp2.port", port); - i2p::context.PublishNTCP2Address (port, true); // publish + uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port); + 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 i2p::context.PublishNTCP2Address (port, false); // unpublish @@ -256,7 +266,7 @@ namespace i2p pos = comma + 1; } 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); restricted = idents.size() > 0; } diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index b7771d56..20c9f3aa 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -358,7 +358,7 @@ namespace http { { s << "
\r\n\r\n

\r\n"; for(auto& it: dest->GetLeaseSets ()) - s << it.second->GetIdentHash ().ToBase32 () << " " << (int)it.second->GetStoreType () << "
\r\n"; + s << it.first.ToBase32 () << " " << (int)it.second->GetStoreType () << "
\r\n"; s << "

\r\n
\r\n"; } else s << "LeaseSets: 0
\r\n"; diff --git a/debian/changelog b/debian/changelog index 4d0757be..a0ad2cd5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,12 @@ +i2pd (2.25.0-1) unstable; urgency=medium + + * updated to version 2.25.0/0.9.40 + + -- orignal Thu, 9 May 2019 16:00:00 +0000 + i2pd (2.24.0-1) unstable; urgency=medium * updated to version 2.24.0/0.9.39 - * update docs, dirs, install, links files -- orignal Thu, 21 Mar 2019 16:00:00 +0000 diff --git a/libi2pd/ChaCha20.cpp b/libi2pd/ChaCha20.cpp index 91a1fbd5..222111b7 100644 --- a/libi2pd/ChaCha20.cpp +++ b/libi2pd/ChaCha20.cpp @@ -9,6 +9,7 @@ * */ +#include "I2PEndian.h" #include "ChaCha20.h" #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++) 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++) state.data[13 + i] = chacha::u8t32le(nonce + i * 4); } void Chacha20SetCounter (Chacha20State& state, uint32_t counter) { - state.data[12] = counter; + state.data[12] = htole32 (counter); state.offset = 0; } diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 22b08e10..52f40089 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -73,7 +73,7 @@ namespace config { ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.transittunnels", value()->default_value(2500), "Maximum active transit sessions (default:2500)") - ("limits.ntcpsoft", value()->default_value(0), "Threshold to start probabalistic backoff with ntcp sessions (default: use system limit)") + ("limits.ntcpsoft", value()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)") ("limits.ntcphard", value()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)") ("limits.ntcpthreads", value()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)") ; @@ -153,8 +153,8 @@ namespace config { ("i2pcontrol.address", value()->default_value("127.0.0.1"), "I2PCP listen address") ("i2pcontrol.port", value()->default_value(7650), "I2PCP listen port") ("i2pcontrol.password", value()->default_value("itoopie"), "I2PCP access password") - ("i2pcontrol.cert", value()->default_value("i2pcontrol.crt.pem"), "I2PCP connection cerificate") - ("i2pcontrol.key", value()->default_value("i2pcontrol.key.pem"), "I2PCP connection cerificate key") + ("i2pcontrol.cert", value()->default_value("i2pcontrol.crt.pem"), "I2PCP connection certificate") + ("i2pcontrol.key", value()->default_value("i2pcontrol.key.pem"), "I2PCP connection certificate key") ; bool upnp_default = false; @@ -164,7 +164,7 @@ namespace config { options_description upnp("UPnP options"); upnp.add_options() ("upnp.enabled", value()->default_value(upnp_default), "Enable or disable UPnP: automatic port forwarding") - ("upnp.name", value()->default_value("I2Pd"), "Name i2pd appears in UPnP forwardings list") + ("upnp.name", value()->default_value("I2Pd"), "Name i2pd appears in UPnP forwarding list") ; options_description precomputation("Precomputation options"); @@ -239,6 +239,7 @@ namespace config { ("ntcp2.enabled", value()->default_value(true), "Enable NTCP2 (default: enabled)") ("ntcp2.published", value()->default_value(false), "Publish NTCP2 (default: disabled)") ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") + ("ntcp2.addressv6", value()->default_value("::"), "Address to bind NTCP2 on") ; options_description nettime("Time sync options"); @@ -256,7 +257,7 @@ namespace config { options_description persist("Network information persisting options"); persist.add_options() ("persist.profiles", value()->default_value(true), "Persist peer profiles (default: true)") - ("persist.addressbook", value()->default_value(true), "Persist full addreses (default: true)") + ("persist.addressbook", value()->default_value(true), "Persist full addresses (default: true)") ; m_OptionsDesc diff --git a/libi2pd/Config.h b/libi2pd/Config.h index 0bbcd5b1..679ae3bb 100644 --- a/libi2pd/Config.h +++ b/libi2pd/Config.h @@ -32,12 +32,12 @@ namespace config { * @param argc Cmdline arguments count, 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 - * terminates the program with exitcode 0. + * If --help is given in parameters, shows its list with description + * and terminates the program with exitcode 0. * * In case of parameter misuse boost throws an exception. * 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. */ @@ -107,7 +107,7 @@ namespace config { /** * @brief Check is value explicitly given or default * @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); } diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 68dc8f90..cd6d5b8e 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1235,7 +1235,9 @@ namespace crypto { #if OPENSSL_AEAD_CHACHA20_POLY1305 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; EVP_EncryptUpdate(ctx, out, &outlen, msg, msgLen); EVP_EncryptFinal_ex(ctx, NULL, &outlen); diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index dab62712..ba90342c 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -213,7 +213,7 @@ namespace client return pool->Reconfigure(inLen, outLen, inQuant, outQuant); } - std::shared_ptr LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident) + std::shared_ptr LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident) { std::shared_ptr remoteLS; { @@ -272,7 +272,8 @@ namespace client if (!m_Pool) return nullptr; if (!m_LeaseSet) UpdateLeaseSet (); - return GetLeaseSetMt (); + auto ls = GetLeaseSetMt (); + return (ls && ls->GetInnerLeaseSet ()) ? ls->GetInnerLeaseSet () : ls; // always non-encrypted } std::shared_ptr LeaseSetDestination::GetLeaseSetMt () @@ -419,12 +420,13 @@ namespace client case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5 { 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 (buf + offset, len - offset, it2->second->requestedIdentity); + auto ls2 = std::make_shared (buf + offset, len - offset, it2->second->requestedBlindedKey); if (ls2->IsValid ()) { m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key + m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup leaseSet = ls2; } } @@ -576,7 +578,7 @@ namespace client else { LogPrint (eLogWarning, "Destination: Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds from Java floodfill for crypto type ", (int)GetIdentity ()->GetCryptoKeyType ()); - // Java floodfill never sends confirmantion back for unknown crypto type + // Java floodfill never sends confirmation back for unknown crypto type // assume it successive and try to verify m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, @@ -591,27 +593,32 @@ namespace client { 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 (); - RequestLeaseSet (GetIdentHash (), - // "this" added due to bug in gcc 4.7-4.8 - [s,this](std::shared_ptr leaseSet) + // we must capture this for gcc 4.7 due the bug + RequestLeaseSet (ls->GetStoreHash (), + [s, ls, this](std::shared_ptr leaseSet) { if (leaseSet) { - auto ls = s->GetLeaseSetMt (); - if (ls && *ls == *leaseSet) + if (*ls == *leaseSet) { // 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.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); return; } 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 - 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 s->Publish (); }); @@ -636,17 +643,23 @@ namespace client return true; } - bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete) + bool LeaseSetDestination::RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete) { - if (!m_Pool || !IsReady ()) + if (!dest || !m_Pool || !IsReady ()) { if (requestComplete) m_Service.post ([requestComplete](void){requestComplete (nullptr);}); return false; } - i2p::data::IdentHash ident; - i2p::data::LeaseSet2::CalculateStoreHash (dest, i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519, ident); // always assume type 11 - m_Service.post (std::bind (&LeaseSetDestination::RequestLeaseSet, shared_from_this (), ident, requestComplete, dest)); + auto storeHash = dest->GetStoreHash (); + auto leaseSet = FindLeaseSet (storeHash); + 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; } @@ -665,21 +678,20 @@ namespace client }); } - void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify) + void LeaseSetDestination::CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify) { - i2p::data::IdentHash ident; - i2p::data::LeaseSet2::CalculateStoreHash (dest, i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519, ident); // always assume type 11 - CancelDestinationRequest (ident, notify); + if (dest) + CancelDestinationRequest (dest->GetStoreHash (), notify); } - void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedIdentity) + void LeaseSetDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey) { std::set excluded; auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); if (floodfill) { auto request = std::make_shared (m_Service); - request->requestedIdentity = requestedIdentity; // for encrypted LeaseSet2 + request->requestedBlindedKey = requestedBlindedKey; // for encrypted LeaseSet2 if (requestComplete) request->requestComplete.push_back (requestComplete); auto ts = i2p::util::GetSecondsSinceEpoch (); @@ -974,7 +986,7 @@ namespace client { auto s = GetSharedFromThis (); RequestDestination (dest, - [s, streamRequestComplete, port](std::shared_ptr ls) + [s, streamRequestComplete, port](std::shared_ptr ls) { if (ls) streamRequestComplete(s->CreateStream (ls, port)); @@ -984,6 +996,24 @@ namespace client } } + void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr 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 ls) + { + if (ls) + streamRequestComplete(s->CreateStream (ls, port)); + else + streamRequestComplete (nullptr); + }); + } + std::shared_ptr ClientDestination::CreateStream (std::shared_ptr remote, int port) { if (m_StreamingDestination) @@ -1097,10 +1127,13 @@ namespace client } else { - // standard LS2 (type 3) assumed for now. TODO: implement others + // standard LS2 (type 3) first auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256; - leaseSet = std::make_shared (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, + auto ls2 = std::make_shared (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels); + if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) // encrypt if type 5 + ls2 = std::make_shared (ls2, m_Keys); + leaseSet = ls2; } SetLeaseSet (leaseSet); } diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 57b6e114..b55a7490 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -82,7 +82,7 @@ namespace client std::list requestComplete; std::shared_ptr outboundTunnel; std::shared_ptr replyTunnel; - std::shared_ptr requestedIdentity; // for encrypted LeaseSet2 only + std::shared_ptr requestedBlindedKey; // for encrypted LeaseSet2 only void Complete (std::shared_ptr ls) { @@ -108,11 +108,11 @@ namespace client boost::asio::io_service& GetService () { return m_Service; }; std::shared_ptr GetTunnelPool () { return m_Pool; }; bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; }; - std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); + std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); - bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete = nullptr); + bool RequestDestinationWithEncryptedLeaseSet (std::shared_ptr dest, RequestComplete requestComplete = nullptr); void CancelDestinationRequest (const i2p::data::IdentHash& dest, bool notify = true); - void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify = true); + void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify = true); // implements GarlicDestination std::shared_ptr GetLeaseSet (); @@ -148,7 +148,7 @@ namespace client void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); void HandleDeliveryStatusMessage (std::shared_ptr msg); - void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedIdentity = nullptr); + void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey = nullptr); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleCleanupTimer (const boost::system::error_code& ecode); @@ -213,6 +213,7 @@ namespace client std::shared_ptr GetStreamingDestination (int port = 0) const; // following methods operate with default streaming destination void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); + void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr dest, int port = 0); std::shared_ptr CreateStream (std::shared_ptr remote, int port = 0); void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); void StopAcceptingStreams (); diff --git a/libi2pd/Ed25519.cpp b/libi2pd/Ed25519.cpp index 64ddeaf4..1096b3b7 100644 --- a/libi2pd/Ed25519.cpp +++ b/libi2pd/Ed25519.cpp @@ -121,8 +121,8 @@ namespace crypto return passed; } - void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, const uint8_t * buf, size_t len, - uint8_t * signature) const + void Ed25519::Sign (const uint8_t * expandedPrivateKey, const uint8_t * publicKeyEncoded, + const uint8_t * buf, size_t len, uint8_t * signature) const { BN_CTX * bnCtx = BN_CTX_new (); // calculate r @@ -153,6 +153,44 @@ namespace crypto 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 (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 { // x3 = (x1*y2+y1*x2)*(z1*z2-d*t1*t2) @@ -506,6 +544,24 @@ namespace crypto 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) { SHA512 (key, EDDSA25519_PRIVATE_KEY_LENGTH, expandedKey); @@ -514,6 +570,18 @@ namespace crypto 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 g_Ed25519; std::unique_ptr& GetEd25519 () { diff --git a/libi2pd/Ed25519.h b/libi2pd/Ed25519.h index 18f749ea..84501b03 100644 --- a/libi2pd/Ed25519.h +++ b/libi2pd/Ed25519.h @@ -81,12 +81,15 @@ namespace crypto void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; #endif void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 + void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 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 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 - + void CreateRedDSAPrivateKey (uint8_t * priv); // priv is 32 bytes + private: EDDSAPoint Sum (const EDDSAPoint& p1, const EDDSAPoint& p2, BN_CTX * ctx) const; diff --git a/libi2pd/FS.h b/libi2pd/FS.h index 87364fea..6f112218 100644 --- a/libi2pd/FS.h +++ b/libi2pd/FS.h @@ -66,7 +66,7 @@ namespace fs { /** @brief Returns current application name, default 'i2pd' */ 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); /** @brief Returns datadir path */ diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 59089072..888e988c 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -578,7 +578,7 @@ namespace garlic tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); else 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); else LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); diff --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index 985e1c22..c1a46e73 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -55,12 +55,16 @@ namespace http { static std::pair parse_header_line(const std::string& line) { std::size_t pos = 0; - std::size_t len = 2; /* strlen(": ") */ + std::size_t len = 1; /*: */ std::size_t max = line.length(); - if ((pos = line.find(": ", pos)) == std::string::npos) - return std::make_pair("", ""); - while ((pos + len) < max && isspace(line.at(pos + len))) - len++; + if ((pos = line.find(':', pos)) == std::string::npos) + return std::make_pair("", ""); // no ':' found + if (pos + 1 < max) // ':' at the end of header is valid + { + while ((pos + len) < max && isspace(line.at(pos + len))) + len++; + if (len == 1) return std::make_pair("", ""); // no following space, but something else + } return std::make_pair(line.substr(0, pos), line.substr(pos + len)); } diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 2fc1df3b..9f6c609b 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -279,7 +279,7 @@ namespace i2p if (!leaseSet) return nullptr; auto m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); - memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32); + memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetStoreHash (), 32); payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // LeaseSet or LeaseSet2 htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); size_t size = DATABASE_STORE_HEADER_SIZE; diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index a6b83fca..f088d3f2 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -338,12 +338,13 @@ namespace data case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: return new i2p::crypto::ECDSAP521Verifier (); case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: return new i2p::crypto::EDDSA25519Verifier (); case SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256: return new i2p::crypto::GOSTR3410_256_Verifier (i2p::crypto::eGOSTR3410CryptoProA); case SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512: 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_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA512_4096: @@ -590,39 +591,53 @@ namespace data 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) { - 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: - m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey)); + return new i2p::crypto::ECDSAP256Signer (priv); break; case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: - m_Signer.reset (new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey)); + return new i2p::crypto::ECDSAP384Signer (priv); break; case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: - m_Signer.reset (new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey)); + return new i2p::crypto::ECDSAP521Signer (priv); break; case SIGNING_KEY_TYPE_RSA_SHA256_2048: case SIGNING_KEY_TYPE_RSA_SHA384_3072: 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; case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: - 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 + return new i2p::crypto::EDDSA25519Signer (priv, nullptr); break; 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; 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; 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 @@ -704,7 +719,6 @@ namespace data LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); // no break here case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: - case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); break; 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: i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, priv, pub); break; + case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: + i2p::crypto::CreateRedDSA25519RandomKeys (priv, pub); + break; default: LogPrint (eLogWarning, "Identity: Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); i2p::crypto::CreateDSARandomKeys (priv, pub); // DSA-SHA1 diff --git a/libi2pd/Identity.h b/libi2pd/Identity.h index 7e3c95dd..72fd14c5 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -163,6 +163,7 @@ namespace data 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 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 PrivateKeys CreateOfflineKeys (SigningKeyType type, uint32_t expires) const; diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index 0c0ea35f..8b9edb79 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -1,6 +1,7 @@ #include #include #include +#include // for crc32 #include "I2PEndian.h" #include "Crypto.h" #include "Ed25519.h" @@ -253,8 +254,147 @@ namespace data memcpy (m_Buffer, buf, len); } + BlindedPublicKey::BlindedPublicKey (std::shared_ptr 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 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 >& 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): - LeaseSet (storeLeases), m_StoreType (storeType) + LeaseSet (storeLeases), m_StoreType (storeType), m_OrigStoreType (storeType) { SetBuffer (buf, len); if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) @@ -263,10 +403,10 @@ namespace data ReadFromBuffer (buf, len); } - LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr identity): - LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) + LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key): + 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) @@ -423,7 +563,7 @@ namespace data return offset; } - void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr identity) + void LeaseSet2::ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr key) { size_t offset = 0; // blinded key @@ -463,33 +603,26 @@ namespace data VerifySignature (blindedVerifier, buf, len, offset); SetIsValid (verified); // handle ciphertext - if (verified && identity && lenOuterCiphertext >= 32) + if (verified && key && lenOuterCiphertext >= 32) { SetIsValid (false); // we must verify it again in Layer 2 if (blindedKeyType == i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519) { // verify blinding char date[9]; - i2p::util::GetCurrentDate (date); + i2p::util::GetDateString (m_PublishedTimestamp, date); uint8_t blinded[32]; - BlindPublicKey (identity, date, blindedKeyType, blinded); + key->GetBlindedKey (date, blinded); if (memcmp (blindedPublicKey, blinded, 32)) { LogPrint (eLogError, "LeaseSet2: blinded public key doesn't match"); 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 // outerInput = subcredential || publishedTimestamp + uint8_t subcredential[36]; + key->GetSubcredential (blindedPublicKey, blindedKeyLen, subcredential); memcpy (subcredential + 32, publishedTimestamp, 4); // outerSalt = outerCiphertext[0:32] // keys = HKDF(outerSalt, outerInput, "ELS2_L1K", 44) @@ -502,6 +635,7 @@ namespace data std::vector outerPlainText (lenOuterPlaintext); i2p::crypto::ChaCha20 (outerCiphertext + 32, lenOuterPlaintext, keys, keys + 32, outerPlainText.data ()); // inner key + // innerInput = authCookie || subcredential || publishedTimestamp, TODO: non-empty authCookie // innerSalt = innerCiphertext[0:32] // keys = HKDF(innerSalt, innerInput, "ELS2_L2K", 44) // skip 1 byte flags @@ -521,50 +655,10 @@ namespace data ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1); } 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 >& 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 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 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 { auto encryptor = m_Encryptor; // TODO: atomic @@ -763,5 +857,77 @@ namespace data memcpy (m_Buffer + 1, buf, len); m_Buffer[0] = storeType; } + + LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr 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 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 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(identity); + i2p::data::LeaseSet2 ls (buf, len, blindedKey); // inner layer + if (ls.IsValid ()) + { + m_InnerLeaseSet = std::make_shared(ls.GetStoreType (), identity, ls.GetBuffer (), ls.GetBufferLen ()); + m_StoreHash = blindedKey->GetStoreHash (); + } + else + LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer"); + } + } } diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index d3e7cfc3..94a19b1b 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -26,7 +26,7 @@ namespace data IdentHash tunnelGateway; uint32_t tunnelID; uint64_t endDate; // 0 means invalid - bool isUpdated; // trasient + bool isUpdated; // transient /* return true if this lease expires within t millisecond + fudge factor */ bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const { auto expire = i2p::util::GetMillisecondsSinceEpoch (); @@ -78,6 +78,7 @@ namespace data bool operator== (const LeaseSet& other) const { return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); }; virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; + virtual uint8_t GetOrigStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only virtual std::shared_ptr GetTransientVerifier () const { return nullptr; }; @@ -127,27 +128,56 @@ namespace data const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7; const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001; + + class BlindedPublicKey // for encrypted LS2 + { + public: + + BlindedPublicKey (std::shared_ptr 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 >& bufs, uint8_t * hash) const; + + private: + + std::vector m_PublicKey; + i2p::data::SigningKeyType m_SigType, m_BlindedSigType; + }; class LeaseSet2: public LeaseSet { public: LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true); - LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr identity); // store type 5, called from local netdb only + LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key); // store type 5, called from local netdb only uint8_t GetStoreType () const { return m_StoreType; }; + uint8_t GetOrigStoreType () const { return m_OrigStoreType; }; uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; }; std::shared_ptr GetTransientVerifier () const { return m_TransientVerifier; }; void Update (const uint8_t * buf, size_t len, bool verifySignature); - static void CalculateStoreHash (std::shared_ptr identity, SigningKeyType blindedKeyType, i2p::data::IdentHash& hash); - // implements RoutingDestination void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; private: 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 identity); + void ReadFromBufferEncrypted (const uint8_t * buf, size_t len, std::shared_ptr key); size_t ReadStandardLS2TypeSpecificPart (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; - // for encrypted LS - static void H (const std::string& p, const std::vector >& bufs, uint8_t * hash); - static void BlindPublicKey (std::shared_ptr identity, const char * date, SigningKeyType blindedKeyType, uint8_t * blindedKey); // blinded key 32 bytes, date is 8 chars "YYYYMMDD" - private: - uint8_t m_StoreType; + uint8_t m_StoreType, m_OrigStoreType; uint32_t m_PublishedTimestamp = 0; std::shared_ptr m_TransientVerifier; std::shared_ptr m_Encryptor; // for standardLS2 @@ -204,6 +230,7 @@ namespace data uint8_t * GetLeases () { return m_Leases; }; const IdentHash& GetIdentHash () const { return m_Identity->GetIdentHash (); }; + std::shared_ptr GetIdentity () const { return m_Identity; }; bool IsExpired () const; uint64_t GetExpirationTime () const { return m_ExpirationTime; }; void SetExpirationTime (uint64_t expirationTime) { m_ExpirationTime = expirationTime; }; @@ -211,6 +238,8 @@ namespace data { return GetBufferLen () == other.GetBufferLen () && !memcmp (GetBuffer (), other.GetBuffer (), GetBufferLen ()); }; 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 GetInnerLeaseSet () const { return nullptr; }; // non-null for encrypted LeaseSet2 private: @@ -227,7 +256,8 @@ namespace data LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, std::vector > tunnels); - LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len); + LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP + virtual ~LocalLeaseSet2 () { delete[] m_Buffer; }; uint8_t * GetBuffer () const { return m_Buffer + 1; }; @@ -235,11 +265,32 @@ namespace data uint8_t GetStoreType () const { return m_Buffer[0]; }; - private: + protected: + + LocalLeaseSet2 (std::shared_ptr 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 size_t m_BufferLen; }; + + class LocalEncryptedLeaseSet2: public LocalLeaseSet2 + { + public: + + LocalEncryptedLeaseSet2 (std::shared_ptr ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519); + + LocalEncryptedLeaseSet2 (std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP + + const IdentHash& GetStoreHash () const { return m_StoreHash; }; + std::shared_ptr GetInnerLeaseSet () const { return m_InnerLeaseSet; }; + + private: + + IdentHash m_StoreHash; + std::shared_ptr m_InnerLeaseSet; + }; } } diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index c571e0f0..0976720e 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -690,7 +690,7 @@ namespace transport } 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); return; } @@ -783,7 +783,7 @@ namespace transport size_t moreBytes = m_Socket.available(ec); 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); HandleReceived (ec, moreBytes); } @@ -887,7 +887,7 @@ namespace transport Terminate (); } else - LogPrint (eLogWarning, "NTCP2: Unexpected temination block size ", size); + LogPrint (eLogWarning, "NTCP2: Unexpected termination block size ", size); break; case eNTCP2BlkPadding: LogPrint (eLogDebug, "NTCP2: padding"); diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index 8be55466..68a4133f 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -1287,7 +1287,7 @@ namespace transport if (it.second->IsTerminationTimeoutExpired (ts)) { auto session = it.second; - // Termniate modifies m_NTCPSession, so we postpone it + // Terminate modifies m_NTCPSession, so we postpone it m_Service.post ([session] { LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds"); session->Terminate (); diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 2780bc8f..02ae2ae8 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -376,7 +376,7 @@ namespace data } m_FloodfillBootstrap = 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; } } @@ -1243,7 +1243,7 @@ namespace data { 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); } else diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 40a0b740..98f95dc3 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -375,7 +375,7 @@ namespace data if (end - contentPos >= contentLength) break; // we are beyond contentLength } - if (numFiles) // check if routers are not outdated + if (numFiles) // check if routers are not outdated { auto ts = i2p::util::GetMillisecondsSinceEpoch (); int numOutdated = 0; diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 7efeadde..4a229546 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -37,7 +37,7 @@ namespace i2p void RouterContext::CreateNewRouter () { m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - SaveKeys (); + SaveKeys (); NewRouterInfo (); } @@ -50,14 +50,14 @@ namespace i2p { port = rand () % (30777 - 9111) + 9111; // I2P network ports range if (port == 9150) port = 9151; // Tor browser - } - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ssu; i2p::config::GetOption("ssu", ssu); - bool ntcp; i2p::config::GetOption("ntcp", ntcp); - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool nat; i2p::config::GetOption("nat", nat); - std::string ifname; i2p::config::GetOption("ifname", ifname); + } + bool ipv4; i2p::config::GetOption("ipv4", ipv4); + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + bool ssu; i2p::config::GetOption("ssu", ssu); + bool ntcp; i2p::config::GetOption("ntcp", ntcp); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool nat; i2p::config::GetOption("nat", nat); + std::string ifname; i2p::config::GetOption("ifname", ifname); std::string ifname4; i2p::config::GetOption("ifname4", ifname4); std::string ifname6; i2p::config::GetOption("ifname6", ifname6); if (ipv4) @@ -79,7 +79,7 @@ namespace i2p } if (ipv6) { - std::string host = "::"; + std::string host = "::1"; if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only i2p::config::GetOption("host", host); else if (!ifname.empty()) @@ -103,11 +103,16 @@ namespace i2p m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); if (ntcp2) // we don't store iv in the address if non published so we must update it from keys - { + { 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 () @@ -120,14 +125,14 @@ namespace i2p void RouterContext::NewNTCP2Keys () { m_StaticKeys.reset (new i2p::crypto::X25519Keys ()); - m_StaticKeys->GenerateKeys (); + m_StaticKeys->GenerateKeys (); m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); m_StaticKeys->GetPrivateKey (m_NTCP2Keys->staticPrivateKey); memcpy (m_NTCP2Keys->staticPublicKey, m_StaticKeys->GetPublicKey (), 32); RAND_bytes (m_NTCP2Keys->iv, 16); // save std::ofstream fk (i2p::fs::DataDirPath (NTCP2_KEYS), std::ofstream::binary | std::ofstream::out); - fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); + fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); } void RouterContext::SetStatus (RouterStatus status) @@ -169,7 +174,7 @@ namespace i2p { if (!m_NTCP2Keys) return; if (!port) - { + { port = rand () % (30777 - 9111) + 9111; // I2P network ports range if (port == 9150) port = 9151; // Tor browser } @@ -186,7 +191,7 @@ namespace i2p } } if (updated) - UpdateRouterInfo (); + UpdateRouterInfo (); } void RouterContext::UpdateNTCP2Address (bool enable) @@ -199,7 +204,7 @@ namespace i2p { found = true; if (!enable) - { + { addresses.erase (it); updated= true; } @@ -207,14 +212,14 @@ namespace i2p } } if (enable && !found) - { - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + { + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); updated = true; } if (updated) UpdateRouterInfo (); } - + void RouterContext::UpdateAddress (const boost::asio::ip::address& host) { bool updated = false; @@ -338,6 +343,51 @@ namespace i2p 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 () { // set caps @@ -347,22 +397,13 @@ namespace i2p caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer m_RouterInfo.SetCaps (caps); - // remove NTCP address - auto& addresses = m_RouterInfo.GetAddresses (); - 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; - } - } + // remove NTCP v4 address + PublishNTCPAddress (false); // delete previous introducers + auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) if (addr->ssu) addr->ssu->introducers.clear (); - // update UpdateRouterInfo (); } @@ -377,27 +418,15 @@ namespace i2p if (m_IsFloodfill) caps |= i2p::data::RouterInfo::eFloodfill; m_RouterInfo.SetCaps (caps); - - auto& addresses = m_RouterInfo.GetAddresses (); // insert NTCP back bool ntcp; i2p::config::GetOption("ntcp", ntcp); - if (ntcp) { - for (const auto& addr : addresses) - { - 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 (ntcp) + PublishNTCPAddress (true); // delete previous introducers + auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) if (addr->ssu) addr->ssu->introducers.clear (); - // update UpdateRouterInfo (); } @@ -486,11 +515,11 @@ namespace i2p if (!found && port) // we have found NTCP2 v4 but not v6 { - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port); + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, host, port); updated = true; } if (updated) - UpdateRouterInfo (); + UpdateRouterInfo (); } void RouterContext::UpdateStats () @@ -533,7 +562,7 @@ namespace i2p } // read NTCP2 keys if available std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); - if (n2k) + if (n2k) { n2k.seekg (0, std::ios::end); len = n2k.tellg(); @@ -541,8 +570,8 @@ namespace i2p if (len == sizeof (NTCP2PrivateKeys)) { m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); - n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); - } + n2k.read ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); + } n2k.close (); } // read RouterInfo @@ -637,7 +666,7 @@ namespace i2p i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () { if (!m_StaticKeys) - { + { if (!m_NTCP2Keys) NewNTCP2Keys (); auto x = new i2p::crypto::X25519Keys (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey); if (!m_StaticKeys) @@ -645,6 +674,6 @@ namespace i2p else delete x; } - return *m_StaticKeys; - } + return *m_StaticKeys; + } } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index f65bc702..19fe08b2 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -81,6 +81,7 @@ namespace i2p void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void PublishNTCP2Address (int port, bool publish = true); void UpdateNTCP2Address (bool enable); + void PublishNTCPAddress (bool publish, bool v4only = true); bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); bool IsUnreachable () const; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index aecda51e..6b520943 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -51,7 +51,7 @@ namespace data 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 (); if (m_RouterIdentity->Verify (buf, l, buf + l)) { @@ -883,7 +883,7 @@ namespace data template std::shared_ptr RouterInfo::GetAddress (Filter filter) const { - // TODO: make it more gereric using comparator + // TODO: make it more generic using comparator #if (BOOST_VERSION >= 105300) auto addresses = boost::atomic_load (&m_Addresses); #else diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index 2197552c..daf7e1e9 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -328,7 +328,11 @@ namespace transport { 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); if (it != sessions->end ()) session = it->second; @@ -750,6 +754,8 @@ namespace transport if (it.second->IsTerminationTimeoutExpired (ts)) { 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] { LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); @@ -776,6 +782,8 @@ namespace transport if (it.second->IsTerminationTimeoutExpired (ts)) { 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] { LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index 255adf42..a7497fd1 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -158,7 +158,7 @@ namespace transport ProcessData (buf + headerSize, len - headerSize); break; case PAYLOAD_TYPE_SESSION_REQUEST: - ProcessSessionRequest (buf, len, senderEndpoint); // buf with header + ProcessSessionRequest (buf, len); // buf with header break; case PAYLOAD_TYPE_SESSION_CREATED: 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"); bool sendRelayTag = true; @@ -212,10 +212,9 @@ namespace transport } 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; } - m_RemoteEndpoint = senderEndpoint; if (!m_DHKeysPair) m_DHKeysPair = transports.GetNextDHKeysPair (); CreateAESandMacKey (buf + headerSize); @@ -1097,7 +1096,7 @@ namespace transport // intro key 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 (); if (addr) memcpy (payload, addr->ssu->key, 32); // intro key diff --git a/libi2pd/SSUSession.h b/libi2pd/SSUSession.h index 7f053d37..8f81838a 100644 --- a/libi2pd/SSUSession.h +++ b/libi2pd/SSUSession.h @@ -80,7 +80,8 @@ namespace transport void Close (); void Done (); 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 (); }; void SendI2NPMessages (const std::vector >& msgs); void SendPeerTest (); // Alice @@ -103,7 +104,7 @@ namespace transport size_t GetSSUHeaderSize (const uint8_t * buf) const; void PostI2NPMessages (std::vector > msgs); 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 SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce); void ProcessSessionCreated (uint8_t * buf, size_t len); @@ -139,7 +140,7 @@ namespace transport friend class SSUData; // TODO: change in later SSUServer& m_Server; - boost::asio::ip::udp::endpoint m_RemoteEndpoint; + const boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::deadline_timer m_ConnectTimer; bool m_IsPeerTest; SessionState m_State; diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index 986cd1ac..21d08f30 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -7,7 +7,8 @@ namespace i2p namespace crypto { #if OPENSSL_EDDSA - EDDSA25519Verifier::EDDSA25519Verifier () + EDDSA25519Verifier::EDDSA25519Verifier (): + m_Pkey (nullptr) { m_MDCtx = EVP_MD_CTX_create (); } diff --git a/libi2pd/Signature.h b/libi2pd/Signature.h index 84d4b0aa..0c5f27d6 100644 --- a/libi2pd/Signature.h +++ b/libi2pd/Signature.h @@ -487,6 +487,42 @@ namespace crypto typedef GOSTR3410Signer GOSTR3410_256_Signer; typedef GOSTR3410Verifier GOSTR3410_512_Verifier; typedef GOSTR3410Signer 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); + } } } diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 4cd1b1bf..7b1f187b 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -290,7 +290,7 @@ namespace stream LogPrint (eLogInfo, "Streaming: offline signature without identity"); 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_TransientVerifier) { @@ -602,7 +602,7 @@ namespace stream size++; // NACK count } size++; // resend delay - htobuf16 (packet + size, 0); // nof flags set + htobuf16 (packet + size, 0); // no flags set size += 2; // flags htobuf16 (packet + size, 0); // no options size += 2; // options size @@ -917,7 +917,12 @@ namespace stream if (leases.empty ()) { 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(m_RemoteIdentity)); + else + m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold } if (!leases.empty ()) diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index 51b6dd4e..acc9cb10 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -182,7 +182,13 @@ namespace util 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; #ifdef _WIN32 gmtime_s(&tm, &t); diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index 4bbcf2de..31dc86aa 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -16,6 +16,7 @@ namespace util uint64_t GetSecondsSinceEpoch (); 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 { diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 67366671..96e8ad4c 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -295,7 +295,10 @@ namespace net { #ifdef WIN32 LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32"); - return boost::asio::ip::address::from_string("127.0.0.1"); + if(ipv6) + return boost::asio::ip::address::from_string("::1"); + else + return boost::asio::ip::address::from_string("127.0.0.1"); #else int af = (ipv6 ? AF_INET6 : AF_INET); ifaddrs * addrs = nullptr; @@ -327,7 +330,7 @@ namespace net std::string fallback; if(ipv6) { - fallback = "::"; + fallback = "::1"; LogPrint(eLogWarning, "NetIface: cannot find ipv6 address for interface ", ifname); } else { fallback = "127.0.0.1"; diff --git a/libi2pd/version.h b/libi2pd/version.h index eccbac8e..4fdb5f27 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -7,7 +7,7 @@ #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 24 +#define I2PD_VERSION_MINOR 25 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) @@ -21,7 +21,7 @@ #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 39 +#define I2P_VERSION_MICRO 40 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index 6dcf063e..75d95da1 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -40,9 +40,9 @@ namespace client void RemoveAddress (const i2p::data::IdentHash& ident); bool Init (); - int Load (std::map& addresses); - int LoadLocal (std::map& addresses); - int Save (const std::map& addresses); + int Load (std::map > & addresses); + int LoadLocal (std::map >& addresses); + int Save (const std::map >& addresses); 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); @@ -50,7 +50,7 @@ namespace client private: - int LoadFromFile (const std::string& filename, std::map& addresses); // returns -1 if can't open file, otherwise number of records + int LoadFromFile (const std::string& filename, std::map >& addresses); // returns -1 if can't open file, otherwise number of records private: @@ -79,7 +79,7 @@ namespace client { if (!m_IsPersist) { - LogPrint(eLogDebug, "Addressbook: Persistance is disabled"); + LogPrint(eLogDebug, "Addressbook: Persistence is disabled"); return nullptr; } std::string filename = storage.Path(ident.ToBase32()); @@ -125,7 +125,7 @@ namespace client storage.Remove( ident.ToBase32() ); } - int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map& addresses) + int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, std::map >& addresses) { int num = 0; 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 addr = s.substr(pos); - i2p::data::IdentHash ident; - ident.FromBase32 (addr); - addresses[name] = ident; + addresses[name] = std::make_shared
(addr); num++; } } return num; } - int AddressBookFilesystemStorage::Load (std::map& addresses) + int AddressBookFilesystemStorage::Load (std::map >& addresses) { int num = LoadFromFile (indexPath, addresses); if (num < 0) @@ -167,7 +165,7 @@ namespace client return num; } - int AddressBookFilesystemStorage::LoadLocal (std::map& addresses) + int AddressBookFilesystemStorage::LoadLocal (std::map >& addresses) { int num = LoadFromFile (localPath, addresses); if (num < 0) return 0; @@ -175,7 +173,7 @@ namespace client return num; } - int AddressBookFilesystemStorage::Save (const std::map& addresses) + int AddressBookFilesystemStorage::Save (const std::map >& addresses) { if (addresses.empty()) { LogPrint(eLogWarning, "Addressbook: not saving empty addressbook"); @@ -190,8 +188,14 @@ namespace client return 0; } - for (const auto& it: addresses) { - f << it.first << "," << it.second.ToBase32 () << std::endl; + for (const auto& it: addresses) + { + f << it.first << ","; + if (it.second->IsIdentHash ()) + f << it.second->identHash.ToBase32 (); + else + f << it.second->blindedPublicKey->ToB33 (); + f << std::endl; num++; } 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(b32); + } + } + + Address::Address (const i2p::data::IdentHash& hash) + { + addressType = eAddressIndentHash; + identHash = hash; + } + AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false), m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) { @@ -268,7 +293,7 @@ namespace client } if (m_IsDownloading) { - LogPrint (eLogInfo, "Addressbook: subscriptions is downloading, abort"); + LogPrint (eLogInfo, "Addressbook: subscriptions are downloading, abort"); for (int i = 0; i < 30; i++) { if (!m_IsDownloading) @@ -291,67 +316,70 @@ namespace client m_Subscriptions.clear (); } - bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident) + std::shared_ptr AddressBook::GetAddress (const std::string& address) { auto pos = address.find(".b32.i2p"); if (pos != std::string::npos) - { - Base32ToByteStream (address.c_str(), pos, ident, 32); - return true; - } + return std::make_shared(address.substr (0, pos)); else { pos = address.find (".i2p"); if (pos != std::string::npos) { - auto identHash = FindAddress (address); - if (identHash) - { - ident = *identHash; - return true; - } - else - { - LookupAddress (address); // TODO: - return false; - } - } - } + auto addr = FindAddress (address); + if (!addr) + LookupAddress (address); // TODO: + return addr; + } + } // if not .b32 we assume full base64 address i2p::data::IdentityEx dest; if (!dest.FromBase64 (address)) - return false; - ident = dest.GetIdentHash (); - return true; + return nullptr; + return std::make_shared(dest.GetIdentHash ()); } - const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address) + std::shared_ptr AddressBook::FindAddress (const std::string& address) { auto it = m_Addresses.find (address); if (it != m_Addresses.end ()) - return &it->second; + return it->second; 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 ident = std::make_shared(); - ident->FromBase64 (base64); - m_Storage->AddAddress (ident); - m_Addresses[address] = ident->GetIdentHash (); - LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ())); + auto pos = jump.find(".b32.i2p"); + if (pos != std::string::npos) + { + m_Addresses[address] = std::make_shared
(jump.substr (0, pos)); + LogPrint (eLogInfo, "Addressbook: added ", address," -> ", jump); + } + else + { + // assume base64 + auto ident = std::make_shared(); + if (ident->FromBase64 (jump)) + { + m_Storage->AddAddress (ident); + m_Addresses[address] = std::make_shared
(ident->GetIdentHash ()); + LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ())); + } + else + LogPrint (eLogError, "Addressbook: malformed address ", jump); + } } - void AddressBook::InsertAddress (std::shared_ptr address) + void AddressBook::InsertFullAddress (std::shared_ptr address) { m_Storage->AddAddress (address); } - std::shared_ptr AddressBook::GetAddress (const std::string& address) + std::shared_ptr AddressBook::GetFullAddress (const std::string& address) { - i2p::data::IdentHash ident; - if (!GetIdentHash (address, ident)) return nullptr; - return m_Storage->GetAddress (ident); + auto addr = GetAddress (address); + if (!addr || !addr->IsIdentHash ()) return nullptr; + return m_Storage->GetAddress (addr->identHash); } void AddressBook::LoadHosts () @@ -408,16 +436,17 @@ namespace client auto it = m_Addresses.find (name); 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); LogPrint (eLogInfo, "Addressbook: updated host: ", name); } } else { - m_Addresses.insert (std::make_pair (name, ident->GetIdentHash ())); + //m_Addresses.emplace (name, std::make_shared
(ident->GetIdentHash ())); + m_Addresses[name] = std::make_shared
(ident->GetIdentHash ()); // for gcc 4.7 m_Storage->AddAddress (ident); if (is_update) LogPrint (eLogInfo, "Addressbook: added new host: ", name); @@ -472,32 +501,33 @@ namespace client void AddressBook::LoadLocal () { - std::map localAddresses; + std::map> localAddresses; m_Storage->LoadLocal (localAddresses); for (const auto& it: localAddresses) { + if (!it.second->IsIdentHash ()) continue; // skip blinded for now auto dot = it.first.find ('.'); if (dot != std::string::npos) { auto domain = it.first.substr (dot + 1); 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) { // address is ours std::shared_ptr resolver; - auto it2 = m_Resolvers.find (it1->second); + auto it2 = m_Resolvers.find (it1->second->identHash); if (it2 != m_Resolvers.end ()) resolver = it2->second; // resolver exists else { // create new resolver resolver = std::make_shared(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) { - const i2p::data::IdentHash * ident = nullptr; + std::shared_ptr addr; auto dot = address.find ('.'); if (dot != std::string::npos) - ident = FindAddress (address.substr (dot + 1)); - if (!ident) + addr = FindAddress (address.substr (dot + 1)); + if (!addr || !addr->IsIdentHash ()) // TODO: { LogPrint (eLogError, "Addressbook: Can't find domain for ", address); return; @@ -649,14 +679,14 @@ namespace client std::unique_lock l(m_LookupsMutex); 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; uint8_t * buf = new uint8_t[len]; memset (buf, 0, 4); htobe32buf (buf + 4, nonce); buf[8] = 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; } } @@ -686,7 +716,7 @@ namespace client // TODO: verify from i2p::data::IdentHash hash(buf + 8); if (!hash.IsZero ()) - m_Addresses[address] = hash; + m_Addresses[address] = std::make_shared
(hash); else LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found"); } @@ -708,14 +738,19 @@ namespace client i2p::http::URL url; // must be run in separate thread 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); 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); return false; } + else + m_Ident = addr->identHash; /* this code block still needs some love */ std::condition_variable newDataReceived; std::mutex newDataReceivedMutex; diff --git a/libi2pd_client/AddressBook.h b/libi2pd_client/AddressBook.h index da640688..47ad9993 100644 --- a/libi2pd_client/AddressBook.h +++ b/libi2pd_client/AddressBook.h @@ -13,6 +13,7 @@ #include "Identity.h" #include "Log.h" #include "Destination.h" +#include "LeaseSet.h" namespace i2p { @@ -28,6 +29,19 @@ namespace client const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53; 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 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"); } class AddressBookStorage // interface for storage @@ -40,9 +54,9 @@ namespace client virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; virtual bool Init () = 0; - virtual int Load (std::map& addresses) = 0; - virtual int LoadLocal (std::map& addresses) = 0; - virtual int Save (const std::map& addresses) = 0; + virtual int Load (std::map >& addresses) = 0; + virtual int LoadLocal (std::map >& addresses) = 0; + virtual int Save (const std::map >& addresses) = 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; @@ -60,12 +74,12 @@ namespace client void Start (); void StartResolvers (); void Stop (); - bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident); - std::shared_ptr GetAddress (const std::string& address); - const i2p::data::IdentHash * FindAddress (const std::string& address); + std::shared_ptr GetAddress (const std::string& address); + std::shared_ptr GetFullAddress (const std::string& address); + std::shared_ptr FindAddress (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 (std::shared_ptr address); + void InsertAddress (const std::string& address, const std::string& jump); // for jump links + void InsertFullAddress (std::shared_ptr address); 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); @@ -93,7 +107,7 @@ namespace client private: std::mutex m_AddressBookMutex; - std::map m_Addresses; + std::map > m_Addresses; std::map > m_Resolvers; // local destination->resolver std::mutex m_LookupsMutex; std::map m_Lookups; // nonce -> address diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index 2a18c8ba..94d74726 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -72,19 +72,26 @@ namespace client 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->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); - i2p::data::IdentHash ident; - if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident)) + auto addr = context.GetAddressBook ().GetAddress (receiver->buffer); + if (!addr) { LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found"); return; } - auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident); - if (leaseSet) - CreateConnection (receiver, leaseSet); + if (addr->IsIdentHash ()) + { + auto leaseSet = GetLocalDestination ()->FindLeaseSet (addr->identHash); + if (leaseSet) + CreateConnection (receiver, leaseSet); + else + GetLocalDestination ()->RequestDestination (addr->identHash, + std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, + this, std::placeholders::_1, receiver)); + } else - GetLocalDestination ()->RequestDestination (ident, - std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, - this, std::placeholders::_1, receiver)); + GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, + std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, + this, std::placeholders::_1, receiver)); } else { @@ -540,29 +547,37 @@ namespace client void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: lookup ", operand); - i2p::data::IdentHash ident; - if (!context.GetAddressBook ().GetIdentHash (operand, ident)) + auto addr = context.GetAddressBook ().GetAddress (operand); + if (!addr) { SendReplyError ("Address Not found"); return; } auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); - auto leaseSet = localDestination->FindLeaseSet (ident); - if (leaseSet) - SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ()); - else - { - auto s = shared_from_this (); - localDestination->RequestDestination (ident, - [s](std::shared_ptr ls) + if (addr->IsIdentHash ()) + { + // 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 requstCallback = + [s](std::shared_ptr ls) { if (ls) s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); else 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) diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index c69e419e..3292419a 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -39,7 +39,7 @@ namespace client if (!m_SharedLocalDestination) CreateNewSharedLocalDestination (); - // addressbook + // addressbook m_AddressBook.Start (); // HTTP proxy @@ -207,16 +207,16 @@ namespace client { m_HttpProxy->Stop (); m_HttpProxy = nullptr; - } + } ReadHttpProxy (); - + // recreate SOCKS proxy if (m_SocksProxy) { m_SocksProxy->Stop (); m_SocksProxy = nullptr; - } - ReadSocksProxy (); + } + ReadSocksProxy (); // delete unused destinations std::unique_lock l(m_DestinationsMutex); @@ -391,10 +391,10 @@ namespace client options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY); options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY); - options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); + options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, ""); - if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; + if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; } void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const @@ -418,21 +418,21 @@ namespace client { int numClientTunnels = 0, numServerTunnels = 0; std::string tunConf; i2p::config::GetOption("tunconf", tunConf); - if (tunConf.empty ()) + if (tunConf.empty ()) { // TODO: cleanup this in 2.8.0 tunConf = i2p::fs::DataDirPath ("tunnels.cfg"); if (i2p::fs::Exists(tunConf)) LogPrint(eLogWarning, "Clients: please rename tunnels.cfg -> tunnels.conf here: ", tunConf); - else + else tunConf = i2p::fs::DataDirPath ("tunnels.conf"); } LogPrint(eLogDebug, "Clients: tunnels config file: ", tunConf); ReadTunnels (tunConf, numClientTunnels, numServerTunnels); - + std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir); if (tunDir.empty ()) - tunDir = i2p::fs::DataDirPath ("tunnels.d"); + tunDir = i2p::fs::DataDirPath ("tunnels.d"); if (i2p::fs::Exists (tunDir)) { std::vector files; @@ -450,7 +450,7 @@ namespace client LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created"); } - + void ClientContext::ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels) { boost::property_tree::ptree pt; @@ -540,7 +540,8 @@ namespace client { // http proxy std::string outproxy = section.second.get("outproxy", ""); - auto tun = std::make_shared(name, address, port, outproxy, localDestination); + bool addresshelper = section.second.get("addresshelper", true); + auto tun = std::make_shared(name, address, port, outproxy, addresshelper, localDestination); clientTunnel = tun; clientEndpoint = tun->GetLocalEndpoint (); } @@ -555,7 +556,7 @@ namespace client { // tcp client auto tun = std::make_shared (name, dest, address, port, localDestination, destinationPort); - clientTunnel = tun; + clientTunnel = tun; clientEndpoint = tun->GetLocalEndpoint (); } uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); @@ -674,7 +675,7 @@ namespace client serverTunnel->SetAccessList (idents); } auto ins = m_ServerTunnels.insert (std::make_pair ( - std::make_pair (localDestination->GetIdentHash (), inPort), + std::make_pair (localDestination->GetIdentHash (), inPort), serverTunnel)); if (ins.second) { @@ -716,6 +717,7 @@ namespace client uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); 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); if (httpProxyKeys.length () > 0) { @@ -732,7 +734,7 @@ namespace client } 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(); } catch (std::exception& e) @@ -741,7 +743,7 @@ namespace client } } } - + void ClientContext::ReadSocksProxy () { std::shared_ptr localDestination; diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index 5fbefece..df4895a4 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -64,44 +64,47 @@ namespace proxy { void HostNotFound(std::string & host); void SendProxyError(std::string & content); - void ForwardToUpstreamProxy(); - void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); - void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec); - void HTTPConnect(const std::string & host, uint16_t port); - void HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream); + void ForwardToUpstreamProxy(); + void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); + void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec); + void HTTPConnect(const std::string & host, uint16_t port); + void HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream); - void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered); - void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered); + void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered); + void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered); - typedef std::function ProxyResolvedHandler; + typedef std::function ProxyResolvedHandler; - void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); + void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler); - void SocksProxySuccess(); - void HandoverToUpstreamProxy(); + void SocksProxySuccess(); + void HandoverToUpstreamProxy(); uint8_t m_recv_chunk[8192]; std::string m_recv_buf; // from client std::string m_send_buf; // to upstream std::shared_ptr m_sock; - std::shared_ptr m_proxysock; - boost::asio::ip::tcp::resolver m_proxy_resolver; - std::string m_OutproxyUrl; - i2p::http::URL m_ProxyURL; - i2p::http::URL m_RequestURL; - uint8_t m_socks_buf[255+8]; // for socks request/response - ssize_t m_req_len; - i2p::http::URL m_ClientRequestURL; - i2p::http::HTTPReq m_ClientRequest; - i2p::http::HTTPRes m_ClientResponse; - std::stringstream m_ClientRequestBuffer; + std::shared_ptr m_proxysock; + boost::asio::ip::tcp::resolver m_proxy_resolver; + std::string m_OutproxyUrl; + bool m_Addresshelper; + i2p::http::URL m_ProxyURL; + i2p::http::URL m_RequestURL; + uint8_t m_socks_buf[255+8]; // for socks request/response + ssize_t m_req_len; + i2p::http::URL m_ClientRequestURL; + i2p::http::HTTPReq m_ClientRequest; + i2p::http::HTTPRes m_ClientResponse; + std::stringstream m_ClientRequestBuffer; + public: HTTPReqHandler(HTTPProxy * parent, std::shared_ptr sock) : I2PServiceHandler(parent), m_sock(sock), m_proxysock(std::make_shared(parent->GetService())), m_proxy_resolver(parent->GetService()), - m_OutproxyUrl(parent->GetOutproxyURL()) {} + m_OutproxyUrl(parent->GetOutproxyURL()), + m_Addresshelper(parent->GetHelperSupport()) {} ~HTTPReqHandler() { Terminate(); } void Handle () { AsyncSockRead(); } /* overload */ }; @@ -114,8 +117,8 @@ namespace proxy { return; } m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)), - std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); + std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(), + std::placeholders::_1, std::placeholders::_2)); } void HTTPReqHandler::Terminate() { @@ -208,7 +211,7 @@ namespace proxy { void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req) { /* drop common headers */ - req.RemoveHeader("Referer"); + req.RemoveHeader("Referrer"); req.RemoveHeader("Via"); req.RemoveHeader("From"); req.RemoveHeader("Forwarded"); @@ -221,7 +224,7 @@ namespace proxy { /* add headers */ /* close connection, if not Connection: (U|u)pgrade (for websocket) */ auto h = req.GetHeader ("Connection"); - auto x = h.find("pgrade"); + auto x = h.find("pgrade"); if (!(x != std::string::npos && std::tolower(h[x - 1]) == 'u')) req.UpdateHeader("Connection", "close"); } @@ -234,8 +237,6 @@ namespace proxy { */ bool HTTPReqHandler::HandleRequest() { - std::string b64; - m_req_len = m_ClientRequest.parse(m_recv_buf); if (m_req_len == 0) @@ -252,10 +253,10 @@ namespace proxy { m_RequestURL.parse(m_ClientRequest.uri); 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 (!addresshelper) + if (!m_Addresshelper) { LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected"); GenericProxyError("Invalid request", "addresshelper is not supported"); @@ -263,8 +264,8 @@ namespace proxy { } if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) { - i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64); - LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host); + i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump); + LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host); std::string full_url = m_RequestURL.to_string(); std::stringstream ss; ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. " @@ -276,7 +277,7 @@ namespace proxy { { std::stringstream ss; ss << "Host " << m_RequestURL.host << " already in router's addressbook. " - << "Click here to update record."; + << "Click here to update record."; GenericProxyInfo("Addresshelper found", ss.str().c_str()); return true; /* request processed */ } @@ -339,9 +340,8 @@ namespace proxy { } } /* check dest_host really exists and inside I2P network */ - i2p::data::IdentHash identHash; 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); return true; /* request processed */ } @@ -400,9 +400,9 @@ namespace proxy { m_ClientRequest.write(m_ClientRequestBuffer); m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); - + // assume http if empty schema - if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") + if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") { // handle upstream http proxy if (!m_ProxyURL.port) m_ProxyURL.port = 80; @@ -430,8 +430,8 @@ namespace proxy { m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); })); } - } - else if (m_ProxyURL.schema == "socks") + } + else if (m_ProxyURL.schema == "socks") { // handle upstream socks proxy if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified @@ -439,8 +439,8 @@ namespace proxy { m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); })); - } - else + } + else { // unknown type, complain GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str()); @@ -511,7 +511,7 @@ namespace proxy { std::string hostname(host); if(str_rmatch(hostname, ".i2p")) GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleHTTPConnectStreamRequestComplete, - shared_from_this(), std::placeholders::_1), host, port); + shared_from_this(), std::placeholders::_1), host, port); else ForwardToUpstreamProxy(); } @@ -623,9 +623,9 @@ namespace proxy { Done (shared_from_this()); } - HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, std::shared_ptr localDestination): - TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), - m_Name (name), m_OutproxyUrl(outproxy) + HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination): + TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), + m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper) { } diff --git a/libi2pd_client/HTTPProxy.h b/libi2pd_client/HTTPProxy.h index 04a43914..590166e3 100644 --- a/libi2pd_client/HTTPProxy.h +++ b/libi2pd_client/HTTPProxy.h @@ -6,12 +6,13 @@ namespace proxy { class HTTPProxy: public i2p::client::TCPIPAcceptor { public: - HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, std::shared_ptr localDestination); + HTTPProxy(const std::string& name, const std::string& address, int port, const std::string & outproxy, bool addresshelper, std::shared_ptr localDestination); HTTPProxy(const std::string& name, const std::string& address, int port, std::shared_ptr localDestination = nullptr) : - HTTPProxy(name, address, port, "", localDestination) {} ; + HTTPProxy(name, address, port, "", true, localDestination) {} ; ~HTTPProxy() {}; std::string GetOutproxyURL() const { return m_OutproxyUrl; } + bool GetHelperSupport() { return m_Addresshelper; } protected: // Implements TCPIPAcceptor @@ -21,6 +22,7 @@ namespace proxy { private: std::string m_Name; std::string m_OutproxyUrl; + bool m_Addresshelper; }; } // http } // i2p diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index e507b066..78190c89 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -70,7 +70,9 @@ namespace client void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len) { - auto ls = std::make_shared (storeType, m_Identity, buf, len); + auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ? + std::make_shared (m_Identity, buf, len): + std::make_shared (storeType, m_Identity, buf, len); ls->SetExpirationTime (m_LeaseSetExpirationTime); SetLeaseSet (ls); } @@ -368,7 +370,7 @@ namespace client size_t offset = identity->FromBuffer (buf, len); if (!offset) { - LogPrint (eLogError, "I2CP: create session maformed identity"); + LogPrint (eLogError, "I2CP: create session malformed identity"); SendSessionStatusMessage (3); // invalid return; } @@ -528,21 +530,35 @@ namespace client if (m_Destination) { uint8_t storeType = buf[offset]; offset++; // store type - // TODO: parse LS2 and obtain correct private keys lengths - size_t signingPrivateKeyLength = 0, encryptionPrivateKeyLength = 0; - if (storeType != i2p::data::NETDB_STORE_TYPE_META_LEASESET2) // no private keys for meta + i2p::data::LeaseSet2 ls (storeType, buf + offset, len - offset); // outer layer only for encrypted + if (!ls.IsValid ()) { - signingPrivateKeyLength = m_Destination->GetIdentity ()->GetSigningPrivateKeyLen (); // no offline keys - encryptionPrivateKeyLength = 256; // ElGamal only - if (len < offset + signingPrivateKeyLength + encryptionPrivateKeyLength) + LogPrint (eLogError, "I2CP: invalid LeaseSet2 of type ", storeType); + return; + } + offset += ls.GetBufferLen (); + // 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) { - LogPrint (eLogError, "I2CP: CreateLeaseSet2 message is too short ", len); - return; + currentKeyType = keyType; + currentKey = buf + offset; } - m_Destination->SetEncryptionPrivateKey (buf + len - encryptionPrivateKeyLength); - // ignore signing private key - } - 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 @@ -603,12 +619,16 @@ namespace client case 1: // address { 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"); SendHostReplyMessage (requestID, nullptr); return; } + else + ident = addr->identHash; break; } default: diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 7a89a24a..0d235161 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -36,7 +36,7 @@ namespace client const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3; const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37; 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_EXPIRES_MESSAGE = 36; const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31; diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 40df0161..7157020f 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -101,9 +101,9 @@ namespace client void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { assert(streamRequestComplete); - i2p::data::IdentHash identHash; - if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash)) - CreateStream(streamRequestComplete, identHash, port); + auto address = i2p::client::context.GetAddressBook ().GetAddress (dest); + if (address) + CreateStream(streamRequestComplete, address, port); else { 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 address, int port) { - if(m_ConnectTimeout) + if(m_ConnectTimeout && !m_LocalDestination->IsReady()) { - if(m_LocalDestination->IsReady()) - m_LocalDestination->CreateStream (streamRequestComplete, identHash, port); - else - { - AddReadyCallback([this, streamRequestComplete, identHash, port] (const boost::system::error_code & ec) { - if(ec) - { - LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message()); - streamRequestComplete(nullptr); - } + AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) { + if(ec) + { + LogPrint(eLogWarning, "I2PService::CeateStream() ", ec.message()); + streamRequestComplete(nullptr); + } + else + { if (address->IsIdentHash ()) + this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port); else - this->m_LocalDestination->CreateStream(streamRequestComplete, identHash, port); - }); - } + this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); + } + }); } 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 upstream, std::shared_ptr downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream) diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h index 55921898..e0dfd2da 100644 --- a/libi2pd_client/I2PService.h +++ b/libi2pd_client/I2PService.h @@ -8,6 +8,7 @@ #include #include "Destination.h" #include "Identity.h" +#include "AddressBook.h" namespace i2p { @@ -49,7 +50,7 @@ namespace client m_LocalDestination = dest; } 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 address, int port); inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); } virtual void Start () = 0; diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index 65600cf8..290ce11e 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -393,15 +393,15 @@ namespace client class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this { public: - I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination, + I2PClientTunnelHandler (I2PClientTunnel * parent, std::shared_ptr address, int destinationPort, std::shared_ptr socket): - I2PServiceHandler(parent), m_DestinationIdentHash(destination), + I2PServiceHandler(parent), m_Address(address), m_DestinationPort (destinationPort), m_Socket(socket) {}; void Handle(); void Terminate(); private: void HandleStreamRequestComplete (std::shared_ptr stream); - i2p::data::IdentHash m_DestinationIdentHash; + std::shared_ptr m_Address; int m_DestinationPort; std::shared_ptr m_Socket; }; @@ -410,7 +410,7 @@ namespace client { GetOwner()->CreateStream ( std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), - m_DestinationIdentHash, m_DestinationPort); + m_Address, m_DestinationPort); } void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr stream) @@ -445,43 +445,39 @@ namespace client I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, const std::string& address, int port, std::shared_ptr localDestination, int destinationPort): TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), - m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort) + m_DestinationPort (destinationPort) { } void I2PClientTunnel::Start () { TCPIPAcceptor::Start (); - GetIdentHash(); + GetAddress (); } void I2PClientTunnel::Stop () { TCPIPAcceptor::Stop(); - auto *originalIdentHash = m_DestinationIdentHash; - m_DestinationIdentHash = nullptr; - delete originalIdentHash; + m_Address = nullptr; } /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ - const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash () + std::shared_ptr I2PClientTunnel::GetAddress () { - if (!m_DestinationIdentHash) + if (!m_Address) { - i2p::data::IdentHash identHash; - if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash)) - m_DestinationIdentHash = new i2p::data::IdentHash (identHash); - else + m_Address = i2p::client::context.GetAddressBook ().GetAddress (m_Destination); + if (!m_Address) LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found"); } - return m_DestinationIdentHash; + return m_Address; } std::shared_ptr I2PClientTunnel::CreateHandler(std::shared_ptr socket) { - const i2p::data::IdentHash *identHash = GetIdentHash(); - if (identHash) - return std::make_shared(this, *identHash, m_DestinationPort, socket); + auto address = GetAddress (); + if (address) + return std::make_shared(this, address, m_DestinationPort, socket); else return nullptr; } @@ -814,9 +810,9 @@ namespace client void I2PUDPClientTunnel::TryResolving() { 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 addr; + while(!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve) { LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest); 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"); 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()); } diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index 0cff9ad5..0d1ac9a8 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -13,6 +13,7 @@ #include "Datagram.h" #include "Streaming.h" #include "I2PService.h" +#include "AddressBook.h" namespace i2p { @@ -129,11 +130,11 @@ namespace client const char* GetName() { return m_Name.c_str (); } private: - const i2p::data::IdentHash * GetIdentHash (); + std::shared_ptr GetAddress (); private: std::string m_Name, m_Destination; - const i2p::data::IdentHash * m_DestinationIdentHash; + std::shared_ptr m_Address; int m_DestinationPort; }; diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index 68f3759e..fa08ec51 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -14,13 +14,13 @@ namespace client 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); if(ls) - { HandleFoundCurrentLeaseSet(ls); - } else RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1)); } diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index de900fe1..acb9ea80 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -467,7 +467,7 @@ namespace client size_t l = dest->FromBase64(destination); if (l > 0) { - context.GetAddressBook().InsertAddress(dest); + context.GetAddressBook().InsertFullAddress(dest); auto leaseSet = session->localDestination->FindLeaseSet(dest->GetIdentHash()); if (leaseSet) Connect(leaseSet); @@ -479,7 +479,7 @@ namespace client } } 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 SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); @@ -610,22 +610,29 @@ namespace client ExtractParams (buf, params); std::string& name = params[SAM_PARAM_NAME]; std::shared_ptr identity; - i2p::data::IdentHash ident; + std::shared_ptr addr; auto session = m_Owner.FindSession(m_ID); auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination; if (name == "ME") SendNamingLookupReply (dest->GetIdentity ()); - else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr) + else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr) SendNamingLookupReply (identity); - else if (context.GetAddressBook ().GetIdentHash (name, ident)) + else if ((addr = context.GetAddressBook ().GetAddress (name))) { - auto leaseSet = dest->FindLeaseSet (ident); - if (leaseSet) - SendNamingLookupReply (leaseSet->GetIdentity ()); + if (addr->IsIdentHash ()) + { + auto leaseSet = dest->FindLeaseSet (addr->identHash); + if (leaseSet) + SendNamingLookupReply (leaseSet->GetIdentity ()); + else + dest->RequestDestination (addr->identHash, + std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, + shared_from_this (), std::placeholders::_1, name)); + } else - dest->RequestDestination (ident, + dest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, ident)); + shared_from_this (), std::placeholders::_1, name)); } else { @@ -650,22 +657,20 @@ namespace client SendMessageReply (m_Buffer, len, true); } - void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) + void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, std::string name) { if (leaseSet) { - context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ()); + context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ()); SendNamingLookupReply (leaseSet->GetIdentity ()); } 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 - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, - context.GetAddressBook ().ToAddress (ident).c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, - context.GetAddressBook ().ToAddress (ident).c_str()); + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif SendMessageReply (m_Buffer, len, false); } @@ -844,7 +849,7 @@ namespace client m_SocketType = eSAMSocketTypeStream; m_IsAccepting = false; m_Stream = stream; - context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); + context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ()); auto session = m_Owner.FindSession (m_ID); if (session) { diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 4506632c..7cd3fd4c 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -35,6 +35,7 @@ namespace client const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; 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_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_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; @@ -123,7 +124,7 @@ namespace client void Connect (std::shared_ptr remote); void HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet); void SendNamingLookupReply (std::shared_ptr identity); - void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident); + void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, std::string name); void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); void SendSessionCreateReplyOk (); diff --git a/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml b/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml index d960ac0d..e9f35676 100644 --- a/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml +++ b/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml @@ -35,6 +35,7 @@ +