From d054c6bc04eabaecc1ef5c814347ddb1eef35af2 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Wed, 5 Dec 2018 13:50:07 +0000
Subject: [PATCH] I2CP: Set offline keys in generated LS2 (router side to
 follow) Propagate error from disconnect message to session listener Refactor
 RLSMH options

---
 .../net/i2p/client/impl/I2PSessionImpl.java   | 22 ++++++++++--
 .../impl/RequestLeaseSetMessageHandler.java   | 35 +++++++++++++++----
 .../client/impl/SetDateMessageHandler.java    |  8 ++++-
 3 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java
index 9e0cadfddd..e7ca36871b 100644
--- a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java
@@ -148,6 +148,10 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
     /** monitor for waiting until a lease set has been granted */
     protected final Object _leaseSetWait = new Object();
 
+    /** set in propogateError(), sync with _stateLock */
+    private String _errorMessage;
+    private Throwable _errorCause;
+
     /**
      *  @since 0.9.8
      */
@@ -694,6 +698,8 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
         try {
             // protect w/ closeSocket()
             synchronized(_stateLock) {
+                _errorMessage = null;
+                _errorCause = null;
                 // If we are in the router JVM, connect using the internal queue
                 if (_context.isRouterContext()) {
                     // _socket and _writer remain null
@@ -780,8 +786,17 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
                     _leaseSetWait.wait(1000);
                 }
                 // if we got a disconnect message while waiting
-                if (isClosed())
-                    throw new IOException("Disconnected from router while waiting for tunnels");
+                synchronized (_stateLock) {
+                    if (isClosed()) {
+                        String msg = "Disconnected from router while waiting for tunnels";
+                        if (_errorMessage != null)
+                            msg += ": " + _errorMessage;
+                        IOException ioe =  new IOException(msg);
+                        if (_errorCause != null)
+                            ioe.initCause(_errorCause);
+                        throw ioe;
+                    }
+                }
             }
             if (_log.shouldLog(Log.INFO)) {
                 long connected = _context.clock().now();
@@ -1258,6 +1273,9 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
         if (_log.shouldLog(level)) 
             _log.log(level, getPrefix() + msgpfx + msg, error);
         if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
+        // Save for throwing out of connect()
+        _errorMessage = msg;
+        _errorCause = error;
     }
 
     /**
diff --git a/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java
index 82a7fb46df..3d8da22b9c 100644
--- a/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java
+++ b/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java
@@ -49,6 +49,15 @@ import net.i2p.util.Log;
 class RequestLeaseSetMessageHandler extends HandlerImpl {
     private final Map<Destination, LeaseInfo> _existingLeaseSets;
 
+    // LS 1
+    private static final String PROP_LS_ENCRYPT = "i2cp.encryptLeaseSet";
+    private static final String PROP_LS_KEY = "i2cp.leaseSetKey";
+    private static final String PROP_LS_PK = "i2cp.leaseSetPrivateKey";
+    private static final String PROP_LS_SPK = "i2cp.leaseSetSigningPrivateKey";
+    // LS 2
+    private static final String PROP_LS_TYPE = "i2cp.leaseSetType";
+    private static final String PROP_LS_ENCTYPE = "i2cp.leaseSetEncType";
+
     public RequestLeaseSetMessageHandler(I2PAppContext context) {
         this(context, RequestLeaseSetMessage.MESSAGE_TYPE);
     }
@@ -70,13 +79,15 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
     protected static boolean requiresLS2(I2PSessionImpl session) {
         if (!session.supportsLS2())
             return false;
-        String s = session.getOptions().getProperty("crypto.encType");
+        if (session.isOffline())
+            return true;
+        String s = session.getOptions().getProperty(PROP_LS_ENCTYPE);
         if (s != null) {
             EncType type = EncType.parseEncType(s);
             if (type != null && type != EncType.ELGAMAL_2048 && type.isAvailable())
                 return true;
         }
-        s = session.getOptions().getProperty("i2cp.leaseSetType");
+        s = session.getOptions().getProperty(PROP_LS_TYPE);
         if (s != null) {
             try {
                 int type = Integer.parseInt(s);
@@ -117,9 +128,9 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
         LeaseInfo li = _existingLeaseSets.get(dest);
         if (li == null) {
             // [enctype:]b64 of private key
-            String spk = session.getOptions().getProperty("i2cp.leaseSetPrivateKey");
+            String spk = session.getOptions().getProperty(PROP_LS_PK);
             // [sigtype:]b64 of private key
-            String sspk = session.getOptions().getProperty("i2cp.leaseSetSigningPrivateKey");
+            String sspk = session.getOptions().getProperty(PROP_LS_SPK);
             PrivateKey privKey = null;
             SigningPrivateKey signingPrivKey = null;
             if (spk != null && sspk != null) {
@@ -178,7 +189,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
                 }
             } else {
                 EncType type = EncType.ELGAMAL_2048;
-                String senc = session.getOptions().getProperty("crypto.encType");
+                String senc = session.getOptions().getProperty(PROP_LS_ENCTYPE);
                 if (senc != null) {
                     EncType newtype = EncType.parseEncType(senc);
                     if (newtype != null) {
@@ -215,8 +226,8 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
             opts = ((SubSession) session).getPrimaryOptions();
         else
             opts = session.getOptions();
-        boolean encrypt = Boolean.parseBoolean(opts.getProperty("i2cp.encryptLeaseSet"));
-        String sk = opts.getProperty("i2cp.leaseSetKey");
+        boolean encrypt = Boolean.parseBoolean(opts.getProperty(PROP_LS_ENCRYPT));
+        String sk = opts.getProperty(PROP_LS_KEY);
         Hash h = dest.calculateHash();
         if (encrypt && sk != null) {
             SessionKey key = new SessionKey();
@@ -231,6 +242,16 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
         } else {
             _context.keyRing().remove(h);
         }
+        // offline keys
+        if (session.isOffline()) {
+            LeaseSet2 ls2 = (LeaseSet2) leaseSet;
+            boolean ok = ls2.setOfflineSignature(session.getOfflineExpiration(), session.getTransientSigningPublicKey(),
+                                                 session.getOfflineSignature());
+            if (!ok) {
+                session.propogateError("Bad offline signature", new Exception());
+                // TODO just let the router handle it for now
+            }
+        }
         try {
             leaseSet.sign(session.getPrivateKey());
             // Workaround for unparsable serialized signing private key for revocation
diff --git a/core/java/src/net/i2p/client/impl/SetDateMessageHandler.java b/core/java/src/net/i2p/client/impl/SetDateMessageHandler.java
index 723bb7e2fe..c15e0f3a96 100644
--- a/core/java/src/net/i2p/client/impl/SetDateMessageHandler.java
+++ b/core/java/src/net/i2p/client/impl/SetDateMessageHandler.java
@@ -34,7 +34,13 @@ class SetDateMessageHandler extends HandlerImpl {
         // we did was get the time from ourselves.
         if (!_context.isRouterContext())
             Clock.getInstance().setNow(msg.getDate().getTime());
-        // TODO - save router's version string for future reference
+        // This saves the various support capabilities based on
+        // the router's version string for future reference
         session.dateUpdated(msg.getVersion());
+        if (session.isOffline() && !session.supportsLS2()) {
+            // TODO check other options also? see RLSMH.requiresLS2()
+            session.propogateError("Router does not support offline keys", new Exception());
+            session.destroySession(false);
+        }
     }
 }
-- 
GitLab