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"); + } +}