From 2487bca47c1af1b09574675fe208960949904f90 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 30 Nov 2018 15:15:31 +0000 Subject: [PATCH] Crypto: Change X25519 key classes from Java keys to I2P keys, in prep for new crypto (Proposal 144) Add EncType Fix PrivateKey constructor w/ EncType Add support to KeyGenerator --- core/java/src/net/i2p/crypto/EncAlgo.java | 5 +- core/java/src/net/i2p/crypto/EncType.java | 9 +++- .../java/src/net/i2p/crypto/KeyGenerator.java | 36 ++++++++++--- core/java/src/net/i2p/crypto/KeyPair.java | 28 +++++++++++ .../i2p/crypto/x25519/spec/X25519Spec.java | 9 ++++ .../net/i2p/crypto/x25519/spec/package.html | 7 +++ core/java/src/net/i2p/data/PrivateKey.java | 5 +- .../noise/protocol/Curve25519DHState.java | 6 +-- .../transport/crypto/X25519KeyFactory.java | 7 ++- .../transport/crypto/X25519PrivateKey.java | 50 ------------------- .../transport/crypto/X25519PublicKey.java | 41 --------------- .../router/transport/ntcp/NTCPTransport.java | 13 ++--- 12 files changed, 103 insertions(+), 113 deletions(-) create mode 100644 core/java/src/net/i2p/crypto/KeyPair.java create mode 100644 core/java/src/net/i2p/crypto/x25519/spec/X25519Spec.java create mode 100644 core/java/src/net/i2p/crypto/x25519/spec/package.html delete mode 100644 router/java/src/net/i2p/router/transport/crypto/X25519PrivateKey.java delete mode 100644 router/java/src/net/i2p/router/transport/crypto/X25519PublicKey.java diff --git a/core/java/src/net/i2p/crypto/EncAlgo.java b/core/java/src/net/i2p/crypto/EncAlgo.java index 751701bcc..99a6c762e 100644 --- a/core/java/src/net/i2p/crypto/EncAlgo.java +++ b/core/java/src/net/i2p/crypto/EncAlgo.java @@ -10,7 +10,10 @@ package net.i2p.crypto; public enum EncAlgo { ELGAMAL("ElGamal"), - EC("EC"); + EC("EC"), + + /** @since 0.9.38 */ + ECIES("ECIES"); private final String name; diff --git a/core/java/src/net/i2p/crypto/EncType.java b/core/java/src/net/i2p/crypto/EncType.java index ca59e0267..97b630855 100644 --- a/core/java/src/net/i2p/crypto/EncType.java +++ b/core/java/src/net/i2p/crypto/EncType.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import static net.i2p.crypto.x25519.spec.X25519Spec.X25519_SPEC; import net.i2p.data.Hash; import net.i2p.data.SimpleDataStructure; @@ -36,7 +37,13 @@ public enum EncType { EC_P384(2, 96, 48, EncAlgo.EC, "EC/None/NoPadding", ECConstants.P384_SPEC, "0.9.20"), /** Pubkey 132 bytes; privkey 66 bytes; */ - EC_P521(3, 132, 66, EncAlgo.EC, "EC/None/NoPadding", ECConstants.P521_SPEC, "0.9.20"); + EC_P521(3, 132, 66, EncAlgo.EC, "EC/None/NoPadding", ECConstants.P521_SPEC, "0.9.20"), + + /** + * Pubkey 32 bytes; privkey 32 bytes + * @since 0.9.38 + */ + ECIES_X25519(4, 32, 32, EncAlgo.ECIES, "EC/None/NoPadding", X25519_SPEC, "0.9.38"); diff --git a/core/java/src/net/i2p/crypto/KeyGenerator.java b/core/java/src/net/i2p/crypto/KeyGenerator.java index b8d06f758..81db9ab86 100644 --- a/core/java/src/net/i2p/crypto/KeyGenerator.java +++ b/core/java/src/net/i2p/crypto/KeyGenerator.java @@ -31,6 +31,8 @@ import java.util.Arrays; import java.util.ArrayList; import java.util.Collection; +import com.southernstorm.noise.crypto.x25519.Curve25519; + import net.i2p.I2PAppContext; import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.EdDSAPublicKey; @@ -173,20 +175,38 @@ public final class KeyGenerator { return keys; } - /** Convert a PrivateKey to its corresponding PublicKey + /** + * Convert a PrivateKey to its corresponding PublicKey. + * As of 0.9.38, supports EncTypes + * * @param priv PrivateKey object * @return the corresponding PublicKey object * @throws IllegalArgumentException on bad key */ public static PublicKey getPublicKey(PrivateKey priv) { - BigInteger a = new NativeBigInteger(1, priv.toByteArray()); - BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp); - PublicKey pub = new PublicKey(); - try { - pub.setData(SigUtil.rectify(aalpha, PublicKey.KEYSIZE_BYTES)); - } catch (InvalidKeyException ike) { - throw new IllegalArgumentException(ike); + EncType type = priv.getType(); + byte[] data; + switch (type) { + case ELGAMAL_2048: + BigInteger a = new NativeBigInteger(1, priv.toByteArray()); + BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp); + try { + data = SigUtil.rectify(aalpha, PublicKey.KEYSIZE_BYTES); + } catch (InvalidKeyException ike) { + throw new IllegalArgumentException(ike); + } + break; + + case ECIES_X25519: + data = new byte[32]; + Curve25519.eval(data, 0, priv.getData(), null); + break; + + default: + throw new IllegalArgumentException("Unsupported algorithm"); + } + PublicKey pub = new PublicKey(type, data); return pub; } diff --git a/core/java/src/net/i2p/crypto/KeyPair.java b/core/java/src/net/i2p/crypto/KeyPair.java new file mode 100644 index 000000000..8fe0131dd --- /dev/null +++ b/core/java/src/net/i2p/crypto/KeyPair.java @@ -0,0 +1,28 @@ +package net.i2p.crypto; + +import net.i2p.data.PrivateKey; +import net.i2p.data.PublicKey; + +/** + * Same as java.security.KeyPair, but with I2P keys + * + * @since 0.9.38 + */ +public class KeyPair { + + private final PublicKey pub; + private final PrivateKey priv; + + public KeyPair(PublicKey publicKey, PrivateKey privateKey) { + pub = publicKey; + priv = privateKey; + } + + public PublicKey getPublic() { + return pub; + } + + public PrivateKey getPrivate() { + return priv; + } +} diff --git a/core/java/src/net/i2p/crypto/x25519/spec/X25519Spec.java b/core/java/src/net/i2p/crypto/x25519/spec/X25519Spec.java new file mode 100644 index 000000000..acd8c9d01 --- /dev/null +++ b/core/java/src/net/i2p/crypto/x25519/spec/X25519Spec.java @@ -0,0 +1,9 @@ +package net.i2p.crypto.x25519.spec; + +import java.security.spec.AlgorithmParameterSpec; + +public class X25519Spec implements AlgorithmParameterSpec { + + public static final X25519Spec X25519_SPEC = new X25519Spec(); + +} diff --git a/core/java/src/net/i2p/crypto/x25519/spec/package.html b/core/java/src/net/i2p/crypto/x25519/spec/package.html new file mode 100644 index 000000000..4a497f7e7 --- /dev/null +++ b/core/java/src/net/i2p/crypto/x25519/spec/package.html @@ -0,0 +1,7 @@ + +

+ AlgorithmParameterSpec for X25519. +

+ Since 0.9.38. +

+ diff --git a/core/java/src/net/i2p/data/PrivateKey.java b/core/java/src/net/i2p/data/PrivateKey.java index 5eafbbd8a..054931a88 100644 --- a/core/java/src/net/i2p/data/PrivateKey.java +++ b/core/java/src/net/i2p/data/PrivateKey.java @@ -53,8 +53,11 @@ public class PrivateKey extends SimpleDataStructure { * @since 0.9.38 */ public PrivateKey(EncType type, byte data[]) { - super(data); + super(); _type = type; + if (data == null) + throw new IllegalArgumentException("Data must be specified"); + _data = data; } /** constructs from base64 diff --git a/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java b/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java index 9abbbe66b..87f01c396 100644 --- a/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java +++ b/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java @@ -22,11 +22,11 @@ package com.southernstorm.noise.protocol; -import java.security.KeyPair; import java.util.Arrays; import com.southernstorm.noise.crypto.x25519.Curve25519; +import net.i2p.crypto.KeyPair; import net.i2p.router.transport.crypto.X25519KeyFactory; /** @@ -78,8 +78,8 @@ class Curve25519DHState implements DHState { @Override public void generateKeyPair() { KeyPair kp = _xdh.getKeys(); - System.arraycopy(kp.getPrivate().getEncoded(), 0, privateKey, 0, 32); - System.arraycopy(kp.getPublic().getEncoded(), 0, publicKey, 0, 32); + System.arraycopy(kp.getPrivate().getData(), 0, privateKey, 0, 32); + System.arraycopy(kp.getPublic().getData(), 0, publicKey, 0, 32); mode = 0x03; } diff --git a/router/java/src/net/i2p/router/transport/crypto/X25519KeyFactory.java b/router/java/src/net/i2p/router/transport/crypto/X25519KeyFactory.java index eabe2f6ab..862945bcf 100644 --- a/router/java/src/net/i2p/router/transport/crypto/X25519KeyFactory.java +++ b/router/java/src/net/i2p/router/transport/crypto/X25519KeyFactory.java @@ -1,11 +1,14 @@ package net.i2p.router.transport.crypto; -import java.security.KeyPair; import java.util.concurrent.LinkedBlockingQueue; import com.southernstorm.noise.crypto.x25519.Curve25519; import net.i2p.I2PAppContext; +import net.i2p.crypto.EncType; +import net.i2p.crypto.KeyPair; +import net.i2p.data.PrivateKey; +import net.i2p.data.PublicKey; import net.i2p.util.I2PThread; import net.i2p.util.Log; import net.i2p.util.SystemVersion; @@ -133,7 +136,7 @@ public class X25519KeyFactory extends I2PThread { } while (priv[31] == 0); byte[] pub = new byte[32]; Curve25519.eval(pub, 0, priv, null); - KeyPair rv = new KeyPair(new X25519PublicKey(pub), new X25519PrivateKey(priv)); + KeyPair rv = new KeyPair(new PublicKey(EncType.ECIES_X25519, pub), new PrivateKey(EncType.ECIES_X25519, priv)); long end = System.currentTimeMillis(); long diff = end - start; _context.statManager().addRateData("crypto.XDHGenerateTime", diff); diff --git a/router/java/src/net/i2p/router/transport/crypto/X25519PrivateKey.java b/router/java/src/net/i2p/router/transport/crypto/X25519PrivateKey.java deleted file mode 100644 index 1dd95a836..000000000 --- a/router/java/src/net/i2p/router/transport/crypto/X25519PrivateKey.java +++ /dev/null @@ -1,50 +0,0 @@ -package net.i2p.router.transport.crypto; - -import java.security.PrivateKey; - -import com.southernstorm.noise.crypto.x25519.Curve25519; - -/** - * A PrivateKey we can stick in a KeyPair. - * Raw data is accessible via getEncoded(). - * Also provides a toPublic() method. - * - * @since 0.9.36 - */ -public class X25519PrivateKey implements PrivateKey { - - private final byte[] _data; - - /** - * Montgomery representation, little-endian - * @param data 32 bytes - * @throws IllegalArgumentException if not 32 bytes - */ - public X25519PrivateKey(byte[] data) { - if (data.length != 32) - throw new IllegalArgumentException(); - _data = data; - } - - public X25519PublicKey toPublic() { - byte[] pub = new byte[32]; - Curve25519.eval(pub, 0, _data, null); - return new X25519PublicKey(pub); - } - - /** - * The raw byte array, there is no encoding. - * @return the data passed in - */ - public byte[] getEncoded() { - return _data; - } - - public String getAlgorithm() { - return "X25519"; - } - - public String getFormat() { - return "raw"; - } -} diff --git a/router/java/src/net/i2p/router/transport/crypto/X25519PublicKey.java b/router/java/src/net/i2p/router/transport/crypto/X25519PublicKey.java deleted file mode 100644 index a57a67284..000000000 --- a/router/java/src/net/i2p/router/transport/crypto/X25519PublicKey.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.i2p.router.transport.crypto; - -import java.security.PublicKey; - -/** - * A PublicKey we can stick in a KeyPair. - * Raw data is accessible via getEncoded(). - * - * @since 0.9.36 - */ -public class X25519PublicKey implements PublicKey { - - private final byte[] _data; - - /** - * Montgomery representation, little-endian - * @param data 32 bytes - * @throws IllegalArgumentException if not 32 bytes - */ - public X25519PublicKey(byte[] data) { - if (data.length != 32) - throw new IllegalArgumentException(); - _data = data; - } - - /** - * The raw byte array, there is no encoding. - * @return the data passed in - */ - public byte[] getEncoded() { - return _data; - } - - public String getAlgorithm() { - return "X25519"; - } - - public String getFormat() { - return "raw"; - } -} diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index e19d89645..c20aca596 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -8,7 +8,6 @@ import java.net.Inet6Address; import java.net.UnknownHostException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.security.KeyPair; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; @@ -27,11 +26,15 @@ import java.util.TreeSet; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; +import net.i2p.crypto.EncType; +import net.i2p.crypto.KeyPair; import net.i2p.crypto.SigType; import net.i2p.data.Base64; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Hash; +import net.i2p.data.PublicKey; +import net.i2p.data.PrivateKey; import net.i2p.data.router.RouterAddress; import net.i2p.data.router.RouterIdentity; import net.i2p.data.router.RouterInfo; @@ -48,8 +51,6 @@ import net.i2p.router.transport.TransportUtil; import static net.i2p.router.transport.TransportUtil.IPv6Config.*; import net.i2p.router.transport.crypto.DHSessionKeyBuilder; import net.i2p.router.transport.crypto.X25519KeyFactory; -import net.i2p.router.transport.crypto.X25519PublicKey; -import net.i2p.router.transport.crypto.X25519PrivateKey; import net.i2p.router.util.DecayingHashSet; import net.i2p.router.util.DecayingBloomFilter; import net.i2p.router.util.EventLog; @@ -257,12 +258,12 @@ public class NTCPTransport extends TransportImpl { } if (priv == null || priv.length != NTCP2_KEY_LEN) { KeyPair keys = xdh.getKeys(); - _ntcp2StaticPrivkey = keys.getPrivate().getEncoded(); - _ntcp2StaticPubkey = keys.getPublic().getEncoded(); + _ntcp2StaticPrivkey = keys.getPrivate().getData(); + _ntcp2StaticPubkey = keys.getPublic().getData(); shouldSave = true; } else { _ntcp2StaticPrivkey = priv; - _ntcp2StaticPubkey = (new X25519PrivateKey(priv)).toPublic().getEncoded(); + _ntcp2StaticPubkey = (new PrivateKey(EncType.ECIES_X25519, priv)).toPublic().getData(); } if (!shouldSave) { s = ctx.getProperty(PROP_NTCP2_IV);