From e9ec043bf4ea80e2a26fbeaa72ca42f028a0bf89 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Sat, 13 Oct 2018 14:36:39 +0000
Subject: [PATCH] Data: Encrypted LS2, other LS2 changes

---
 .../src/net/i2p/data/EncryptedLeaseSet.java   | 195 ++++++++++++++++++
 core/java/src/net/i2p/data/LeaseSet2.java     |   8 +-
 core/java/src/net/i2p/data/MetaLeaseSet.java  |   8 -
 3 files changed, 201 insertions(+), 10 deletions(-)
 create mode 100644 core/java/src/net/i2p/data/EncryptedLeaseSet.java

diff --git a/core/java/src/net/i2p/data/EncryptedLeaseSet.java b/core/java/src/net/i2p/data/EncryptedLeaseSet.java
new file mode 100644
index 0000000000..23de3e9453
--- /dev/null
+++ b/core/java/src/net/i2p/data/EncryptedLeaseSet.java
@@ -0,0 +1,195 @@
+package net.i2p.data;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import net.i2p.crypto.SigType;
+import net.i2p.util.Clock;
+
+/**
+ * Use getSigningKey() / setSigningKey() (revocation key in super) for the blinded key.
+ *
+ * PRELIMINARY - Subject to change - see proposal 123
+ *
+ * @since 0.9.38
+ */
+public class EncryptedLeaseSet extends LeaseSet2 {
+
+    // includes IV and MAC
+    private byte[] _encryptedData;
+
+    private static final int MIN_ENCRYPTED_SIZE = 8 + 16;
+    private static final int MAX_ENCRYPTED_SIZE = 4096;
+
+    public EncryptedLeaseSet() {
+        super();
+    }
+
+    ///// overrides below here
+
+    @Override
+    public int getType() {
+        return KEY_TYPE_ENCRYPTED_LS2;
+    }
+
+    /**
+     *  This does NOT validate the signature
+     *
+     *  @throws IllegalStateException if called more than once or Destination already set
+     */
+    @Override
+    public void readBytes(InputStream in) throws DataFormatException, IOException {
+        if (_signingKey != null)
+            throw new IllegalStateException();
+        // LS2 header
+        readHeader(in);
+        // Encrypted LS2 part
+        int encryptedSize = (int) DataHelper.readLong(in, 2);
+        if (encryptedSize < MIN_ENCRYPTED_SIZE ||
+            encryptedSize > MAX_ENCRYPTED_SIZE)
+            throw new DataFormatException("bad LS size: " + encryptedSize);
+        _encryptedData = new byte[encryptedSize];
+        DataHelper.read(in, _encryptedData);
+        // signature type depends on offline or not
+        SigType type = isOffline() ? _transientSigningPublicKey.getType() : _signingKey.getType();
+        _signature = new Signature(type);
+        _signature.readBytes(in);
+    }
+
+    /**
+     *  Without sig. This does NOT validate the signature
+     */
+    @Override
+    protected void writeBytesWithoutSig(OutputStream out) throws DataFormatException, IOException {
+        if (_signingKey == null)
+            throw new DataFormatException("Not enough data to write out a LeaseSet");
+        // LS2 header
+        writeHeader(out);
+        // Encrypted LS2 part
+        DataHelper.writeLong(out, 2, _encryptedData.length);
+        out.write(_encryptedData);
+    }
+    
+    /**
+     *  Overridden because we have a blinded key, not a dest
+     */
+    @Override
+    public boolean verifyOfflineSignature() {
+        return verifyOfflineSignature(_signingKey);
+    }
+
+    /**
+     *  Overridden because we have a blinded key, not a dest
+     */
+    @Override
+    protected void readHeader(InputStream in) throws DataFormatException, IOException {
+        int stype = (int) DataHelper.readLong(in, 2);
+        SigType type = SigType.getByCode(stype);
+        if (type == null)
+            throw new DataFormatException("unknown key type " + stype);
+        _signingKey = new SigningPublicKey(type);
+        _signingKey.readBytes(in);
+        _published = DataHelper.readLong(in, 4) * 1000;
+        _expires = _published + (DataHelper.readLong(in, 2) * 1000);
+        _flags = (int) DataHelper.readLong(in, 2);
+        if (isOffline())
+            readOfflineBytes(in);
+    }
+
+    /**
+     *  Overridden because we have a blinded key, not a dest
+     */
+    @Override
+    protected void writeHeader(OutputStream out) throws DataFormatException, IOException {
+        DataHelper.writeLong(out, 2, _signingKey.getType().getCode());
+        _signingKey.writeBytes(out);
+        if (_published <= 0)
+            _published = Clock.getInstance().now();
+        DataHelper.writeLong(out, 4, _published / 1000);
+        DataHelper.writeLong(out, 2, (_expires - _published) / 1000);
+        DataHelper.writeLong(out, 2, _flags);
+        if (isOffline())
+            writeOfflineBytes(out);
+    }
+
+    /**
+     *  Overridden because we have a blinded key, not a dest
+     */
+    @Override
+    protected void readOfflineBytes(InputStream in) throws DataFormatException, IOException {
+        _transientExpires = DataHelper.readLong(in, 4) * 1000;
+        int itype = (int) DataHelper.readLong(in, 2);
+        SigType type = SigType.getByCode(itype);
+        if (type == null)
+            throw new DataFormatException("Unknown sig type " + itype);
+        _transientSigningPublicKey = new SigningPublicKey(type);
+        _transientSigningPublicKey.readBytes(in);
+        SigType stype = _signingKey.getType();
+        _offlineSignature = new Signature(stype);
+        _offlineSignature.readBytes(in);
+    }
+
+    /**
+     *  Overridden because we have a blinded key, not a dest
+     */
+    @Override
+    protected void writeOfflineBytes(OutputStream out) throws DataFormatException, IOException {
+        if (_transientSigningPublicKey == null || _offlineSignature == null)
+            throw new DataFormatException("No offline key/sig");
+        DataHelper.writeLong(out, 4, _transientExpires / 1000);
+        DataHelper.writeLong(out, 2, _signingKey.getType().getCode());
+        _transientSigningPublicKey.writeBytes(out);
+        _offlineSignature.writeBytes(out);
+    }
+    
+    /**
+     *  Number of bytes, NOT including signature
+     */
+    @Override
+    public int size() {
+        int rv = _signingKey.length()
+             + 12
+             + _encryptedData.length;
+        if (isOffline())
+            rv += 2 + _transientSigningPublicKey.length() + _offlineSignature.length();
+        return rv;
+    }
+
+    // encrypt / decrypt TODO
+
+    @Override
+    public boolean equals(Object object) {
+        if (object == this) return true;
+        if ((object == null) || !(object instanceof EncryptedLeaseSet)) return false;
+        EncryptedLeaseSet ls = (EncryptedLeaseSet) object;
+        return
+               DataHelper.eq(_signature, ls.getSignature())
+               && DataHelper.eq(_signingKey, ls.getSigningKey());
+    }
+    
+    /** the destination has enough randomness in it to use it by itself for speed */
+    @Override
+    public int hashCode() {
+        if (_encryptionKey == null)
+            return 0;
+        return _encryptionKey.hashCode();
+    }
+    
+    @Override
+    public String toString() {
+        StringBuilder buf = new StringBuilder(128);
+        buf.append("[EncryptedLeaseSet: ");
+        buf.append("\n\tBlinded Key: ").append(_signingKey);
+        if (isOffline()) {
+            buf.append("\n\tTransient Key: ").append(_transientSigningPublicKey);
+            buf.append("\n\tTransient Expires: ").append(new java.util.Date(_transientExpires));
+            buf.append("\n\tOffline Signature: ").append(_offlineSignature);
+        }
+        buf.append("\n\tSignature: ").append(_signature);
+        buf.append("\n\tPublished: ").append(new java.util.Date(_published));
+        buf.append("\n\tExpires: ").append(new java.util.Date(_expires));
+        buf.append("]");
+        return buf.toString();
+    }
+}
diff --git a/core/java/src/net/i2p/data/LeaseSet2.java b/core/java/src/net/i2p/data/LeaseSet2.java
index 9ba74b7174..ae832cb717 100644
--- a/core/java/src/net/i2p/data/LeaseSet2.java
+++ b/core/java/src/net/i2p/data/LeaseSet2.java
@@ -124,6 +124,10 @@ public class LeaseSet2 extends LeaseSet {
     }
 
     public boolean verifyOfflineSignature() {
+        return verifyOfflineSignature(_destination.getSigningPublicKey());
+    }
+
+    protected boolean verifyOfflineSignature(SigningPublicKey spk) {
         if (!isOffline())
             return false;
         I2PAppContext ctx = I2PAppContext.getGlobalContext();
@@ -276,7 +280,7 @@ public class LeaseSet2 extends LeaseSet {
             writeOfflineBytes(out);
     }
 
-    private void readOfflineBytes(InputStream in) throws DataFormatException, IOException {
+    protected void readOfflineBytes(InputStream in) throws DataFormatException, IOException {
         _transientExpires = DataHelper.readLong(in, 4) * 1000;
         int itype = (int) DataHelper.readLong(in, 2);
         SigType type = SigType.getByCode(itype);
@@ -289,7 +293,7 @@ public class LeaseSet2 extends LeaseSet {
         _offlineSignature.readBytes(in);
     }
 
-    private void writeOfflineBytes(OutputStream out) throws DataFormatException, IOException {
+    protected void writeOfflineBytes(OutputStream out) throws DataFormatException, IOException {
         if (_transientSigningPublicKey == null || _offlineSignature == null)
             throw new DataFormatException("No offline key/sig");
         DataHelper.writeLong(out, 4, _transientExpires / 1000);
diff --git a/core/java/src/net/i2p/data/MetaLeaseSet.java b/core/java/src/net/i2p/data/MetaLeaseSet.java
index 66d9e66f2a..eec7dca1f1 100644
--- a/core/java/src/net/i2p/data/MetaLeaseSet.java
+++ b/core/java/src/net/i2p/data/MetaLeaseSet.java
@@ -1,18 +1,10 @@
 package net.i2p.data;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.Properties;
 
-import net.i2p.I2PAppContext;
-import net.i2p.crypto.DSAEngine;
-import net.i2p.crypto.EncType;
-import net.i2p.crypto.SigAlgo;
 import net.i2p.crypto.SigType;
-import net.i2p.util.Clock;
-import net.i2p.util.OrderedProperties;
 
 /**
  * PRELIMINARY - Subject to change - see proposal 123
-- 
GitLab