HandshakeState API
This commit is contained in:
parent
1764404d1d
commit
b29ba50927
@ -26,6 +26,7 @@ import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.AEADBadTagException;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
@ -130,13 +131,22 @@ class AESGCMCipherState implements CipherState {
|
||||
|
||||
@Override
|
||||
public int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset,
|
||||
byte[] ciphertext, int ciphertextOffset, int length) {
|
||||
byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException {
|
||||
int space;
|
||||
if (ciphertextOffset > ciphertext.length)
|
||||
space = 0;
|
||||
else
|
||||
space = ciphertext.length - ciphertextOffset;
|
||||
if (keySpec == null) {
|
||||
// The key is not set yet - return the plaintext as-is.
|
||||
if (length > space)
|
||||
throw new ShortBufferException();
|
||||
if (plaintext != ciphertext || plaintextOffset != ciphertextOffset)
|
||||
System.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);
|
||||
return length;
|
||||
}
|
||||
if (space < 16 || length > (space - 16))
|
||||
throw new ShortBufferException();
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, createGCMParams());
|
||||
} catch (InvalidKeyException e) {
|
||||
@ -152,8 +162,6 @@ class AESGCMCipherState implements CipherState {
|
||||
int result = cipher.update(plaintext, plaintextOffset, length, ciphertext, ciphertextOffset);
|
||||
result += cipher.doFinal(ciphertext, ciphertextOffset + result);
|
||||
return result;
|
||||
} catch (ShortBufferException e) {
|
||||
return -1;
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
return -1;
|
||||
} catch (BadPaddingException e) {
|
||||
@ -164,7 +172,14 @@ class AESGCMCipherState implements CipherState {
|
||||
@Override
|
||||
public int decryptWithAd(byte[] ad, byte[] ciphertext,
|
||||
int ciphertextOffset, byte[] plaintext, int plaintextOffset,
|
||||
int length) {
|
||||
int length) throws ShortBufferException, AEADBadTagException {
|
||||
int space;
|
||||
if (plaintextOffset > plaintext.length)
|
||||
space = 0;
|
||||
else
|
||||
space = plaintext.length - plaintextOffset;
|
||||
if (length > space)
|
||||
throw new ShortBufferException();
|
||||
if (keySpec == null) {
|
||||
// The key is not set yet - return the ciphertext as-is.
|
||||
if (plaintext != ciphertext || plaintextOffset != ciphertextOffset)
|
||||
@ -175,10 +190,10 @@ class AESGCMCipherState implements CipherState {
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, createGCMParams());
|
||||
} catch (InvalidKeyException e) {
|
||||
// Shouldn't happen.
|
||||
return -1;
|
||||
throw new AEADBadTagException();
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
// Shouldn't happen.
|
||||
return -1;
|
||||
throw new AEADBadTagException();
|
||||
}
|
||||
if (ad != null)
|
||||
cipher.updateAAD(ad);
|
||||
@ -186,12 +201,12 @@ class AESGCMCipherState implements CipherState {
|
||||
int result = cipher.update(ciphertext, ciphertextOffset, length, plaintext, plaintextOffset);
|
||||
result += cipher.doFinal(plaintext, plaintextOffset + result);
|
||||
return result;
|
||||
} catch (ShortBufferException e) {
|
||||
return -1;
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
return -1;
|
||||
throw new AEADBadTagException();
|
||||
} catch (AEADBadTagException e) {
|
||||
throw e;
|
||||
} catch (BadPaddingException e) {
|
||||
return -1;
|
||||
throw new AEADBadTagException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,9 @@ package com.southernstorm.noise.protocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.AEADBadTagException;
|
||||
import javax.crypto.ShortBufferException;
|
||||
|
||||
import com.southernstorm.noise.crypto.ChaChaCore;
|
||||
import com.southernstorm.noise.crypto.Poly1305;
|
||||
|
||||
@ -207,13 +210,22 @@ class ChaChaPolyCipherState implements CipherState {
|
||||
|
||||
@Override
|
||||
public int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset,
|
||||
byte[] ciphertext, int ciphertextOffset, int length) {
|
||||
byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException {
|
||||
int space;
|
||||
if (ciphertextOffset > ciphertext.length)
|
||||
space = 0;
|
||||
else
|
||||
space = ciphertext.length - ciphertextOffset;
|
||||
if (!haskey) {
|
||||
// The key is not set yet - return the plaintext as-is.
|
||||
if (length > space)
|
||||
throw new ShortBufferException();
|
||||
if (plaintext != ciphertext || plaintextOffset != ciphertextOffset)
|
||||
System.arraycopy(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);
|
||||
return length;
|
||||
}
|
||||
if (space < 16 || length > (space - 16))
|
||||
throw new ShortBufferException();
|
||||
setup(ad);
|
||||
encrypt(plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);
|
||||
finish(ad, length);
|
||||
@ -224,7 +236,14 @@ class ChaChaPolyCipherState implements CipherState {
|
||||
@Override
|
||||
public int decryptWithAd(byte[] ad, byte[] ciphertext,
|
||||
int ciphertextOffset, byte[] plaintext, int plaintextOffset,
|
||||
int length) {
|
||||
int length) throws ShortBufferException, AEADBadTagException {
|
||||
int space;
|
||||
if (plaintextOffset > plaintext.length)
|
||||
space = 0;
|
||||
else
|
||||
space = plaintext.length - plaintextOffset;
|
||||
if (length > space)
|
||||
throw new ShortBufferException();
|
||||
if (!haskey) {
|
||||
// The key is not set yet - return the ciphertext as-is.
|
||||
if (plaintext != ciphertext || plaintextOffset != ciphertextOffset)
|
||||
@ -232,7 +251,7 @@ class ChaChaPolyCipherState implements CipherState {
|
||||
return length;
|
||||
}
|
||||
if (length < 16)
|
||||
return -1;
|
||||
throw new AEADBadTagException();
|
||||
int dataLen = length = 16;
|
||||
setup(ad);
|
||||
poly.update(ciphertext, ciphertextOffset, dataLen);
|
||||
@ -241,7 +260,7 @@ class ChaChaPolyCipherState implements CipherState {
|
||||
for (int index = 0; index < 16; ++index)
|
||||
temp |= (polyKey[index] ^ ciphertext[ciphertextOffset + dataLen + index]);
|
||||
if (temp != 0)
|
||||
return -1;
|
||||
throw new AEADBadTagException();
|
||||
encrypt(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen);
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
|
||||
package com.southernstorm.noise.protocol;
|
||||
|
||||
import javax.crypto.AEADBadTagException;
|
||||
import javax.crypto.ShortBufferException;
|
||||
|
||||
/**
|
||||
* Interface to an authenticated cipher for use in the Noise protocol.
|
||||
*
|
||||
@ -92,6 +95,9 @@ public interface CipherState extends Destroyable {
|
||||
* @return The length of the ciphertext plus the MAC tag, or -1 if the
|
||||
* ciphertext buffer is not large enough to hold the result.
|
||||
*
|
||||
* @throws ShortBufferException The ciphertext buffer does not have
|
||||
* enough space to hold the ciphertext plus MAC.
|
||||
*
|
||||
* The plaintext and ciphertext buffers can be the same for in-place
|
||||
* encryption. In that case, plaintextOffset must be identical to
|
||||
* ciphertextOffset.
|
||||
@ -99,7 +105,7 @@ public interface CipherState extends Destroyable {
|
||||
* There must be enough space in the ciphertext buffer to accomodate
|
||||
* length + getMACLength() bytes of data starting at ciphertextOffset.
|
||||
*/
|
||||
int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length);
|
||||
int encryptWithAd(byte[] ad, byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException;
|
||||
|
||||
/**
|
||||
* Decrypts a ciphertext buffer using the cipher and a block of associated data.
|
||||
@ -113,15 +119,18 @@ public interface CipherState extends Destroyable {
|
||||
* @param plaintextOffset The first offset within the plaintext buffer
|
||||
* to place the plaintext.
|
||||
* @param length The length of the incoming ciphertext plus the MAC tag.
|
||||
* @return The length of the plaintext with the MAC tag stripped off,
|
||||
* or -1 if the tag did not verify, or -1 if the plaintext buffer is
|
||||
* not big enough to hold the result.
|
||||
* @return The length of the plaintext with the MAC tag stripped off.
|
||||
*
|
||||
* @throws ShortBufferException The plaintext buffer does not have
|
||||
* enough space to store the decrypted data.
|
||||
*
|
||||
* @throws AEADBadTagException The MAC value failed to verify.
|
||||
*
|
||||
* The plaintext and ciphertext buffers can be the same for in-place
|
||||
* decryption. In that case, ciphertextOffset must be identical to
|
||||
* plaintextOffset.
|
||||
*/
|
||||
int decryptWithAd(byte[] ad, byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length);
|
||||
int decryptWithAd(byte[] ad, byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length) throws ShortBufferException, AEADBadTagException;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this cipher and initializes it with a key.
|
||||
|
||||
@ -141,4 +141,16 @@ class Curve25519DHState implements DHState {
|
||||
throw new IllegalArgumentException("Incompatible DH algorithms");
|
||||
Curve25519.eval(sharedKey, offset, privateKey, ((Curve25519DHState)publicDH).publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(DHState other) {
|
||||
if (!(other instanceof Curve25519DHState))
|
||||
throw new IllegalStateException("Mismatched DH key objects");
|
||||
if (other == this)
|
||||
return;
|
||||
Curve25519DHState dh = (Curve25519DHState)other;
|
||||
System.arraycopy(dh.privateKey, 0, privateKey, 0, 32);
|
||||
System.arraycopy(dh.publicKey, 0, publicKey, 0, 32);
|
||||
mode = dh.mode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,4 +143,14 @@ public interface DHState extends Destroyable {
|
||||
* type as this object, or one of the objects does not contain a valid key.
|
||||
*/
|
||||
void calculate(byte[] sharedKey, int offset, DHState publicDH);
|
||||
|
||||
/**
|
||||
* Copies the key values from another DH object of the same type.
|
||||
*
|
||||
* @param other The other DH object to copy from
|
||||
*
|
||||
* @throws IllegalStateException The other DH object does not have
|
||||
* the same type as this object.
|
||||
*/
|
||||
void copyFrom(DHState other);
|
||||
}
|
||||
|
||||
1025
NoiseJava/src/com/southernstorm/noise/protocol/HandshakeState.java
Normal file
1025
NoiseJava/src/com/southernstorm/noise/protocol/HandshakeState.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -36,15 +36,10 @@ import com.southernstorm.noise.crypto.Blake2sMessageDigest;
|
||||
public final class Noise {
|
||||
|
||||
/**
|
||||
* Destroys the contents of a byte array.
|
||||
*
|
||||
* @param array The array whose contents should be destroyed.
|
||||
* Maximum length for Noise packets.
|
||||
*/
|
||||
public static void destroy(byte[] array)
|
||||
{
|
||||
Arrays.fill(array, (byte)0);
|
||||
}
|
||||
|
||||
public static final int MAX_PACKET_LEN = 65535;
|
||||
|
||||
private static SecureRandom random = new SecureRandom();
|
||||
|
||||
/**
|
||||
@ -135,4 +130,33 @@ public final class Noise {
|
||||
}
|
||||
throw new NoSuchAlgorithmException("Unknown Noise hash algorithm name: " + name);
|
||||
}
|
||||
|
||||
// The rest of this class consists of internal utility functions
|
||||
// that are not part of the public API.
|
||||
|
||||
/**
|
||||
* Destroys the contents of a byte array.
|
||||
*
|
||||
* @param array The array whose contents should be destroyed.
|
||||
*/
|
||||
static void destroy(byte[] array)
|
||||
{
|
||||
Arrays.fill(array, (byte)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a copy of part of an array.
|
||||
*
|
||||
* @param data The buffer containing the data to copy.
|
||||
* @param offset Offset of the first byte to copy.
|
||||
* @param length The number of bytes to copy.
|
||||
*
|
||||
* @return A new array with a copy of the sub-array.
|
||||
*/
|
||||
static byte[] copySubArray(byte[] data, int offset, int length)
|
||||
{
|
||||
byte[] copy = new byte [length];
|
||||
System.arraycopy(data, offset, copy, 0, length);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
329
NoiseJava/src/com/southernstorm/noise/protocol/Pattern.java
Normal file
329
NoiseJava/src/com/southernstorm/noise/protocol/Pattern.java
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.southernstorm.noise.protocol;
|
||||
|
||||
/**
|
||||
* Information about all supported handshake patterns.
|
||||
*/
|
||||
class Pattern {
|
||||
|
||||
private Pattern() {}
|
||||
|
||||
// Token codes.
|
||||
public static final byte S = 1;
|
||||
public static final byte E = 2;
|
||||
public static final byte DHEE = 3;
|
||||
public static final byte DHES = 4;
|
||||
public static final byte DHSE = 5;
|
||||
public static final byte DHSS = 6;
|
||||
public static final byte FLIP_DIR = 7;
|
||||
|
||||
// Pattern flag bits.
|
||||
public static final byte FLAG_LOCAL_STATIC = 0x01;
|
||||
public static final byte FLAG_LOCAL_EPHEMERAL = 0x02;
|
||||
public static final byte FLAG_LOCAL_REQUIRED = 0x04;
|
||||
public static final byte FLAG_LOCAL_EPHEM_REQ = 0x08;
|
||||
public static final byte FLAG_REMOTE_STATIC = 0x10;
|
||||
public static final byte FLAG_REMOTE_EPHEMERAL = 0x20;
|
||||
public static final byte FLAG_REMOTE_REQUIRED = 0x40;
|
||||
public static final byte FLAG_REMOTE_EPHEM_REQ = (byte)0x80;
|
||||
|
||||
private static final byte[] noise_pattern_N = {
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_REQUIRED,
|
||||
|
||||
E,
|
||||
DHES
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_K = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_LOCAL_REQUIRED |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_REQUIRED,
|
||||
|
||||
E,
|
||||
DHES,
|
||||
DHSS
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_X = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_REQUIRED,
|
||||
|
||||
E,
|
||||
DHES,
|
||||
S,
|
||||
DHSS
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_NN = {
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_NK = {
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL |
|
||||
FLAG_REMOTE_REQUIRED,
|
||||
|
||||
E,
|
||||
DHES,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_NX = {
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
S,
|
||||
DHSE
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_XN = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
FLIP_DIR,
|
||||
S,
|
||||
DHSE
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_XK = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL |
|
||||
FLAG_REMOTE_REQUIRED,
|
||||
|
||||
E,
|
||||
DHES,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
FLIP_DIR,
|
||||
S,
|
||||
DHSE
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_XX = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
S,
|
||||
DHSE,
|
||||
FLIP_DIR,
|
||||
S,
|
||||
DHSE
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_KN = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_LOCAL_REQUIRED |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
DHES
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_KK = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_LOCAL_REQUIRED |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL |
|
||||
FLAG_REMOTE_REQUIRED,
|
||||
|
||||
E,
|
||||
DHES,
|
||||
DHSS,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
DHES
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_KX = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_LOCAL_REQUIRED |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
DHES,
|
||||
S,
|
||||
DHSE
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_IN = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
S,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
DHES
|
||||
};
|
||||
|
||||
static final byte[] noise_pattern_IK = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL |
|
||||
FLAG_REMOTE_REQUIRED,
|
||||
|
||||
E,
|
||||
DHES,
|
||||
S,
|
||||
DHSS,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
DHES
|
||||
};
|
||||
|
||||
private static final byte[] noise_pattern_IX = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL,
|
||||
|
||||
E,
|
||||
S,
|
||||
FLIP_DIR,
|
||||
E,
|
||||
DHEE,
|
||||
DHES,
|
||||
S,
|
||||
DHSE
|
||||
};
|
||||
|
||||
static final byte[] noise_pattern_XXfallback = {
|
||||
FLAG_LOCAL_STATIC |
|
||||
FLAG_LOCAL_EPHEMERAL |
|
||||
FLAG_REMOTE_STATIC |
|
||||
FLAG_REMOTE_EPHEMERAL |
|
||||
FLAG_REMOTE_EPHEM_REQ,
|
||||
|
||||
E,
|
||||
DHEE,
|
||||
S,
|
||||
DHSE,
|
||||
FLIP_DIR,
|
||||
S,
|
||||
DHSE
|
||||
};
|
||||
|
||||
/**
|
||||
* Look up the description information for a pattern.
|
||||
*
|
||||
* @param name The name of the pattern.
|
||||
* @return The pattern description or null.
|
||||
*/
|
||||
public static byte[] 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"))
|
||||
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;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the local and remote flags for a pattern.
|
||||
*
|
||||
* @param flags The flags, assuming that the initiator is "local".
|
||||
* @return The reversed flags, with the responder now being "local".
|
||||
*/
|
||||
public static byte reverseFlags(byte flags)
|
||||
{
|
||||
return (byte)(((flags >> 4) & 0x0F) | ((flags << 4) & 0xF0));
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,9 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.AEADBadTagException;
|
||||
import javax.crypto.ShortBufferException;
|
||||
|
||||
/**
|
||||
* Symmetric state for helping manage a Noise handshake.
|
||||
*/
|
||||
@ -43,16 +46,17 @@ public class SymmetricState implements Destroyable {
|
||||
* Constructs a new symmetric state object.
|
||||
*
|
||||
* @param protocolName The name of the Noise protocol, which is assumed to be valid.
|
||||
* @param cipherName The name of the cipher within protocolName.
|
||||
* @param hashName The name of the hash within protocolName.
|
||||
*
|
||||
* @throws NoSuchAlgorithmException The cipher or hash algorithm in the
|
||||
* protocol name is not supported.
|
||||
*/
|
||||
public SymmetricState(String protocolName) throws NoSuchAlgorithmException
|
||||
public SymmetricState(String protocolName, String cipherName, String hashName) throws NoSuchAlgorithmException
|
||||
{
|
||||
String[] components = protocolName.split("_");
|
||||
name = protocolName;
|
||||
cipher = Noise.createCipher(components[3]);
|
||||
hash = Noise.createHash(components[4]);
|
||||
cipher = Noise.createCipher(cipherName);
|
||||
hash = Noise.createHash(hashName);
|
||||
int keyLength = cipher.getKeyLength();
|
||||
ck = new byte [keyLength];
|
||||
int hashLength = hash.getDigestLength();
|
||||
@ -128,6 +132,38 @@ public class SymmetricState implements Destroyable {
|
||||
hashTwo(h, 0, h.length, data, offset, length, h, 0, h.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes a pre-shared key into the chaining key and handshake hash.
|
||||
*
|
||||
* @param key The pre-shared key value.
|
||||
*/
|
||||
public void mixPreSharedKey(byte[] key)
|
||||
{
|
||||
byte[] temp = new byte [hash.getDigestLength()];
|
||||
try {
|
||||
hkdf(ck, 0, ck.length, key, 0, key.length, ck, 0, ck.length, temp, 0, temp.length);
|
||||
mixHash(temp, 0, temp.length);
|
||||
} finally {
|
||||
Noise.destroy(temp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes a pre-supplied public key into the handshake hash.
|
||||
*
|
||||
* @param dh The object containing the public key.
|
||||
*/
|
||||
public void mixPublicKey(DHState dh)
|
||||
{
|
||||
byte[] temp = new byte [dh.getPublicKeyLength()];
|
||||
try {
|
||||
dh.getPublicKey(temp, 0);
|
||||
mixHash(temp, 0, temp.length);
|
||||
} finally {
|
||||
Noise.destroy(temp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a block of plaintext and mixes the ciphertext into the handshake hash.
|
||||
*
|
||||
@ -141,6 +177,9 @@ public class SymmetricState implements Destroyable {
|
||||
* @param length The length of the plaintext.
|
||||
* @return The length of the ciphertext plus the MAC tag.
|
||||
*
|
||||
* @throws ShortBufferException There is not enough space in the
|
||||
* ciphertext buffer for the encrypted data plus MAC value.
|
||||
*
|
||||
* The plaintext and ciphertext buffers can be the same for in-place
|
||||
* encryption. In that case, plaintextOffset must be identical to
|
||||
* ciphertextOffset.
|
||||
@ -148,7 +187,7 @@ public class SymmetricState implements Destroyable {
|
||||
* There must be enough space in the ciphertext buffer to accomodate
|
||||
* length + getMACLength() bytes of data starting at ciphertextOffset.
|
||||
*/
|
||||
public int encryptAndHash(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length)
|
||||
public int encryptAndHash(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException
|
||||
{
|
||||
int ciphertextLength = cipher.encryptWithAd(h, plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);
|
||||
mixHash(ciphertext, ciphertextOffset, ciphertextLength);
|
||||
@ -166,14 +205,18 @@ public class SymmetricState implements Destroyable {
|
||||
* @param plaintextOffset The first offset within the plaintext buffer
|
||||
* to place the plaintext.
|
||||
* @param length The length of the incoming ciphertext plus the MAC tag.
|
||||
* @return The length of the plaintext with the MAC tag stripped off,
|
||||
* or -1 if the tag did not verify.
|
||||
* @return The length of the plaintext with the MAC tag stripped off.
|
||||
*
|
||||
* @throws ShortBufferException There is not enough space in the plaintext
|
||||
* buffer for the decrypted data.
|
||||
*
|
||||
* @throws AEADBadTagException The MAC value failed to verify.
|
||||
*
|
||||
* The plaintext and ciphertext buffers can be the same for in-place
|
||||
* decryption. In that case, ciphertextOffset must be identical to
|
||||
* plaintextOffset.
|
||||
*/
|
||||
public int decryptAndHash(byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length)
|
||||
public int decryptAndHash(byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length) throws ShortBufferException, AEADBadTagException
|
||||
{
|
||||
mixHash(ciphertext, ciphertextOffset, length);
|
||||
return cipher.decryptWithAd(h, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user