From 8484a22fc4f2be4fab355f3628f12c3da3f500f1 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Wed, 23 Oct 2019 12:11:17 +0000
Subject: [PATCH] Router: Add KeyManager support for multiple leaseset private
 keys

---
 .../java/src/net/i2p/router/KeyManager.java   |  23 +++-
 .../java/src/net/i2p/router/LeaseSetKeys.java | 102 +++++++++++++++++-
 2 files changed, 119 insertions(+), 6 deletions(-)

diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java
index b3d9687862..3b418e27e9 100644
--- a/router/java/src/net/i2p/router/KeyManager.java
+++ b/router/java/src/net/i2p/router/KeyManager.java
@@ -15,6 +15,7 @@ import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -110,15 +111,29 @@ public class KeyManager {
     public synchronized SigningPublicKey getSigningPublicKey() { return _signingPublicKey; }
     
     /**
-     *  client
+     *  Client with a single key
+     *
      *  @param leaseRevocationPrivateKey unused, may be null
      */
     public void registerKeys(Destination dest, SigningPrivateKey leaseRevocationPrivateKey, PrivateKey endpointDecryptionKey) {
-        if (_log.shouldLog(Log.INFO))
-            _log.info("Registering keys for destination " + dest.calculateHash().toBase64());
+        if (_log.shouldInfo())
+            _log.info("Registering keys for destination " + dest.toBase32());
         LeaseSetKeys keys = new LeaseSetKeys(dest, leaseRevocationPrivateKey, endpointDecryptionKey);
         _leaseSetKeys.put(dest.calculateHash(), keys);
     }
+    
+    /**
+     *  Client with multiple keys
+     *
+     *  @param leaseRevocationPrivateKey unused, may be null
+     *  @since 0.9.44
+     */
+    public void registerKeys(Destination dest, SigningPrivateKey leaseRevocationPrivateKey, List<PrivateKey> endpointDecryptionKeys) {
+        if (_log.shouldInfo())
+            _log.info("Registering keys for destination " + dest.toBase32());
+        LeaseSetKeys keys = new LeaseSetKeys(dest, leaseRevocationPrivateKey, endpointDecryptionKeys);
+        _leaseSetKeys.put(dest.calculateHash(), keys);
+    }
    
     /**
      *  Read/Write the router keys from/to disk
@@ -129,7 +144,7 @@ public class KeyManager {
 
     /** client */
     public LeaseSetKeys unregisterKeys(Destination dest) {
-        if (_log.shouldLog(Log.INFO))
+        if (_log.shouldInfo())
             _log.info("Unregistering keys for destination " + dest.calculateHash().toBase64());
         return _leaseSetKeys.remove(dest.calculateHash());
     }
diff --git a/router/java/src/net/i2p/router/LeaseSetKeys.java b/router/java/src/net/i2p/router/LeaseSetKeys.java
index 75556d817f..ac623c05e7 100644
--- a/router/java/src/net/i2p/router/LeaseSetKeys.java
+++ b/router/java/src/net/i2p/router/LeaseSetKeys.java
@@ -8,6 +8,12 @@ package net.i2p.router;
  *
  */
 
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+import net.i2p.crypto.EncType;
 import net.i2p.data.Destination;
 import net.i2p.data.PrivateKey;
 import net.i2p.data.SigningPrivateKey;
@@ -19,15 +25,66 @@ import net.i2p.data.SigningPrivateKey;
 public class LeaseSetKeys {
     private final SigningPrivateKey _revocationKey;
     private final PrivateKey _decryptionKey;
+    private final PrivateKey _decryptionKeyEC;
+
+    /**
+     * Unmodifiable, ElGamal only
+     * @since 0.9.44
+     */
+    public static final Set<EncType> SET_ELG = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048));
+    private static final Set<EncType> SET_EC = Collections.unmodifiableSet(EnumSet.of(EncType.ECIES_X25519));
+    private static final Set<EncType> SET_BOTH = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048, EncType.ECIES_X25519));
+    private static final Set<EncType> SET_NONE = Collections.unmodifiableSet(EnumSet.noneOf(EncType.class));
 
     /**
+     *  Client with a single key
+     *
      *  @param dest unused
      *  @param revocationKey unused, may be null
      *  @param decryptionKey non-null
      */
     public LeaseSetKeys(Destination dest, SigningPrivateKey revocationKey, PrivateKey decryptionKey) {
-	_revocationKey = revocationKey;
-	_decryptionKey = decryptionKey;
+        _revocationKey = revocationKey;
+        EncType type = decryptionKey.getType();
+        if (type == EncType.ELGAMAL_2048) {
+            _decryptionKey = decryptionKey;
+            _decryptionKeyEC = null;
+        } else if (type == EncType.ECIES_X25519) {
+            _decryptionKey = null;
+            _decryptionKeyEC = decryptionKey;
+        } else {
+            throw new IllegalArgumentException("Unknown type " + type);
+        }
+    }
+
+    /**
+     *  Client with multiple keys
+     *
+     *  @param dest unused
+     *  @param revocationKey unused, may be null
+     *  @param decryptionKeys non-null
+     *  @since 0.9.44
+     */
+    public LeaseSetKeys(Destination dest, SigningPrivateKey revocationKey, List<PrivateKey> decryptionKeys) {
+        _revocationKey = revocationKey;
+        PrivateKey elg = null;
+        PrivateKey ec = null;
+        for (PrivateKey pk : decryptionKeys) {
+            EncType type = pk.getType();
+            if (type == EncType.ELGAMAL_2048) {
+                if (elg != null)
+                    throw new IllegalArgumentException("Multiple keys same type");
+                elg = pk;
+            } else if (type == EncType.ECIES_X25519) {
+                if (ec != null)
+                    throw new IllegalArgumentException("Multiple keys same type");
+                ec = pk;
+            } else {
+                throw new IllegalArgumentException("Unknown type " + type);
+            }
+        }
+        _decryptionKey = elg;
+        _decryptionKeyEC = ec;
     }
 
     /**
@@ -43,7 +100,48 @@ public class LeaseSetKeys {
      * know on what router the destination is connected and as such can't encrypt 
      * to that router's normal public key.
      *
+     * @return ElGamal key or null if the LS does not support ElGamal
      */
     public PrivateKey getDecryptionKey() { return _decryptionKey; }
 
