Define the basic interfaces for the protocol API

This commit is contained in:
Rhys Weatherley 2016-06-17 09:58:42 +10:00
parent 34a2430725
commit 85b163413f
6 changed files with 791 additions and 1 deletions

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

View File

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

View File

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

View File

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

View File

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

View File

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