From b37160fa8d08c45a457fe9e803763a96de3d83f5 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Thu, 21 Feb 2019 14:19:38 +0000
Subject: [PATCH] Crypto: Keygen for RedDSA, allow RedDSA for unblinded keys
 (Enc LS2)

---
 core/java/src/net/i2p/crypto/Blinding.java    | 20 +++++++-----
 .../java/src/net/i2p/crypto/KeyGenerator.java |  7 ++++-
 core/java/src/net/i2p/crypto/SU3File.java     |  3 +-
 .../net/i2p/crypto/eddsa/EdDSAPrivateKey.java |  2 +-
 .../i2p/crypto/eddsa/KeyPairGenerator.java    | 10 +++---
 .../i2p/crypto/eddsa/RedKeyPairGenerator.java | 31 +++++++++++++++++++
 .../src/net/i2p/data/EncryptedLeaseSet.java   |  6 ++--
 7 files changed, 61 insertions(+), 18 deletions(-)
 create mode 100644 core/java/src/net/i2p/crypto/eddsa/RedKeyPairGenerator.java

diff --git a/core/java/src/net/i2p/crypto/Blinding.java b/core/java/src/net/i2p/crypto/Blinding.java
index d9a423cbf9..6ef788d324 100644
--- a/core/java/src/net/i2p/crypto/Blinding.java
+++ b/core/java/src/net/i2p/crypto/Blinding.java
@@ -39,16 +39,18 @@ public final class Blinding {
     private Blinding() {}
 
     /**
-     *  Only for SigType EdDSA_SHA512_Ed25519.
+     *  Only for SigTypes EdDSA_SHA512_Ed25519 and RedDSA_SHA512_Ed25519.
      *
-     *  @param key must be SigType EdDSA_SHA512_Ed25519
+     *  @param key must be SigType EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519
      *  @param alpha must be SigType RedDSA_SHA512_Ed25519
      *  @return SigType RedDSA_SHA512_Ed25519
      *  @throws UnsupportedOperationException unless supported SigTypes
      *  @throws IllegalArgumentException on bad inputs
      */
     public static SigningPublicKey blind(SigningPublicKey key, SigningPrivateKey alpha) {
-        if (key.getType() != TYPE || alpha.getType() != TYPER)
+        SigType type = key.getType();
+        if ((type != TYPE && type != TYPER) ||
+            alpha.getType() != TYPER)
             throw new UnsupportedOperationException();
         try {
             EdDSAPublicKey jk = SigUtil.toJavaEdDSAKey(key);
@@ -61,16 +63,18 @@ public final class Blinding {
     }
 
     /**
-     *  Only for SigType EdDSA_SHA512_Ed25519.
+     *  Only for SigTypes EdDSA_SHA512_Ed25519 and RedDSA_SHA512_Ed25519.
      *
-     *  @param key must be SigType EdDSA_SHA512_Ed25519
+     *  @param key must be SigType EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519
      *  @param alpha must be SigType RedDSA_SHA512_Ed25519
      *  @return SigType RedDSA_SHA512_Ed25519
      *  @throws UnsupportedOperationException unless supported SigTypes
      *  @throws IllegalArgumentException on bad inputs
      */
     public static SigningPrivateKey blind(SigningPrivateKey key, SigningPrivateKey alpha) {
-        if (key.getType() != TYPE || alpha.getType() != TYPER)
+        SigType type = key.getType();
+        if ((type != TYPE && type != TYPER) ||
+            alpha.getType() != TYPER)
             throw new UnsupportedOperationException();
         try {
             EdDSAPrivateKey jk = SigUtil.toJavaEdDSAKey(key);
@@ -122,9 +126,9 @@ public final class Blinding {
 
     /**
      *  Generate alpha for the given time.
-     *  Only for SigType EdDSA_SHA512_Ed25519.
+     *  Only for SigType EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519.
      *
-     *  @param dest spk must be SigType EdDSA_SHA512_Ed25519
+     *  @param dest spk must be SigType EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519
      *  @param secret may be null or zero-length
      *  @param now for what time?
      *  @return SigType RedDSA_SHA512_Ed25519
diff --git a/core/java/src/net/i2p/crypto/KeyGenerator.java b/core/java/src/net/i2p/crypto/KeyGenerator.java
index 21b839a6e2..d7f1b6135a 100644
--- a/core/java/src/net/i2p/crypto/KeyGenerator.java
+++ b/core/java/src/net/i2p/crypto/KeyGenerator.java
@@ -35,6 +35,7 @@ import com.southernstorm.noise.crypto.x25519.Curve25519;
 import net.i2p.I2PAppContext;
 import net.i2p.crypto.eddsa.EdDSAPrivateKey;
 import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.RedKeyPairGenerator;
 import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
 import net.i2p.crypto.provider.I2PProvider;
 import net.i2p.data.Hash;
@@ -289,7 +290,11 @@ public final class KeyGenerator {
             return generateSigningKeys();
         java.security.KeyPair kp;
         if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
-            net.i2p.crypto.eddsa.KeyPairGenerator kpg = new net.i2p.crypto.eddsa.KeyPairGenerator();
+            net.i2p.crypto.eddsa.KeyPairGenerator kpg;
+            if (type == SigType.RedDSA_SHA512_Ed25519)
+                kpg = new RedKeyPairGenerator();
+            else
+                kpg = new net.i2p.crypto.eddsa.KeyPairGenerator();
             kpg.initialize(type.getParams(), _context.random());
             kp = kpg.generateKeyPair();
         } else {
diff --git a/core/java/src/net/i2p/crypto/SU3File.java b/core/java/src/net/i2p/crypto/SU3File.java
index 65b2ee542d..70da7e9c43 100644
--- a/core/java/src/net/i2p/crypto/SU3File.java
+++ b/core/java/src/net/i2p/crypto/SU3File.java
@@ -673,7 +673,8 @@ public class SU3File {
         for (SigType t : EnumSet.allOf(SigType.class)) {
             if (!t.isAvailable())
                 continue;
-            if (t == SigType.EdDSA_SHA512_Ed25519)
+            if (t == SigType.EdDSA_SHA512_Ed25519 ||
+                t == SigType.RedDSA_SHA512_Ed25519)
                 continue; // not supported by keytool, and does double hashing right now
             buf.append("      ").append(t).append("\t(code: ").append(t.getCode()).append(')');
             if (t.getCode() == DEFAULT_SIG_CODE)
diff --git a/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java b/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
index 8703c2945d..36d88e1932 100644
--- a/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
+++ b/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
@@ -71,7 +71,7 @@ public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
     }
 
     /**
-     * Returns the public key in its canonical encoding.
+     * Returns the private key in its canonical encoding.
      *<p>
      * This implements the following specs:
      *<ul><li>
diff --git a/core/java/src/net/i2p/crypto/eddsa/KeyPairGenerator.java b/core/java/src/net/i2p/crypto/eddsa/KeyPairGenerator.java
index f42f272d06..3480355cf3 100644
--- a/core/java/src/net/i2p/crypto/eddsa/KeyPairGenerator.java
+++ b/core/java/src/net/i2p/crypto/eddsa/KeyPairGenerator.java
@@ -21,11 +21,11 @@ import net.i2p.util.RandomSource;
  *
  *  @since 0.9.15
  */
-public final class KeyPairGenerator extends KeyPairGeneratorSpi {
-    private static final int DEFAULT_KEYSIZE = 256;
-    private EdDSAParameterSpec edParams;
-    private SecureRandom random;
-    private boolean initialized;
+public class KeyPairGenerator extends KeyPairGeneratorSpi {
+    protected static final int DEFAULT_KEYSIZE = 256;
+    protected EdDSAParameterSpec edParams;
+    protected SecureRandom random;
+    protected boolean initialized;
 
     private static final Hashtable<Integer, AlgorithmParameterSpec> edParameters;
 
diff --git a/core/java/src/net/i2p/crypto/eddsa/RedKeyPairGenerator.java b/core/java/src/net/i2p/crypto/eddsa/RedKeyPairGenerator.java
new file mode 100644
index 0000000000..de0e532af5
--- /dev/null
+++ b/core/java/src/net/i2p/crypto/eddsa/RedKeyPairGenerator.java
@@ -0,0 +1,31 @@
+package net.i2p.crypto.eddsa;
+
+import java.security.KeyPair;
+
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
+import net.i2p.util.RandomSource;
+
+/**
+ *  Default keysize is 256 (Ed25519)
+ *
+ *  @since 0.9.39
+ */
+public final class RedKeyPairGenerator extends KeyPairGenerator {
+
+    @Override
+    public KeyPair generateKeyPair() {
+        if (!initialized)
+            initialize(DEFAULT_KEYSIZE, RandomSource.getInstance());
+
+        // 64 bytes
+        byte[] seed = new byte[edParams.getCurve().getField().getb()/4];
+        random.nextBytes(seed);
+        byte[] b = EdDSABlinding.reduce(seed);
+
+        EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(b, null, edParams);
+        EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(privKey.getA(), edParams);
+
+        return new KeyPair(new EdDSAPublicKey(pubKey), new EdDSAPrivateKey(privKey));
+    }
+}
diff --git a/core/java/src/net/i2p/data/EncryptedLeaseSet.java b/core/java/src/net/i2p/data/EncryptedLeaseSet.java
index f83f9c85b7..0d70b9010e 100644
--- a/core/java/src/net/i2p/data/EncryptedLeaseSet.java
+++ b/core/java/src/net/i2p/data/EncryptedLeaseSet.java
@@ -82,7 +82,7 @@ public class EncryptedLeaseSet extends LeaseSet2 {
     /**
      * Overridden to set the blinded key
      *
-     * @param dest non-null, must be EdDSA_SHA512_Ed25519
+     * @param dest non-null, must be EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519
      * @throws IllegalStateException if already signed
      * @throws IllegalArgumentException if not EdDSA
      */
@@ -90,7 +90,9 @@ public class EncryptedLeaseSet extends LeaseSet2 {
     public void setDestination(Destination dest) {
         super.setDestination(dest);
         SigningPublicKey spk = dest.getSigningPublicKey();
-        if (spk.getType() != SigType.EdDSA_SHA512_Ed25519)
+        SigType type = spk.getType();
+        if (type != SigType.EdDSA_SHA512_Ed25519 &&
+            type != SigType.RedDSA_SHA512_Ed25519)
             throw new IllegalArgumentException();
         SigningPublicKey bpk = blind();
         if (_signingKey == null)
-- 
GitLab