diff --git a/ChangeLog b/ChangeLog index 59a19a3c..d8ab87de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/Makefile.osx b/Makefile.osx index 3358cb10..13376040 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -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. - 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 diff --git a/Win32/installer.iss b/Win32/installer.iss index c4e1fffd..e340c65f 100644 --- a/Win32/installer.iss +++ b/Win32/installer.iss @@ -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] diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 705cce4e..9a2cffd5 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,7 +3,7 @@ package="org.purplei2p.i2pd" android:installLocation="auto" android:versionCode="1" - android:versionName="2.19.0"> + android:versionName="2.20.0"> +#include +#include +#include #include #include -#include //#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); } diff --git a/android/src/org/purplei2p/i2pd/I2PDActivity.java b/android/src/org/purplei2p/i2pd/I2PDActivity.java index 28e30251..bc6f7209 100755 --- a/android/src/org/purplei2p/i2pd/I2PDActivity.java +++ b/android/src/org/purplei2p/i2pd/I2PDActivity.java @@ -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); + } + } } diff --git a/android_binary_only/jni/Android.mk b/android_binary_only/jni/Android.mk index ae56110c..fa677c83 100755 --- a/android_binary_only/jni/Android.mk +++ b/android_binary_only/jni/Android.mk @@ -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) diff --git a/appveyor.yml b/appveyor.yml index 27f563ef..e0c09fc1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.19.{build} +version: 2.20.{build} pull_requests: do_not_increment_build_number: true branches: diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index e7e60f37..22990995 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -2,7 +2,7 @@ FROM alpine:latest LABEL authors "Mikal Villa , Darknet Villain " # 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} diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index 02986475..3539c19b 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.19.0 +Version: 2.20.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index db480d35..edad8228 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -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 - 2.20.0 +- update to 2.20.0 + * Tue Jun 26 2018 orignal - 2.19.0 - update to 2.19.0 diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index cda3f2ec..09b00707 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -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   supported
\r\n"; + s << "NTCP2"; + if (address->host.is_v6 ()) s << "v6"; + s << "   supported
\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 << "  "; break; } case i2p::data::RouterInfo::eTransportSSU: if (address->host.is_v6 ()) - s << "SSU6     "; + s << "SSUv6     "; else s << "SSU     "; break; @@ -462,14 +466,14 @@ namespace http { s << "
!! Invalid !!
\r\n"; s << "
\r\n"; s << "\r\n

\r\n"; - s << "Expires: " << ls.GetExpirationTime() << "
\r\n"; + s << "Expires: " << ConvertTime(ls.GetExpirationTime()) << "
\r\n"; auto leases = ls.GetNonExpiredLeases(); s << "Non Expired Leases: " << leases.size() << "
\r\n"; for ( auto & l : leases ) { s << "Gateway: " << l->tunnelGateway.ToBase64() << "
\r\n"; s << "TunnelID: " << l->tunnelID << "
\r\n"; - s << "EndDate: " << l->endDate << "
\r\n"; + s << "EndDate: " << ConvertTime(l->endDate) << "
\r\n"; } s << "

\r\n
\r\n\r\n"; } @@ -583,7 +587,7 @@ namespace http { } if (!tmp_s6.str ().empty ()) { - s << "
\r\n\r\n

"; + s << "

\r\n\r\n

"; s << tmp_s6.str () << "

\r\n
\r\n"; } } @@ -628,7 +632,7 @@ namespace http { auto sessions6 = ssuServer->GetSessionsV6 (); if (!sessions6.empty ()) { - s << "
\r\n\r\n

"; + s << "

\r\n\r\n

"; 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 socket): m_Socket (socket), m_Timer (socket->get_io_service ()), m_BufferLen (0), expected_host(hostname) diff --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp index ba7b408c..3aad19c6 100644 --- a/daemon/UPnP.cpp +++ b/daemon/UPnP.cpp @@ -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); } } diff --git a/debian/changelog b/debian/changelog index 74c366d1..9d721eb8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +i2pd (2.20.0-1) unstable; urgency=medium + + * updated to version 2.20.0/0.9.36 + + -- orignal 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 diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 098a7baa..9eaaf068 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -233,7 +233,7 @@ namespace config { options_description ntcp2("NTCP2 Options"); ntcp2.add_options() - ("ntcp2.enabled", value()->default_value(false), "Enable NTCP2 (default: disabled)") + ("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)") ; diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index b54a83ef..58f928b4 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -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 #include #include @@ -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 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 (); - } - } + SendSessionCreated (); + } else - { - LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); 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 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 (): diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 3e202dc5..2b6748ab 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -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*/; diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index caddd110..e9de08e2 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -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; } diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index b2774aa2..a3d33113 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -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; } } diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 95e88963..79a70462 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -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

(); 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 diff --git a/libi2pd/version.h b/libi2pd/version.h index 129035ad..7c015e84 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 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) diff --git a/qt/i2pd_qt/android/AndroidManifest.xml b/qt/i2pd_qt/android/AndroidManifest.xml index d98e24d4..c62e118e 100644 --- a/qt/i2pd_qt/android/AndroidManifest.xml +++ b/qt/i2pd_qt/android/AndroidManifest.xml @@ -1,5 +1,5 @@ - +