Merge pull request #1229 from PurpleI2P/openssl

2.20
This commit is contained in:
orignal 2018-08-23 11:21:49 -04:00 committed by GitHub
commit 73b6338f62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 417 additions and 193 deletions

View File

@ -1,6 +1,17 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.20.0] - 2018-08-23
### Added
- Full implementation of NTCP2
- Assets for android
### Changed
- armeabi-v7a and x86 in one apk for android
- NTCP2 is enabled by default
- Show lease's expiration time in readable format in the web console
### Fixed
- Correct names for transports in the web console
## [2.19.0] - 2018-06-26
### Added
- ECIES support for RouterInfo

View File

@ -31,6 +31,14 @@ endif
# Disabled, since it will be the default make rule. I think its better
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
#install: all
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
# cp -r i2p ${PREFIX}/
install-brew: all
install -d ${PREFIX}/bin ${PREFIX}/etc/i2pd ${PREFIX}/share/doc/i2pd ${PREFIX}/share/i2pd ${PREFIX}/share/man/man1 ${PREFIX}/var/lib/i2pd
install -m 755 ${I2PD} ${PREFIX}/bin/
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
@gzip debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf

View File

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

View File

@ -3,7 +3,7 @@
package="org.purplei2p.i2pd"
android:installLocation="auto"
android:versionCode="1"
android:versionName="2.19.0">
android:versionName="2.20.0">
<uses-sdk
android:minSdkVersion="14"

View File

@ -15,12 +15,15 @@ ipv6 = false
# ntcpproxy = http://127.0.0.1:8118
# ssu = true
bandwidth = O
bandwidth = L
# share = 100
# notransit = true
# floodfill = true
[ntcp2]
enabled = true
[http]
enabled = true
address = 127.0.0.1

View File