+    /**
+     * Decryption key which can open up garlic messages encrypted to the 
+     * LeaseSet's public key.  This is used because the general public does not
+     * know on what router the destination is connected and as such can't encrypt 
+     * to that router's normal public key.
+     *
+     * @return key of the specified type or null if the LS does not support that type
+     * @since 0.9.44
+     */
+    public PrivateKey getDecryptionKey(EncType type) {
+        if (type == EncType.ELGAMAL_2048)
+            return _decryptionKey;
+        if (type == EncType.ECIES_X25519)
+            return _decryptionKeyEC;
+        return null;
+    }
+
+    /**
+     * Do we support this type of encryption?
+     *
+     * @since 0.9.44
+     */
+    public boolean isSupported(EncType type) {
+        if (type == EncType.ELGAMAL_2048)
+            return _decryptionKey != null;
+        if (type == EncType.ECIES_X25519)
+            return _decryptionKeyEC != null;
+        return false;
+    }
+
+    /**
+     *  What types of encryption are supported?
+     *
+     *  @since 0.9.44
+     */
+    public Set<EncType> getSupportedEncryption() {
+        if (_decryptionKey != null)
+            return (_decryptionKeyEC != null) ? SET_BOTH : SET_ELG;
+        return (_decryptionKeyEC != null) ? SET_EC : SET_NONE;
+    }
 }
-- 
GitLab