com.sicheng.common.security.keccak.KeccakSponge Maven / Gradle / Ivy
/**
* 本作品使用 木兰公共许可证,第2版(Mulan PubL v2) 开源协议,请遵守相关条款,或者联系sicheng.net获取商用授权。
* Copyright (c) 2016 SiCheng.Net
* This software is licensed under Mulan PubL v2.
* You can use this software according to the terms and conditions of the Mulan PubL v2.
* You may obtain a copy of Mulan PubL v2 at:
* http://license.coscl.org.cn/MulanPubL-2.0
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PubL v2 for more details.
*/
package com.sicheng.common.security.keccak;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.UnaryOperator;
/**
* The Keccak sponge
* function for cryptographic hashing.
*
* An object of this type is immutable, and can be freely shared and reused.
*
*
* The sponge function represented by this code is entirely based on the
* Keccak
* sponge function family
* created by Guido Bertoni, Joan Daemen, Michaël Peeters, and Gilles Van
* Assche. Detailed specifications are defined by The
* Keccak Reference version
* 3.0
* [January 2011].
*
*
* The Keccak sponge function
* powers the SHA-3 hash functions, and the related SHAKE extended output
* functions, both standardised by
* FIPS PUB
* 202 [August 2015]. For convenience, these standardised instances of the
* Keccak sponge function have
* been made available in the companion class {@link FIPS202}.
*
*
* @author Bobulous
* @see FIPS202
*/
public final class KeccakSponge implements UnaryOperator {
/**
* The set of valid values for the permutation width. The width is the total
* number of bits in the Keccak state (the sum of the bitrate and the
* capacity).
*/
private static final Set VALID_WIDTHS;
static {
Set widths = new HashSet<>(16);
widths.addAll(Arrays.asList(new Short[]{
25,
50,
100,
200,
400,
800,
1600
}));
VALID_WIDTHS = Collections.unmodifiableSet(widths);
}
private final short bitrate;
private final short capacity;
private final byte laneLength;
private final int outputLengthInBits;
/**
* Suffix bits which define a hash application "domain".
*
* This concept does not appear to be found within the
* Keccak
* Reference v3.0, but is found in
* FIPS
* PUB 202 and in the summary
* The Keccak sponge
* function family: Specifications summary.
*/
private final String suffixBits;
/**
* Returns the bitrate of this {@code KeccakSponge} instance. The bitrate is
* the (maximum) number of bits within each block absorbed into or squeezed
* from the permutation state. After the absorption or squeezing of each
* block, the state is permuted using the
* Keccak-F algorithm.
*
* @return the bitrate in bits.
*/
public int getBitrate() {
return bitrate;
}
/**
* Returns the capacity of this {@code KeccakSponge} instance. The capacity
* is the size, in bits, of the portion of the permutation state which is
* not modified by the absorption of a new input block (at least not
* immediately), and which is not copied in the squeezing of a new output
* block, but which is affected when the state is permuted.
*
* @return the capacity in bits.
*/
public int getCapacity() {
return capacity;
}
/**
* Returns the permutation width of this {@code KeccakSponge} instance. The
* width is the size of the permutation state in bits, and is equal to the
* sum of the bitrate and the capacity.
*
* @return the permutation width in bits.
*/
public int getPermutationWidth() {
return bitrate + capacity;
}
/**
* Returns the lane length of this {@code KeccakSponge} instance. The
* permutation state is composed of a 5×5 array of "lanes". The length of
* each lane is therefore the permutation state width divided by
* twenty-five.
*
* @return the length in bits of each lane.
*/
public int getLaneLength() {
return laneLength;
}
/**
* Returns the number of rounds per permutation of this {@code KeccakSponge}
* instance. When the Keccak state is permuted it executes a "round" of
* transformations (named theta, rho, pi, chi, and iota) multiple times. The
* number of rounds in each permutation depends upon the permutation width,
* and will be a quantity between twelve and twenty-four.
*
* @return the number of rounds in each permutation.
*/
public int getNumberOfRoundsPerPermutation() {
return KeccakUtilities.getNumberOfRoundsPerPermutationWithLaneLength(
laneLength);
}
/**
* Returns the domain suffix used by this {@code KeccakSponge} instance. The
* domain suffix is optional, but if a suffix is being used then it will be
* a {@code String} representing a bitstring (composed of only '0' and '1'
* digits) which will be appended to the input after the message bits but
* before the padding bits, prior to the start of the hash processing.
*
* Use of a domain suffix allows different Keccak applications to
* differentiate themselves. For example, SHA3 hash functions use a domain
* suffix of "01", SHAKE extendable-output functions use a domain suffix of
* "1111", and RawSHAKE uses "11". If this {@code KeccakSponge} instance
* does not use a domain suffix then this method will return
* {@code Optional.empty()}.
*
* @return the domain suffix as an {@code Optional}.
*/
public Optional getSuffixBits() {
if (suffixBits.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(suffixBits);
}
}
/**
* Returns the length of the hash result which will be generated by this
* {@code KeccakSponge}.
*
* @return the output length in bits.
*/
public int getOutputLengthInBits() {
return outputLengthInBits;
}
/**
* Constructs a Keccak sponge function with the given parameters. The
* resulting {@code KeccakSponge} object is immutable and can be safely
* shared and reused.
*
* The bitrate is the maximum number of bits in each block while absorbing
* the input message into the Keccak sponge, and while squeezing the sponge
* to generate the resulting hash result. After each block has been absorbed
* or squeezed, the Keccak sponge state will be permuted.
* (NOTICE: Currently support is only implemented for
* bitrates which are exactly divisible by 8.)
*
* The capacity is the number of additional bits (on top of the bitrate) in
* the sponge permutation state. The sum of the bitrate and the capacity is
* the total "width" of the sponge permutation state. The state width must
* be any of the values 25, 50, 100, 200, 400, 800, or 1600, and so the sum
* of the bitrate and the capacity must be equal to one of these valid
* widths. (NOTICE: Currently support is only implemented
* for widths of 200 or greater.)
*
*
* The suffix bitstring (or "domain suffix") will be appended to any input
* message processed by this sponge function. Using a domain suffix allows
* different results to be returned for different applications, even when
* the input message and all other parameters are identical. For example,
* the SHA-3 hash functions all use a domain suffix of "01", the SHAKE
* extendable output functions use a domain suffix of "1111", and the
* RawSHAKE extendable output functions use a domain suffix of "11".
*
*
* The output length determines the length, in bits, of the hash result
* which will be generated. The output length can be any non-zero size, and
* the sponge will be squeezed repeatedly until the hash result is of the
* requested length. Bear in mind that (all other parameters remaining
* identical) varying the output length will change only the later bits of
* the hash result. For example, if using output length 256 and then output
* length 512, the first 256 bits will be identical in both hash results.
* For this reason, it is recommended that you not use different output
* lengths for the same input message. See
* NIST
* FIPS PUB 202 "Appendix A.2 Additional Consideration for
* Extendable-Output Functions" for a note about this.
*
* @param bitrate must be a number of bits greater than zero.
* @param capacity must be a number of bits, greater than zero, such that
* the sum {@code bitrate + capacity} is a valid Keccak state "width".
* @param suffixBits a {@code String} which can contain only digits "0" and
* "1". Can be an empty {@code String} if no domain suffix is required. Must
* not be {@code null}.
* @param outputLength the hash output length in bits. Must be greater than
* zero.
* @throws NullPointerException if the {@code suffixBits} parameter is
* {@code null}.
* @throws IllegalArgumentException if any of the parameters has been
* provided with an invalid value.
*/
public KeccakSponge(int bitrate, int capacity, String suffixBits,
int outputLength) {
validateBitrate(bitrate);
validateCapacity(capacity);
validateSuffixBits(suffixBits);
validateOutputLength(outputLength);
short width = (short) (bitrate + capacity);
validatePermutationWidth(width);
this.bitrate = (short) bitrate;
this.capacity = (short) capacity;
this.suffixBits = suffixBits;
this.laneLength = (byte) (width / 25);
this.outputLengthInBits = outputLength;
}
/**
* Applies this Keccak sponge function to the given message and returns the
* calculated hash result. Every bit contained by the byte array will be
* considered part of the input message. The returned byte array will
* contain all of the bits of the calculated hash.
*
* The given message byte array will not be modified by this method.
* However, to avoid problems, the message byte array should not be
* accessible to any other threads while it is being used to calculate the
* hash. If the message comes from a shared resource then take a copy and
* pass the copy to this method.
*
* The length, in bits, of the resulting hash will be equal to the output
* length configured for this {@code KeccakSponge} object, as returned by
* the method {@link #getOutputLengthInBits()}. If the output length is not
* exactly divisible by eight then the last few bits of the final byte of
* the returned byte array will not be part of the hash result binary. For
* example, if the output length is 10 bits then this method will return a
* byte array which contains two bytes, and only the two least-significant
* bits of the second byte will actually be part of the hash result; the
* most-significant six bits of the second byte will not be
* part of the hash result binary.
*
* @param message a byte array which exactly represents the message. Must
* not be {@code null}.
* @return a byte array which contains the calculated hash.
*/
@Override
public byte[] apply(byte[] message) {
return apply(message.length * Byte.SIZE, message);
}
/**
* Applies this Keccak sponge function to the specified number of bits from
* the given message byte array and returns the calculated hash result. Only
* the first {@code messageLengthInBits} bits found within the provided
* {@code message} will be considered part of the input message to be
* hashed, and any subsequent bits will be ignored.
*
* Bits are taken first from the bytes with the lowest index, and from the
* bits with the lowest binary indices. For example, if the given message
* byte array contains two bytes, and the specified message length is ten
* bits, then all of the byte at array index zero will be included, and from
* the second byte only the bits at index zero (the least significant bit)
* and index one will be included. The most-significant six bits of the
* second byte will be ignored.
*
* The given message byte array will not be modified by this method.
* However, to avoid problems, the message byte array should not be
* accessible to any other threads while it is being used to calculate the
* hash. If the message comes from a shared resource then take a copy and
* pass the copy to this method.
*
* The length, in bits, of the resulting hash will be equal to the output
* length configured for this {@code KeccakSponge} object, as returned by
* the method {@link #getOutputLengthInBits()}. If the output length is not
* exactly divisible by eight then the last few bits of the final byte of
* the returned byte array will not be part of the hash result binary. For
* example, if the output length is 10 bits then this method will return a
* byte array which contains two bytes, and only the two least-significant
* bits of the second byte will actually be part of the hash result; the
* most-significant six bits of the second byte will not be
* part of the hash result binary.
*
* @param messageLengthInBits the number of bits in the given byte array to
* be considered part of the message to be hashed. Must not be negative, and
* must not be larger than the total number of bits available in the given
* byte array.
* @param message a byte array which contains all of the message bits, and
* possibly subsequent unwanted bits. Must not be {@code null}.
* @return a byte array which contains the calculated hash.
*/
public byte[] apply(int messageLengthInBits, byte[] message) {
validateMessageLength(message, messageLengthInBits);
int inputLengthInBits = calculateTotalInputLength(messageLengthInBits);
byte[] input = createSufficientlyLargeByteArray(inputLengthInBits);
moveMessageBitsIntoInput(message, messageLengthInBits, input);
appendDomainSuffixToInput(input, messageLengthInBits);
padInput(input, messageLengthInBits);
KeccakState state = createKeccakStateForLength(laneLength);
state.absorb(input, inputLengthInBits, bitrate);
byte[] hash = state.squeeze(bitrate, outputLengthInBits);
return hash;
}
private KeccakState createKeccakStateForLength(int laneLength) {
switch (laneLength) {
case 64:
return new KeccakState1600();
case 32:
return new KeccakState800();
case 16:
return new KeccakState400();
case 8:
return new KeccakState200();
default:
throw new UnsupportedOperationException(
"Permutation width currently not supported.");
}
}
/**
* Applies this Keccak sponge function to every byte returned by the given
* {@code InputStream} and returns the calculated hash result. Every bit
* from every byte read from the stream will be considered to be part of the
* input message to be hashed.
*
* This method will return the hash result as a byte array. The length, in
* bits, of the resulting hash will be equal to the output length configured
* for this {@code KeccakSponge} object, as returned by the method
* {@link #getOutputLengthInBits()}. If the output length is not exactly
* divisible by eight then the last few bits of the final byte of the
* returned byte array will not be part of the hash result binary. For
* example, if the output length is 10 bits then this method will return a
* byte array which contains two bytes, and only the two least-significant
* bits of the second byte will actually be part of the hash result; the
* most-significant six bits of the second byte will not be
* part of the hash result binary.
*
* @param stream an {@code InputStream} from which the message bytes should
* be read. Must not be {@code null}.
* @return a byte array which contains the calculated hash.
* @throws IOException if an {@code IOException} is thrown by any of the
* stream reading operations.
*/
public byte[] apply(InputStream stream) throws IOException {
// TODO: Add support for cases where bitrate is not divisible by 8.
requireWholeByteBitrate(bitrate);
Objects.requireNonNull(stream);
KeccakState state = createKeccakStateForLength(laneLength);
byte[] block = createSufficientlyLargeByteArray(bitrate);
int finalBlockMessageBits = absorbInitialStreamBlocksIntoState(stream,
block, state);
byte[] finalBlock = prepareFinalBlockArray(finalBlockMessageBits, block);
appendDomainSuffixToInput(finalBlock, finalBlockMessageBits);
padInput(finalBlock, finalBlockMessageBits);
state.absorb(finalBlock, finalBlock.length * Byte.SIZE, bitrate);
byte[] hash = state.squeeze(bitrate, outputLengthInBits);
return hash;
}
/**
* Calculates the total length of the input that will be absorbed into the
* Keccak sponge, including the input message bits, any domain suffix bits,
* and the pad10*1 padding bits.
*
* The returned result will be greater than zero and will be a multiple of
* the specified bitrate.
*
* @param messageLengthInBits the total number of bits in the original input
* message. Must not be negative.
* @return the total length, in bits, of the input binary which will be
* processed by this Keccak sponge function.
*/
private int calculateTotalInputLength(int messageLengthInBits) {
assert messageLengthInBits >= 0;
int minimumPaddedLength = calculateMinimumLengthAfterPadding(
messageLengthInBits);
if (minimumPaddedLength % bitrate == 0) {
return minimumPaddedLength;
} else {
return minimumPaddedLength + bitrate - minimumPaddedLength % bitrate;
}
}
/*
* Returns the minimum length once message bits, suffix bits, and high
* padding bits are counted.
*/
private int calculateMinimumLengthAfterPadding(int messageLengthInBits) {
// The padding always starts and ends with a high '1' bit, so the
// padding length will always be at least two bits.
return messageLengthInBits + suffixBits.length() + 2;
}
/**
* Appends the domain suffix bits, if any, to the given input array, at the
* first bit index which comes after the input message bits.
*
* @param input a byte array which already contains the message bits.
* @param suffixStartBitIndex the bit index immediately following the final
* message bit index.
*/
private void appendDomainSuffixToInput(byte[] input, int suffixStartBitIndex) {
assert input != null;
assert suffixStartBitIndex >= 0;
assert suffixBits != null;
for (int suffixBitIndex = 0; suffixBitIndex < suffixBits.length();
++suffixBitIndex) {
boolean suffixBitHigh = suffixBits.charAt(suffixBitIndex) == '1';
if (suffixBitHigh) {
int targetInputBit = suffixStartBitIndex + suffixBitIndex;
int targetInputByte = targetInputBit / Byte.SIZE;
int targetInputByteBitIndex = targetInputBit % Byte.SIZE;
input[targetInputByte] += 1 << targetInputByteBitIndex;
}
}
}
/**
* Applies pad10*1 (multi-rate padding) to the end of the binary held in the
* input array. This padding guarantees that the final input binary length
* will be non-zero and will be a multiple of the specified bitrate.
*
* @param input a byte array which already contains the message bits and any
* suffix bits.
* @param messageLengthInBits the length of the original message in bits
* (not including any domain suffix bits).
*/
private void padInput(byte[] input, int messageLengthInBits) {
assert input != null;
assert messageLengthInBits >= 0;
int lengthOfMessageWithSuffix = messageLengthInBits + suffixBits.
length();
int zeroPaddingBitsRequired = calculateZeroPaddingBitsRequired(
messageLengthInBits);
int padStartIndex = lengthOfMessageWithSuffix;
int padEndIndex = lengthOfMessageWithSuffix + 1
+ zeroPaddingBitsRequired;
setInputBitHigh(input, padStartIndex);
setInputBitHigh(input, padEndIndex);
}
private int calculateZeroPaddingBitsRequired(int messageLengthInBits) {
int bitsIncludingPadEnds = calculateMinimumLengthAfterPadding(
messageLengthInBits);
int zeroPaddingBitsRequired;
if (bitsIncludingPadEnds % bitrate == 0) {
zeroPaddingBitsRequired = 0;
} else {
zeroPaddingBitsRequired = bitrate - bitsIncludingPadEnds % bitrate;
}
return zeroPaddingBitsRequired;
}
private void setInputBitHigh(byte[] input, int inputBitIndex) {
assert input != null;
assert inputBitIndex >= 0;
int inputByteIndex = inputBitIndex / Byte.SIZE;
byte outputByteBitIndex = (byte) (inputBitIndex % Byte.SIZE);
byte byteBitValue = (byte) (1 << outputByteBitIndex);
input[inputByteIndex] += byteBitValue;
}
/**
* Reads as many {@code bitrate}-sized blocks as possible from the given
* {@code InputStream} and returns the total number of bits read from the
* stream. After each block is read from the stream, it is absorbed into the
* given {@code KeccakState}.
*
* If the final read contains a number of bits which is less than
* {@code bitrate} then this method halts and does not absorb those bits
* into the state. The calling method must use the {@code block} array and
* the returned number of read bits in order to make sure that these
* remaining bits are absorbed into the permutation state.
*
* @param stream an {@code InputStream} which provides the input message
* bytes.
* @param block the array into which each bitrate-sized block of message
* bits should be read.
* @param state the {@code KeccakState} being used for the hash calculation.
* @return the total number of bits read from the stream.
* @throws IOException
*/
private int absorbInitialStreamBlocksIntoState(InputStream stream,
byte[] block, KeccakState state) throws IOException {
assert stream != null;
assert block != null;
assert state != null;
int bitsInCurrentBlock = readBlockFromStream(stream, block);
while (bitsInCurrentBlock == bitrate) {
state.absorbBitsIntoState(block, 0, bitsInCurrentBlock);
state.permute();
bitsInCurrentBlock = readBlockFromStream(stream, block);
}
return bitsInCurrentBlock;
}
/**
* Repeatedly reads from the given {@code InputStream} until either the
* stream ends, or the provided byte array has been filled with a whole
* block.
*
* @param stream an {@code InputStream} which provides the input message
* bytes.
* @param block the array into which the message bytes should be read.
* @return the total number of bits which were filled in the {@code block}
* array. If the stream ends then this number may be lower than the the size
* of the array, and the remainder of the array will be filled with zero
* bits.
* @throws IOException if an {@code IOException} is thrown by any of the
* stream reading operations.
*/
private int readBlockFromStream(InputStream stream, byte[] block) throws
IOException {
assert block != null;
assert block.length * Byte.SIZE == bitrate;
assert stream != null;
int filledBytes = 0;
int readBytes = stream.read(block);
while (readBytes > 0) {
filledBytes += readBytes;
readBytes = stream.read(block, filledBytes, block.length
- filledBytes);
}
if (filledBytes < block.length) {
Arrays.fill(block, filledBytes, block.length, (byte) 0);
}
return filledBytes * Byte.SIZE;
}
/**
* Returns an array which is large enough to hold the final message bits
* (read from an {@code InputStream}), and any suffix bits, and the pad10*1
* padding bits.
*
* @param finalBlockMessageLengthInBits the number of message bits in the
* final block which was read from the stream.
* @param finalBlock the byte array which holds the final message bits.
* @return a byte array which is sufficiently large to hold all of the final
* message bits, suffix bits, and padding.
*/
private byte[] prepareFinalBlockArray(int finalBlockMessageLengthInBits,
byte[] finalBlock) {
assert finalBlockMessageLengthInBits >= 0;
assert finalBlock != null;
int minimumLengthAfterPadding = calculateMinimumLengthAfterPadding(
finalBlockMessageLengthInBits);
if (minimumLengthAfterPadding <= bitrate) {
// The existing byte array is large enough so simply return it.
return finalBlock;
} else {
return resizedFinalBlockArray(finalBlockMessageLengthInBits,
finalBlock, minimumLengthAfterPadding);
}
}
private byte[] resizedFinalBlockArray(int finalBlockMessageLengthInBits,
byte[] finalBlock, int minimumLengthAfterPadding) {
int blocksRequired
= divideThenRoundUp(minimumLengthAfterPadding, bitrate);
byte[] finalBlocks = new byte[blocksRequired * bitrate / Byte.SIZE];
int bytesToCopy = divideThenRoundUp(finalBlockMessageLengthInBits,
Byte.SIZE);
System.arraycopy(finalBlock, 0, finalBlocks, 0, bytesToCopy);
return finalBlocks;
}
/**
* Returns a summary of this Keccak sponge function. Do not rely on the
* format of the returned text; it may change in future. All of the data
* found in the summary can be retrieved directly from this object using
* dedicated methods, so there should never be any need to parse data out of
* the text returned by this method.
*
* @return a {@code String} which summarises the settings of this
* {@code KeccakSponge}.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
sb.append("Keccak[");
sb.append(getBitrate());
sb.append(", ");
sb.append(getCapacity());
sb.append("](M");
if (getSuffixBits().isPresent()) {
sb.append(" || ");
sb.append(getSuffixBits().get());
sb.append(',');
} else {
sb.append(',');
}
sb.append(' ');
sb.append(getOutputLengthInBits());
sb.append(')');
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof KeccakSponge)) {
return false;
}
KeccakSponge that = (KeccakSponge) obj;
return this.bitrate == that.bitrate
&& this.capacity == that.capacity
&& this.outputLengthInBits == that.outputLengthInBits
&& this.suffixBits.equals(that.suffixBits);
}
@Override
public int hashCode() {
int hash = 7;
hash = 41 * hash + this.bitrate;
hash = 41 * hash + this.capacity;
hash = 41 * hash + Objects.hashCode(this.suffixBits);
hash = 41 * hash + this.outputLengthInBits;
return hash;
}
private static void validateBitrate(int bitrate) {
if (bitrate < 1) {
throw new IllegalArgumentException(
"bitrate must be greater than zero.");
}
if (bitrate % Byte.SIZE != 0) {
// TODO: Find KATs for Keccak with non-whole-byte bitrates, and add support to this library.
throw new UnsupportedOperationException(
"Currently only bitrates exactly divisible by 8 are supported.");
}
if (bitrate >= 1600) {
throw new IllegalArgumentException(
"bitrate must be less than 1600 bits.");
}
}
private static void validateSuffixBits(String suffixBits) {
Objects.requireNonNull(suffixBits);
int length = suffixBits.length();
for (int index = 0; index < length; ++index) {
char c = suffixBits.charAt(index);
if (c != '1' && c != '0') {
throw new IllegalArgumentException(
"If suffixBits is provided then it must be a bitstring. "
+ "It can contain only digits 0 and 1 and nothing else.");
}
}
}
private static void validateCapacity(int capacity) {
if (capacity < 1) {
throw new IllegalArgumentException(
"capacity must be greater than zero.");
}
if (capacity >= 1600) {
throw new IllegalArgumentException(
"capacity must be less than 1600 bits.");
}
}
private static void validateOutputLength(int outputLength) {
if (outputLength < 1) {
throw new IllegalArgumentException(
"outputLength must be greater than zero.");
}
}
private static void validatePermutationWidth(short width) {
// TODO: Add support for smaller widths (with lanes of less than one byte).
// (Have not been able to find KATs for widths below 200 bits.)
if (width < 200) {
throw new UnsupportedOperationException(
"Support is not yet in place for permutations widths smaller than 200 bits.");
}
if (!VALID_WIDTHS.contains(width)) {
List validWidthList = new ArrayList<>(VALID_WIDTHS);
validWidthList.sort((a, b) -> a - b);
throw new IllegalArgumentException(
"Sum of bitrate and capacity must equal a valid width: "
+ validWidthList + ".");
}
}
private static void validateMessageLength(byte[] message,
int messageLengthInBits) {
if (messageLengthInBits < 0) {
throw new IllegalArgumentException(
"messageLengthInBits cannot be negative.");
}
if (messageLengthInBits > message.length * Byte.SIZE) {
throw new IllegalArgumentException(
"messageLengthInBits cannot be greater than the bit length of the message byte array.");
}
}
/**
* Copies the original message bits into the specified input array.
*
* @param message a byte array which represents the original message binary.
* @param messageLengthInBits the number of bits in the message byte array
* which actually form the message binary.
* @param input an empty byte array which is sufficiently large to take all
* of the message bits, any suffix bits, and the pad10*1 padding bits.
*/
private static void moveMessageBitsIntoInput(byte[] message,
int messageLengthInBits, byte[] input) {
assert message != null;
assert messageLengthInBits >= 0;
assert input != null;
if (messageLengthInBits % Byte.SIZE == 0) {
System.arraycopy(message, 0, input, 0, messageLengthInBits
/ Byte.SIZE);
} else {
partialByteCopy(message, input, messageLengthInBits);
}
}
/**
* Copies only the specified number of bits from the source array to the
* destination array.
*
* @param source the byte array to read bits from.
* @param destination the byte array to write bits into.
* @param bitLimit the exact number of bits to read from the source into the
* destination.
*/
private static void partialByteCopy(byte[] source, byte[] destination,
int bitLimit) {
assert source != null;
assert destination != null;
assert bitLimit >= 0;
int wholeByteCount = bitLimit / Byte.SIZE;
System.arraycopy(source, 0, destination, 0, wholeByteCount);
int remainingBits = bitLimit % Byte.SIZE;
for (int bitIndex = 0; bitIndex < remainingBits; ++bitIndex) {
int bitValue = (1 << bitIndex);
boolean sourceBitHigh = (source[wholeByteCount] & bitValue) != 0;
if (sourceBitHigh) {
destination[wholeByteCount] += bitValue;
}
}
}
private static void requireWholeByteBitrate(int bitrate) {
assert bitrate > 0;
if (bitrate % Byte.SIZE != 0) {
throw new UnsupportedOperationException(
"bitrate must be divisible by eight in order to process byte stream.");
}
}
/**
* Returns a new byte array sufficiently large to hold the specified number
* of bits.
*
* @param bitCount the number of bits which needs to be wholly contained by
* the generated byte array.
* @return a byte array of sufficient size.
*/
private static byte[] createSufficientlyLargeByteArray(int bitCount) {
assert bitCount > 0;
int bytesRequired = divideThenRoundUp(bitCount, Byte.SIZE);
return new byte[bytesRequired];
}
/*
* Java integer division rounds down any fractional remainder, but sometimes
* we need to divide integers and then round up any fractional remainder.
*/
private static int divideThenRoundUp(int dividend, int divisor) {
assert dividend >= 0;
assert divisor > 0;
if (dividend == 0) {
return 0;
}
if (dividend % divisor == 0) {
return dividend / divisor;
} else {
return 1 + dividend / divisor;
}
}
/*
* This main method is being used to run the profiler, so leave it in place
* until optimisation has been completed.
*/
public static void main(String[] args) {
KeccakSponge spongeFunction = new KeccakSponge(576, 1024, "", 512);
byte[] message = new byte[]{(byte) 19};
byte[] hash = spongeFunction.apply(5, message);
for (int i = 0; i < 900000; ++i) {
hash = spongeFunction.apply(hash);
}
}
}