@ -25,7 +25,7 @@ android {
targetSdkVersion 28
minSdkVersion 14
versionCode 1
versionName "2.19.0"
versionName "2.20.0"
ndk {
abiFilters 'armeabi-v7a'
abiFilters 'x86'

View File

@ -68,6 +68,6 @@ include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := miniupnpc
LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnp-2.0/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a
LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnp-2.0/include
LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnpc-2.1/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a
LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnpc-2.1/include
include $(PREBUILT_STATIC_LIBRARY)

View File

@ -1,8 +1,8 @@
#APP_ABI := all
#APP_ABI := armeabi-v7a x86
APP_ABI := armeabi-v7a x86
#APP_ABI := x86
#APP_ABI := x86_64
APP_ABI := armeabi-v7a
#APP_ABI := armeabi-v7a
#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

View File

@ -1,10 +1,13 @@
#include "DaemonAndroid.h"
#include "Daemon.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <exception>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/exception_ptr.hpp>
#include <exception>
//#include "mainwindow.h"
#include "FS.h"
#include "DaemonAndroid.h"
#include "Daemon.h"
namespace i2p
{
@ -80,6 +83,17 @@ namespace android
//mutex=new QMutex(QMutex::Recursive);
//setRunningCallback(0);
//m_IsRunning=false;
// make sure assets are ready before proceed
i2p::fs::DetectDataDir("", false);
int numAttempts = 0;
do
{
if (i2p::fs::Exists (i2p::fs::DataDirPath("assets.ready"))) break; // assets ready
numAttempts++;
std::this_thread::sleep_for (std::chrono::seconds(1)); // otherwise wait for 1 more second
}
while (numAttempts <= 10); // 10 seconds max
return Daemon.init(argc,argv);
}

View File

@ -1,6 +1,9 @@
package org.purplei2p.i2pd;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -25,21 +28,27 @@ import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
// For future package update checking
import org.purplei2p.i2pd.BuildConfig;
public class I2PDActivity extends Activity {
private static final String TAG = "i2pdActvt";
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
private TextView textView;
private boolean assetsCopied;
private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/";
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener =
new DaemonSingleton.StateUpdateListener() {
@Override
public void daemonStateUpdate() {
public void daemonStateUpdate()
{
processAssets();
runOnUiThread(new Runnable(){
@Override
public void run() {
try {
@ -55,7 +64,7 @@ public class I2PDActivity extends Activity {
(DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+
(DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"")
);
} catch (Throwable tr) {
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
}
@ -64,7 +73,7 @@ public class I2PDActivity extends Activity {
};
private static volatile long graceStartedMillis;
private static final Object graceStartedMillis_LOCK=new Object();
private static String formatGraceTimeRemaining() {
long remainingSeconds;
synchronized (graceStartedMillis_LOCK){
@ -74,25 +83,19 @@ public class I2PDActivity extends Activity {
long remSec=remainingSeconds-remainingMinutes*60;
return remainingMinutes+":"+(remSec/10)+remSec%10;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// copy assets
copyAsset("certificates");
copyAsset("i2pd.conf");
copyAsset("subsciptions.txt");
copyAsset("tunnels.conf");
textView = new TextView(this);
setContentView(textView);
daemon.addStateChangeListener(daemonStateUpdatedListener);
daemonStateUpdatedListener.daemonStateUpdate();
// set the app be foreground
doBindService();
final Timer gracefulQuitTimer = getGracefulQuitTimer();
if(gracefulQuitTimer!=null){
long gracefulStopAtMillis;
@ -102,7 +105,7 @@ public class I2PDActivity extends Activity {
rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
@ -115,7 +118,7 @@ public class I2PDActivity extends Activity {
Log.e(TAG, "", tr);
}
}
private static void cancelGracefulStop() {
Timer gracefulQuitTimer = getGracefulQuitTimer();
if(gracefulQuitTimer!=null) {
@ -123,7 +126,7 @@ public class I2PDActivity extends Activity {
setGracefulQuitTimer(null);
}
}
private CharSequence throwableToString(Throwable tr) {
StringWriter sw = new StringWriter(8192);
PrintWriter pw = new PrintWriter(sw);
@ -131,9 +134,9 @@ public class I2PDActivity extends Activity {
pw.close();
return sw.toString();
}
// private LocalService mBoundService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
@ -142,12 +145,12 @@ public class I2PDActivity extends Activity {
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
// mBoundService = ((LocalService.LocalBinder)service).getService();
// Tell the user about this for our demo.
// Toast.makeText(Binding.this, R.string.local_service_connected,
// Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
@ -158,10 +161,9 @@ public class I2PDActivity extends Activity {
// Toast.LENGTH_SHORT).show();
}
};
private static volatile boolean mIsBound;
private void doBindService() {
synchronized (I2PDActivity.class) {
if (mIsBound) return;
@ -173,7 +175,7 @@ public class I2PDActivity extends Activity {
mIsBound = true;
}
}
private void doUnbindService() {
synchronized (I2PDActivity.class) {
if (mIsBound) {
@ -183,21 +185,21 @@ public class I2PDActivity extends Activity {
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.options_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch(id){
case R.id.action_stop:
i2pdStop();
@ -206,14 +208,14 @@ public class I2PDActivity extends Activity {
i2pdGracefulStop();
return true;
}
return super.onOptionsItemSelected(item);
}
private void i2pdStop() {
cancelGracefulStop();
new Thread(new Runnable(){
@Override
public void run() {
Log.d(TAG, "stopping");
@ -223,12 +225,12 @@ public class I2PDActivity extends Activity {
Log.e(TAG, "", tr);
}
}
},"stop").start();
}
private static volatile Timer gracefulQuitTimer;
private void i2pdGracefulStop() {
if(daemon.getState()==DaemonSingleton.State.stopped){
Toast.makeText(this, R.string.already_stopped,
@ -243,7 +245,7 @@ public class I2PDActivity extends Activity {
Toast.makeText(this, R.string.graceful_stop_is_in_progress,
Toast.LENGTH_SHORT).show();
new Thread(new Runnable(){
@Override
public void run() {
try{
@ -263,21 +265,21 @@ public class I2PDActivity extends Activity {
Log.e(TAG,"",tr);
}
}
},"gracInit").start();
}
private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) {
if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel();
final Timer gracefulQuitTimer = new Timer(true);
setGracefulQuitTimer(gracefulQuitTimer);
gracefulQuitTimer.schedule(new TimerTask(){
@Override
public void run() {
i2pdStop();
}
}, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis()));
final TimerTask tickerTask = new TimerTask() {
@Override
@ -287,30 +289,30 @@ public class I2PDActivity extends Activity {
};
gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/);
}
private static Timer getGracefulQuitTimer() {
return gracefulQuitTimer;
}
private static void setGracefulQuitTimer(Timer gracefulQuitTimer) {
I2PDActivity.gracefulQuitTimer = gracefulQuitTimer;
}
/**
* Copy the asset at the specified path to this app's data directory. If the
* asset is a directory, its contents are also copied.
*
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyAsset(String path) {
AssetManager manager = getAssets();
// If we have a directory, we make it and recurse. If a file, we copy its
// contents.
try {
String[] contents = manager.list(path);
// The documentation suggests that list throws an IOException, but doesn't
// say under what conditions. It'd be nice if it did so when the path was
// to a file. That doesn't appear to be the case. If the returned array is
@ -318,30 +320,30 @@ public class I2PDActivity extends Activity {
// directories will get turned into files.
if (contents == null || contents.length == 0)
throw new IOException();
// Make the directory.
File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/", path);
File dir = new File(i2pdpath, path);
dir.mkdirs();
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + "/" + entry);
}
} catch (IOException e) {
} catch (IOException e) {
copyFileAsset(path);
}
}
/**
* Copy the asset file specified by path to app's data directory. Assumes
* parent directories have already been created.
*
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyFileAsset(String path) {
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/", path);
try {
File file = new File(i2pdpath, path);
if(!file.exists()) try {
InputStream in = getAssets().open(path);
OutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[1024];
@ -352,8 +354,66 @@ public class I2PDActivity extends Activity {
}
out.close();
in.close();
} catch (IOException e) {
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
for (File child : fileOrDirectory.listFiles()) {
deleteRecursive(child);
}
}
fileOrDirectory.delete();
}
private void processAssets() {
if (!assetsCopied) try {
assetsCopied = true; // prevent from running on every state update
File holderfile = new File(i2pdpath, "assets.ready");
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder();
if (holderfile.exists()) try { // if holder file exists, read assets version string
BufferedReader br = new BufferedReader(new FileReader(holderfile));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
}
br.close();
}
catch (IOException e) {
Log.e(TAG, "", e);
}
// if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName)) try {
holderfile.delete();
File certpath = new File(i2pdpath, "certificates");
deleteRecursive(certpath);
}
catch (Throwable tr) {
Log.e(TAG, "", tr);
}
// copy assets. If processed file exists, it won't be overwrited
copyAsset("certificates");
copyAsset("i2pd.conf");
copyAsset("subscriptions.txt");
copyAsset("tunnels.conf");
// update holder file about successful copying
FileWriter writer = new FileWriter(holderfile);
writer.append(versionName);
writer.flush();
writer.close();
}
catch (Throwable tr)
{
Log.e(TAG,"copy assets",tr);
}
}
}

View File

@ -69,6 +69,6 @@ include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := miniupnpc
LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnp-2.0/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a
LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnp-2.0/include
LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnpc-2.1/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a
LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnpc-2.1/include
include $(PREBUILT_STATIC_LIBRARY)

View File

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

View File

@ -2,7 +2,7 @@ FROM alpine:latest
LABEL authors "Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
# Expose git branch, tag and URL variables as arguments
ARG GIT_BRANCH="master"
ARG GIT_BRANCH="openssl"
ENV GIT_BRANCH=${GIT_BRANCH}
ARG GIT_TAG=""
ENV GIT_TAG=${GIT_TAG}

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.19.0
Version: 2.20.0
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd

View File

@ -1,5 +1,5 @@
Name: i2pd
Version: 2.19.0
Version: 2.20.0
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@ -96,6 +96,9 @@ getent passwd i2pd >/dev/null || \
%changelog
* Thu Aug 23 2018 orignal <i2porignal@yandex.ru> - 2.20.0
- update to 2.20.0
* Tue Jun 26 2018 orignal <i2porignal@yandex.ru> - 2.19.0
- update to 2.19.0

View File

@ -92,6 +92,8 @@ namespace http {
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
static std::string ConvertTime (uint64_t time);
static void ShowUptime (std::stringstream& s, int seconds)
{
int num;
@ -262,7 +264,9 @@ namespace http {
{
if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ())
{
s << "NTCP2&nbsp;&nbsp; supported <br>\r\n";
s << "NTCP2";
if (address->host.is_v6 ()) s << "v6";
s << "&nbsp;&nbsp; supported <br>\r\n";
continue;
}
switch (address->transportStyle)
@ -271,13 +275,13 @@ namespace http {
{
s << "NTCP";
if (address->IsPublishedNTCP2 ()) s << "2";
if (address->host.is_v6 ()) s << "6";
if (address->host.is_v6 ()) s << "v6";
s << "&nbsp;&nbsp;";
break;
}
case i2p::data::RouterInfo::eTransportSSU:
if (address->host.is_v6 ())
s << "SSU6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
s << "SSUv6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
else
s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
break;
@ -462,14 +466,14 @@ namespace http {
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
s << "<b>Expires:</b> " << ls.GetExpirationTime() << "<br>\r\n";
s << "<b>Expires:</b> " << ConvertTime(ls.GetExpirationTime()) << "<br>\r\n";
auto leases = ls.GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
{
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << l->endDate << "<br>\r\n";
s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n";
}
s << "</p>\r\n</div>\r\n</div>\r\n";
}
@ -583,7 +587,7 @@ namespace http {
}
if (!tmp_s6.str ().empty ())
{
s << "<div class='slide'><label for='slide_ntcp6'><b>" << name << "6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_ntcp6'/>\r\n<p class='content'>";
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name << "v6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "v6'/>\r\n<p class='content'>";
s << tmp_s6.str () << "</p>\r\n</div>\r\n";
}
}
@ -628,7 +632,7 @@ namespace http {
auto sessions6 = ssuServer->GetSessionsV6 ();
if (!sessions6.empty ())
{
s << "<div class='slide'><label for='slide_ssu6'><b>SSU6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu6'/>\r\n<p class='content'>";
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssuv6'/>\r\n<p class='content'>";
for (const auto& it: sessions6)
{
auto endpoint = it.second->GetRemoteEndpoint ();
@ -763,6 +767,16 @@ namespace http {
}
}
std::string ConvertTime (uint64_t time)
{
ldiv_t divTime = ldiv(time,1000);
time_t t = divTime.quot;
struct tm *tm = localtime(&t);
char date[128];
snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03ld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem);
return date;
}
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0),
expected_host(hostname)

View File

@ -131,8 +131,8 @@ namespace transport
const auto& a = context.GetRouterInfo().GetAddresses();
for (const auto& address : a)
{
if (!address->host.is_v6 ())
TryPortMapping (address);
if (!address->host.is_v6 () && address->port)
TryPortMapping (address);
}
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
@ -148,7 +148,7 @@ namespace transport
const auto& a = context.GetRouterInfo().GetAddresses();
for (const auto& address : a)
{
if (!address->host.is_v6 ())
if (!address->host.is_v6 () && address->port)
CloseMapping (address);
}
}

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
i2pd (2.20.0-1) unstable; urgency=medium
* updated to version 2.20.0/0.9.36
-- orignal <orignal@i2pmail.org> Thu, 23 Aug 2018 16:00:00 +0000
i2pd (2.19.0-1) unstable; urgency=medium
* updated to version 2.19.0/0.9.35

View File

@ -233,7 +233,7 @@ namespace config {
options_description ntcp2("NTCP2 Options");
ntcp2.add_options()
("ntcp2.enabled", value<bool>()->default_value(false), "Enable NTCP2 (default: disabled)")
("ntcp2.enabled", value<bool>()->default_value(true), "Enable NTCP2 (default: enabled)")
("ntcp2.published", value<bool>()->default_value(false), "Publish NTCP2 (default: disabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
;

View File

@ -1,3 +1,14 @@
/*
* Copyright (c) 2013-2018, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*
* Kovri go write your own code
*
*/
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
@ -244,6 +255,140 @@ namespace transport
SHA256_Final (m_H, &ctx); //h = SHA256(h || ciphertext)
}
bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen)
{
// decrypt X
i2p::crypto::CBCDecryption decryption;
decryption.SetKey (i2p::context.GetIdentHash ());
decryption.SetIV (i2p::context.GetNTCP2IV ());
decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ());
decryption.GetIV (m_IV); // save IV for SessionCreated
// decryption key for next block
KDF1Bob ();
// verify MAC and decrypt options block (32 bytes), use m_H as AD
uint8_t nonce[12], options[16];
memset (nonce, 0, 12); // set nonce to zero
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, m_H, 32, m_K, nonce, options, 16, false)) // decrypt
{
// options
if (options[1] == 2) // ver is always 2
{
paddingLen = bufbe16toh (options + 2);
m_SessionRequestBufferLen = paddingLen + 64;
m3p2Len = bufbe16toh (options + 4);
if (m3p2Len < 16)
{
LogPrint (eLogWarning, "NTCP2: SessionRequest m3p2len=", m3p2Len, " is too short");
return false;
}
// check timestamp
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t tsA = bufbe32toh (options + 8);
if (tsA < ts - NTCP2_CLOCK_SKEW || tsA > ts + NTCP2_CLOCK_SKEW)
{
LogPrint (eLogWarning, "NTCP2: SessionRequest time difference ", (int)(ts - tsA), " exceeds clock skew");
return false;
}
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", (int)options[1]);
return false;
}
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed ");
return false;
}
return true;
}
bool NTCP2Establisher::ProcessSessionCreatedMessage (uint16_t& paddingLen)
{
m_SessionCreatedBufferLen = 64;
// decrypt Y
i2p::crypto::CBCDecryption decryption;
decryption.SetKey (m_RemoteIdentHash);
decryption.SetIV (m_IV);
decryption.Decrypt (m_SessionCreatedBuffer, 32, GetRemotePub ());
// decryption key for next block (m_K)
KDF2Alice ();
// decrypt and verify MAC
uint8_t payload[16];
uint8_t nonce[12];
memset (nonce, 0, 12); // set nonce to zero
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, m_H, 32, m_K, nonce, payload, 16, false)) // decrypt
{
// options
paddingLen = bufbe16toh(payload + 2);
// check timestamp
auto ts = i2p::util::GetSecondsSinceEpoch ();
uint32_t tsB = bufbe32toh (payload + 8);
if (tsB < ts - NTCP2_CLOCK_SKEW || tsB > ts + NTCP2_CLOCK_SKEW)
{
LogPrint (eLogWarning, "NTCP2: SessionCreated time difference ", (int)(ts - tsB), " exceeds clock skew");
return false;
}
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed ");
return false;
}
return true;
}
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce)
{
// update AD
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, m_SessionCreatedBuffer + 32, 32); // encrypted payload
SHA256_Final (m_H, &ctx);
int paddingLength = m_SessionCreatedBufferLen - 64;
if (paddingLength > 0)
{
SHA256_CTX ctx1;
SHA256_Init (&ctx1);
SHA256_Update (&ctx1, m_H, 32);
SHA256_Update (&ctx1, m_SessionCreatedBuffer + 64, paddingLength);
SHA256_Final (m_H, &ctx1);
}
if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, m_H, 32, m_K, nonce, m_RemoteStaticKey, 32, false)) // decrypt S
{
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
return false;
}
return true;
}
bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf)
{
// update AD again
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, m_SessionConfirmedBuffer, 48);
SHA256_Final (m_H, &ctx);
KDF3Bob ();
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
{
// caclulate new h again for KDF data
memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext
SHA256 (m_SessionConfirmedBuffer + 16, m3p2Len + 32, m_H); //h = SHA256(h || ciphertext);
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed ");
return false;
}
return true;
}
NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT),
m_Server (server), m_Socket (m_Server.GetService ()),
@ -368,52 +513,28 @@ namespace transport
}
else
{
// decrypt X
i2p::crypto::CBCDecryption decryption;
decryption.SetKey (i2p::context.GetIdentHash ());
decryption.SetIV (i2p::context.GetNTCP2IV ());
decryption.Decrypt (m_Establisher->m_SessionRequestBuffer, 32, m_Establisher->GetRemotePub ());
decryption.GetIV (m_Establisher->m_IV); // save IV for SessionCreated
// decryption key for next block
m_Establisher->KDF1Bob ();
// verify MAC and decrypt options block (32 bytes), use m_H as AD
uint8_t nonce[12], options[16];
memset (nonce, 0, 12); // set nonce to zero
if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionRequestBuffer + 32, 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, options, 16, false)) // decrypt
LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred);
uint16_t paddingLen = 0;
if (m_Establisher->ProcessSessionRequestMessage (paddingLen))
{
if (options[1] == 2)
if (paddingLen > 0)
{
uint16_t paddingLen = bufbe16toh (options + 2);
m_Establisher->m_SessionRequestBufferLen = paddingLen + 64;
m_Establisher->m3p2Len = bufbe16toh (options + 4);
// TODO: check tsA
if (paddingLen > 0)
if (paddingLen <= 287 - 64) // session request is 287 bytes max
{
if (paddingLen <= 287 - 64) // session request is 287 bytes max
{
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long");
Terminate ();
}
boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
else
SendSessionCreated ();
{
LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long");
Terminate ();
}
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionRequest version mismatch ", (int)options[1]);
Terminate ();
}
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed ");
Terminate ();
SendSessionCreated ();
}
else
Terminate ();
}
}
@ -446,23 +567,9 @@ namespace transport
else
{
LogPrint (eLogDebug, "NTCP2: SessionCreated received ", bytes_transferred);
m_Establisher->m_SessionCreatedBufferLen = 64;
// decrypt Y
i2p::crypto::CBCDecryption decryption;
decryption.SetKey (GetRemoteIdentity ()->GetIdentHash ());
decryption.SetIV (m_Establisher->m_IV);
decryption.Decrypt (m_Establisher->m_SessionCreatedBuffer, 32, m_Establisher->GetRemotePub ());
// decryption key for next block (m_K)
m_Establisher->KDF2Alice ();
// decrypt and verify MAC
uint8_t payload[16];
uint8_t nonce[12];
memset (nonce, 0, 12); // set nonce to zero
if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionCreatedBuffer + 32, 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, payload, 16, false)) // decrypt
{
uint16_t paddingLen = bufbe16toh(payload + 2);
LogPrint (eLogDebug, "NTCP2: padding length ", paddingLen);
// TODO: check tsB
uint16_t paddingLen = 0;
if (m_Establisher->ProcessSessionCreatedMessage (paddingLen))
{
if (paddingLen > 0)
{
if (paddingLen <= 287 - 64) // session created is 287 bytes max
@ -480,10 +587,7 @@ namespace transport
SendSessionConfirmed ();
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed ");
Terminate ();
}
}
}
@ -559,38 +663,16 @@ namespace transport
else
{
LogPrint (eLogDebug, "NTCP2: SessionConfirmed received");
// update AD
uint8_t h[80];
memcpy (h, m_Establisher->GetH (), 32);
memcpy (h + 32, m_Establisher->m_SessionCreatedBuffer + 32, 32); // encrypted payload
SHA256 (h, 64, h);
int paddingLength = m_Establisher->m_SessionCreatedBufferLen - 64;
if (paddingLength > 0)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, h, 32);
SHA256_Update (&ctx, m_Establisher->m_SessionCreatedBuffer + 64, paddingLength);
SHA256_Final (h, &ctx);
}
// part 1
uint8_t nonce[12];
CreateNonce (1, nonce);
if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionConfirmedBuffer, 32, h, 32, m_Establisher->GetK (), nonce, m_Establisher->m_RemoteStaticKey, 32, false)) // decrypt S
if (m_Establisher->ProcessSessionConfirmedMessagePart1 (nonce))
{
// part 2
// update AD again
memcpy (h + 32, m_Establisher->m_SessionConfirmedBuffer, 48);
SHA256 (h, 80, m_Establisher->m_H);
std::vector<uint8_t> buf(m_Establisher->m3p2Len - 16); // -MAC
m_Establisher->KDF3Bob ();
memset (nonce, 0, 12); // set nonce to 0 again
if (i2p::crypto::AEADChaCha20Poly1305 (m_Establisher->m_SessionConfirmedBuffer + 48, m_Establisher->m3p2Len - 16, m_Establisher->GetH (), 32, m_Establisher->GetK (), nonce, buf.data (), m_Establisher->m3p2Len - 16, false)) // decrypt
if (m_Establisher->ProcessSessionConfirmedMessagePart2 (nonce, buf.data ()))
{
// caclulate new h again for KDF data
memcpy (m_Establisher->m_SessionConfirmedBuffer + 16, m_Establisher->GetH (), 32); // h || ciphertext
SHA256 (m_Establisher->m_SessionConfirmedBuffer + 16, m_Establisher->m3p2Len + 32, m_Establisher->m_H); //h = SHA256(h || ciphertext);
KeyDerivationFunctionDataPhase ();
// Bob data phase keys
m_SendKey = m_Kba;
@ -599,7 +681,7 @@ namespace transport
m_ReceiveSipKey = m_Sipkeysab;
memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8);
memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8);
// payload
// process RI
if (buf[0] != eNTCP2BlkRouterInfo)
{
@ -644,19 +726,13 @@ namespace transport
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ());
m_Server.AddNTCP2Session (shared_from_this ());
Established ();
ReceiveLength ();
}
ReceiveLength ();
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed ");
Terminate ();
}
}
else
{
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed ");
Terminate ();
}
}
}
@ -691,12 +767,20 @@ namespace transport
else
{
i2p::crypto::Siphash<8> (m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey);
// m_NextRecivedLen comes from the network in BigEndian
// m_NextReceivedLen comes from the network in BigEndian
m_NextReceivedLen = be16toh (m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key);
LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen);
if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer;
m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen];
Receive ();
if (m_NextReceivedLen >= 16)
{
if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer;
m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen];
Receive ();
}
else
{
LogPrint (eLogError, "NTCP2: received length ", m_NextReceivedLen, " is too short");
Terminate ();
}
}
}
@ -865,7 +949,7 @@ namespace transport
payload[s] = eNTCP2BlkPadding; // blk
htobe16buf (payload + s + 1, paddingSize); // size
s += 3;
RAND_bytes (payload + s, paddingSize);
memset (payload + s, 0, paddingSize);
s += paddingSize;
// send
SendNextFrame (payload, s);
@ -875,6 +959,7 @@ namespace transport
void NTCP2Session::SendRouterInfo ()
{
if (!IsEstablished ()) return;
auto riLen = i2p::context.GetRouterInfo ().GetBufferLen ();
int paddingSize = (riLen*NTCP2_MAX_PADDING_RATIO)/100;
size_t payloadLen = riLen + paddingSize + 7; // 7 = 2*3 bytes header + 1 byte RI flag
@ -892,6 +977,7 @@ namespace transport
void NTCP2Session::SendTermination (NTCP2TerminationReason reason)
{
if (!IsEstablished ()) return;
uint8_t payload[12] = { eNTCP2BlkTermination, 0, 9 };
htobe64buf (payload + 3, m_ReceiveSequenceNumber);
payload[11] = (uint8_t)reason;
@ -921,7 +1007,7 @@ namespace transport
void NTCP2Session::SendLocalRouterInfo ()
{
if (!IsOutgoing ()) // we send it in SessionConfirmed
SendRouterInfo ();
m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
}
NTCP2Server::NTCP2Server ():

View File

@ -1,3 +1,13 @@
/*
* Copyright (c) 2013-2018, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*
* Kovri go write your own code
*
*/
#ifndef NTCP2_H__
#define NTCP2_H__
@ -26,6 +36,8 @@ namespace transport
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const int NTCP2_CLOCK_SKEW = 60; // in seconds
enum NTCP2BlockType
{
eNTCP2BlkDateTime = 0,
@ -91,6 +103,11 @@ namespace transport
void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce);
void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce);
bool ProcessSessionRequestMessage (uint16_t& paddingLen);
bool ProcessSessionCreatedMessage (uint16_t& paddingLen);
bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce);
bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
BN_CTX * m_Ctx;
uint8_t m_EphemeralPrivateKey[32], m_EphemeralPublicKey[32], m_RemoteEphemeralPublicKey[32]; // x25519
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/;

