NTCP2: Noise library changes:

Use key factory for key pair generation
Hardcode Noise name and compute first hash in advance
Expose mixHash method so we can process message 1 and 2 padding
Add method to get chaining key and hash for siphash key generation
Remove unused patterns, methods, code
Debug and speed test code
Finals
This commit is contained in:
zzz
2018-06-26 16:06:18 +00:00
parent 70f018eb87
commit da810467b3
10 changed files with 321 additions and 1383 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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();
}
/**

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}
****/
}