diff --git a/router/java/src/com/southernstorm/noise/crypto/Curve25519.java b/router/java/src/com/southernstorm/noise/crypto/Curve25519.java index 5183e2260..f0529b3b3 100644 --- a/router/java/src/com/southernstorm/noise/crypto/Curve25519.java +++ b/router/java/src/com/southernstorm/noise/crypto/Curve25519.java @@ -40,22 +40,22 @@ public final class Curve25519 { // Numbers modulo 2^255 - 19 are broken up into ten 26-bit words. private static final int NUM_LIMBS_255BIT = 10; private static final int NUM_LIMBS_510BIT = 20; - private int[] x_1; - private int[] x_2; - private int[] x_3; - private int[] z_2; - private int[] z_3; - private int[] A; - private int[] B; - private int[] C; - private int[] D; - private int[] E; - private int[] AA; - private int[] BB; - private int[] DA; - private int[] CB; - private long[] t1; - private int[] t2; + private final int[] x_1; + private final int[] x_2; + private final int[] x_3; + private final int[] z_2; + private final int[] z_3; + private final int[] A; + private final int[] B; + private final int[] C; + private final int[] D; + private final int[] E; + private final int[] AA; + private final int[] BB; + private final int[] DA; + private final int[] CB; + private final long[] t1; + private final int[] t2; /** * Constructs the temporary state holder for Curve25519 evaluation. @@ -188,7 +188,7 @@ public final class Curve25519 { } // At this point "x" will either be the answer or it will be the - // answer plus (2^255 - 19). Perform a trial subtraction to + // answer plus (2^255 - 19). Perform a trial subtraction to // complete the reduction process. reduceQuick(result); } diff --git a/router/java/src/com/southernstorm/noise/crypto/Poly1305.java b/router/java/src/com/southernstorm/noise/crypto/Poly1305.java index 6a2a6c3ee..e1efb8e68 100644 --- a/router/java/src/com/southernstorm/noise/crypto/Poly1305.java +++ b/router/java/src/com/southernstorm/noise/crypto/Poly1305.java @@ -32,12 +32,12 @@ import com.southernstorm.noise.protocol.Destroyable; public final class Poly1305 implements Destroyable { // The 130-bit intermediate values are broken up into five 26-bit words. - private byte[] nonce; - private byte[] block; - private int[] h; - private int[] r; - private int[] c; - private long[] t; + private final byte[] nonce; + private final byte[] block; + private final int[] h; + private final int[] r; + private final int[] c; + private final long[] t; private int posn; /** @@ -319,9 +319,9 @@ public final class Poly1305 implements Destroyable { public void destroy() { Arrays.fill(nonce, (byte)0); Arrays.fill(block, (byte)0); - Arrays.fill(h, (int)0); - Arrays.fill(r, (int)0); - Arrays.fill(c, (int)0); + Arrays.fill(h, 0); + Arrays.fill(r, 0); + Arrays.fill(c, 0); Arrays.fill(t, (long)0); } } diff --git a/router/java/src/com/southernstorm/noise/protocol/ChaChaPolyCipherState.java b/router/java/src/com/southernstorm/noise/protocol/ChaChaPolyCipherState.java index 25b1a4c0a..6d50e1de4 100644 --- a/router/java/src/com/southernstorm/noise/protocol/ChaChaPolyCipherState.java +++ b/router/java/src/com/southernstorm/noise/protocol/ChaChaPolyCipherState.java @@ -33,14 +33,16 @@ import com.southernstorm.noise.crypto.Poly1305; /** * Implements the ChaChaPoly cipher for Noise. */ -class ChaChaPolyCipherState implements CipherState { +public class ChaChaPolyCipherState implements CipherState { - private Poly1305 poly; - private int[] input; - private int[] output; - private byte[] polyKey; - long n; + private final Poly1305 poly; + private final int[] input; + private final int[] output; + private final byte[] polyKey; + private long n; private boolean haskey; + // I2P debug to be removed + private byte[] initialKey; /** * Constructs a new cipher state for the "ChaChaPoly" algorithm. @@ -80,6 +82,9 @@ class ChaChaPolyCipherState implements CipherState { @Override public void initializeKey(byte[] key, int offset) { + // I2P debug to be removed + initialKey = new byte[32]; + System.arraycopy(key, 0, initialKey, 0, 32); ChaChaCore.initKey256(input, key, offset); n = 0; haskey = true; @@ -287,4 +292,39 @@ class ChaChaPolyCipherState implements CipherState { public void setNonce(long nonce) { n = nonce; } + + /** + * I2P debug to be removed + * @return null if none yet + */ + public byte[] getKey() { + if (!haskey) + return null; + return initialKey; + } + + /** + * I2P debug + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(" Cipher State:\n" + + " nonce: "); + buf.append(n); + buf.append("\n" + + " init key: "); + // I2P debug to be removed + if (haskey) + buf.append(net.i2p.data.Base64.encode(initialKey)); + else + buf.append("null"); + buf.append("\n poly key: "); + if (haskey) + buf.append(net.i2p.data.Base64.encode(polyKey)); + else + buf.append("null"); + buf.append('\n'); + return buf.toString(); + } } diff --git a/router/java/src/com/southernstorm/noise/protocol/CipherState.java b/router/java/src/com/southernstorm/noise/protocol/CipherState.java index 3168139f4..66fd8bda5 100644 --- a/router/java/src/com/southernstorm/noise/protocol/CipherState.java +++ b/router/java/src/com/southernstorm/noise/protocol/CipherState.java @@ -155,4 +155,9 @@ public interface CipherState extends Destroyable { * value goes backwards then security may be compromised. */ void setNonce(long nonce); + + /** + * I2P debug to be removed + */ + public byte[] getKey(); } diff --git a/router/java/src/com/southernstorm/noise/protocol/CipherStatePair.java b/router/java/src/com/southernstorm/noise/protocol/CipherStatePair.java index 8ec07f29b..694e44549 100644 --- a/router/java/src/com/southernstorm/noise/protocol/CipherStatePair.java +++ b/router/java/src/com/southernstorm/noise/protocol/CipherStatePair.java @@ -69,10 +69,7 @@ public final class CipherStatePair implements Destroyable { */ public void senderOnly() { - if (recv != null) { - recv.destroy(); - recv = null; - } + recv.destroy(); } /** @@ -82,10 +79,7 @@ public final class CipherStatePair implements Destroyable { */ public void receiverOnly() { - if (send != null) { - send.destroy(); - send = null; - } + send.destroy(); } /** diff --git a/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java b/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java index 771faa2f7..2a39ed7b8 100644 --- a/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java +++ b/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java @@ -22,27 +22,32 @@ package com.southernstorm.noise.protocol; +import java.security.KeyPair; import java.util.Arrays; import com.southernstorm.noise.crypto.Curve25519; +import net.i2p.router.transport.crypto.X25519KeyFactory; + /** * Implementation of the Curve25519 algorithm for the Noise protocol. */ class Curve25519DHState implements DHState { - private byte[] publicKey; - private byte[] privateKey; + private final byte[] publicKey; + private final byte[] privateKey; private int mode; + private final X25519KeyFactory _xdh; /** * Constructs a new Diffie-Hellman object for Curve25519. */ - public Curve25519DHState() + public Curve25519DHState(X25519KeyFactory xdh) { publicKey = new byte [32]; privateKey = new byte [32]; mode = 0; + _xdh = xdh; } @Override @@ -72,8 +77,9 @@ class Curve25519DHState implements DHState { @Override public void generateKeyPair() { - Noise.random(privateKey); - Curve25519.eval(publicKey, 0, privateKey, null); + KeyPair kp = _xdh.getKeys(); + System.arraycopy(kp.getPrivate().getEncoded(), 0, privateKey, 0, 32); + System.arraycopy(kp.getPublic().getEncoded(), 0, publicKey, 0, 32); mode = 0x03; } diff --git a/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java b/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java index daa67b14d..377e4f1fa 100644 --- a/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java +++ b/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java @@ -28,13 +28,15 @@ import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.ShortBufferException; +import net.i2p.router.transport.crypto.X25519KeyFactory; + /** * Interface to a Noise handshake. */ public class HandshakeState implements Destroyable { - private SymmetricState symmetric; - private boolean isInitiator; + private final SymmetricState symmetric; + private final boolean isInitiator; private DHState localKeyPair; private DHState localEphemeral; private DHState localHybrid; @@ -44,11 +46,12 @@ public class HandshakeState implements Destroyable { private DHState fixedEphemeral; private DHState fixedHybrid; private int action; - private int requirements; - private short[] pattern; + private final int requirements; private int patternIndex; - private byte[] preSharedKey; - private byte[] prologue; + // not supported + private static final byte[] preSharedKey = null; + // not supported + private static final byte[] prologue = null; /** * Enumerated value that indicates that the handshake object @@ -132,11 +135,39 @@ public class HandshakeState implements Destroyable { */ private static final int FALLBACK_POSSIBLE = 0x40; + public static final String protocolName = "Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256"; + private static final String prefix; + private static final String patternId; + private static String dh; + private static final String cipher; + private static final String hash; + private static final short[] pattern; + + static { + // Parse the protocol name into its components. + String[] components = protocolName.split("_"); + if (components.length != 5) + throw new IllegalArgumentException("Protocol name must have 5 components"); + prefix = components[0]; + patternId = components[1].substring(0, 2); + dh = components[2]; + cipher = components[3]; + hash = components[4]; + if (!prefix.equals("Noise") && !prefix.equals("NoisePSK")) + throw new IllegalArgumentException("Prefix must be Noise or NoisePSK"); + pattern = Pattern.lookup(patternId); + if (pattern == null) + throw new IllegalArgumentException("Handshake pattern is not recognized"); + if (!dh.equals("25519")) + throw new IllegalArgumentException("Unknown Noise DH algorithm name: " + dh); + } + /** * Creates a new Noise handshake. + * Noise protocol name is hardcoded. * - * @param protocolName The name of the Noise protocol. * @param role The role, HandshakeState.INITIATOR or HandshakeState.RESPONDER. + * @param xdh The key pair factory for ephemeral keys * * @throws IllegalArgumentException The protocolName is not * formatted correctly, or the role is not recognized. @@ -144,23 +175,8 @@ public class HandshakeState implements Destroyable { * @throws NoSuchAlgorithmException One of the cryptographic algorithms * that is specified in the protocolName is not supported. */ - public HandshakeState(String protocolName, int role) throws NoSuchAlgorithmException + public HandshakeState(int role, X25519KeyFactory xdh) throws NoSuchAlgorithmException { - // Parse the protocol name into its components. - String[] components = protocolName.split("_"); - if (components.length != 5) - throw new IllegalArgumentException("Protocol name must have 5 components"); - String prefix = components[0]; - String patternId = components[1]; - String dh = components[2]; - String hybrid = null; - String cipher = components[3]; - String hash = components[4]; - if (!prefix.equals("Noise") && !prefix.equals("NoisePSK")) - throw new IllegalArgumentException("Prefix must be Noise or NoisePSK"); - pattern = Pattern.lookup(patternId); - if (pattern == null) - throw new IllegalArgumentException("Handshake pattern is not recognized"); short flags = pattern[0]; int extraReqs = 0; if ((flags & Pattern.FLAG_REMOTE_REQUIRED) != 0 && patternId.length() > 1) @@ -169,24 +185,13 @@ public class HandshakeState implements Destroyable { // Reverse the pattern flags so that the responder is "local". flags = Pattern.reverseFlags(flags); } - int index = dh.indexOf('+'); - if (index != -1) { - // The DH name has two components: regular and hybrid. - hybrid = dh.substring(index + 1); - dh = dh.substring(0, index); - if ((flags & Pattern.FLAG_LOCAL_HYBRID) == 0 || (flags & Pattern.FLAG_REMOTE_HYBRID) == 0) - throw new IllegalArgumentException("Hybrid function specified for non-hybrid pattern"); - } else { - if ((flags & Pattern.FLAG_LOCAL_HYBRID) != 0 || (flags & Pattern.FLAG_REMOTE_HYBRID) != 0) - throw new IllegalArgumentException("Hybrid function not specified for hybrid pattern"); - } // Check that the role is correctly specified. if (role != INITIATOR && role != RESPONDER) throw new IllegalArgumentException("Role must be initiator or responder"); // Initialize this object. This will also create the cipher and hash objects. - symmetric = new SymmetricState(protocolName, cipher, hash); + symmetric = new SymmetricState(cipher, hash); isInitiator = (role == INITIATOR); action = NO_ACTION; requirements = extraReqs | computeRequirements(flags, prefix, role, false); @@ -194,28 +199,14 @@ public class HandshakeState implements Destroyable { // Create the DH objects that we will need later. if ((flags & Pattern.FLAG_LOCAL_STATIC) != 0) - localKeyPair = Noise.createDH(dh); + localKeyPair = new Curve25519DHState(xdh); if ((flags & Pattern.FLAG_LOCAL_EPHEMERAL) != 0) - localEphemeral = Noise.createDH(dh); - if ((flags & Pattern.FLAG_LOCAL_HYBRID) != 0) - localHybrid = Noise.createDH(hybrid); + localEphemeral = new Curve25519DHState(xdh); if ((flags & Pattern.FLAG_REMOTE_STATIC) != 0) - remotePublicKey = Noise.createDH(dh); + remotePublicKey = new Curve25519DHState(xdh); if ((flags & Pattern.FLAG_REMOTE_EPHEMERAL) != 0) - remoteEphemeral = Noise.createDH(dh); - if ((flags & Pattern.FLAG_REMOTE_HYBRID) != 0) - remoteHybrid = Noise.createDH(hybrid); + remoteEphemeral = new Curve25519DHState(xdh); - // We cannot use hybrid algorithms like New Hope for ephemeral or static keys, - // as the unbalanced nature of the algorithm only works with "f" and "ff" tokens. - if (localKeyPair instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + localKeyPair.getDHName() + "' for static keys"); - if (localEphemeral instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + localEphemeral.getDHName() + "' for ephemeral keys"); - if (remotePublicKey instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + remotePublicKey.getDHName() + "' for static keys"); - if (remoteEphemeral instanceof DHStateHybrid) - throw new NoSuchAlgorithmException("Cannot use '" + remoteEphemeral.getDHName() + "' for ephemeral keys"); } /** @@ -237,91 +228,6 @@ public class HandshakeState implements Destroyable { { return isInitiator ? INITIATOR : RESPONDER; } - - /** - * Determine if this handshake needs a pre-shared key value - * and one has not been configured yet. - * - * @return true if a pre-shared key is needed; false if not. - */ - public boolean needsPreSharedKey() - { - if (preSharedKey != null) - return false; - else - return (requirements & PSK_REQUIRED) != 0; - } - - /** - * Determine if this object has already been configured with a - * pre-shared key. - * - * @return true if the pre-shared key has already been configured; - * false if one is not needed or it has not been configured yet. - */ - public boolean hasPreSharedKey() - { - return preSharedKey != null; - } - - /** - * Sets the pre-shared key for this handshake. - * - * @param key Buffer containing the pre-shared key value. - * @param offset Offset into the buffer of the first byte of the key. - * @param length The length of the pre-shared key, which must be 32. - * - * @throws IllegalArgumentException The length is not 32. - * - * @throws UnsupportedOperationException Pre-shared keys are not - * supported for this handshake type. - * - * @throws IllegalStateException The handshake has already started, - * so the pre-shared key can no longer be set. - */ - public void setPreSharedKey(byte[] key, int offset, int length) - { - if (length != 32) { - throw new IllegalArgumentException - ("Pre-shared keys must be 32 bytes in length"); - } - if ((requirements & PSK_REQUIRED) == 0) { - throw new UnsupportedOperationException - ("Pre-shared keys are not supported for this handshake"); - } - if (action != NO_ACTION) { - throw new IllegalStateException - ("Handshake has already started; cannot set pre-shared key"); - } - if (preSharedKey != null) { - Noise.destroy(preSharedKey); - preSharedKey = null; - } - preSharedKey = Noise.copySubArray(key, offset, length); - } - - /** - * Sets the prologue for this handshake. - * - * @param prologue Buffer containing the prologue value. - * @param offset Offset into the buffer of the first byte of the prologue. - * @param length The length of the prologue in bytes. - * - * @throws IllegalStateException The handshake has already started, - * so the prologue can no longer be set. - */ - public void setPrologue(byte[] prologue, int offset, int length) - { - if (action != NO_ACTION) { - throw new IllegalStateException - ("Handshake has already started; cannot set prologue"); - } - if (this.prologue != null) { - Noise.destroy(this.prologue); - this.prologue = null; - } - this.prologue = Noise.copySubArray(prologue, offset, length); - } /** * Gets the keypair object for the local static key. @@ -406,60 +312,6 @@ public class HandshakeState implements Destroyable { return false; } - /** - * Gets the DHState object containing a fixed local ephemeral - * key value for this handshake. - * - * @return The fixed ephemeral key object, or null if a local - * ephemeral key is not required by this handshake. - * - * This function is intended for testing only. It can be used - * to establish a fixed ephemeral key for test vectors. This - * function should not be used in real applications. - */ - public DHState getFixedEphemeralKey() - { - if (fixedEphemeral != null) - return fixedEphemeral; - if (localEphemeral == null) - return null; - try { - fixedEphemeral = Noise.createDH(localEphemeral.getDHName()); - } catch (NoSuchAlgorithmException e) { - // This shouldn't happen - the local ephemeral key would - // have already been created with the same name! - fixedEphemeral = null; - } - return fixedEphemeral; - } - - /** - * Gets the DHState object containing a fixed local hybrid - * key value for this handshake. - * - * @return The fixed hybrid key object, or null if a local - * hybrid key is not required by this handshake. - * - * This function is intended for testing only. It can be used - * to establish a fixed hybrid key for test vectors. This - * function should not be used in real applications. - */ - public DHState getFixedHybridKey() - { - if (fixedHybrid != null) - return fixedHybrid; - if (localHybrid == null) - return null; - try { - fixedHybrid = Noise.createDH(localHybrid.getDHName()); - } catch (NoSuchAlgorithmException e) { - // This shouldn't happen - the local hybrid key would - // have already been created with the same name! - fixedHybrid = null; - } - return fixedHybrid; - } - // Empty value for when the prologue is not supplied. private static final byte[] emptyPrologue = new byte [0]; @@ -481,7 +333,7 @@ public class HandshakeState implements Destroyable { * @see #getAction() * @see #writeMessage(byte[], int, byte[], int, int) * @see #readMessage(byte[], int, int, byte[], int) - * @see #fallback() + * see #fallback() */ public void start() { @@ -504,10 +356,6 @@ public class HandshakeState implements Destroyable { if (remotePublicKey == null || !remotePublicKey.hasPublicKey()) throw new IllegalStateException("Remote static key required"); } - if ((requirements & PSK_REQUIRED) != 0) { - if (preSharedKey == null) - throw new IllegalStateException("Pre-shared key required"); - } // Hash the prologue value. if (prologue != null) @@ -515,10 +363,6 @@ public class HandshakeState implements Destroyable { else symmetric.mixHash(emptyPrologue, 0, 0); - // Hash the pre-shared key into the chaining key and handshake hash. - if (preSharedKey != null) - symmetric.mixPreSharedKey(preSharedKey); - // Mix the pre-supplied public keys into the handshake hash. if (isInitiator) { if ((requirements & LOCAL_PREMSG) != 0) @@ -553,151 +397,6 @@ public class HandshakeState implements Destroyable { action = READ_MESSAGE; } - /** - * Falls back to the "XXfallback" handshake pattern. - * - * This function is intended used to help implement the "Noise Pipes" protocol. - * It resets a HandshakeState object with the original handshake pattern - * (usually "IK"), converting it into an object with the handshake pattern - * "XXfallback". Information from the previous session such as the local - * keypair, the initiator's ephemeral key, the prologue value, and the - * pre-shared key, are passed to the new session. - * - * Once the fallback has been initiated, the application can set - * new values for the handshake parameters if the values from the - * previous session do not apply. For example, the application may - * use a different prologue for the fallback than for the original - * session. - * - * After setting any new parameters, the application calls start() - * again to restart the handshake from where it left off before the fallback. - * - * Note that this function reverses the roles of initiator and responder. - * - * @throws UnsupportedOperationException The current handshake pattern - * is not compatible with "XXfallback". - * - * @throws IllegalStateException The previous protocol has not started - * or it has not reached the fallback position yet. - * - * @throws NoSuchAlgorithmException One of the cryptographic algorithms - * that is specified in the new protocolName is not supported. - * - * @see #start() - */ - public void fallback() throws NoSuchAlgorithmException - { - fallback("XXfallback"); - } - - /** - * Falls back to another handshake pattern. - * - * @param patternName The name of the pattern to fall back to; - * e.g. "XXfallback", "NXfallback", etc. - * - * This function resets a HandshakeState object with the original - * handshake pattern, and converts it into an object with the new handshake - * patternName. Information from the previous session such as the local - * keypair, the initiator's ephemeral key, the prologue value, and the - * pre-shared key, are passed to the new session. - * - * Once the fallback has been initiated, the application can set - * new values for the handshake parameters if the values from the - * previous session do not apply. For example, the application may - * use a different prologue for the fallback than for the original - * session. - * - * After setting any new parameters, the application calls start() - * again to restart the handshake from where it left off before the fallback. - * - * The new pattern may have greater key requirements than the original; - * for example changing from "NK" from "XXfallback" requires that the - * initiator's static public key be set. The application is responsible for - * setting any extra keys before calling start(). - * - * Note that this function reverses the roles of initiator and responder. - * - * @throws UnsupportedOperationException The current handshake pattern - * is not compatible with the patternName, or patternName is not a - * fallback pattern. - * - * @throws IllegalStateException The previous protocol has not started - * or it has not reached the fallback position yet. - * - * @throws NoSuchAlgorithmException One of the cryptographic algorithms - * that is specified in the new protocolName is not supported. - * - * @see #start() - */ - public void fallback(String patternName) throws NoSuchAlgorithmException - { - // The original pattern must end in "K" for fallback to be possible. - if ((requirements & FALLBACK_POSSIBLE) == 0) - throw new UnsupportedOperationException("Previous handshake pattern does not support fallback"); - - // Check that "patternName" supports fallback. - short[] newPattern = Pattern.lookup(patternName); - if (newPattern == null || (newPattern[0] & Pattern.FLAG_REMOTE_EPHEM_REQ) == 0) - throw new UnsupportedOperationException("New pattern is not a fallback pattern"); - - // The initiator should be waiting for a return message from the - // responder, and the responder should have failed on the first - // handshake message from the initiator. We also allow the - // responder to fallback after processing the first message - // successfully; it decides to always fall back anyway. - if (isInitiator) { - if ((action != FAILED && action != READ_MESSAGE) || !localEphemeral.hasPublicKey()) - throw new IllegalStateException("Initiator cannot fall back from this state"); - } else { - if ((action != FAILED && action != WRITE_MESSAGE) || !remoteEphemeral.hasPublicKey()) - throw new IllegalStateException("Responder cannot fall back from this state"); - } - - // Format a new protocol name for the fallback variant - // and recreate the SymmetricState object. - String[] components = symmetric.getProtocolName().split("_"); - components[1] = patternName; - StringBuilder builder = new StringBuilder(); - builder.append(components[0]); - for (int index = 1; index < components.length; ++index) { - builder.append('_'); - builder.append(components[index]); - } - String name = builder.toString(); - SymmetricState newSymmetric = new SymmetricState(name, components[3], components[4]); - symmetric.destroy(); - symmetric = newSymmetric; - - // Convert the HandshakeState to the "XXfallback" pattern. - if (isInitiator) { - if (remoteEphemeral != null) - remoteEphemeral.clearKey(); - if (remoteHybrid != null) - remoteHybrid.clearKey(); - if (remotePublicKey != null) - remotePublicKey.clearKey(); - isInitiator = false; - } else { - if (localEphemeral != null) - localEphemeral.clearKey(); - if (localHybrid != null) - localHybrid.clearKey(); - if ((newPattern[0] & Pattern.FLAG_REMOTE_REQUIRED) == 0 && remotePublicKey != null) - remotePublicKey.clearKey(); - isInitiator = true; - } - action = NO_ACTION; - pattern = newPattern; - patternIndex = 1; - short flags = pattern[0]; - if (!isInitiator) { - // Reverse the pattern flags so that the responder is "local". - flags = Pattern.reverseFlags(flags); - } - requirements = computeRequirements(flags, components[0], isInitiator ? INITIATOR : RESPONDER, true); - } - /** * Gets the next action that the application should perform for * the handshake part of the protocol. @@ -795,9 +494,9 @@ public class HandshakeState implements Destroyable { switch (token) { case Pattern.E: { - // Generate a local ephemeral keypair and add the public - // key to the message. If we are running fixed vector tests, - // then the ephemeral key may have already been provided. + // Generate a local ephemeral keypair and add the public + // key to the message. If we are running fixed vector tests, + // then the ephemeral key may have already been provided. if (localEphemeral == null) throw new IllegalStateException("Pattern definition error"); if (fixedEphemeral == null) @@ -811,7 +510,7 @@ public class HandshakeState implements Destroyable { symmetric.mixHash(message, messagePosn, len); // If the protocol is using pre-shared keys, then also mix - // the local ephemeral key into the chaining key. + // the local ephemeral key into the chaining key. if (preSharedKey != null) symmetric.mixKey(message, messagePosn, len); messagePosn += len; @@ -820,7 +519,7 @@ public class HandshakeState implements Destroyable { case Pattern.S: { - // Encrypt the local static public key and add it to the message. + // Encrypt the local static public key and add it to the message. if (localKeyPair == null) throw new IllegalStateException("Pattern definition error"); len = localKeyPair.getPublicKeyLength(); @@ -859,52 +558,6 @@ public class HandshakeState implements Destroyable { } break; - case Pattern.SS: - { - // DH operation with initiator and responder static keys. - mixDH(localKeyPair, remotePublicKey); - } - break; - - case Pattern.F: - { - // Generate a local hybrid keypair and add the public - // key to the message. If we are running fixed vector tests, - // then a fixed hybrid key may have already been provided. - if (localHybrid == null) - throw new IllegalStateException("Pattern definition error"); - if (localHybrid instanceof DHStateHybrid) { - // The DH object is something like New Hope which needs to - // generate keys relative to the other party's public key. - DHStateHybrid hybrid = (DHStateHybrid)localHybrid; - if (fixedHybrid == null) - hybrid.generateKeyPair(remoteHybrid); - else - hybrid.copyFrom(fixedHybrid, remoteHybrid); - } else { - if (fixedHybrid == null) - localHybrid.generateKeyPair(); - else - localHybrid.copyFrom(fixedHybrid); - } - len = localHybrid.getPublicKeyLength(); - if (space < len) - throw new ShortBufferException(); - macLen = symmetric.getMACLength(); - if (space < (len + macLen)) - throw new ShortBufferException(); - localHybrid.getPublicKey(message, messagePosn); - messagePosn += symmetric.encryptAndHash(message, messagePosn, message, messagePosn, len); - } - break; - - case Pattern.FF: - { - // DH operation with initiator and responder hybrid keys. - mixDH(localHybrid, remoteHybrid); - } - break; - default: { // Unknown token code. Abort. @@ -993,7 +646,7 @@ public class HandshakeState implements Destroyable { switch (token) { case Pattern.E: { - // Save the remote ephemeral key and hash it. + // Save the remote ephemeral key and hash it. if (remoteEphemeral == null) throw new IllegalStateException("Pattern definition error"); len = remoteEphemeral.getPublicKeyLength(); @@ -1002,15 +655,15 @@ public class HandshakeState implements Destroyable { symmetric.mixHash(message, messageOffset, len); remoteEphemeral.setPublicKey(message, messageOffset); if (remoteEphemeral.isNullPublicKey()) { - // The remote ephemeral key is null, which means that it is - // not contributing anything to the security of the session - // and is in fact downgrading the security to "none at all" - // in some of the message patterns. Reject all such keys. + // The remote ephemeral key is null, which means that it is + // not contributing anything to the security of the session + // and is in fact downgrading the security to "none at all" + // in some of the message patterns. Reject all such keys. throw new BadPaddingException("Null remote public key"); } // If the protocol is using pre-shared keys, then also mix - // the remote ephemeral key into the chaining key. + // the remote ephemeral key into the chaining key. if (preSharedKey != null) symmetric.mixKey(message, messageOffset, len); messageOffset += len; @@ -1065,47 +718,6 @@ public class HandshakeState implements Destroyable { } break; - case Pattern.SS: - { - // DH operation with initiator and responder static keys. - mixDH(localKeyPair, remotePublicKey); - } - break; - - case Pattern.F: - { - // Decrypt and read the remote hybrid ephemeral key. - if (remoteHybrid == null) - throw new IllegalStateException("Pattern definition error"); - if (remoteHybrid instanceof DHStateHybrid) { - // The DH object is something like New Hope. The public key - // length may need to change based on whether we already have - // generated a local hybrid keypair or not. - ((DHStateHybrid)remoteHybrid).specifyPeer(localHybrid); - } - len = remoteHybrid.getPublicKeyLength(); - macLen = symmetric.getMACLength(); - if (space < (len + macLen)) - throw new ShortBufferException(); - byte[] temp = new byte [len]; - try { - if (symmetric.decryptAndHash(message, messageOffset, temp, 0, len + macLen) != len) - throw new ShortBufferException(); - remoteHybrid.setPublicKey(temp, 0); - } finally { - Noise.destroy(temp); - } - messageOffset += len + macLen; - } - break; - - case Pattern.FF: - { - // DH operation with initiator and responder hybrid keys. - mixDH(localHybrid, remoteHybrid); - } - break; - default: { // Unknown token code. Abort. @@ -1235,7 +847,7 @@ public class HandshakeState implements Destroyable { */ private static int computeRequirements(short flags, String prefix, int role, boolean isFallback) { - int requirements = 0; + int requirements = 0; if ((flags & Pattern.FLAG_LOCAL_STATIC) != 0) { requirements |= LOCAL_REQUIRED; } @@ -1252,9 +864,83 @@ public class HandshakeState implements Destroyable { if (isFallback) requirements |= FALLBACK_PREMSG; } - if (prefix.equals("NoisePSK")) { - requirements |= PSK_REQUIRED; - } return requirements; } + + /** + * I2P for mixing in padding in messages 1 and 2 + */ + public void mixHash(byte[] data, int offset, int length) { + symmetric.mixHash(data, offset, length); + } + + /** + * I2P for getting chaining key for siphash calc + * @return a copy + */ + public byte[] getChainingKey() { + return symmetric.getChainingKey(); + } + + /** + * I2P for getting current hash for siphash calculation + * @return NOT a copy, do not modify + */ + public byte[] getHash() { + return symmetric.getHandshakeHash(); + } + + /** + * I2P debug + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("Handshake State:\n"); + buf.append(symmetric.toString()); + + byte[] tmp = new byte[32]; + + DHState dh = localKeyPair; + buf.append("Local static public key (s) : "); + if (dh != null && dh.hasPublicKey()) { + dh.getPublicKey(tmp, 0); + buf.append(net.i2p.data.Base64.encode(tmp)); + } else { + buf.append("null"); + } + buf.append('\n'); + + dh = remotePublicKey; + buf.append("Remote static public key (rs) : "); + if (dh != null && dh.hasPublicKey()) { + dh.getPublicKey(tmp, 0); + buf.append(net.i2p.data.Base64.encode(tmp)); + } else { + buf.append("null"); + } + buf.append('\n'); + + dh = localEphemeral; + buf.append("Local ephemeral public key (e) : "); + if (dh != null && dh.hasPublicKey()) { + dh.getPublicKey(tmp, 0); + buf.append(net.i2p.data.Base64.encode(tmp)); + } else { + buf.append("null"); + } + buf.append('\n'); + + dh = remoteEphemeral; + buf.append("Remote ephemeral public key (re) : "); + if (dh != null && dh.hasPublicKey()) { + dh.getPublicKey(tmp, 0); + buf.append(net.i2p.data.Base64.encode(tmp)); + } else { + buf.append("null"); + } + buf.append('\n'); + + return buf.toString(); + } } diff --git a/router/java/src/com/southernstorm/noise/protocol/Noise.java b/router/java/src/com/southernstorm/noise/protocol/Noise.java index 34948ad48..29764faa2 100644 --- a/router/java/src/com/southernstorm/noise/protocol/Noise.java +++ b/router/java/src/com/southernstorm/noise/protocol/Noise.java @@ -29,11 +29,6 @@ import java.util.Arrays; import javax.crypto.BadPaddingException; -import com.southernstorm.noise.crypto.Blake2bMessageDigest; -import com.southernstorm.noise.crypto.Blake2sMessageDigest; -import com.southernstorm.noise.crypto.SHA256MessageDigest; -import com.southernstorm.noise.crypto.SHA512MessageDigest; - /** * Utility functions for the Noise protocol library. */ @@ -44,57 +39,6 @@ public final class Noise { */ public static final int MAX_PACKET_LEN = 65535; - private static SecureRandom random = new SecureRandom(); - - /** - * Generates random data using the system random number generator. - * - * @param data The data buffer to fill with random data. - */ - public static void random(byte[] data) - { - random.nextBytes(data); - } - - private static boolean forceFallbacks = false; - - /** - * Force the use of plain Java fallback crypto implementations. - * - * @param force Set to true for force fallbacks, false to - * try to use the system implementation before falling back. - * - * This function is intended for testing purposes to toggle between - * the system JCA/JCE implementations and the plain Java fallback - * reference implementations. - */ - public static void setForceFallbacks(boolean force) - { - forceFallbacks = force; - } - - /** - * Creates a Diffie-Hellman object from its Noise protocol name. - * - * @param name The name of the DH algorithm; e.g. "25519", "448", etc. - * - * @return The Diffie-Hellman object if the name is recognized. - * - * @throws NoSuchAlgorithmException The name is not recognized as a - * valid Noise protocol name, or there is no cryptography provider - * in the system that implements the algorithm. - */ - public static DHState createDH(String name) throws NoSuchAlgorithmException - { - if (name.equals("25519")) - return new Curve25519DHState(); - if (name.equals("448")) - return new Curve448DHState(); - if (name.equals("NewHope")) - return new NewHopeDHState(); - throw new NoSuchAlgorithmException("Unknown Noise DH algorithm name: " + name); - } - /** * Creates a cipher object from its Noise protocol name. * @@ -108,20 +52,7 @@ public final class Noise { */ public static CipherState createCipher(String name) throws NoSuchAlgorithmException { - if (name.equals("AESGCM")) { - if (forceFallbacks) - return new AESGCMFallbackCipherState(); - // "AES/GCM/NoPadding" exists in some recent JDK's but it is flaky - // to use and not easily back-portable to older Android versions. - // We instead emulate AESGCM on top of "AES/CTR/NoPadding". - try { - return new AESGCMOnCtrCipherState(); - } catch (NoSuchAlgorithmException e1) { - // Could not find anything useful in the JCA/JCE so - // use the pure Java fallback implementation instead. - return new AESGCMFallbackCipherState(); - } - } else if (name.equals("ChaChaPoly")) { + if (name.equals("ChaChaPoly")) { return new ChaChaPolyCipherState(); } throw new NoSuchAlgorithmException("Unknown Noise cipher algorithm name: " + name); @@ -145,41 +76,9 @@ public final class Noise { // The only algorithm that is required to be implemented by a // JDK is "SHA-256", although "SHA-512" is fairly common as well. if (name.equals("SHA256")) { - if (forceFallbacks) - return new SHA256MessageDigest(); try { return MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { - return new SHA256MessageDigest(); - } - } else if (name.equals("SHA512")) { - if (forceFallbacks) - return new SHA512MessageDigest(); - try { - return MessageDigest.getInstance("SHA-512"); - } catch (NoSuchAlgorithmException e) { - return new SHA512MessageDigest(); - } - } else if (name.equals("BLAKE2b")) { - // Bouncy Castle registers the BLAKE2b variant we - // want under the name "BLAKE2B-512". - if (forceFallbacks) - return new Blake2bMessageDigest(); - try { - return MessageDigest.getInstance("BLAKE2B-512"); - } catch (NoSuchAlgorithmException e) { - return new Blake2bMessageDigest(); - } - } else if (name.equals("BLAKE2s")) { - // Bouncy Castle doesn't currently (June 2016) have an - // implementation of BLAKE2s, but look for the most - // obvious provider name in case one is added in the future. - if (forceFallbacks) - return new Blake2sMessageDigest(); - try { - return MessageDigest.getInstance("BLAKE2S-256"); - } catch (NoSuchAlgorithmException e) { - return new Blake2sMessageDigest(); } } throw new NoSuchAlgorithmException("Unknown Noise hash algorithm name: " + name); diff --git a/router/java/src/com/southernstorm/noise/protocol/Pattern.java b/router/java/src/com/southernstorm/noise/protocol/Pattern.java index 157af6ad7..63620c60d 100644 --- a/router/java/src/com/southernstorm/noise/protocol/Pattern.java +++ b/router/java/src/com/southernstorm/noise/protocol/Pattern.java @@ -54,89 +54,6 @@ class Pattern { public static final short FLAG_REMOTE_HYBRID = 0x1000; public static final short FLAG_REMOTE_HYBRID_REQ = 0x2000; - private static final short[] noise_pattern_N = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - ES - }; - - private static final short[] noise_pattern_K = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - ES, - SS - }; - - private static final short[] noise_pattern_X = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - ES, - S, - SS - }; - - private static final short[] noise_pattern_NN = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE - }; - - private static final short[] noise_pattern_NK = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - ES, - FLIP_DIR, - E, - EE - }; - - private static final short[] noise_pattern_NX = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - S, - ES - }; - - private static final short[] noise_pattern_XN = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - FLIP_DIR, - S, - SE - }; - private static final short[] noise_pattern_XK = { FLAG_LOCAL_STATIC | FLAG_LOCAL_EPHEMERAL | @@ -154,583 +71,6 @@ class Pattern { SE }; - private static final short[] noise_pattern_XX = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - S, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KN = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_KK = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - ES, - SS, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_KX = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - EE, - SE, - S, - ES - }; - - private static final short[] noise_pattern_IN = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL, - - E, - S, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_IK = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - ES, - S, - SS, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_IX = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - S, - FLIP_DIR, - E, - EE, - SE, - S, - ES - }; - - private static final short[] noise_pattern_XXfallback = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_EPHEM_REQ, - - E, - EE, - S, - SE, - FLIP_DIR, - S, - ES - }; - - private static final short[] noise_pattern_Xnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_REQUIRED, - - E, - S, - ES, - SS - }; - - private static final short[] noise_pattern_NXnoidh = { - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - S, - EE, - ES - }; - - private static final short[] noise_pattern_XXnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - S, - EE, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KXnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - FLIP_DIR, - E, - S, - EE, - SE, - ES - }; - - private static final short[] noise_pattern_IKnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_REQUIRED, - - E, - S, - ES, - SS, - FLIP_DIR, - E, - EE, - SE - }; - - private static final short[] noise_pattern_IXnoidh = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL, - - E, - S, - FLIP_DIR, - E, - S, - EE, - SE, - ES - }; - - private static final short[] noise_pattern_NNhfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF - }; - - private static final short[] noise_pattern_NKhfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - FLIP_DIR, - E, - F, - EE, - FF - }; - - private static final short[] noise_pattern_NXhfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - S, - ES - }; - - private static final short[] noise_pattern_XNhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_XKhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - FLIP_DIR, - E, - F, - EE, - FF, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_XXhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - S, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KNhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_KKhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - SS, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_KXhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - EE, - FF, - SE, - S, - ES - }; - - private static final short[] noise_pattern_INhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_IKhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_REQUIRED, - - E, - F, - ES, - S, - SS, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_IXhfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - FLIP_DIR, - E, - F, - EE, - FF, - SE, - S, - ES - }; - - private static final short[] noise_pattern_XXfallback_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_EPHEM_REQ | - FLAG_REMOTE_HYBRID | - FLAG_REMOTE_HYBRID_REQ, - - E, - F, - EE, - FF, - S, - SE, - FLIP_DIR, - S, - ES - }; - - private static final short[] noise_pattern_NXnoidh_hfs = { - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - S, - EE, - FF, - ES - }; - - private static final short[] noise_pattern_XXnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - S, - EE, - FF, - ES, - FLIP_DIR, - S, - SE - }; - - private static final short[] noise_pattern_KXnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_REQUIRED | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - FLIP_DIR, - E, - F, - S, - EE, - FF, - SE, - ES - }; - - private static final short[] noise_pattern_IKnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - ES, - SS, - FLIP_DIR, - E, - F, - EE, - FF, - SE - }; - - private static final short[] noise_pattern_IXnoidh_hfs = { - FLAG_LOCAL_STATIC | - FLAG_LOCAL_EPHEMERAL | - FLAG_LOCAL_HYBRID | - FLAG_REMOTE_STATIC | - FLAG_REMOTE_EPHEMERAL | - FLAG_REMOTE_HYBRID, - - E, - F, - S, - FLIP_DIR, - E, - F, - S, - EE, - FF, - SE, - ES - }; - /** * Look up the description information for a pattern. * @@ -739,86 +79,8 @@ class Pattern { */ public static short[] lookup(String name) { - if (name.equals("N")) - return noise_pattern_N; - else if (name.equals("K")) - return noise_pattern_K; - else if (name.equals("X")) - return noise_pattern_X; - else if (name.equals("NN")) - return noise_pattern_NN; - else if (name.equals("NK")) - return noise_pattern_NK; - else if (name.equals("NX")) - return noise_pattern_NX; - else if (name.equals("XN")) - return noise_pattern_XN; - else if (name.equals("XK")) + if (name.equals("XK")) return noise_pattern_XK; - else if (name.equals("XX")) - return noise_pattern_XX; - else if (name.equals("KN")) - return noise_pattern_KN; - else if (name.equals("KK")) - return noise_pattern_KK; - else if (name.equals("KX")) - return noise_pattern_KX; - else if (name.equals("IN")) - return noise_pattern_IN; - else if (name.equals("IK")) - return noise_pattern_IK; - else if (name.equals("IX")) - return noise_pattern_IX; - else if (name.equals("XXfallback")) - return noise_pattern_XXfallback; - else if (name.equals("Xnoidh")) - return noise_pattern_Xnoidh; - else if (name.equals("NXnoidh")) - return noise_pattern_NXnoidh; - else if (name.equals("XXnoidh")) - return noise_pattern_XXnoidh; - else if (name.equals("KXnoidh")) - return noise_pattern_KXnoidh; - else if (name.equals("IKnoidh")) - return noise_pattern_IKnoidh; - else if (name.equals("IXnoidh")) - return noise_pattern_IXnoidh; - else if (name.equals("NNhfs")) - return noise_pattern_NNhfs; - else if (name.equals("NKhfs")) - return noise_pattern_NKhfs; - else if (name.equals("NXhfs")) - return noise_pattern_NXhfs; - else if (name.equals("XNhfs")) - return noise_pattern_XNhfs; - else if (name.equals("XKhfs")) - return noise_pattern_XKhfs; - else if (name.equals("XXhfs")) - return noise_pattern_XXhfs; - else if (name.equals("KNhfs")) - return noise_pattern_KNhfs; - else if (name.equals("KKhfs")) - return noise_pattern_KKhfs; - else if (name.equals("KXhfs")) - return noise_pattern_KXhfs; - else if (name.equals("INhfs")) - return noise_pattern_INhfs; - else if (name.equals("IKhfs")) - return noise_pattern_IKhfs; - else if (name.equals("IXhfs")) - return noise_pattern_IXhfs; - else if (name.equals("XXfallback+hfs")) - return noise_pattern_XXfallback_hfs; - else if (name.equals("NXnoidh+hfs")) - return noise_pattern_NXnoidh_hfs; - else if (name.equals("XXnoidh+hfs")) - return noise_pattern_XXnoidh_hfs; - else if (name.equals("KXnoidh+hfs")) - return noise_pattern_KXnoidh_hfs; - else if (name.equals("IKnoidh+hfs")) - return noise_pattern_IKnoidh_hfs; - else if (name.equals("IXnoidh+hfs")) - return noise_pattern_IXnoidh_hfs; return null; } diff --git a/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java b/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java index ae54fd3f2..398f0bc9e 100644 --- a/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java +++ b/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java @@ -36,15 +36,41 @@ import javax.crypto.ShortBufferException; */ class SymmetricState implements Destroyable { - private String name; - private CipherState cipher; - private MessageDigest hash; - private byte[] ck; - private byte[] h; - private byte[] prev_h; + // precalculated hash of the Noise name + private static final byte[] INIT_HASH; + + static { + byte[] protocolNameBytes; + try { + protocolNameBytes = HandshakeState.protocolName.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + // If UTF-8 is not supported, then we are definitely in trouble! + throw new UnsupportedOperationException("UTF-8 encoding is not supported"); + } + INIT_HASH = new byte[32]; + if (protocolNameBytes.length <= 32) { + System.arraycopy(protocolNameBytes, 0, INIT_HASH, 0, protocolNameBytes.length); + Arrays.fill(INIT_HASH, protocolNameBytes.length, 32, (byte)0); + } else { + try { + MessageDigest hash = Noise.createHash("SHA256"); + hash.update(protocolNameBytes, 0, protocolNameBytes.length); + hash.digest(INIT_HASH, 0, 32); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + } + + private final CipherState cipher; + private final MessageDigest hash; + private final byte[] ck; + private final byte[] h; + private final byte[] prev_h; /** * Constructs a new symmetric state object. + * Noise protocol name is hardcoded. * * @param protocolName The name of the Noise protocol, which is assumed to be valid. * @param cipherName The name of the cipher within protocolName. @@ -53,9 +79,8 @@ class SymmetricState implements Destroyable { * @throws NoSuchAlgorithmException The cipher or hash algorithm in the * protocol name is not supported. */ - public SymmetricState(String protocolName, String cipherName, String hashName) throws NoSuchAlgorithmException + public SymmetricState(String cipherName, String hashName) throws NoSuchAlgorithmException { - name = protocolName; cipher = Noise.createCipher(cipherName); hash = Noise.createHash(hashName); int hashLength = hash.getDigestLength(); @@ -63,21 +88,7 @@ class SymmetricState implements Destroyable { h = new byte [hashLength]; prev_h = new byte [hashLength]; - byte[] protocolNameBytes; - try { - protocolNameBytes = protocolName.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - // If UTF-8 is not supported, then we are definitely in trouble! - throw new UnsupportedOperationException("UTF-8 encoding is not supported"); - } - - if (protocolNameBytes.length <= hashLength) { - System.arraycopy(protocolNameBytes, 0, h, 0, protocolNameBytes.length); - Arrays.fill(h, protocolNameBytes.length, h.length, (byte)0); - } else { - hashOne(protocolNameBytes, 0, protocolNameBytes.length, h, 0, h.length); - } - + System.arraycopy(INIT_HASH, 0, h, 0, hashLength); System.arraycopy(h, 0, ck, 0, hashLength); } @@ -88,7 +99,7 @@ class SymmetricState implements Destroyable { */ public String getProtocolName() { - return name; + return HandshakeState.protocolName; } /** @@ -311,31 +322,11 @@ class SymmetricState implements Destroyable { @Override public void destroy() { - if (cipher != null) { - cipher.destroy(); - cipher = null; - } - if (hash != null) { - // The built-in fallback implementations are destroyable. - // JCA/JCE implementations aren't, so try reset() instead. - if (hash instanceof Destroyable) - ((Destroyable)hash).destroy(); - else - hash.reset(); - hash = null; - } - if (ck != null) { - Noise.destroy(ck); - ck = null; - } - if (h != null) { - Noise.destroy(h); - h = null; - } - if (prev_h != null) { - Noise.destroy(prev_h); - prev_h = null; - } + cipher.destroy(); + hash.reset(); + Noise.destroy(ck); + Noise.destroy(h); + Noise.destroy(prev_h); } /** @@ -481,4 +472,59 @@ class SymmetricState implements Destroyable { Noise.destroy(tempHash); } } + + /** + * I2P for getting chaining key for siphash calculation + * @return a copy + */ + public byte[] getChainingKey() { + byte[] rv = new byte[ck.length]; + System.arraycopy(ck, 0, rv, 0, ck.length); + return rv; + } + + /** + * I2P debug + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(" Symmetric State:\n" + + " ck: "); + buf.append(net.i2p.data.Base64.encode(ck)); + buf.append("\n" + + " h: "); + buf.append(net.i2p.data.Base64.encode(h)); + buf.append('\n'); + buf.append(cipher.toString()); + return buf.toString(); + } + +/**** + private static final int LENGTH = 33; + + public static void main(String args[]) throws Exception { + net.i2p.I2PAppContext ctx = net.i2p.I2PAppContext.getGlobalContext(); + byte[] rand = new byte[32]; + byte[] data = new byte[LENGTH]; + byte[] out = new byte[32]; + System.out.println("Warmup"); + int RUNS = 25000; + SymmetricState ss = new SymmetricState("ChaChaPoly", "SHA256"); + for (int i = 0; i < RUNS; i++) { + ctx.random().nextBytes(rand); + ctx.random().nextBytes(data); + ss.hmac(rand, 0, 32, data, 0, LENGTH, out, 0, 32); + } + System.out.println("Start"); + RUNS = 500000; + long start = System.currentTimeMillis(); + for (int i = 0; i < RUNS; i++) { + ss.hmac(rand, 0, 32, data, 0, LENGTH, out, 0, 32); + } + long time = System.currentTimeMillis() - start; + System.out.println("Time for " + RUNS + " HMAC-SHA256 computations:"); + System.out.println("Noise time (ms): " + time); + } +****/ }