View File

@ -402,7 +402,7 @@ namespace transport
uint32_t tsA1 = be32toh (tsA);
if (tsA1 < ts - NTCP_CLOCK_SKEW || tsA1 > ts + NTCP_CLOCK_SKEW)
{
LogPrint (eLogError, "NTCP: Phase3 time difference ", ts - tsA1, " exceeds clock skew");
LogPrint (eLogError, "NTCP: Phase3 time difference ", (int)(ts - tsA1), " exceeds clock skew");
Terminate ();
return;
}
@ -485,7 +485,7 @@ namespace transport
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (tsB < ts - NTCP_CLOCK_SKEW || tsB > ts + NTCP_CLOCK_SKEW)
{
LogPrint (eLogError, "NTCP: Phase4 time difference ", ts - tsB, " exceeds clock skew");
LogPrint (eLogError, "NTCP: Phase4 time difference ", (int)(ts - tsB), " exceeds clock skew");
Terminate ();
return;
}

View File

@ -164,6 +164,7 @@ namespace i2p
void RouterContext::PublishNTCP2Address (int port, bool publish)
{
if (!m_NTCP2Keys) return;
if (!port)
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
bool updated = false;
@ -173,6 +174,7 @@ namespace i2p
{
address->port = port;
address->ntcp2->isPublished = publish;
address->ntcp2->iv = m_NTCP2Keys->iv;
updated = true;
}
}

View File

@ -668,7 +668,7 @@ namespace data
addr->host = boost::asio::ip::address::from_string (host);
addr->port = port;
addr->transportStyle = eTransportNTCP;
addr->cost = 2;
addr->cost = 6;
addr->date = 0;
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
@ -703,7 +703,7 @@ namespace data
auto addr = std::make_shared<Address>();
addr->port = 0;
addr->transportStyle = eTransportNTCP;
addr->cost = 14;
addr->cost = 3;
addr->date = 0;
addr->ntcp2.reset (new NTCP2Ext ());
addr->ntcp2->isNTCP2Only = true; // NTCP2 only address

View File

@ -7,7 +7,7 @@
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 19
#define I2PD_VERSION_MINOR 20
#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 35
#define I2P_VERSION_MICRO 36
#define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<manifest package="org.purplei2p.i2pd" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.19.0" android:versionCode="2" android:installLocation="auto">
<manifest package="org.purplei2p.i2pd" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.20.0" android:versionCode="1" android:installLocation="auto">
<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="23"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- <application android:hardwareAccelerated="true" -->