diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp
index 1762da29a..90a83ff64 100644
--- a/apps/i2ptunnel/jsp/editClient.jsp
+++ b/apps/i2ptunnel/jsp/editClient.jsp
@@ -512,6 +512,12 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
class="tickbox" />
+ <% }
+ if (editBean.isSigTypeAvailable(7)) { %>
+
+
+ class="tickbox" />
+
<% } // isAvailable %>
diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp
index 437f8775b..49ccf20bd 100644
--- a/apps/i2ptunnel/jsp/editServer.jsp
+++ b/apps/i2ptunnel/jsp/editServer.jsp
@@ -599,6 +599,12 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
class="tickbox" />
+ <% }
+ if (editBean.isSigTypeAvailable(7)) { %>
+
+
+ class="tickbox" />
+
<% } // isAvailable %>
diff --git a/core/java/src/net/i2p/crypto/DSAEngine.java b/core/java/src/net/i2p/crypto/DSAEngine.java
index 20e2b3d03..7a7dfa104 100644
--- a/core/java/src/net/i2p/crypto/DSAEngine.java
+++ b/core/java/src/net/i2p/crypto/DSAEngine.java
@@ -42,6 +42,7 @@ import java.security.interfaces.ECKey;
import java.security.interfaces.RSAKey;
import net.i2p.I2PAppContext;
+import net.i2p.crypto.eddsa.EdDSAKey;
import net.i2p.data.Hash;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
@@ -503,7 +504,11 @@ public class DSAEngine {
if (type == SigType.DSA_SHA1)
return altVerifySigSHA1(signature, data, offset, len, verifyingKey);
- java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
+ java.security.Signature jsig;
+ if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
+ jsig = new net.i2p.crypto.eddsa.EdDSAEngine(type.getDigestInstance());
+ else
+ jsig = java.security.Signature.getInstance(type.getAlgorithmName());
PublicKey pubKey = SigUtil.toJavaKey(verifyingKey);
jsig.initVerify(pubKey);
jsig.update(data, offset, len);
@@ -543,7 +548,11 @@ public class DSAEngine {
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
String algo = getRawAlgo(type);
- java.security.Signature jsig = java.security.Signature.getInstance(algo);
+ java.security.Signature jsig;
+ if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
+ jsig = new net.i2p.crypto.eddsa.EdDSAEngine(); // Ignore algo, EdDSAKey includes a hash specification.
+ else
+ jsig = java.security.Signature.getInstance(algo);
jsig.initVerify(pubKey);
jsig.update(hash.getData());
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
@@ -580,7 +589,11 @@ public class DSAEngine {
if (type == SigType.DSA_SHA1)
return altSignSHA1(data, offset, len, privateKey);
- java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
+ java.security.Signature jsig;
+ if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
+ jsig = new net.i2p.crypto.eddsa.EdDSAEngine(type.getDigestInstance());
+ else
+ jsig = java.security.Signature.getInstance(type.getAlgorithmName());
PrivateKey privKey = SigUtil.toJavaKey(privateKey);
jsig.initSign(privKey, _context.random());
jsig.update(data, offset, len);
@@ -613,7 +626,11 @@ public class DSAEngine {
if (type.getHashLen() != hashlen)
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
- java.security.Signature jsig = java.security.Signature.getInstance(algo);
+ java.security.Signature jsig;
+ if (type.getBaseAlgorithm() == SigAlgo.EdDSA)
+ jsig = new net.i2p.crypto.eddsa.EdDSAEngine(); // Ignore algo, EdDSAKey includes a hash specification.
+ else
+ jsig = java.security.Signature.getInstance(algo);
jsig.initSign(privKey, _context.random());
jsig.update(hash.getData());
return SigUtil.fromJavaSig(jsig.sign(), type);
@@ -640,6 +657,8 @@ public class DSAEngine {
return "NONEwithDSA";
case EC:
return "NONEwithECDSA";
+ case EdDSA:
+ return "NONEwithEdDSA";
case RSA:
return "NONEwithRSA";
default:
@@ -653,6 +672,8 @@ public class DSAEngine {
return "NONEwithDSA";
if (key instanceof ECKey)
return "NONEwithECDSA";
+ if (key instanceof EdDSAKey)
+ return "NONEwithEdDSA";
if (key instanceof RSAKey)
return "NONEwithRSA";
throw new UnsupportedOperationException("Raw signatures unsupported for " + key.getClass().getName());
diff --git a/core/java/src/net/i2p/crypto/KeyGenerator.java b/core/java/src/net/i2p/crypto/KeyGenerator.java
index 025da6f59..69f53f71c 100644
--- a/core/java/src/net/i2p/crypto/KeyGenerator.java
+++ b/core/java/src/net/i2p/crypto/KeyGenerator.java
@@ -216,40 +216,46 @@ public class KeyGenerator {
public SimpleDataStructure[] generateSigningKeys(SigType type) throws GeneralSecurityException {
if (type == SigType.DSA_SHA1)
return generateSigningKeys();
- KeyPairGenerator kpg = KeyPairGenerator.getInstance(type.getBaseAlgorithm().getName());
KeyPair kp;
- try {
+ if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
+ net.i2p.crypto.eddsa.KeyPairGenerator kpg = new net.i2p.crypto.eddsa.KeyPairGenerator();
kpg.initialize(type.getParams(), _context.random());
kp = kpg.generateKeyPair();
- } catch (ProviderException pe) {
- // java.security.ProviderException: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_DOMAIN_PARAMS_INVALID
- // This is a RuntimeException, thx Sun
- // Fails for P-192 only, on Ubuntu
- Log log = _context.logManager().getLog(KeyGenerator.class);
- String pname = kpg.getProvider().getName();
- if ("BC".equals(pname)) {
- if (log.shouldLog(Log.WARN))
- log.warn("BC KPG failed for " + type, pe);
- throw new GeneralSecurityException("BC KPG for " + type, pe);
- }
- if (!ECConstants.isBCAvailable())
- throw new GeneralSecurityException(pname + " KPG failed for " + type, pe);
- if (log.shouldLog(Log.WARN))
- log.warn(pname + " KPG failed for " + type + ", trying BC" /* , pe */ );
+ } else {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(type.getBaseAlgorithm().getName());
try {
- kpg = KeyPairGenerator.getInstance(type.getBaseAlgorithm().getName(), "BC");
kpg.initialize(type.getParams(), _context.random());
kp = kpg.generateKeyPair();
- } catch (ProviderException pe2) {
+ } catch (ProviderException pe) {
+ // java.security.ProviderException: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_DOMAIN_PARAMS_INVALID
+ // This is a RuntimeException, thx Sun
+ // Fails for P-192 only, on Ubuntu
+ Log log = _context.logManager().getLog(KeyGenerator.class);
+ String pname = kpg.getProvider().getName();
+ if ("BC".equals(pname)) {
+ if (log.shouldLog(Log.WARN))
+ log.warn("BC KPG failed for " + type, pe);
+ throw new GeneralSecurityException("BC KPG for " + type, pe);
+ }
+ if (!ECConstants.isBCAvailable())
+ throw new GeneralSecurityException(pname + " KPG failed for " + type, pe);
if (log.shouldLog(Log.WARN))
- log.warn("BC KPG failed for " + type + " also", pe2);
- // throw original exception
- throw new GeneralSecurityException(pname + " KPG for " + type, pe);
- } catch (GeneralSecurityException gse) {
- if (log.shouldLog(Log.WARN))
- log.warn("BC KPG failed for " + type + " also", gse);
- // throw original exception
- throw new GeneralSecurityException(pname + " KPG for " + type, pe);
+ log.warn(pname + " KPG failed for " + type + ", trying BC" /* , pe */ );
+ try {
+ kpg = KeyPairGenerator.getInstance(type.getBaseAlgorithm().getName(), "BC");
+ kpg.initialize(type.getParams(), _context.random());
+ kp = kpg.generateKeyPair();
+ } catch (ProviderException pe2) {
+ if (log.shouldLog(Log.WARN))
+ log.warn("BC KPG failed for " + type + " also", pe2);
+ // throw original exception
+ throw new GeneralSecurityException(pname + " KPG for " + type, pe);
+ } catch (GeneralSecurityException gse) {
+ if (log.shouldLog(Log.WARN))
+ log.warn("BC KPG failed for " + type + " also", gse);
+ // throw original exception
+ throw new GeneralSecurityException(pname + " KPG for " + type, pe);
+ }
}
}
java.security.PublicKey pubkey = kp.getPublic();
diff --git a/core/java/src/net/i2p/crypto/SU3File.java b/core/java/src/net/i2p/crypto/SU3File.java
index b401c8f28..6647c8d45 100644
--- a/core/java/src/net/i2p/crypto/SU3File.java
+++ b/core/java/src/net/i2p/crypto/SU3File.java
@@ -515,6 +515,10 @@ public class SU3File {
StringBuilder buf = new StringBuilder(256);
buf.append("Available signature types:\n");
for (SigType t : EnumSet.allOf(SigType.class)) {
+ if (!t.isAvailable())
+ continue;
+ if (t == SigType.EdDSA_SHA512_25519)
+ 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)
buf.append(" DEFAULT");
diff --git a/core/java/src/net/i2p/crypto/SigAlgo.java b/core/java/src/net/i2p/crypto/SigAlgo.java
index 6229fe4ca..8e0523c1b 100644
--- a/core/java/src/net/i2p/crypto/SigAlgo.java
+++ b/core/java/src/net/i2p/crypto/SigAlgo.java
@@ -9,6 +9,7 @@ public enum SigAlgo {
DSA("DSA"),
EC("EC"),
+ EdDSA("EdDSA"),
RSA("RSA")
;
diff --git a/core/java/src/net/i2p/crypto/SigType.java b/core/java/src/net/i2p/crypto/SigType.java
index 2ce774c84..a5fe5304e 100644
--- a/core/java/src/net/i2p/crypto/SigType.java
+++ b/core/java/src/net/i2p/crypto/SigType.java
@@ -9,6 +9,7 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.data.Hash;
import net.i2p.data.SimpleDataStructure;
@@ -47,7 +48,8 @@ public enum SigType {
// TESTING....................
-
+ /** Pubkey 32 bytes; privkey 32 bytes; hash 64 bytes; sig 64 bytes; */
+ EdDSA_SHA512_25519(7, 32, 32, 64, 64, SigAlgo.EdDSA, "SHA-512", "SHA512withEdDSA", EdDSANamedCurveTable.getByName("ed25519-sha-512"));
// others..........
@@ -169,7 +171,8 @@ public enum SigType {
return true;
try {
getParams();
- Signature.getInstance(getAlgorithmName());
+ if (getBaseAlgorithm() != SigAlgo.EdDSA)
+ Signature.getInstance(getAlgorithmName());
getDigestInstance();
getHashInstance();
} catch (Exception e) {
diff --git a/core/java/src/net/i2p/crypto/SigUtil.java b/core/java/src/net/i2p/crypto/SigUtil.java
index 0be589dc6..cbf330ccb 100644
--- a/core/java/src/net/i2p/crypto/SigUtil.java
+++ b/core/java/src/net/i2p/crypto/SigUtil.java
@@ -5,7 +5,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
-
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
@@ -33,6 +32,11 @@ import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
@@ -47,8 +51,10 @@ import net.i2p.util.NativeBigInteger;
*/
public class SigUtil {
- private static final Map _pubkeyCache = new LHMCache(64);
- private static final Map _privkeyCache = new LHMCache(16);
+ private static final Map _ECPubkeyCache = new LHMCache(64);
+ private static final Map _ECPrivkeyCache = new LHMCache(16);
+ private static final Map _EdPubkeyCache = new LHMCache(64);
+ private static final Map _EdPrivkeyCache = new LHMCache(16);
private SigUtil() {}
@@ -62,6 +68,8 @@ public class SigUtil {
return toJavaDSAKey(pk);
case EC:
return toJavaECKey(pk);
+ case EdDSA:
+ return toJavaEdDSAKey(pk);
case RSA:
return toJavaRSAKey(pk);
default:
@@ -79,6 +87,8 @@ public class SigUtil {
return toJavaDSAKey(pk);
case EC:
return toJavaECKey(pk);
+ case EdDSA:
+ return toJavaEdDSAKey(pk);
case RSA:
return toJavaRSAKey(pk);
default:
@@ -96,6 +106,8 @@ public class SigUtil {
return fromJavaKey((DSAPublicKey) pk);
case EC:
return fromJavaKey((ECPublicKey) pk, type);
+ case EdDSA:
+ return fromJavaKey((EdDSAPublicKey) pk, type);
case RSA:
return fromJavaKey((RSAPublicKey) pk, type);
default:
@@ -113,6 +125,8 @@ public class SigUtil {
return fromJavaKey((DSAPrivateKey) pk);
case EC:
return fromJavaKey((ECPrivateKey) pk, type);
+ case EdDSA:
+ return fromJavaKey((EdDSAPrivateKey) pk, type);
case RSA:
return fromJavaKey((RSAPrivateKey) pk, type);
default:
@@ -126,14 +140,14 @@ public class SigUtil {
public static ECPublicKey toJavaECKey(SigningPublicKey pk)
throws GeneralSecurityException {
ECPublicKey rv;
- synchronized (_pubkeyCache) {
- rv = _pubkeyCache.get(pk);
+ synchronized (_ECPubkeyCache) {
+ rv = _ECPubkeyCache.get(pk);
}
if (rv != null)
return rv;
rv = cvtToJavaECKey(pk);
- synchronized (_pubkeyCache) {
- _pubkeyCache.put(pk, rv);
+ synchronized (_ECPubkeyCache) {
+ _ECPubkeyCache.put(pk, rv);
}
return rv;
}
@@ -144,14 +158,14 @@ public class SigUtil {
public static ECPrivateKey toJavaECKey(SigningPrivateKey pk)
throws GeneralSecurityException {
ECPrivateKey rv;
- synchronized (_privkeyCache) {
- rv = _privkeyCache.get(pk);
+ synchronized (_ECPrivkeyCache) {
+ rv = _ECPrivkeyCache.get(pk);
}
if (rv != null)
return rv;
rv = cvtToJavaECKey(pk);
- synchronized (_privkeyCache) {
- _privkeyCache.put(pk, rv);
+ synchronized (_ECPrivkeyCache) {
+ _ECPrivkeyCache.put(pk, rv);
}
return rv;
}
@@ -196,6 +210,72 @@ public class SigUtil {
return new SigningPrivateKey(type, bs);
}
+ /**
+ * @return JAVA EdDSA public key!
+ */
+ public static EdDSAPublicKey toJavaEdDSAKey(SigningPublicKey pk)
+ throws GeneralSecurityException {
+ EdDSAPublicKey rv;
+ synchronized (_EdPubkeyCache) {
+ rv = _EdPubkeyCache.get(pk);
+ }
+ if (rv != null)
+ return rv;
+ rv = cvtToJavaEdDSAKey(pk);
+ synchronized (_EdPubkeyCache) {
+ _EdPubkeyCache.put(pk, rv);
+ }
+ return rv;
+ }
+
+ /**
+ * @return JAVA EdDSA private key!
+ */
+ public static EdDSAPrivateKey toJavaEdDSAKey(SigningPrivateKey pk)
+ throws GeneralSecurityException {
+ EdDSAPrivateKey rv;
+ synchronized (_EdPrivkeyCache) {
+ rv = _EdPrivkeyCache.get(pk);
+ }
+ if (rv != null)
+ return rv;
+ rv = cvtToJavaEdDSAKey(pk);
+ synchronized (_EdPrivkeyCache) {
+ _EdPrivkeyCache.put(pk, rv);
+ }
+ return rv;
+ }
+
+ private static EdDSAPublicKey cvtToJavaEdDSAKey(SigningPublicKey pk)
+ throws GeneralSecurityException {
+ try {
+ return new EdDSAPublicKey(new EdDSAPublicKeySpec(
+ pk.getData(), (EdDSAParameterSpec) pk.getType().getParams()));
+ } catch (IllegalArgumentException iae) {
+ throw new InvalidKeyException(iae);
+ }
+ }
+
+ private static EdDSAPrivateKey cvtToJavaEdDSAKey(SigningPrivateKey pk)
+ throws GeneralSecurityException {
+ try {
+ return new EdDSAPrivateKey(new EdDSAPrivateKeySpec(
+ pk.getData(), (EdDSAParameterSpec) pk.getType().getParams()));
+ } catch (IllegalArgumentException iae) {
+ throw new InvalidKeyException(iae);
+ }
+ }
+
+ public static SigningPublicKey fromJavaKey(EdDSAPublicKey pk, SigType type)
+ throws GeneralSecurityException {
+ return new SigningPublicKey(type, pk.getAbyte());
+ }
+
+ public static SigningPrivateKey fromJavaKey(EdDSAPrivateKey pk, SigType type)
+ throws GeneralSecurityException {
+ return new SigningPrivateKey(type, pk.getSeed());
+ }
+
public static DSAPublicKey toJavaDSAKey(SigningPublicKey pk)
throws GeneralSecurityException {
KeyFactory kf = KeyFactory.getInstance("DSA");
@@ -290,8 +370,8 @@ public class SigUtil {
* @return ASN.1 representation
*/
public static byte[] toJavaSig(Signature sig) {
- // RSA sigs are not ASN encoded
- if (sig.getType().getBaseAlgorithm() == SigAlgo.RSA)
+ // RSA and EdDSA sigs are not ASN encoded
+ if (sig.getType().getBaseAlgorithm() == SigAlgo.RSA || sig.getType().getBaseAlgorithm() == SigAlgo.EdDSA)
return sig.getData();
return sigBytesToASN1(sig.getData());
}
@@ -302,8 +382,8 @@ public class SigUtil {
*/
public static Signature fromJavaSig(byte[] asn, SigType type)
throws SignatureException {
- // RSA sigs are not ASN encoded
- if (type.getBaseAlgorithm() == SigAlgo.RSA)
+ // RSA and EdDSA sigs are not ASN encoded
+ if (type.getBaseAlgorithm() == SigAlgo.RSA || type.getBaseAlgorithm() == SigAlgo.EdDSA)
return new Signature(type, asn);
return new Signature(type, aSN1ToSigBytes(asn, type.getSigLen()));
}
@@ -530,11 +610,17 @@ public class SigUtil {
}
public static void clearCaches() {
- synchronized(_pubkeyCache) {
- _pubkeyCache.clear();
+ synchronized(_ECPubkeyCache) {
+ _ECPubkeyCache.clear();
}
- synchronized(_privkeyCache) {
- _privkeyCache.clear();
+ synchronized(_ECPrivkeyCache) {
+ _ECPrivkeyCache.clear();
+ }
+ synchronized(_EdPubkeyCache) {
+ _EdPubkeyCache.clear();
+ }
+ synchronized(_EdPrivkeyCache) {
+ _EdPrivkeyCache.clear();
}
}
}
diff --git a/core/java/src/net/i2p/crypto/provider/I2PProvider.java b/core/java/src/net/i2p/crypto/provider/I2PProvider.java
new file mode 100644
index 000000000..ad001907e
--- /dev/null
+++ b/core/java/src/net/i2p/crypto/provider/I2PProvider.java
@@ -0,0 +1,42 @@
+package net.i2p.crypto.provider;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.Provider;
+
+public final class I2PProvider extends Provider {
+ public static final String PROVIDER_NAME = "I2P";
+ private static final String INFO = "I2P Security Provider v0.1, implementing" +
+ "several algorithms used by I2P.";
+
+ /**
+ * Construct a new provider. This should only be required when
+ * using runtime registration of the provider using the
+ * Security.addProvider() mechanism.
+ */
+ public I2PProvider() {
+ super(PROVIDER_NAME, 0.1, INFO);
+
+ AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public Void run() {
+ setup();
+ return null;
+ }
+ });
+ }
+
+ private void setup() {
+ // TODO: Implement SPIs for existing code
+ //put("Cipher.AES", "net.i2p.crypto.provider.CipherSpi$aesCBC");
+ //put("Cipher.ElGamal", "net.i2p.crypto.provider.CipherSpi$elGamal");
+ //put("Mac.HmacMD5-I2P", "net.i2p.crypto.provider.MacSpi");
+ put("MessageDigest.SHA-1", "net.i2p.crypto.SHA1");
+ //put("Signature.SHA1withDSA", "net.i2p.crypto.provider.SignatureSpi");
+
+ // EdDSA
+ put("KeyFactory.EdDSA", "net.i2p.crypto.eddsa.KeyFactory");
+ put("KeyPairGenerator.EdDSA", "net.i2p.crypto.eddsa.KeyPairGenerator");
+ put("Signature.SHA512withEdDSA", "net.i2p.crypto.eddsa.EdDSAEngine");
+ }
+}