Define the basic interfaces for the protocol API
This commit is contained in:
parent
34a2430725
commit
85b163413f
132
NoiseJava/src/com/southernstorm/noise/protocol/CipherState.java
Normal file
132
NoiseJava/src/com/southernstorm/noise/protocol/CipherState.java
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface to an authenticated cipher for use in the Noise protocol.
|
||||
*
|
||||
* CipherState objects are used to encrypt or decrypt data during a
|
||||
* session. Once the handshake has completed, HandshakeState.split()
|
||||
* will create two CipherState objects for encrypting packets sent to
|
||||
* the other party, and decrypting packets received from the other party.
|
||||
*/
|
||||
public interface CipherState extends Destroyable {
|
||||
|
||||
/**
|
||||
* Gets the Noise protocol name for this cipher.
|
||||
*
|
||||
* @return The cipher name.
|
||||
*/
|
||||
String getCipherName();
|
||||
|
||||
/**
|
||||
* Gets the length of the key values for this cipher.
|
||||
*
|
||||
* @return The length of the key in bytes; usually 32.
|
||||
*/
|
||||
int getKeyLength();
|
||||
|
||||
/**
|
||||
* Gets the length of the MAC values for this cipher.
|
||||
*
|
||||
* @return The length of MAC values in bytes, or zero if the
|
||||
* key has not yet been initialized.
|
||||
*/
|
||||
int getMACLength();
|
||||
|
||||
/**
|
||||
* Initializes the key on this cipher object.
|
||||
*
|
||||
* @param key Points to a buffer that contains the key.
|
||||
* @param offset The offset of the key in the key buffer.
|
||||
*
|
||||
* The key buffer must contain at least getKeyLength() bytes
|
||||
* starting at offset.
|
||||
*
|
||||
* @see hasKey()
|
||||
*/
|
||||
void initializeKey(byte[] key, int offset);
|
||||
|
||||
/**
|
||||
* Determine if this cipher object has been configured with a key.
|
||||
*
|
||||
* @return true if this cipher object has a key; false if the
|
||||
* key has not yet been set with initializeKey().
|
||||
*
|
||||
* @see initializeKey()
|
||||
*/
|
||||
boolean hasKey();
|
||||
|
||||
/**
|
||||
* Encrypts a plaintext buffer using the cipher and a block of associated data.
|
||||
*
|
||||
* @param ad The associated data, or null if there is none.
|
||||
* @param plaintext The buffer containing the plaintext to encrypt.
|
||||
* @param plaintextOffset The offset within the plaintext buffer of the
|
||||
* first byte or plaintext data.
|
||||
* @param ciphertext The buffer to place the ciphertext in. This can
|
||||
* be the same as the plaintext buffer.
|
||||
* @param ciphertextOffset The first offset within the ciphertext buffer
|
||||
* to place the ciphertext and the MAC tag.
|
||||
* @param length The length of the plaintext.
|
||||
* @return The length of the ciphertext plus the MAC tag.
|
||||
*
|
||||
* The plaintext and ciphertext buffers can be the same for in-place
|
||||
* encryption. In that case, plaintextOffset must be identical to
|
||||
* ciphertextOffset.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Decrypts a ciphertext buffer using the cipher and a block of associated data.
|
||||
*
|
||||
* @param ad The associated data, or null if there is none.
|
||||
* @param ciphertext The buffer containing the ciphertext to decrypt.
|
||||
* @param ciphertextOffset The offset within the ciphertext buffer of
|
||||
* the first byte of ciphertext data.
|
||||
* @param plaintext The buffer to place the plaintext in. This can be
|
||||
* the same as the ciphertext buffer.
|
||||
* @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.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Creates a new instance of this cipher and initializes it with a key.
|
||||
*
|
||||
* @param key The buffer containing the key.
|
||||
* @param offset The offset into the key buffer of the first key byte.
|
||||
* @return A new CipherState of the same class as this one.
|
||||
*/
|
||||
CipherState fork(byte[] key, int offset);
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Class that contains a pair of CipherState objects.
|
||||
*
|
||||
* CipherState pairs typically arise when HandshakeState.split() is called.
|
||||
*/
|
||||
public final class CipherStatePair implements Destroyable {
|
||||
|
||||
private CipherState send;
|
||||
private CipherState recv;
|
||||
|
||||
/**
|
||||
* Constructs a pair of CipherState objects.
|
||||
*
|
||||
* @param sender The CipherState to use to send packets to the remote party.
|
||||
* @param receiver The CipherState to use to receive packets from the remote party.
|
||||
*/
|
||||
public CipherStatePair(CipherState sender, CipherState receiver)
|
||||
{
|
||||
send = sender;
|
||||
recv = receiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CipherState to use to send packets to the remote party.
|
||||
*
|
||||
* @return The sending CipherState.
|
||||
*/
|
||||
public CipherState getSender() {
|
||||
return send;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CipherState to use to receive packets from the remote party.
|
||||
*
|
||||
* @return The receiving CipherState.
|
||||
*/
|
||||
public CipherState getReceiver() {
|
||||
return recv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the receiving CipherState and retains only the sending CipherState.
|
||||
*
|
||||
* This function is intended for use with one-way handshake patterns.
|
||||
*/
|
||||
public void senderOnly()
|
||||
{
|
||||
if (recv != null) {
|
||||
recv.destroy();
|
||||
recv = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the sending CipherState and retains only the receiving CipherState.
|
||||
*
|
||||
* This function is intended for use with one-way handshake patterns.
|
||||
*/
|
||||
public void receiverOnly()
|
||||
{
|
||||
if (send != null) {
|
||||
send.destroy();
|
||||
send = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the sender and receiver.
|
||||
*/
|
||||
public void swap()
|
||||
{
|
||||
CipherState temp = send;
|
||||
send = recv;
|
||||
recv = temp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
senderOnly();
|
||||
receiverOnly();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface for objects that implement destroying.
|
||||
*
|
||||
* Applications that use the Noise protocol can inadvertently leave
|
||||
* sensitive data in the heap if steps are not taken to clean up.
|
||||
*
|
||||
* This interface can be implemented by objects that know how to
|
||||
* securely clean up after themselves.
|
||||
*
|
||||
* The Noise.destroy() function can help with destroying byte arrays
|
||||
* that hold sensitive values.
|
||||
*/
|
||||
public interface Destroyable {
|
||||
|
||||
/**
|
||||
* Destroys all sensitive state in the current object.
|
||||
*/
|
||||
void destroy();
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface to a cryptographic hash algorithm.
|
||||
*/
|
||||
public interface HashState extends Destroyable {
|
||||
|
||||
/**
|
||||
* Gets the length of the hash output for this algorithm.
|
||||
*
|
||||
* @return The length of the hash in bytes.
|
||||
*/
|
||||
int getHashLength();
|
||||
|
||||
/**
|
||||
* Gets the block length for this algorithm.
|
||||
*
|
||||
* @return The length of the block in bytes.
|
||||
*/
|
||||
int getBlockLength();
|
||||
|
||||
/**
|
||||
* Resets the hash for a new hashing session.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Updates the hash state with more data.
|
||||
*
|
||||
* @param data Buffer containing the data.
|
||||
* @param offset Offset into the data buffer of the first
|
||||
* byte to be hashed.
|
||||
* @param length Length of the region to be hashed.
|
||||
*/
|
||||
void update(byte[] data, int offset, int length);
|
||||
|
||||
/**
|
||||
* Finishes a hashing session and returns the output hash.
|
||||
*
|
||||
* @param output Buffer to put the hash into.
|
||||
* @param offset Offset of the first byte of the hash
|
||||
* in the output buffer.
|
||||
*/
|
||||
void finish(byte[] output, int offset);
|
||||
}
|
||||
@ -1,7 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Constants for the Noise protocol library.
|
||||
* Constants and utility functions for the Noise protocol library.
|
||||
*/
|
||||
public final class Noise {
|
||||
|
||||
@ -78,4 +102,25 @@ public final class Noise {
|
||||
*/
|
||||
public static final int PATTERN_XX_FALLBACK = 0x5010;
|
||||
|
||||
/**
|
||||
* Destroys the contents of a byte array.
|
||||
*
|
||||
* @param array The array whose contents should be destroyed.
|
||||
*/
|
||||
public static void destroy(byte[] array)
|
||||
{
|
||||
Arrays.fill(array, (byte)0);
|
||||
}
|
||||
|
||||
public static CipherState createCipher(String name)
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HashState createHash(String name)
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,397 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Symmetric state for helping manage a Noise handshake.
|
||||
*/
|
||||
public class SymmetricState implements Destroyable {
|
||||
|
||||
private String name;
|
||||
private CipherState cipher;
|
||||
private HashState hash;
|
||||
private byte[] ck;
|
||||
private byte[] h;
|
||||
|
||||
/**
|
||||
* Constructs a new symmetric state object.
|
||||
*
|
||||
* @param protocolName The name of the Noise protocol, which is assumed to be valid.
|
||||
*
|
||||
* @throws NoSuchAlgorithmException The cipher or hash algorithm in the
|
||||
* protocol name is not supported.
|
||||
*/
|
||||
public SymmetricState(String protocolName) throws NoSuchAlgorithmException
|
||||
{
|
||||
String[] components = protocolName.split("_");
|
||||
name = protocolName;
|
||||
cipher = Noise.createCipher(components[3]);
|
||||
hash = Noise.createHash(components[4]);
|
||||
int keyLength = cipher.getKeyLength();
|
||||
ck = new byte [keyLength];
|
||||
int hashLength = hash.getHashLength();
|
||||
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);
|
||||
}
|
||||
|
||||
System.arraycopy(h, 0, ck, 0, keyLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the Noise protocol.
|
||||
*
|
||||
* @return The protocol name.
|
||||
*/
|
||||
public String getProtocolName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of MAC values in the current state.
|
||||
*
|
||||
* @return The length of the MAC value for the underlying cipher
|
||||
* or zero if the cipher has not yet been initialized with a key.
|
||||
*/
|
||||
public int getMACLength()
|
||||
{
|
||||
return cipher.getMACLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes data into the chaining key.
|
||||
*
|
||||
* @param data The buffer containing the data to mix in.
|
||||
* @param offset The offset of the first data byte to mix in.
|
||||
* @param length The number of bytes to mix in.
|
||||
*/
|
||||
public void mixKey(byte[] data, int offset, int length)
|
||||
{
|
||||
int keyLength = cipher.getKeyLength();
|
||||
byte[] tempKey = new byte [keyLength];
|
||||
try {
|
||||
hkdf(ck, 0, ck.length, data, offset, length, ck, 0, ck.length, tempKey, 0, keyLength);
|
||||
cipher.initializeKey(tempKey, 0);
|
||||
} finally {
|
||||
Noise.destroy(tempKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes data into the handshake hash.
|
||||
*
|
||||
* @param data The buffer containing the data to mix in.
|
||||
* @param offset The offset of the first data byte to mix in.
|
||||
* @param length The number of bytes to mix in.
|
||||
*/
|
||||
public void mixHash(byte[] data, int offset, int length)
|
||||
{
|
||||
hashTwo(h, 0, h.length, data, offset, length, h, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a block of plaintext and mixes the ciphertext into the handshake hash.
|
||||
*
|
||||
* @param plaintext The buffer containing the plaintext to encrypt.
|
||||
* @param plaintextOffset The offset within the plaintext buffer of the
|
||||
* first byte or plaintext data.
|
||||
* @param ciphertext The buffer to place the ciphertext in. This can
|
||||
* be the same as the plaintext buffer.
|
||||
* @param ciphertextOffset The first offset within the ciphertext buffer
|
||||
* to place the ciphertext and the MAC tag.
|
||||
* @param length The length of the plaintext.
|
||||
* @return The length of the ciphertext plus the MAC tag.
|
||||
*
|
||||
* The plaintext and ciphertext buffers can be the same for in-place
|
||||
* encryption. In that case, plaintextOffset must be identical to
|
||||
* ciphertextOffset.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
int ciphertextLength = cipher.encryptWithAd(h, plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);
|
||||
mixHash(ciphertext, ciphertextOffset, ciphertextLength);
|
||||
return ciphertextLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a block of ciphertext and mixes it into the handshake hash.
|
||||
*
|
||||
* @param ciphertext The buffer containing the ciphertext to decrypt.
|
||||
* @param ciphertextOffset The offset within the ciphertext buffer of
|
||||
* the first byte of ciphertext data.
|
||||
* @param plaintext The buffer to place the plaintext in. This can be
|
||||
* the same as the ciphertext buffer.
|
||||
* @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.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
mixHash(ciphertext, ciphertextOffset, length);
|
||||
return cipher.decryptWithAd(h, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the symmetric state into two ciphers for session encryption.
|
||||
*
|
||||
* @return The pair of ciphers for sending and receiving.
|
||||
*/
|
||||
public CipherStatePair split()
|
||||
{
|
||||
return split(new byte[0], 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the symmetric state into two ciphers for session encryption,
|
||||
* and optionally mixes in a secondary symmetric key.
|
||||
*
|
||||
* @param secondaryKey The buffer containing the secondary key.
|
||||
* @param offset The offset of the first secondary key byte.
|
||||
* @param length The length of the secondary key in bytes, which
|
||||
* must be either 0 or 32.
|
||||
* @return The pair of ciphers for sending and receiving.
|
||||
*
|
||||
* @throws IllegalArgumentException The length is not 0 or 32.
|
||||
*/
|
||||
public CipherStatePair split(byte[] secondaryKey, int offset, int length)
|
||||
{
|
||||
if (length != 0 && length != 32)
|
||||
throw new IllegalArgumentException("Secondary keys must be 0 or 32 bytes in length");
|
||||
int keyLength = cipher.getKeyLength();
|
||||
byte[] k1 = new byte [keyLength];
|
||||
byte[] k2 = new byte [keyLength];
|
||||
try {
|
||||
hkdf(ck, 0, ck.length, secondaryKey, offset, length, k1, 0, k1.length, k2, 0, k2.length);
|
||||
CipherState c1 = null;
|
||||
CipherState c2 = null;
|
||||
CipherStatePair pair = null;
|
||||
try {
|
||||
c1 = cipher.fork(k1, 0);
|
||||
c2 = cipher.fork(k2, 0);
|
||||
pair = new CipherStatePair(c1, c2);
|
||||
} finally {
|
||||
if (c1 == null || c2 == null || pair == null) {
|
||||
// Could not create some of the objects. Clean up the others
|
||||
// to avoid accidental leakage of k1 or k2.
|
||||
if (c1 != null)
|
||||
c1.destroy();
|
||||
if (c2 != null)
|
||||
c2.destroy();
|
||||
pair = null;
|
||||
}
|
||||
}
|
||||
return pair;
|
||||
} finally {
|
||||
Noise.destroy(k1);
|
||||
Noise.destroy(k2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of the handshake hash.
|
||||
*
|
||||
* @return The handshake hash. This must not be modified by the caller.
|
||||
*
|
||||
* The handshake hash value is only of use to the application after
|
||||
* split() has been called.
|
||||
*/
|
||||
public byte[] getHandshakeHash()
|
||||
{
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (cipher != null) {
|
||||
cipher.destroy();
|
||||
cipher = null;
|
||||
}
|
||||
if (hash != null) {
|
||||
hash.destroy();
|
||||
hash = null;
|
||||
}
|
||||
if (ck != null) {
|
||||
Noise.destroy(ck);
|
||||
ck = null;
|
||||
}
|
||||
if (h != null) {
|
||||
Noise.destroy(h);
|
||||
h = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes a single data buffer.
|
||||
*
|
||||
* @param data The buffer containing the data to hash.
|
||||
* @param offset Offset into the data buffer of the first byte to hash.
|
||||
* @param length Length of the data to be hashed.
|
||||
* @param output The buffer to receive the output hash value.
|
||||
* @param outputOffset Offset into the output buffer to place the hash value.
|
||||
*
|
||||
* The output buffer can be the same as the input data buffer.
|
||||
*/
|
||||
private void hashOne(byte[] data, int offset, int length, byte[] output, int outputOffset)
|
||||
{
|
||||
hash.reset();
|
||||
hash.update(data, offset, length);
|
||||
hash.finish(output, outputOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes two data buffers.
|
||||
*
|
||||
* @param data1 The buffer containing the first data to hash.
|
||||
* @param offset1 Offset into the first data buffer of the first byte to hash.
|
||||
* @param length1 Length of the first data to be hashed.
|
||||
* @param data2 The buffer containing the second data to hash.
|
||||
* @param offset2 Offset into the second data buffer of the first byte to hash.
|
||||
* @param length2 Length of the second data to be hashed.
|
||||
* @param output The buffer to receive the output hash value.
|
||||
* @param outputOffset Offset into the output buffer to place the hash value.
|
||||
*
|
||||
* The output buffer can be same as either of the input buffers.
|
||||
*/
|
||||
private void hashTwo(byte[] data1, int offset1, int length1,
|
||||
byte[] data2, int offset2, int length2,
|
||||
byte[] output, int outputOffset)
|
||||
{
|
||||
hash.reset();
|
||||
hash.update(data1, offset1, length1);
|
||||
hash.update(data2, offset2, length2);
|
||||
hash.finish(output, outputOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a HMAC value using key and data values.
|
||||
*
|
||||
* @param key The buffer that contains the key.
|
||||
* @param keyOffset The offset of the key in the key buffer.
|
||||
* @param keyLength The length of the key in bytes.
|
||||
* @param data The buffer that contains the data.
|
||||
* @param dataOffset The offset of the data in the data buffer.
|
||||
* @param dataLength The length of the data in bytes.
|
||||
* @param output The output buffer to place the HMAC value in.
|
||||
* @param outputOffset Offset into the output buffer for the HMAC value.
|
||||
*/
|
||||
private void hmac(byte[] key, int keyOffset, int keyLength,
|
||||
byte[] data, int dataOffset, int dataLength,
|
||||
byte[] output, int outputOffset)
|
||||
{
|
||||
int blockLength = hash.getBlockLength();
|
||||
int hashLength = hash.getHashLength();
|
||||
byte[] block = new byte [blockLength];
|
||||
int index;
|
||||
try {
|
||||
if (keyLength <= blockLength) {
|
||||
System.arraycopy(key, keyOffset, block, 0, keyLength);
|
||||
Arrays.fill(block, keyLength, blockLength, (byte)0);
|
||||
} else {
|
||||
hash.reset();
|
||||
hash.update(key, keyOffset, keyLength);
|
||||
hash.finish(block, 0);
|
||||
Arrays.fill(block, hashLength, blockLength, (byte)0);
|
||||
}
|
||||
for (index = 0; index < blockLength; ++index)
|
||||
block[index] ^= (byte)0x36;
|
||||
hash.reset();
|
||||
hash.update(block, 0, blockLength);
|
||||
hash.update(data, dataOffset, dataLength);
|
||||
hash.finish(output, outputOffset);
|
||||
for (index = 0; index < blockLength; ++index)
|
||||
block[index] ^= (byte)(0x36 ^ 0x5C);
|
||||
hash.reset();
|
||||
hash.update(block, 0, blockLength);
|
||||
hash.update(output, outputOffset, hashLength);
|
||||
hash.finish(output, outputOffset);
|
||||
} finally {
|
||||
Noise.destroy(block);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a HKDF value.
|
||||
*
|
||||
* @param key The buffer that contains the key.
|
||||
* @param keyOffset The offset of the key in the key buffer.
|
||||
* @param keyLength The length of the key in bytes.
|
||||
* @param data The buffer that contains the data.
|
||||
* @param dataOffset The offset of the data in the data buffer.
|
||||
* @param dataLength The length of the data in bytes.
|
||||
* @param output1 The first output buffer.
|
||||
* @param output1Offset Offset into the first output buffer.
|
||||
* @param output1Length Length of the first output which can be
|
||||
* less than the hash length.
|
||||
* @param output2 The second output buffer.
|
||||
* @param output2Offset Offset into the second output buffer.
|
||||
* @param output2Length Length of the second output which can be
|
||||
* less than the hash length.
|
||||
*/
|
||||
private void hkdf(byte[] key, int keyOffset, int keyLength,
|
||||
byte[] data, int dataOffset, int dataLength,
|
||||
byte[] output1, int output1Offset, int output1Length,
|
||||
byte[] output2, int output2Offset, int output2Length)
|
||||
{
|
||||
int hashLength = hash.getHashLength();
|
||||
byte[] tempKey = new byte [hashLength];
|
||||
byte[] tempHash = new byte [hashLength + 1];
|
||||
try {
|
||||
hmac(key, keyOffset, keyLength, data, dataOffset, dataLength, tempKey, 0);
|
||||
tempHash[0] = (byte)0x01;
|
||||
hmac(tempKey, 0, hashLength, tempHash, 0, 1, tempHash, 0);
|
||||
System.arraycopy(tempHash, 0, output1, output1Offset, output1Length);
|
||||
tempHash[hashLength] = (byte)0x02;
|
||||
hmac(tempKey, 0, hashLength, tempHash, 0, hashLength + 1, tempHash, 0);
|
||||
System.arraycopy(tempHash, 0, output2, output2Offset, output2Length);
|
||||
} finally {
|
||||
Noise.destroy(tempKey);
|
||||
Noise.destroy(tempHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user