Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java112
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java764
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java1454
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/engines/ThreefishReferenceEngine.java378
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java110
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java286
-rw-r--r--core/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java36
-rw-r--r--core/src/main/java/org/bouncycastle/util/Arrays.java41
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java2
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java5
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/SkeinDigestTest.java291
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/SkeinMacTest.java159
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/Threefish1024Test.java60
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/Threefish256Test.java45
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/Threefish512Test.java50
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/test/speedy/ThroughputTest.java187
-rw-r--r--docs/specifications.html19
-rw-r--r--prov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java665
-rw-r--r--prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java110
-rw-r--r--prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java5
-rw-r--r--prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java4
-rw-r--r--prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java172
-rw-r--r--prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java3
-rw-r--r--prov/src/test/java/org/bouncycastle/jce/provider/test/SkeinTest.java316
24 files changed, 5269 insertions, 5 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
new file mode 100644
index 00000000..f22cf22b
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes,
+ * based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p>
+ *
+ * @see SkeinEngine
+ * @see SkeinParameters
+ */
+public class SkeinDigest implements ExtendedDigest, Memoable
+{
+ /** 256 bit block size - Skein-256 */
+ public static final int SKEIN_256 = SkeinEngine.SKEIN_256;
+ /** 512 bit block size - Skein-512 */
+ public static final int SKEIN_512 = SkeinEngine.SKEIN_512;
+ /** 1024 bit block size - Skein-1024 */
+ public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+ private SkeinEngine engine;
+
+ /**
+ * Constructs a Skein digest with an internal state size and output size.
+ *
+ * @param stateSizeBits
+ * the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+ * {@link #SKEIN_1024}.
+ * @param digestSizeBits
+ * the output/digest size to produce in bits, which must be an integral number of
+ * bytes.
+ */
+ public SkeinDigest(int stateSizeBits, int digestSizeBits)
+ {
+ this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+ init(null);
+ }
+
+ public SkeinDigest(SkeinDigest digest)
+ {
+ this.engine = new SkeinEngine(digest.engine);
+ }
+
+ public void reset(Memoable other)
+ {
+ SkeinDigest d = (SkeinDigest)other;
+ engine.reset(d.engine);
+ }
+
+ public Memoable copy()
+ {
+ return new SkeinDigest(this);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Skein-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8);
+ }
+
+ public int getDigestSize()
+ {
+ return engine.getOutputSize();
+ }
+
+ public int getByteLength()
+ {
+ return engine.getBlockSize();
+ }
+
+ /**
+ * Optionally initialises the Skein digest with the provided parameters.<br>
+ * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function.
+ *
+ * @param params
+ * the parameters to apply to this engine, or <code>null</code> to use no parameters.
+ */
+ public void init(SkeinParameters params)
+ {
+ engine.init(params);
+ }
+
+ public void reset()
+ {
+ engine.reset();
+ }
+
+ public void update(byte in)
+ {
+ engine.update(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ engine.update(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return engine.doFinal(out, outOff);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java b/core/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
new file mode 100644
index 00000000..63e258d9
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
@@ -0,0 +1,764 @@
+package org.bouncycastle.crypto.digests;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
+ * sizes, based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p>
+ * This implementation is the basis for {@link SkeinDigest} and {@link SkeinMac}, implementing the
+ * parameter based configuration system that allows Skein to be adapted to multiple applications. <br>
+ * Initialising the engine with {@link SkeinParameters} allows standard and arbitrary parameters to
+ * be applied during the Skein hash function.
+ * <p>
+ * Implemented:
+ * <ul>
+ * <li>256, 512 and 1024 bit internal states.</li>
+ * <li>Full 96 bit input length.</li>
+ * <li>Parameters defined in the Skein specification, and arbitrary other pre and post message
+ * parameters.</li>
+ * <li>Arbitrary output size in 1 byte intervals.</li>
+ * </ul>
+ * <p>
+ * Not implemented:
+ * <ul>
+ * <li>Sub-byte length input (bit padding).</li>
+ * <li>Tree hashing.</li>
+ * </ul>
+ *
+ * @see SkeinParameters
+ */
+public class SkeinEngine implements Memoable
+{
+ /** 256 bit block size - Skein 256 */
+ public static final int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256;
+ /** 512 bit block size - Skein 512 */
+ public static final int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512;
+ /** 1024 bit block size - Skein 1024 */
+ public static final int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024;
+
+ // Minimal at present, but more complex when tree hashing is implemented
+ private static class Configuration
+ {
+ private byte[] bytes = new byte[32];
+
+ public Configuration(long outputSizeBits)
+ {
+ // 0..3 = ASCII SHA3
+ bytes[0] = 'S';
+ bytes[1] = 'H';
+ bytes[2] = 'A';
+ bytes[3] = '3';
+
+ // 4..5 = version number in LSB order
+ bytes[4] = 1;
+ bytes[5] = 0;
+
+ // 8..15 = output length
+ ThreefishEngine.wordToBytes(outputSizeBits, bytes, 8);
+ }
+
+ public byte[] getBytes()
+ {
+ return bytes;
+ }
+
+ }
+
+ public static class Parameter
+ {
+ private int type;
+ private byte[] value;
+
+ public Parameter(int type, byte[] value)
+ {
+ this.type = type;
+ this.value = value;
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public byte[] getValue()
+ {
+ return value;
+ }
+
+ }
+
+ /** The parameter type for the Skein key. */
+ private static final int PARAM_TYPE_KEY = 0;
+
+ /** The parameter type for the Skein configuration block. */
+ private static final int PARAM_TYPE_CONFIG = 4;
+
+ /** The parameter type for the message. */
+ private static final int PARAM_TYPE_MESSAGE = 48;
+
+ /** The parameter type for the output transformation. */
+ private static final int PARAM_TYPE_OUTPUT = 63;
+
+ /**
+ * Precalculated UBI(CFG) states for common state/output combinations without key or other
+ * pre-message params.
+ */
+ private static final Hashtable INITIAL_STATES = new Hashtable();
+
+ static
+ {
+ // From Appendix C of the Skein 1.3 NIST submission
+ initialState(SKEIN_256, 128, new long[] {
+ 0xe1111906964d7260L,
+ 0x883daaa77c8d811cL,
+ 0x10080df491960f7aL,
+ 0xccf7dde5b45bc1c2L });
+
+ initialState(SKEIN_256, 160, new long[] {
+ 0x1420231472825e98L,
+ 0x2ac4e9a25a77e590L,
+ 0xd47a58568838d63eL,
+ 0x2dd2e4968586ab7dL });
+
+ initialState(SKEIN_256, 224, new long[] {
+ 0xc6098a8c9ae5ea0bL,
+ 0x876d568608c5191cL,
+ 0x99cb88d7d7f53884L,
+ 0x384bddb1aeddb5deL });
+
+ initialState(SKEIN_256, 256, new long[] {
+ 0xfc9da860d048b449L,
+ 0x2fca66479fa7d833L,
+ 0xb33bc3896656840fL,
+ 0x6a54e920fde8da69L });
+
+ initialState(SKEIN_512, 128, new long[] {
+ 0xa8bc7bf36fbf9f52L,
+ 0x1e9872cebd1af0aaL,
+ 0x309b1790b32190d3L,
+ 0xbcfbb8543f94805cL,
+ 0x0da61bcd6e31b11bL,
+ 0x1a18ebead46a32e3L,
+ 0xa2cc5b18ce84aa82L,
+ 0x6982ab289d46982dL });
+
+ initialState(SKEIN_512, 160, new long[] {
+ 0x28b81a2ae013bd91L,
+ 0xc2f11668b5bdf78fL,
+ 0x1760d8f3f6a56f12L,
+ 0x4fb747588239904fL,
+ 0x21ede07f7eaf5056L,
+ 0xd908922e63ed70b8L,
+ 0xb8ec76ffeccb52faL,
+ 0x01a47bb8a3f27a6eL });
+
+ initialState(SKEIN_512, 224, new long[] {
+ 0xccd0616248677224L,
+ 0xcba65cf3a92339efL,
+ 0x8ccd69d652ff4b64L,
+ 0x398aed7b3ab890b4L,
+ 0x0f59d1b1457d2bd0L,
+ 0x6776fe6575d4eb3dL,
+ 0x99fbc70e997413e9L,
+ 0x9e2cfccfe1c41ef7L });
+
+ initialState(SKEIN_512, 384, new long[] {
+ 0xa3f6c6bf3a75ef5fL,
+ 0xb0fef9ccfd84faa4L,
+ 0x9d77dd663d770cfeL,
+ 0xd798cbf3b468fddaL,
+ 0x1bc4a6668a0e4465L,
+ 0x7ed7d434e5807407L,
+ 0x548fc1acd4ec44d6L,
+ 0x266e17546aa18ff8L });
+
+ initialState(SKEIN_512, 512, new long[] {
+ 0x4903adff749c51ceL,
+ 0x0d95de399746df03L,
+ 0x8fd1934127c79bceL,
+ 0x9a255629ff352cb1L,
+ 0x5db62599df6ca7b0L,
+ 0xeabe394ca9d5c3f4L,
+ 0x991112c71a75b523L,
+ 0xae18a40b660fcc33L });
+ }
+
+ private static void initialState(int blockSize, int outputSize, long[] state)
+ {
+ INITIAL_STATES.put(variantIdentifier(blockSize / 8, outputSize / 8), state);
+ }
+
+ private static Integer variantIdentifier(int blockSizeBytes, int outputSizeBytes)
+ {
+ return new Integer((outputSizeBytes << 16) | blockSizeBytes);
+ }
+
+ private static class UbiTweak
+ {
+ /** Point at which position might overflow long, so switch to add with carry logic */
+ private static final long LOW_RANGE = Long.MAX_VALUE - Integer.MAX_VALUE;
+
+ /** Bit 127 = final */
+ private static final long T1_FINAL = 1L << 63;
+
+ /** Bit 126 = first */
+ private static final long T1_FIRST = 1L << 62;
+
+ /** UBI uses a 128 bit tweak */
+ private long tweak[] = new long[2];
+
+ /** Whether 64 bit position exceeded */
+ private boolean extendedPosition;
+
+ public UbiTweak()
+ {
+ reset();
+ }
+
+ public void reset(UbiTweak tweak)
+ {
+ this.tweak = Arrays.clone(tweak.tweak, this.tweak);
+ this.extendedPosition = tweak.extendedPosition;
+ }
+
+ public void reset()
+ {
+ tweak[0] = 0;
+ tweak[1] = 0;
+ extendedPosition = false;
+ setFirst(true);
+ }
+
+ public void setType(int type)
+ {
+ // Bits 120..125 = type
+ tweak[1] = (tweak[1] & 0xFFFFFFC000000000L) | ((type & 0x3FL) << 56);
+ }
+
+ public int getType()
+ {
+ return (int)((tweak[1] >>> 56) & 0x3FL);
+ }
+
+ public void setFirst(boolean first)
+ {
+ if (first)
+ {
+ tweak[1] |= T1_FIRST;
+ } else
+ {
+ tweak[1] &= ~T1_FIRST;
+ }
+ }
+
+ public boolean isFirst()
+ {
+ return ((tweak[1] & T1_FIRST) != 0);
+ }
+
+ public void setFinal(boolean last)
+ {
+ if (last)
+ {
+ tweak[1] |= T1_FINAL;
+ } else
+ {
+ tweak[1] &= ~T1_FINAL;
+ }
+ }
+
+ public boolean isFinal()
+ {
+ return ((tweak[1] & T1_FINAL) != 0);
+ }
+
+ /**
+ * Advances the position in the tweak by the specified value.
+ */
+ public void advancePosition(int advance)
+ {
+ // Bits 0..95 = position
+ if (extendedPosition)
+ {
+ long[] parts = new long[3];
+ parts[0] = tweak[0] & 0xFFFFFFFFL;
+ parts[1] = (tweak[0] >>> 32) & 0xFFFFFFFFL;
+ parts[2] = tweak[1] & 0xFFFFFFFFL;
+
+ long carry = advance;
+ for (int i = 0; i < parts.length; i++)
+ {
+ carry += parts[i];
+ parts[i] = carry;
+ carry >>>= 32;
+ }
+ tweak[0] = ((parts[1] & 0xFFFFFFFFL) << 32) | (parts[0] & 0xFFFFFFFFL);
+ tweak[1] = (tweak[1] & 0xFFFFFFFF00000000L) | (parts[2] & 0xFFFFFFFFL);
+ } else
+ {
+ long position = tweak[0];
+ position += advance;
+ tweak[0] = position;
+ if (position > LOW_RANGE)
+ {
+ extendedPosition = true;
+ }
+ }
+ }
+
+ public long[] getWords()
+ {
+ return tweak;
+ }
+
+ public String toString()
+ {
+ return getType() + " first: " + isFirst() + ", final: " + isFinal();
+ }
+
+ }
+
+ /**
+ * The Unique Block Iteration chaining mode.
+ */
+ // TODO: This might be better as methods...
+ private class UBI
+ {
+ private final UbiTweak tweak = new UbiTweak();
+
+ /** Buffer for the current block of message data */
+ private byte[] currentBlock;
+
+ /** Offset into the current message block */
+ private int currentOffset;
+
+ /** Buffer for message words for feedback into encrypted block */
+ private long[] message;
+
+ public UBI(int blockSize)
+ {
+ currentBlock = new byte[blockSize];
+ message = new long[currentBlock.length / 8];
+ }
+
+ public void reset(UBI ubi)
+ {
+ currentBlock = Arrays.clone(ubi.currentBlock, currentBlock);
+ currentOffset = ubi.currentOffset;
+ message = Arrays.clone(ubi.message, this.message);
+ tweak.reset(ubi.tweak);
+ }
+
+ public void reset(int type)
+ {
+ tweak.reset();
+ tweak.setType(type);
+ currentOffset = 0;
+ }
+
+ public void update(byte[] value, int offset, int len, long[] output)
+ {
+ /*
+ * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
+ * are subsequent bytes (last block must be processed in doFinal() with final=true set).
+ */
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentOffset == currentBlock.length)
+ {
+ processBlock(output);
+ tweak.setFirst(false);
+ currentOffset = 0;
+ }
+
+ int toCopy = Math.min((len - copied), currentBlock.length - currentOffset);
+ System.arraycopy(value, offset + copied, currentBlock, currentOffset, toCopy);
+ copied += toCopy;
+ currentOffset += toCopy;
+ tweak.advancePosition(toCopy);
+ }
+ }
+
+ private void processBlock(long[] output)
+ {
+ threefish.init(true, chain, tweak.getWords());
+ for (int i = 0; i < message.length; i++)
+ {
+ message[i] = ThreefishEngine.bytesToWord(currentBlock, i * 8);
+ }
+
+ threefish.processBlock(message, output);
+
+ for (int i = 0; i < output.length; i++)
+ {
+ output[i] ^= message[i];
+ }
+ }
+
+ public void doFinal(long[] output)
+ {
+ // Pad remainder of current block with zeroes
+ for (int i = currentOffset; i < currentBlock.length; i++)
+ {
+ currentBlock[i] = 0;
+ }
+
+ tweak.setFinal(true);
+ processBlock(output);
+ }
+
+ }
+
+ /** Underlying Threefish tweakable block cipher */
+ final ThreefishEngine threefish;
+
+ /** Size of the digest output, in bytes */
+ private final int outputSizeBytes;
+
+ /** The current chaining/state value */
+ long[] chain;
+
+ /** The initial state value */
+ private long[] initialState;
+
+ /** The (optional) key parameter */
+ private byte[] key;
+
+ /** Parameters to apply prior to the message */
+ private Parameter[] preMessageParameters;
+
+ /** Parameters to apply after the message, but prior to output */
+ private Parameter[] postMessageParameters;
+
+ /** The current UBI operation */
+ private final UBI ubi;
+
+ /** Buffer for single byte update method */
+ private final byte[] singleByte = new byte[1];
+
+ /**
+ * Constructs a Skein engine.
+ *
+ * @param stateSizeBits
+ * the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+ * {@link #SKEIN_1024}.
+ * @param outputSizeBits
+ * the output/digest size to produce in bits, which must be an integral number of
+ * bytes.
+ */
+ public SkeinEngine(int blockSizeBits, int outputSizeBits)
+ {
+ if (outputSizeBits % 8 != 0)
+ {
+ throw new IllegalArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits);
+ }
+ // TODO: Prevent digest sizes > block size?
+ this.outputSizeBytes = outputSizeBits / 8;
+
+ this.threefish = new ThreefishEngine(blockSizeBits);
+ this.ubi = new UBI(threefish.getBlockSize());
+ }
+
+ /**
+ * Creates a SkeinEngine as an exact copy of an existing instance.
+ */
+ public SkeinEngine(SkeinEngine engine)
+ {
+ this(engine.getBlockSize() * 8, engine.getOutputSize() * 8);
+ copyIn(engine);
+ }
+
+ private void copyIn(SkeinEngine engine)
+ {
+ this.ubi.reset(engine.ubi);
+ this.chain = Arrays.clone(engine.chain, this.chain);
+ this.initialState = Arrays.clone(engine.initialState, this.initialState);
+ this.key = Arrays.clone(engine.key, this.key);
+ this.preMessageParameters = clone(engine.preMessageParameters, this.preMessageParameters);
+ this.postMessageParameters = clone(engine.postMessageParameters, this.postMessageParameters);
+ }
+
+ private static Parameter[] clone(Parameter[] data, Parameter[] existing)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ if ((existing == null) || (existing.length != data.length))
+ {
+ existing = new Parameter[data.length];
+ }
+ System.arraycopy(data, 0, existing, 0, existing.length);
+ return existing;
+ }
+
+ public Memoable copy()
+ {
+ return new SkeinEngine(this);
+ }
+
+ public void reset(Memoable other)
+ {
+ SkeinEngine s = (SkeinEngine)other;
+ if ((getBlockSize() != s.getBlockSize()) || (outputSizeBytes != s.outputSizeBytes))
+ {
+ throw new IllegalArgumentException("Incompatible parameters in provided SkeinEngine.");
+ }
+ copyIn(s);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSizeBytes;
+ }
+
+ public int getBlockSize()
+ {
+ return threefish.getBlockSize();
+ }
+
+ /**
+ * Initialises the Skein engine with the provided parameters. See {@link SkeinParameters} for
+ * details on the parameterisation of the Skein hash function.
+ *
+ * @param params
+ * the parameters to apply to this engine, or <code>null</code> to use no parameters.
+ */
+ public void init(SkeinParameters params)
+ {
+ this.chain = null;
+ this.key = null;
+ this.preMessageParameters = null;
+ this.postMessageParameters = null;
+
+ if (params != null)
+ {
+ byte[] key = params.getKey();
+ if (key.length < 16)
+ {
+ throw new IllegalArgumentException("Skein key must be at least 128 bits.");
+ }
+ initParams(params.getParameters());
+ }
+ createInitialState();
+
+ // Initialise message block
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void initParams(Hashtable parameters)
+ {
+ Enumeration keys = parameters.keys();
+ final Vector pre = new Vector();
+ final Vector post = new Vector();
+
+ while (keys.hasMoreElements())
+ {
+ Integer type = (Integer)keys.nextElement();
+ byte[] value = (byte[])parameters.get(type);
+
+ if (type.intValue() == PARAM_TYPE_KEY)
+ {
+ this.key = value;
+ } else if (type.intValue() < PARAM_TYPE_MESSAGE)
+ {
+ pre.addElement(new Parameter(type.intValue(), value));
+ } else
+ {
+ post.addElement(new Parameter(type.intValue(), value));
+ }
+ }
+ preMessageParameters = new Parameter[pre.size()];
+ pre.copyInto(preMessageParameters);
+ sort(preMessageParameters);
+
+ postMessageParameters = new Parameter[post.size()];
+ post.copyInto(postMessageParameters);
+ sort(postMessageParameters);
+ }
+
+ private static void sort(Parameter[] params)
+ {
+ if (params == null)
+ {
+ return;
+ }
+ // Insertion sort, for Java 1.1 compatibility
+ for (int i = 1; i < params.length; i++)
+ {
+ Parameter param = params[i];
+ int hole = i;
+ while (hole > 0 && param.getType() < params[hole - 1].getType())
+ {
+ params[hole] = params[hole - 1];
+ hole = hole - 1;
+ }
+ params[hole] = param;
+ }
+ }
+
+ /**
+ * Calculate the initial (pre message block) chaining state.
+ */
+ private void createInitialState()
+ {
+ long[] precalc = (long[])INITIAL_STATES.get(variantIdentifier(getBlockSize(), getOutputSize()));
+ if ((key == null) && (precalc != null))
+ {
+ // Precalculated UBI(CFG)
+ chain = Arrays.clone(precalc);
+ } else
+ {
+ // Blank initial state
+ chain = new long[getBlockSize() / 8];
+
+ // Process key block
+ if (key != null)
+ {
+ ubiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
+ }
+
+ // Process configuration block
+ ubiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).getBytes());
+ }
+
+ // Process additional pre-message parameters
+ if (preMessageParameters != null)
+ {
+ for (int i = 0; i < preMessageParameters.length; i++)
+ {
+ Parameter param = preMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+ initialState = Arrays.clone(chain);
+ }
+
+ /**
+ * Reset the engine to the initial state (with the key and any pre-message parameters , ready to
+ * accept message input.
+ */
+ public void reset()
+ {
+ System.arraycopy(initialState, 0, chain, 0, chain.length);
+
+ ubiInit(PARAM_TYPE_MESSAGE);
+ }
+
+ private void ubiComplete(int type, byte[] value)
+ {
+ ubiInit(type);
+ this.ubi.update(value, 0, value.length, chain);
+ ubiFinal();
+ }
+
+ private void ubiInit(int type)
+ {
+ this.ubi.reset(type);
+ }
+
+ private void ubiFinal()
+ {
+ ubi.doFinal(chain);
+ }
+
+ private void checkInitialised()
+ {
+ if (this.ubi == null)
+ {
+ throw new IllegalArgumentException("Skein engine is not initialised.");
+ }
+ }
+
+ public void update(byte in)
+ {
+ singleByte[0] = in;
+ update(singleByte, 0, 1);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ checkInitialised();
+ ubi.update(in, inOff, len, chain);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ checkInitialised();
+ if (out.length < (outOff + outputSizeBytes))
+ {
+ throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes");
+ }
+
+ // Finalise message block
+ ubiFinal();
+
+ // Process additional post-message parameters
+ if (postMessageParameters != null)
+ {
+ for (int i = 0; i < postMessageParameters.length; i++)
+ {
+ Parameter param = postMessageParameters[i];
+ ubiComplete(param.getType(), param.getValue());
+ }
+ }
+
+ // Perform the output transform
+ final int blockSize = getBlockSize();
+ final int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
+ for (int i = 0; i < blocksRequired; i++)
+ {
+ final int toWrite = Math.min(blockSize, outputSizeBytes - (i * blockSize));
+ output(i, out, outOff + (i * blockSize), toWrite);
+ }
+
+ reset();
+
+ return outputSizeBytes;
+ }
+
+ private void output(long outputSequence, byte[] out, int outOff, int outputBytes)
+ {
+ byte[] currentBytes = new byte[8];
+ ThreefishEngine.wordToBytes(outputSequence, currentBytes, 0);
+
+ // Output is a sequence of UBI invocations all of which use and preserve the pre-output
+ // state
+ long[] outputWords = new long[chain.length];
+ ubiInit(PARAM_TYPE_OUTPUT);
+ this.ubi.update(currentBytes, 0, currentBytes.length, outputWords);
+ ubi.doFinal(outputWords);
+
+ final int wordsRequired = ((outputBytes + 8 - 1) / 8);
+ for (int i = 0; i < wordsRequired; i++)
+ {
+ int toWrite = Math.min(8, outputBytes - (i * 8));
+ if (toWrite == 8)
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], out, outOff + (i * 8));
+ } else
+ {
+ ThreefishEngine.wordToBytes(outputWords[i], currentBytes, 0);
+ System.arraycopy(currentBytes, 0, out, outOff + (i * 8), toWrite);
+ }
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
new file mode 100644
index 00000000..220f33a0
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
@@ -0,0 +1,1454 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+
+/**
+ * Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block
+ * sizes.
+ * <p>
+ * This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST
+ * SHA-3 competition in October 2010.
+ * <p>
+ * Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p>
+ * This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables
+ * to speed up key schedule injection. <br>
+ * 2 x block size state is retained by each cipher instance.
+ */
+public class ThreefishEngine implements BlockCipher
+{
+ /** 256 bit block size - Threefish-256 */
+ public static final int BLOCKSIZE_256 = 256;
+ /** 512 bit block size - Threefish-512 */
+ public static final int BLOCKSIZE_512 = 512;
+ /** 1024 bit block size - Threefish-1024 */
+ public static final int BLOCKSIZE_1024 = 1024;
+
+ /** Size of the tweak in bytes (always 128 bit/16 bytes) */
+ private static final int TWEAK_SIZE_BYTES = 16;
+ private static final int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8;
+
+ /** Rounds in Threefish-256 */
+ private static final int ROUNDS_256 = 72;
+ /** Rounds in Threefish-512 */
+ private static final int ROUNDS_512 = 72;
+ /** Rounds in Threefish-1024 */
+ private static final int ROUNDS_1024 = 80;
+
+ /** Max rounds of any of the variants */
+ private static final int MAX_ROUNDS = ROUNDS_1024;
+
+ /** Key schedule parity constant */
+ private static final long C_240 = 0x1BD11BDAA9FC1A22L;
+
+ /* Pre-calculated modulo arithmetic tables for key schedule lookups */
+ private static int[] MOD9 = new int[MAX_ROUNDS];
+ private static int[] MOD17 = new int[MOD9.length];
+ private static int[] MOD5 = new int[MOD9.length];
+ private static int[] MOD3 = new int[MOD9.length];
+
+ static
+ {
+ for (int i = 0; i < MOD9.length; i++)
+ {
+ MOD17[i] = i % 17;
+ MOD9[i] = i % 9;
+ MOD5[i] = i % 5;
+ MOD3[i] = i % 3;
+ }
+ }
+
+ /** Block size in bytes */
+ private final int blocksizeBytes;
+
+ /** Block size in 64 bit words */
+ private final int blocksizeWords;
+
+ /** Buffer for byte oriented processBytes to call internal word API */
+ private final long[] currentBlock;
+
+ /**
+ * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup
+ */
+ private final long[] t = new long[5];
+
+ /** Key schedule words */
+ private final long[] kw;
+
+ /** The internal cipher implementation (varies by blocksize) */
+ private final ThreefishCipher cipher;
+
+ private boolean forEncryption;
+
+ /**
+ * Constructs a new Threefish cipher, with a specified block size.
+ *
+ * @param blocksizeBits
+ * the block size in bits, one of {@link #BLOCKSIZE_256}, {@link #BLOCKSIZE_512},
+ * {@link #BLOCKSIZE_1024}.
+ */
+ public ThreefishEngine(final int blocksizeBits)
+ {
+ this.blocksizeBytes = (blocksizeBits / 8);
+ this.blocksizeWords = (this.blocksizeBytes / 8);
+ this.currentBlock = new long[blocksizeWords];
+
+ /*
+ * Provide room for original key words, extended key word and repeat of key words for modulo
+ * free lookup of key schedule words.
+ */
+ this.kw = new long[2 * blocksizeWords + 1];
+
+ switch (blocksizeBits) {
+ case BLOCKSIZE_256:
+ cipher = new Threefish256Cipher(kw, t);
+ break;
+ case BLOCKSIZE_512:
+ cipher = new Threefish512Cipher(kw, t);
+ break;
+ case BLOCKSIZE_1024:
+ cipher = new Threefish1024Cipher(kw, t);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits");
+ }
+ }
+
+ /**
+ * Initialise the engine.
+ *
+ * @param params
+ * an instance of {@link TweakableBlockCipherParameters}, or {@link KeyParameter} (to
+ * use a 0 tweak)
+ */
+ public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
+ {
+ final byte[] keyBytes;
+ final byte[] tweakBytes;
+
+ if (params instanceof TweakableBlockCipherParameters)
+ {
+ TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)params;
+ keyBytes = tParams.getKey().getKey();
+ tweakBytes = tParams.getTweak();
+ } else if (params instanceof KeyParameter)
+ {
+ keyBytes = ((KeyParameter)params).getKey();
+ tweakBytes = null;
+ } else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Threefish init - "
+ + params.getClass().getName());
+ }
+
+ long[] keyWords = null;
+ long[] tweakWords = null;
+
+ if (keyBytes != null)
+ {
+ if (keyBytes.length != this.blocksizeBytes)
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeBytes
+ + " bytes)");
+ }
+ keyWords = new long[blocksizeWords];
+ for (int i = 0; i < keyWords.length; i++)
+ {
+ keyWords[i] = bytesToWord(keyBytes, i * 8);
+ }
+ }
+ if (tweakBytes != null)
+ {
+ if (tweakBytes.length != TWEAK_SIZE_BYTES)
+ {
+ throw new IllegalArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes");
+ }
+ tweakWords = new long[] { bytesToWord(tweakBytes, 0), bytesToWord(tweakBytes, 8) };
+ }
+ init(forEncryption, keyWords, tweakWords);
+ }
+
+ /**
+ * Initialise the engine, specifying the key and tweak directly.
+ *
+ * @param forEncryption
+ * the cipher mode.
+ * @param key
+ * the words of the key, or <code>null</code> to use the current key.
+ * @param tweak
+ * the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak.
+ */
+ public void init(boolean forEncryption, final long[] key, final long[] tweak)
+ {
+ this.forEncryption = forEncryption;
+ if (key != null)
+ {
+ setKey(key);
+ }
+ if (tweak != null)
+ {
+ setTweak(tweak);
+ }
+ }
+
+ private void setKey(long[] key)
+ {
+ if (key.length != this.blocksizeWords)
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeWords
+ + " words)");
+ }
+
+ /*
+ * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512,
+ * 20k for 1024).
+ *
+ * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations
+ * used, to avoid expensive mod computations during cipher operation.
+ */
+
+ long knw = C_240;
+ for (int i = 0; i < blocksizeWords; i++)
+ {
+ kw[i] = key[i];
+ knw = knw ^ kw[i];
+ }
+ kw[blocksizeWords] = knw;
+ System.arraycopy(kw, 0, kw, blocksizeWords + 1, blocksizeWords);
+ }
+
+ private void setTweak(long[] tweak)
+ {
+ if (tweak.length != TWEAK_SIZE_WORDS)
+ {
+ throw new IllegalArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words.");
+ }
+
+ /*
+ * Tweak schedule partially repeated to avoid mod computations during cipher operation
+ */
+ t[0] = tweak[0];
+ t[1] = tweak[1];
+ t[2] = t[0] ^ t[1];
+ t[3] = t[0];
+ t[4] = t[1];
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Threefish-" + (blocksizeBytes * 8);
+ }
+
+ public int getBlockSize()
+ {
+ return blocksizeBytes;
+ }
+
+ public void reset()
+ {
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException,
+ IllegalStateException
+ {
+ if ((outOff + blocksizeBytes) > out.length)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if ((inOff + blocksizeBytes) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ currentBlock[i >> 3] = bytesToWord(in, inOff + i);
+ }
+ processBlock(this.currentBlock, this.currentBlock);
+ for (int i = 0; i < blocksizeBytes; i += 8)
+ {
+ wordToBytes(this.currentBlock[i >> 3], out, outOff + i);
+ }
+
+ return blocksizeBytes;
+ }
+
+ /**
+ * Process a block of data represented as 64 bit words.
+ *
+ * @param in
+ * a block sized buffer of words to process.
+ * @param out
+ * a block sized buffer of words to receive the output of the operation.
+ * @return the number of 8 byte words processed (which will be the same as the block size).
+ * @throws DataLengthException
+ * if either the input or output is not block sized.
+ * @throws IllegalStateException
+ * if this engine is not initialised.
+ */
+ public int processBlock(long[] in, long[] out) throws DataLengthException, IllegalStateException
+ {
+ if (kw[blocksizeWords] == 0)
+ {
+ throw new IllegalStateException("Threefish engine not initialised");
+ }
+
+ if (in.length != blocksizeWords)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+ if (out.length != blocksizeWords)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ cipher.encryptBlock(in, out);
+ } else
+ {
+ cipher.decryptBlock(in, out);
+ }
+
+ return blocksizeWords;
+ }
+
+ /**
+ * Read a single 64 bit word from input in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static long bytesToWord(final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+
+ long word = 0;
+ int index = off;
+
+ word = (bytes[index++] & 0xffL);
+ word |= (bytes[index++] & 0xffL) << 8;
+ word |= (bytes[index++] & 0xffL) << 16;
+ word |= (bytes[index++] & 0xffL) << 24;
+ word |= (bytes[index++] & 0xffL) << 32;
+ word |= (bytes[index++] & 0xffL) << 40;
+ word |= (bytes[index++] & 0xffL) << 48;
+ word |= (bytes[index++] & 0xffL) << 56;
+
+ return word;
+ }
+
+ /**
+ * Write a 64 bit word to output in LSB first order.
+ */
+ // At least package protected for efficient access from inner class
+ public static void wordToBytes(final long word, final byte[] bytes, final int off)
+ {
+ if ((off + 8) > bytes.length)
+ {
+ // Help the JIT avoid index checks
+ throw new IllegalArgumentException();
+ }
+ int index = off;
+
+ bytes[index++] = (byte)word;
+ bytes[index++] = (byte)(word >> 8);
+ bytes[index++] = (byte)(word >> 16);
+ bytes[index++] = (byte)(word >> 24);
+ bytes[index++] = (byte)(word >> 32);
+ bytes[index++] = (byte)(word >> 40);
+ bytes[index++] = (byte)(word >> 48);
+ bytes[index++] = (byte)(word >> 56);
+ }
+
+ /**
+ * Rotate left + xor part of the mix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long rotlXor(long x, int n, long xor)
+ {
+ return ((x << n) | (x >>> -n)) ^ xor;
+ }
+
+ /**
+ * Rotate xor + rotate right part of the unmix operation.
+ */
+ // Package protected for efficient access from inner class
+ static long xorRotr(long x, int n, long xor)
+ {
+ long xored = x ^ xor;
+ return (xored >>> n) | (xored << -n);
+ }
+
+ private static abstract class ThreefishCipher
+ {
+ /** The extended + repeated tweak words */
+ protected final long[] t;
+ /** The extended + repeated key words */
+ protected final long[] kw;
+
+ protected ThreefishCipher(final long[] kw, final long[] t)
+ {
+ this.kw = kw;
+ this.t = t;
+ }
+
+ abstract void encryptBlock(long[] block, long[] out);
+
+ abstract void decryptBlock(long[] block, long[] out);
+
+ }
+
+ private static final class Threefish256Cipher extends ThreefishCipher
+ {
+ /** Mix rotation constants defined in Skein 1.3 specification */
+ private static final int ROTATION_0_0 = 14, ROTATION_0_1 = 16;
+ private static final int ROTATION_1_0 = 52, ROTATION_1_1 = 57;
+ private static final int ROTATION_2_0 = 23, ROTATION_2_1 = 40;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 37;
+
+ private static final int ROTATION_4_0 = 25, ROTATION_4_1 = 33;
+ private static final int ROTATION_5_0 = 46, ROTATION_5_1 = 12;
+ private static final int ROTATION_6_0 = 58, ROTATION_6_1 = 22;
+ private static final int ROTATION_7_0 = 32, ROTATION_7_1 = 32;
+
+ public Threefish256Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 4 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1] + t[0];
+ b2 += kw[2] + t[1];
+ b3 += kw[3];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 2 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_256 / 4); d += 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 2 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_1_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_1_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_3_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_3_1, b2 += b1);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm5];
+ b1 += kw[dm5 + 1] + t[dm3];
+ b2 += kw[dm5 + 2] + t[dm3 + 1];
+ b3 += kw[dm5 + 3] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_5_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_5_1, b2 += b1);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b2 += b3);
+
+ b3 = rotlXor(b3, ROTATION_7_0, b0 += b3);
+ b1 = rotlXor(b1, ROTATION_7_1, b2 += b1);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm5 + 1];
+ b1 += kw[dm5 + 2] + t[dm3 + 1];
+ b2 += kw[dm5 + 3] + t[dm3 + 2];
+ b3 += kw[dm5 + 4] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod5 = MOD5;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 9)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+
+ for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm5 = mod5[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm5 + 1];
+ b1 -= kw[dm5 + 2] + t[dm3 + 1];
+ b2 -= kw[dm5 + 3] + t[dm3 + 2];
+ b3 -= kw[dm5 + 4] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b3 = xorRotr(b3, ROTATION_7_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_7_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_5_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_5_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm5];
+ b1 -= kw[dm5 + 1] + t[dm3];
+ b2 -= kw[dm5 + 2] + t[dm3 + 1];
+ b3 -= kw[dm5 + 3] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b3 = xorRotr(b3, ROTATION_3_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_3_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b2);
+ b2 -= b3;
+
+ b3 = xorRotr(b3, ROTATION_1_0, b0);
+ b0 -= b3;
+ b1 = xorRotr(b1, ROTATION_1_1, b2);
+ b2 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1] + t[0];
+ b2 -= kw[2] + t[1];
+ b3 -= kw[3];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ }
+
+ }
+
+ private static final class Threefish512Cipher extends ThreefishCipher
+ {
+ /** Mix rotation constants defined in Skein 1.3 specification */
+ private static final int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37;
+ private static final int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42;
+ private static final int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39;
+ private static final int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56;
+
+ private static final int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24;
+ private static final int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17;
+ private static final int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43;
+ private static final int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22;
+
+ protected Threefish512Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ public void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 8 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5] + t[0];
+ b6 += kw[6] + t[1];
+ b7 += kw[7];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_512 / 4); d += 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_1_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_1_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_1_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_1_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_2_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_2_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_2_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_2_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_3_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_3_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_3_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_3_3, b4 += b3);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm9];
+ b1 += kw[dm9 + 1];
+ b2 += kw[dm9 + 2];
+ b3 += kw[dm9 + 3];
+ b4 += kw[dm9 + 4];
+ b5 += kw[dm9 + 5] + t[dm3];
+ b6 += kw[dm9 + 6] + t[dm3 + 1];
+ b7 += kw[dm9 + 7] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+
+ b1 = rotlXor(b1, ROTATION_5_0, b2 += b1);
+ b7 = rotlXor(b7, ROTATION_5_1, b4 += b7);
+ b5 = rotlXor(b5, ROTATION_5_2, b6 += b5);
+ b3 = rotlXor(b3, ROTATION_5_3, b0 += b3);
+
+ b1 = rotlXor(b1, ROTATION_6_0, b4 += b1);
+ b3 = rotlXor(b3, ROTATION_6_1, b6 += b3);
+ b5 = rotlXor(b5, ROTATION_6_2, b0 += b5);
+ b7 = rotlXor(b7, ROTATION_6_3, b2 += b7);
+
+ b1 = rotlXor(b1, ROTATION_7_0, b6 += b1);
+ b7 = rotlXor(b7, ROTATION_7_1, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_7_2, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_7_3, b4 += b3);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm9 + 1];
+ b1 += kw[dm9 + 2];
+ b2 += kw[dm9 + 3];
+ b3 += kw[dm9 + 4];
+ b4 += kw[dm9 + 5];
+ b5 += kw[dm9 + 6] + t[dm3 + 1];
+ b6 += kw[dm9 + 7] + t[dm3 + 2];
+ b7 += kw[dm9 + 8] + d + 1;
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ }
+
+ public void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod9 = MOD9;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 17)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+
+ for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm9 = mod9[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm9 + 1];
+ b1 -= kw[dm9 + 2];
+ b2 -= kw[dm9 + 3];
+ b3 -= kw[dm9 + 4];
+ b4 -= kw[dm9 + 5];
+ b5 -= kw[dm9 + 6] + t[dm3 + 1];
+ b6 -= kw[dm9 + 7] + t[dm3 + 2];
+ b7 -= kw[dm9 + 8] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+
+ b1 = xorRotr(b1, ROTATION_7_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_7_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_7_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_6_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_6_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_6_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_6_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_5_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_5_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_5_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_5_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm9];
+ b1 -= kw[dm9 + 1];
+ b2 -= kw[dm9 + 2];
+ b3 -= kw[dm9 + 3];
+ b4 -= kw[dm9 + 4];
+ b5 -= kw[dm9 + 5] + t[dm3];
+ b6 -= kw[dm9 + 6] + t[dm3 + 1];
+ b7 -= kw[dm9 + 7] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b1 = xorRotr(b1, ROTATION_3_0, b6);
+ b6 -= b1;
+ b7 = xorRotr(b7, ROTATION_3_1, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_3_2, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_3, b4);
+ b4 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_2_0, b4);
+ b4 -= b1;
+ b3 = xorRotr(b3, ROTATION_2_1, b6);
+ b6 -= b3;
+ b5 = xorRotr(b5, ROTATION_2_2, b0);
+ b0 -= b5;
+ b7 = xorRotr(b7, ROTATION_2_3, b2);
+ b2 -= b7;
+
+ b1 = xorRotr(b1, ROTATION_1_0, b2);
+ b2 -= b1;
+ b7 = xorRotr(b7, ROTATION_1_1, b4);
+ b4 -= b7;
+ b5 = xorRotr(b5, ROTATION_1_2, b6);
+ b6 -= b5;
+ b3 = xorRotr(b3, ROTATION_1_3, b0);
+ b0 -= b3;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5] + t[0];
+ b6 -= kw[6] + t[1];
+ b7 -= kw[7];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ }
+ }
+
+ private static final class Threefish1024Cipher extends ThreefishCipher
+ {
+ /** Mix rotation constants defined in Skein 1.3 specification */
+ private static final int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47;
+ private static final int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37;
+ private static final int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55;
+ private static final int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52;
+ private static final int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13;
+ private static final int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17;
+ private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41;
+ private static final int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25;
+
+ private static final int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31;
+ private static final int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30;
+ private static final int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51;
+ private static final int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41;
+ private static final int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46;
+ private static final int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25;
+ private static final int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52;
+ private static final int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20;
+
+ public Threefish1024Cipher(long[] kw, long[] t)
+ {
+ super(kw, t);
+ }
+
+ void encryptBlock(long[] block, long[] out)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ /*
+ * Read 16 words of plaintext data, not using arrays for cipher state
+ */
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ /*
+ * First subkey injection.
+ */
+ b0 += kw[0];
+ b1 += kw[1];
+ b2 += kw[2];
+ b3 += kw[3];
+ b4 += kw[4];
+ b5 += kw[5];
+ b6 += kw[6];
+ b7 += kw[7];
+ b8 += kw[8];
+ b9 += kw[9];
+ b10 += kw[10];
+ b11 += kw[11];
+ b12 += kw[12];
+ b13 += kw[13] + t[0];
+ b14 += kw[14] + t[1];
+ b15 += kw[15];
+
+ /*
+ * Rounds loop, unrolled to 8 rounds per iteration.
+ *
+ * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+ * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+ * index/lookup).
+ *
+ * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+ * inlining constant rotation values (avoiding array index/lookup).
+ */
+
+ for (int d = 1; d < (ROUNDS_1024 / 4); d += 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /*
+ * 4 rounds of mix and permute.
+ *
+ * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+ * operations in each 4 round block.
+ */
+ b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_0_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_0_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_0_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_0_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_1_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_1_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_1_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_1_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_1_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_1_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_1_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_1_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_2_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_2_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_2_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_2_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_2_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_2_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_2_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_2_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_3_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_3_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_3_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_3_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_3_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_3_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_3_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_3_7, b12 += b7);
+
+ /*
+ * Subkey injection for first 4 rounds.
+ */
+ b0 += kw[dm17];
+ b1 += kw[dm17 + 1];
+ b2 += kw[dm17 + 2];
+ b3 += kw[dm17 + 3];
+ b4 += kw[dm17 + 4];
+ b5 += kw[dm17 + 5];
+ b6 += kw[dm17 + 6];
+ b7 += kw[dm17 + 7];
+ b8 += kw[dm17 + 8];
+ b9 += kw[dm17 + 9];
+ b10 += kw[dm17 + 10];
+ b11 += kw[dm17 + 11];
+ b12 += kw[dm17 + 12];
+ b13 += kw[dm17 + 13] + t[dm3];
+ b14 += kw[dm17 + 14] + t[dm3 + 1];
+ b15 += kw[dm17 + 15] + d;
+
+ /*
+ * 4 more rounds of mix/permute
+ */
+ b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+ b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+ b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+ b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+ b9 = rotlXor(b9, ROTATION_4_4, b8 += b9);
+ b11 = rotlXor(b11, ROTATION_4_5, b10 += b11);
+ b13 = rotlXor(b13, ROTATION_4_6, b12 += b13);
+ b15 = rotlXor(b15, ROTATION_4_7, b14 += b15);
+
+ b9 = rotlXor(b9, ROTATION_5_0, b0 += b9);
+ b13 = rotlXor(b13, ROTATION_5_1, b2 += b13);
+ b11 = rotlXor(b11, ROTATION_5_2, b6 += b11);
+ b15 = rotlXor(b15, ROTATION_5_3, b4 += b15);
+ b7 = rotlXor(b7, ROTATION_5_4, b10 += b7);
+ b3 = rotlXor(b3, ROTATION_5_5, b12 += b3);
+ b5 = rotlXor(b5, ROTATION_5_6, b14 += b5);
+ b1 = rotlXor(b1, ROTATION_5_7, b8 += b1);
+
+ b7 = rotlXor(b7, ROTATION_6_0, b0 += b7);
+ b5 = rotlXor(b5, ROTATION_6_1, b2 += b5);
+ b3 = rotlXor(b3, ROTATION_6_2, b4 += b3);
+ b1 = rotlXor(b1, ROTATION_6_3, b6 += b1);
+ b15 = rotlXor(b15, ROTATION_6_4, b12 += b15);
+ b13 = rotlXor(b13, ROTATION_6_5, b14 += b13);
+ b11 = rotlXor(b11, ROTATION_6_6, b8 += b11);
+ b9 = rotlXor(b9, ROTATION_6_7, b10 += b9);
+
+ b15 = rotlXor(b15, ROTATION_7_0, b0 += b15);
+ b11 = rotlXor(b11, ROTATION_7_1, b2 += b11);
+ b13 = rotlXor(b13, ROTATION_7_2, b6 += b13);
+ b9 = rotlXor(b9, ROTATION_7_3, b4 += b9);
+ b1 = rotlXor(b1, ROTATION_7_4, b14 += b1);
+ b5 = rotlXor(b5, ROTATION_7_5, b8 += b5);
+ b3 = rotlXor(b3, ROTATION_7_6, b10 += b3);
+ b7 = rotlXor(b7, ROTATION_7_7, b12 += b7);
+
+ /*
+ * Subkey injection for next 4 rounds.
+ */
+ b0 += kw[dm17 + 1];
+ b1 += kw[dm17 + 2];
+ b2 += kw[dm17 + 3];
+ b3 += kw[dm17 + 4];
+ b4 += kw[dm17 + 5];
+ b5 += kw[dm17 + 6];
+ b6 += kw[dm17 + 7];
+ b7 += kw[dm17 + 8];
+ b8 += kw[dm17 + 9];
+ b9 += kw[dm17 + 10];
+ b10 += kw[dm17 + 11];
+ b11 += kw[dm17 + 12];
+ b12 += kw[dm17 + 13];
+ b13 += kw[dm17 + 14] + t[dm3 + 1];
+ b14 += kw[dm17 + 15] + t[dm3 + 2];
+ b15 += kw[dm17 + 16] + d + 1;
+
+ }
+
+ /*
+ * Output cipher state.
+ */
+ out[0] = b0;
+ out[1] = b1;
+ out[2] = b2;
+ out[3] = b3;
+ out[4] = b4;
+ out[5] = b5;
+ out[6] = b6;
+ out[7] = b7;
+ out[8] = b8;
+ out[9] = b9;
+ out[10] = b10;
+ out[11] = b11;
+ out[12] = b12;
+ out[13] = b13;
+ out[14] = b14;
+ out[15] = b15;
+ }
+
+ void decryptBlock(long[] block, long[] state)
+ {
+ final long[] kw = this.kw;
+ final long[] t = this.t;
+ final int[] mod17 = MOD17;
+ final int[] mod3 = MOD3;
+
+ /* Help the JIT avoid index bounds checks */
+ if (kw.length != 33)
+ {
+ throw new IllegalArgumentException();
+ }
+ if (t.length != 5)
+ {
+ throw new IllegalArgumentException();
+ }
+
+ long b0 = block[0];
+ long b1 = block[1];
+ long b2 = block[2];
+ long b3 = block[3];
+ long b4 = block[4];
+ long b5 = block[5];
+ long b6 = block[6];
+ long b7 = block[7];
+ long b8 = block[8];
+ long b9 = block[9];
+ long b10 = block[10];
+ long b11 = block[11];
+ long b12 = block[12];
+ long b13 = block[13];
+ long b14 = block[14];
+ long b15 = block[15];
+
+ for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2)
+ {
+ final int dm17 = mod17[d];
+ final int dm3 = mod3[d];
+
+ /* Reverse key injection for second 4 rounds */
+ b0 -= kw[dm17 + 1];
+ b1 -= kw[dm17 + 2];
+ b2 -= kw[dm17 + 3];
+ b3 -= kw[dm17 + 4];
+ b4 -= kw[dm17 + 5];
+ b5 -= kw[dm17 + 6];
+ b6 -= kw[dm17 + 7];
+ b7 -= kw[dm17 + 8];
+ b8 -= kw[dm17 + 9];
+ b9 -= kw[dm17 + 10];
+ b10 -= kw[dm17 + 11];
+ b11 -= kw[dm17 + 12];
+ b12 -= kw[dm17 + 13];
+ b13 -= kw[dm17 + 14] + t[dm3 + 1];
+ b14 -= kw[dm17 + 15] + t[dm3 + 2];
+ b15 -= kw[dm17 + 16] + d + 1;
+
+ /* Reverse second 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_7_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_7_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_7_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_7_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_7_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_7_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_7_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_7_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_6_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_6_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_6_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_6_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_6_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_6_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_6_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_6_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_5_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_5_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_5_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_5_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_5_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_5_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_5_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_5_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_4_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_4_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_4_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_4_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_4_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_4_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_4_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_4_7, b14);
+ b14 -= b15;
+
+ /* Reverse key injection for first 4 rounds */
+ b0 -= kw[dm17];
+ b1 -= kw[dm17 + 1];
+ b2 -= kw[dm17 + 2];
+ b3 -= kw[dm17 + 3];
+ b4 -= kw[dm17 + 4];
+ b5 -= kw[dm17 + 5];
+ b6 -= kw[dm17 + 6];
+ b7 -= kw[dm17 + 7];
+ b8 -= kw[dm17 + 8];
+ b9 -= kw[dm17 + 9];
+ b10 -= kw[dm17 + 10];
+ b11 -= kw[dm17 + 11];
+ b12 -= kw[dm17 + 12];
+ b13 -= kw[dm17 + 13] + t[dm3];
+ b14 -= kw[dm17 + 14] + t[dm3 + 1];
+ b15 -= kw[dm17 + 15] + d;
+
+ /* Reverse first 4 mix/permute rounds */
+ b15 = xorRotr(b15, ROTATION_3_0, b0);
+ b0 -= b15;
+ b11 = xorRotr(b11, ROTATION_3_1, b2);
+ b2 -= b11;
+ b13 = xorRotr(b13, ROTATION_3_2, b6);
+ b6 -= b13;
+ b9 = xorRotr(b9, ROTATION_3_3, b4);
+ b4 -= b9;
+ b1 = xorRotr(b1, ROTATION_3_4, b14);
+ b14 -= b1;
+ b5 = xorRotr(b5, ROTATION_3_5, b8);
+ b8 -= b5;
+ b3 = xorRotr(b3, ROTATION_3_6, b10);
+ b10 -= b3;
+ b7 = xorRotr(b7, ROTATION_3_7, b12);
+ b12 -= b7;
+
+ b7 = xorRotr(b7, ROTATION_2_0, b0);
+ b0 -= b7;
+ b5 = xorRotr(b5, ROTATION_2_1, b2);
+ b2 -= b5;
+ b3 = xorRotr(b3, ROTATION_2_2, b4);
+ b4 -= b3;
+ b1 = xorRotr(b1, ROTATION_2_3, b6);
+ b6 -= b1;
+ b15 = xorRotr(b15, ROTATION_2_4, b12);
+ b12 -= b15;
+ b13 = xorRotr(b13, ROTATION_2_5, b14);
+ b14 -= b13;
+ b11 = xorRotr(b11, ROTATION_2_6, b8);
+ b8 -= b11;
+ b9 = xorRotr(b9, ROTATION_2_7, b10);
+ b10 -= b9;
+
+ b9 = xorRotr(b9, ROTATION_1_0, b0);
+ b0 -= b9;
+ b13 = xorRotr(b13, ROTATION_1_1, b2);
+ b2 -= b13;
+ b11 = xorRotr(b11, ROTATION_1_2, b6);
+ b6 -= b11;
+ b15 = xorRotr(b15, ROTATION_1_3, b4);
+ b4 -= b15;
+ b7 = xorRotr(b7, ROTATION_1_4, b10);
+ b10 -= b7;
+ b3 = xorRotr(b3, ROTATION_1_5, b12);
+ b12 -= b3;
+ b5 = xorRotr(b5, ROTATION_1_6, b14);
+ b14 -= b5;
+ b1 = xorRotr(b1, ROTATION_1_7, b8);
+ b8 -= b1;
+
+ b1 = xorRotr(b1, ROTATION_0_0, b0);
+ b0 -= b1;
+ b3 = xorRotr(b3, ROTATION_0_1, b2);
+ b2 -= b3;
+ b5 = xorRotr(b5, ROTATION_0_2, b4);
+ b4 -= b5;
+ b7 = xorRotr(b7, ROTATION_0_3, b6);
+ b6 -= b7;
+ b9 = xorRotr(b9, ROTATION_0_4, b8);
+ b8 -= b9;
+ b11 = xorRotr(b11, ROTATION_0_5, b10);
+ b10 -= b11;
+ b13 = xorRotr(b13, ROTATION_0_6, b12);
+ b12 -= b13;
+ b15 = xorRotr(b15, ROTATION_0_7, b14);
+ b14 -= b15;
+ }
+
+ /*
+ * First subkey uninjection.
+ */
+ b0 -= kw[0];
+ b1 -= kw[1];
+ b2 -= kw[2];
+ b3 -= kw[3];
+ b4 -= kw[4];
+ b5 -= kw[5];
+ b6 -= kw[6];
+ b7 -= kw[7];
+ b8 -= kw[8];
+ b9 -= kw[9];
+ b10 -= kw[10];
+ b11 -= kw[11];
+ b12 -= kw[12];
+ b13 -= kw[13] + t[0];
+ b14 -= kw[14] + t[1];
+ b15 -= kw[15];
+
+ /*
+ * Output cipher state.
+ */
+ state[0] = b0;
+ state[1] = b1;
+ state[2] = b2;
+ state[3] = b3;
+ state[4] = b4;
+ state[5] = b5;
+ state[6] = b6;
+ state[7] = b7;
+ state[8] = b8;
+ state[9] = b9;
+ state[10] = b10;
+ state[11] = b11;
+ state[12] = b12;
+ state[13] = b13;
+ state[14] = b14;
+ state[15] = b15;
+ }
+
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ThreefishReferenceEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ThreefishReferenceEngine.java
new file mode 100644
index 00000000..c3f691f5
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/engines/ThreefishReferenceEngine.java
@@ -0,0 +1,378 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+
+public class ThreefishReferenceEngine implements BlockCipher
+{
+
+ /** The tweak input is always 128 bits */
+ private static final int TWEAK_SIZE = 16;
+
+ private static long C_240 = 0x1BD11BDAA9FC1A22L;
+
+ private final int blocksize = 64;
+ private final int rounds = 72;
+ private final int words = 8;
+
+ private boolean forEncryption;
+
+ private long[] block = new long[words];
+
+ private int[][] rotations = R8;
+
+ /**
+ * Rotation constants Rd,j for Nw = 8.
+ */
+ private static final int[][] R8 = {
+ { 46, 36, 19, 37 },
+ { 33, 27, 14, 42 },
+ { 17, 49, 36, 39 },
+ { 44, 9, 54, 56 },
+ { 39, 30, 34, 24 },
+ { 13, 50, 10, 17 },
+ { 25, 29, 39, 43 },
+ { 8, 35, 56, 22 } };
+
+ private long[] t;
+
+ private long kw[];
+
+ public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException
+ {
+ if (params instanceof TweakableBlockCipherParameters)
+ {
+ init(forEncryption, (TweakableBlockCipherParameters) params);
+ } else if (params instanceof KeyParameter)
+ {
+ init(forEncryption, new TweakableBlockCipherParameters((KeyParameter) params, new byte[TWEAK_SIZE]));
+ } else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Threefish init - "
+ + params.getClass().getName());
+ }
+ }
+
+ public void init(boolean forEncryption, TweakableBlockCipherParameters params) throws IllegalArgumentException
+ {
+ // TODO: Remove some of the NPEs that can be avoided in the Params
+ // classes
+ if ((params.getKey() == null) || (params.getKey().getKey() == null)
+ || (params.getKey().getKey().length != blocksize))
+ {
+ throw new IllegalArgumentException("Threefish key must be same size as block (%d bytes)" + blocksize);
+ }
+
+ if ((params.getTweak() == null) || (params.getTweak().length != TWEAK_SIZE))
+ {
+ throw new IllegalArgumentException("Threefish tweak must be %d bytes" + TWEAK_SIZE);
+ }
+
+ this.forEncryption = forEncryption;
+
+ generateKeySchedule(params.getKey().getKey(), params.getTweak());
+ }
+
+ private void generateKeySchedule(byte[] key, byte[] tweak)
+ {
+ // TODO: This key schedule can/should be generated incrementally/on demand during encrypt/decrypt
+ // to reduce memory overhead (currently 1.2MB = (rounds/4+1)=19 * words=8 * 8 bytes/word)
+
+ t = new long[3];
+ t[0] = BytesToWord(tweak, 0);
+ t[1] = BytesToWord(tweak, 8);
+ t[2] = t[0] ^ t[1];
+
+ kw = new long[words + 1];
+
+ long knw = C_240;
+ for (int i = 0; i < words; i++)
+ {
+ kw[i] = BytesToWord(key, i * 8);
+ knw = knw ^ kw[i];
+ }
+ kw[kw.length - 1] = knw;
+ }
+
+ private static long BytesToWord(byte[] bytes, int off)
+ {
+ long word = 0;
+ int index = off;
+
+ word = (bytes[index++] & 0xffL);
+ word |= (bytes[index++] & 0xffL) << 8;
+ word |= (bytes[index++] & 0xffL) << 16;
+ word |= (bytes[index++] & 0xffL) << 24;
+ word |= (bytes[index++] & 0xffL) << 32;
+ word |= (bytes[index++] & 0xffL) << 40;
+ word |= (bytes[index++] & 0xffL) << 48;
+ word |= (bytes[index++] & 0xffL) << 56;
+
+ return word;
+ }
+
+ private static void WordToBytes(long word, byte[] bytes, int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte) word;
+ bytes[index++] = (byte) (word >> 8);
+ bytes[index++] = (byte) (word >> 16);
+ bytes[index++] = (byte) (word >> 24);
+ bytes[index++] = (byte) (word >> 32);
+ bytes[index++] = (byte) (word >> 40);
+ bytes[index++] = (byte) (word >> 48);
+ bytes[index++] = (byte) (word >> 56);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Threefish";
+ }
+
+ public int getBlockSize()
+ {
+ return blocksize;
+ }
+
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException,
+ IllegalStateException
+ {
+ // TODO: Check init state
+ if (kw == null)
+ {
+ throw new IllegalStateException("Threefish engine not initialised");
+ }
+
+ if ((inOff + blocksize) > in.length)
+ {
+ throw new DataLengthException("Input buffer too short");
+ }
+
+ if ((outOff + blocksize) > out.length)
+ {
+ throw new DataLengthException("Output buffer too short");
+ }
+
+ if (forEncryption)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock();
+ packBlock(out, outOff);
+ } else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock();
+ packBlock(out, outOff);
+ }
+
+ return blocksize;
+ }
+
+ private void decryptBlock()
+ {
+ for (int d = rounds; d > 0; d--)
+ {
+ // Add subkey every 4 rounds
+ if ((d % 4) == 0)
+ {
+ uninjectSubkey(d/4);
+ }
+
+ // Permute
+ unpermute();
+
+ // Mix
+ for (int j = 0; j < words / 2; j++)
+ {
+ unmix(j, d - 1);
+ }
+ }
+
+ // Remove first subkey
+ uninjectSubkey(0);
+ }
+
+ private void injectSubkey(int s)
+ {
+ for (int i = 0; i < (words - 3); i++)
+ {
+ block[i] += kw[(s + i) % (words + 1)];
+ }
+ block[words - 3] += kw[(s + words - 3) % (words + 1)] + t[s % 3];
+ block[words - 2] += kw[(s + words - 2) % (words + 1)] + t[(s + 1) % 3];
+ block[words - 1] += kw[(s + words - 1) % (words + 1)] + s;
+ }
+
+ private void uninjectSubkey(int s)
+ {
+ for (int i = 0; i < (words - 3); i++)
+ {
+ block[i] -= kw[(s + i) % (words + 1)];
+ }
+ block[words - 3] -= kw[(s + words - 3) % (words + 1)] + t[s % 3];
+ block[words - 2] -= kw[(s + words - 2) % (words + 1)] + t[(s + 1) % 3];
+ block[words - 1] -= kw[(s + words - 1) % (words + 1)] + s;
+ }
+
+ private void encryptBlock()
+ {
+ for (int d = 0; d < rounds; d++)
+ {
+ // Add subkey every 4 rounds
+ if ((d % 4) == 0)
+ {
+ injectSubkey(d/4);
+ }
+
+ // Mix
+ for (int j = 0; j < words / 2; j++)
+ {
+ mix(j, d);
+ }
+
+ // Permute
+ permute();
+ }
+
+ // Final key addition
+ injectSubkey(rounds/4);
+ }
+
+ private void permute()
+ {
+ // Permute in place for Nw = 8
+ long f0 = block[0];
+ long f3 = block[3];
+
+ block[0] = block[2];
+ block[1] = block[1];
+ block[2] = block[4];
+ block[3] = block[7];
+ block[4] = block[6];
+ block[5] = block[5];
+ block[6] = f0;
+ block[7] = f3;
+ }
+
+ private void unpermute()
+ {
+ // TODO: Change these to tables
+ // Permute in place for Nw = 8
+ long f6 = block[6];
+ long f7 = block[7];
+
+ block[7] = block[3];
+ block[6] = block[4];
+ block[5] = block[5];
+ block[4] = block[2];
+ block[3] = f7;
+ block[2] = block[0];
+ block[1] = block[1];
+ block[0] = f6;
+ }
+
+ private void mix(int j, int d)
+ {
+ // ed,2j and ed,2j+1
+ int b0 = 2 * j;
+ int b1 = b0 + 1;
+
+ // y0 = x0 + x1
+ block[b0] = block[b0] + block[b1];
+
+ // y1 = (x1 <<< R(d mod 8,j)) xor y0
+ block[b1] = Long.rotateLeft(block[b1], rotations[d % 8][j]) ^ block[b0];
+ }
+
+ private void unmix(int j, int d)
+ {
+ // ed,2j and ed,2j+1
+ int b0 = 2 * j;
+ int b1 = b0 + 1;
+
+ // x1 = (y1 ^ y0) >>> R(d mod 8, j))
+ block[b1] = Long.rotateRight(block[b1] ^ block[b0], rotations[d % 8][j]);
+
+ // x0 = y0 - x1
+ block[b0] = block[b0] - block[b1];
+
+ }
+
+ public static void main(String[] args)
+ {
+ ThreefishReferenceEngine engine = new ThreefishReferenceEngine();
+ engine.fu();
+ }
+
+ private void fu() {
+ block[0] = 0x12;
+ block[1] = 0x34;
+ block[2] = 0x56;
+ block[3] = 0x78;
+ block[4] = 0x90;
+ block[5] = 0xAB;
+ block[6] = 0xCD;
+ block[7] = 0xEF;
+
+ for(int i = 0; i < block.length; i++) {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ mix(0, 4);
+ System.err.println("=========");
+ for(int i = 0; i < block.length; i++) {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ unmix(0, 4);
+ System.err.println("=========");
+ for(int i = 0; i < block.length; i++) {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ permute();
+ System.err.println("=========");
+ for(int i = 0; i < block.length; i++) {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ unpermute();
+ System.err.println("=========");
+ for(int i = 0; i < block.length; i++) {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ generateKeySchedule(new byte[blocksize], new byte[TWEAK_SIZE]);
+ injectSubkey(5);
+ System.err.println("=========");
+ for(int i = 0; i < block.length; i++) {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ uninjectSubkey(5);
+ System.err.println("=========");
+ for(int i = 0; i < block.length; i++) {
+ System.err.println(i + " : " + Long.toHexString(block[i]));
+ }
+ }
+
+ private void packBlock(byte[] out, int outOff)
+ {
+ for (int i = 0; i < block.length; i++)
+ {
+ WordToBytes(block[i], out, outOff + (i * 8));
+ }
+ }
+
+ private long[] unpackBlock(byte[] bytes, int index)
+ {
+ for (int i = 0; i < block.length; i++)
+ {
+ block[i] = BytesToWord(bytes, index + (i * 8));
+ }
+ return block;
+ }
+
+ public void reset()
+ {
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java b/core/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
new file mode 100644
index 00000000..6be4b7e4
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
@@ -0,0 +1,110 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.SkeinEngine;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.SkeinParameters;
+
+/**
+ * Implementation of the Skein parameterised MAC function in 256, 512 and 1024 bit block sizes,
+ * based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p>
+ *
+ * @see SkeinEngine
+ * @see SkeinParameters
+ */
+public class SkeinMac implements Mac
+{
+ /** 256 bit block size - Skein MAC-256 */
+ public static final int SKEIN_256 = SkeinEngine.SKEIN_256;
+ /** 512 bit block size - Skein MAC-512 */
+ public static final int SKEIN_512 = SkeinEngine.SKEIN_512;
+ /** 1024 bit block size - Skein MAC-1024 */
+ public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+ private SkeinEngine engine;
+
+ /**
+ * Constructs a Skein MAC with an internal state size and output size.
+ *
+ * @param stateSizeBits
+ * the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+ * {@link #SKEIN_1024}.
+ * @param digestSizeBits
+ * the output/MAC size to produce in bits, which must be an integral number of bytes.
+ */
+ public SkeinMac(int stateSizeBits, int digestSizeBits)
+ {
+ this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+ }
+
+ public SkeinMac(SkeinMac mac)
+ {
+ this.engine = new SkeinEngine(mac.engine);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Skein-MAC-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8);
+ }
+
+ /**
+ * Initialises the Skein digest with the provided parameters.<br>
+ * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function.
+ * @param params an instance of {@link SkeinParameters} or {@link KeyParameter}.
+ */
+ public void init(CipherParameters params) throws IllegalArgumentException
+ {
+ SkeinParameters skeinParameters;
+ if (params instanceof SkeinParameters)
+ {
+ skeinParameters = (SkeinParameters) params;
+ } else if (params instanceof KeyParameter)
+ {
+ skeinParameters = new SkeinParameters.Builder().setKey(((KeyParameter) params).getKey()).build();
+ } else
+ {
+ throw new IllegalArgumentException("Invalid parameter passed to Skein MAC init - "
+ + params.getClass().getName());
+ }
+ if (skeinParameters.getKey() == null)
+ {
+ throw new IllegalArgumentException("Skein MAC requires a key parameter.");
+ }
+ engine.init(skeinParameters);
+ }
+
+ public int getMacSize()
+ {
+ return engine.getOutputSize();
+ }
+
+ public void reset()
+ {
+ engine.reset();
+ }
+
+ public void update(byte in)
+ {
+ engine.update(in);
+ }
+
+ public void update(byte[] in, int inOff, int len)
+ {
+ engine.update(in, inOff, len);
+ }
+
+ public int doFinal(byte[] out, int outOff)
+ {
+ return engine.doFinal(out, outOff);
+ }
+
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
new file mode 100644
index 00000000..b1060453
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
@@ -0,0 +1,286 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.security.spec.AlgorithmParameterSpec;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.digests.SkeinDigest;
+import org.bouncycastle.crypto.digests.SkeinEngine;
+import org.bouncycastle.crypto.macs.SkeinMac;
+
+/**
+ * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
+ * <p>
+ * Parameterised Skein can be used for:
+ * <ul>
+ * <li>MAC generation, by providing a {@link SkeinParameters.Builder#setKey(byte[]) key}.</li>
+ * <li>Randomised hashing, by providing a {@link SkeinParameters.Builder#setNonce(byte[]) nonce}.</li>
+ * <li>A hash function for digital signatures, associating a
+ * {@link SkeinParameters.Builder#setPublicKey(byte[]) public key} with the message digest.</li>
+ * <li>A key derivation function, by providing a
+ * {@link SkeinParameters.Builder#setKeyIdentifier(byte[]) key identifier}.</li>
+ * <li>Personalised hashing, by providing a
+ * {@link SkeinParameters.Builder#setPersonalisation(Date, String, String) recommended format} or
+ * {@link SkeinParameters.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li>
+ * </ul>
+ *
+ * @see SkeinEngine
+ * @see SkeinDigest
+ * @see SkeinMac
+ */
+public class SkeinParameters implements CipherParameters, AlgorithmParameterSpec
+{
+ /**
+ * The parameter type for a secret key, supporting MAC or KDF functions: {@value
+ * #PARAM_TYPE_KEY}.
+ */
+ public static final int PARAM_TYPE_KEY = 0;
+
+ /**
+ * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}.
+ */
+ public static final int PARAM_TYPE_CONFIG = 4;
+
+ /**
+ * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}.
+ */
+ public static final int PARAM_TYPE_PERSONALISATION = 8;
+
+ /**
+ * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}.
+ */
+ public static final int PARAM_TYPE_PUBLIC_KEY = 12;
+
+ /**
+ * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}.
+ */
+ public static final int PARAM_TYPE_KEY_IDENTIFIER = 16;
+
+ /**
+ * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}.
+ */
+ public static final int PARAM_TYPE_NONCE = 20;
+
+ /**
+ * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}.
+ */
+ public static final int PARAM_TYPE_MESSAGE = 48;
+
+ /**
+ * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}.
+ */
+ public static final int PARAM_TYPE_OUTPUT = 63;
+
+ private Hashtable parameters;
+
+ public SkeinParameters()
+ {
+ this(new Hashtable());
+ }
+
+ private SkeinParameters(final Hashtable parameters)
+ {
+ this.parameters = parameters;
+ }
+
+ /**
+ * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object.
+ */
+ public Hashtable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or <code>null</code> if not
+ * set.
+ */
+ public byte[] getKey()
+ {
+ return (byte[])parameters.get(Integer.valueOf(PARAM_TYPE_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getPersonalisation()
+ {
+ return (byte[])parameters.get(Integer.valueOf(PARAM_TYPE_PERSONALISATION));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getPublicKey()
+ {
+ return (byte[])parameters.get(Integer.valueOf(PARAM_TYPE_PUBLIC_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getKeyIdentifier()
+ {
+ return (byte[])parameters.get(Integer.valueOf(PARAM_TYPE_KEY_IDENTIFIER));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or <code>null</code> if
+ * not set.
+ */
+ public byte[] getNonce()
+ {
+ return (byte[])parameters.get(Integer.valueOf(PARAM_TYPE_NONCE));
+ }
+
+ /**
+ * A builder for {@link SkeinParameters}.
+ */
+ public static class Builder
+ {
+ private Hashtable parameters = new Hashtable();
+
+ public Builder()
+ {
+ }
+
+ public Builder(SkeinParameters params)
+ {
+ Enumeration keys = params.parameters.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, params.parameters.get(key));
+ }
+ }
+
+ /**
+ * Sets a parameters to apply to the Skein hash function.<br>
+ * Parameter types must be in the range 0,5..62, and cannot use the value {@value
+ * SkeinParameters#PARAM_TYPE_MESSAGE} (reserved for message body).
+ * <p>
+ * Parameters with type < {@value SkeinParameters#PARAM_TYPE_MESSAGE} are processed before
+ * the message content, parameters with type > {@value SkeinParameters#PARAM_TYPE_MESSAGE}
+ * are processed after the message and prior to output.
+ *
+ * @param type
+ * the type of the parameter, in the range 5..62.
+ * @param value
+ * the byte sequence of the parameter.
+ * @return
+ */
+ public Builder set(int type, byte[] value)
+ {
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Parameter value must not be null.");
+ }
+ if ((type != PARAM_TYPE_KEY)
+ && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+ {
+ throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62.");
+ }
+ if (type == PARAM_TYPE_CONFIG)
+ {
+ throw new IllegalArgumentException("Parameter type " + PARAM_TYPE_CONFIG
+ + " is reserved for internal use.");
+ }
+ this.parameters.put(Integer.valueOf(type), value);
+ return this;
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY} parameter.
+ */
+ public Builder setKey(byte[] key)
+ {
+ return set(PARAM_TYPE_KEY, key);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_PERSONALISATION} parameter.
+ */
+ public Builder setPersonalisation(byte[] personalisation)
+ {
+ return set(PARAM_TYPE_PERSONALISATION, personalisation);
+ }
+
+ /**
+ * Implements the recommended personalisation format for Skein defined in Section 4.11 of
+ * the Skein 1.3 specification.
+ * <p>
+ * The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte
+ * sequence using UTF-8 encoding.
+ *
+ * @param date
+ * the date the personalised application of the Skein was defined.
+ * @param emailAddress
+ * the email address of the creation of the personalised application.
+ * @param distinguisher
+ * an arbitrary personalisation string distinguishing the application.
+ * @return
+ */
+ public Builder setPersonalisation(Date date, String emailAddress, String distinguisher)
+ {
+ try
+ {
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
+ final DateFormat format = new SimpleDateFormat("YYYYMMDD");
+ out.write(format.format(date));
+ out.write(" ");
+ out.write(emailAddress);
+ out.write(" ");
+ out.write(distinguisher);
+ out.close();
+ return set(PARAM_TYPE_PERSONALISATION, bout.toByteArray());
+ } catch (IOException e)
+ {
+ throw new IllegalStateException("Byte I/O failed.", e);
+ }
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setPublicKey(byte[] publicKey)
+ {
+ return set(PARAM_TYPE_PUBLIC_KEY, publicKey);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setKeyIdentifier(byte[] keyIdentifier)
+ {
+ return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_NONCE} parameter.
+ */
+ public Builder setNonce(byte[] nonce)
+ {
+ return set(PARAM_TYPE_NONCE, nonce);
+ }
+
+ /**
+ * Constructs a new {@link SkeinParameters} instance with the parameters provided to this
+ * builder.
+ */
+ public SkeinParameters build()
+ {
+ return new SkeinParameters(parameters);
+ }
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java
new file mode 100644
index 00000000..230b24bc
--- /dev/null
+++ b/core/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java
@@ -0,0 +1,36 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameters for tweakable block ciphers.
+ */
+public class TweakableBlockCipherParameters implements CipherParameters
+{
+ private final byte[] tweak;
+ private final KeyParameter key;
+
+ public TweakableBlockCipherParameters(final KeyParameter key, final byte[] tweak) {
+ this.key = key;
+ this.tweak = Arrays.clone(tweak);
+ }
+
+ /**
+ * Gets the key.
+ * @return the key to use, or <code>null</code> to use the current key.
+ */
+ public KeyParameter getKey()
+ {
+ return key;
+ }
+
+ /**
+ * Gets the tweak value.
+ * @return the tweak to use, or <code>null</code> to use the current tweak.
+ */
+ public byte[] getTweak()
+ {
+ return tweak;
+ }
+}
diff --git a/core/src/main/java/org/bouncycastle/util/Arrays.java b/core/src/main/java/org/bouncycastle/util/Arrays.java
index 457320eb..4c11cad6 100644
--- a/core/src/main/java/org/bouncycastle/util/Arrays.java
+++ b/core/src/main/java/org/bouncycastle/util/Arrays.java
@@ -423,6 +423,20 @@ public final class Arrays
return copy;
}
+ public static byte[] clone(byte[] data, byte[] existing)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ if ((existing == null) || (existing.length != data.length))
+ {
+ return clone(data);
+ }
+ System.arraycopy(data, 0, existing, 0, existing.length);
+ return existing;
+ }
+
public static byte[][] clone(byte[][] data)
{
if (data == null)
@@ -470,6 +484,33 @@ public final class Arrays
return copy;
}
+ public static long[] clone(long[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ long[] copy = new long[data.length];
+
+ System.arraycopy(data, 0, copy, 0, data.length);
+
+ return copy;
+ }
+
+ public static long[] clone(long[] data, long[] existing)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+ if ((existing == null) || (existing.length != data.length))
+ {
+ return clone(data);
+ }
+ System.arraycopy(data, 0, existing, 0, existing.length);
+ return existing;
+ }
+
public static short[] clone(short[] data)
{
if (data == null)
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java
index e1dfa829..407a0440 100644
--- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java
+++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java
@@ -43,7 +43,7 @@ public abstract class CipherTest
//
// state tests
//
- byte[] buf = new byte[16];
+ byte[] buf = new byte[128];
try
{
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java
index 7f3732d6..c646e197 100644
--- a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java
+++ b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java
@@ -38,6 +38,11 @@ public class RegressionTest
new SkipjackTest(),
new BlowfishTest(),
new TwofishTest(),
+ new Threefish256Test(),
+ new Threefish512Test(),
+ new Threefish1024Test(),
+ new SkeinDigestTest(),
+ new SkeinMacTest(),
new CAST5Test(),
new CAST6Test(),
new GOST28147Test(),
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SkeinDigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SkeinDigestTest.java
new file mode 100644
index 00000000..454d2243
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/test/SkeinDigestTest.java
@@ -0,0 +1,291 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.SkeinDigest;
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SkeinDigestTest extends SimpleTest
+{
+ private static class Case
+ {
+ private byte[] message;
+ private byte[] digest;
+ private int blockSize;
+ private int outputSize;
+
+ public Case(int blockSize, int outputSize, String message, String digest)
+ {
+ this.blockSize = blockSize;
+ this.outputSize = outputSize;
+ this.message = Hex.decode(message);
+ this.digest = Hex.decode(digest);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSize;
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ }
+
+ // Test cases from skein_golden_kat.txt and skein_golden_kat_short.txt in Skein 1.3 NIST CD
+ private static final Case[] TEST_CASES = {
+ new Case(256, 256, "", "c8877087da56e072870daa843f176e9453115929094c3a40c463a196c29bf7ba"),
+ new Case(256, 256, "fb", "088eb23cc2bccfb8171aa64e966d4af937325167dfcd170700ffd21f8a4cbdac"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "5c3002ff57a627089ea2f97a5000d5678416389019e80e45a3bbcab118315d26"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "640c894a4bba6574c83e920ddf7dd2982fc634881bbbcb9d774eae0a285e89ce"),
+ new Case(256, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "0cd491b7715704c3a15a45a1ca8d93f8f646d3a1"),
+ new Case(256, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "afd1e2d0f5b6cd4e1f8b3935fa2497d27ee97e72060adac099543487"),
+ new Case(256, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "4de6fe2bfdaa3717a4261030ef0e044ced9225d066354610842a24a3eafd1dcf"),
+ new Case(256, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "954620fb31e8b782a2794c6542827026fe069d715df04261629fcbe81d7d529b"
+ + "95ba021fa4239fb00afaa75f5fd8e78b"),
+ new Case(256, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "51347e27c7eabba514959f899a6715ef6ad5cf01c23170590e6a8af399470bf9"
+ + "0ea7409960a708c1dbaa90e86389df254abc763639bb8cdf7fb663b29d9557c3"),
+ new Case(256, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "6c9b6facbaf116b538aa655e0be0168084aa9f1be445f7e06714585e5999a6c9"
+ + "84fffa9d41a316028692d4aad18f573fbf27cf78e84de26da1928382b023987d"
+ + "cfe002b6201ea33713c54a8a5d9eb346f0365e04330d2faaf7bc8aba92a5d7fb"
+ + "6345c6fb26750bce65ab2045c233627679ac6e9acb33602e26fe3526063ecc8b"),
+
+ new Case(512, 512, "", "bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af4"
+ + "1fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a"),
+ new Case(512, 512, "fb", "c49e03d50b4b2cc46bd3b7ef7014c8a45b016399fd1714467b7596c86de98240"
+ + "e35bf7f9772b7d65465cd4cffab14e6bc154c54fc67b8bc340abf08eff572b9e"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "abefb179d52f68f86941acbbe014cc67ec66ad78b7ba9508eb1400ee2cbdb06f"
+ + "9fe7c2a260a0272d0d80e8ef5e8737c0c6a5f1c02ceb00fb2746f664b85fcef5"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "5c5b7956f9d973c0989aa40a71aa9c48a65af2757590e9a758343c7e23ea2df4"
+ + "057ce0b49f9514987feff97f648e1dd065926e2c371a0211ca977c213f14149f"),
+ new Case(512, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "ef03079d61b57c6047e15fa2b35b46fa24279539"),
+ new Case(512, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "d9e3219b214e15246a2038f76a573e018ef69b385b3bd0576b558231"),
+ new Case(512, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "809dd3f763a11af90912bbb92bc0d94361cbadab10142992000c88b4ceb88648"),
+ new Case(512, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "825f5cbd5da8807a7b4d3e7bd9cd089ca3a256bcc064cd73a9355bf3ae67f2bf"
+ + "93ac7074b3b19907a0665ba3a878b262"),
+ new Case(512, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "1a0d5abf4432e7c612d658f8dcfa35b0d1ab68b8d6bd4dd115c23cc57b5c5bcd"
+ + "de9bff0ece4208596e499f211bc07594d0cb6f3c12b0e110174b2a9b4b2cb6a9"),
+
+ new Case(1024, 1024, "", "0fff9563bb3279289227ac77d319b6fff8d7e9f09da1247b72a0a265cd6d2a62"
+ + "645ad547ed8193db48cff847c06494a03f55666d3b47eb4c20456c9373c86297"
+ + "d630d5578ebd34cb40991578f9f52b18003efa35d3da6553ff35db91b81ab890"
+ + "bec1b189b7f52cb2a783ebb7d823d725b0b4a71f6824e88f68f982eefc6d19c6"),
+ new Case(1024, 1024, "fb", "6426bdc57b2771a6ef1b0dd39f8096a9a07554565743ac3de851d28258fcff22"
+ + "9993e11c4e6bebc8b6ecb0ad1b140276081aa390ec3875960336119427827473"
+ + "4770671b79f076771e2cfdaaf5adc9b10cbae43d8e6cd2b1c1f5d6c82dc96618"
+ + "00ddc476f25865b8748253173187d81da971c027d91d32fb390301c2110d2db2"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8",
+ "140e93726ab0b0467c0b8a834ad8cda4d1769d273661902b70db0dcb5ee692ac"
+ + "b3f852d03b11f857850f2428432811309c1dcbe5724f00267ea3667e89fadb4e"
+ + "4911da6b0ba8a7eddf87c1c67152ef0f07b7fead3557318478bdef5ad1e5926d"
+ + "7071fdd4bfa5076d4b3253f8de479ebdf5357676f1641b2f097e9b785e9e528e"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a129233",
+ "31105e1ef042c30b95b16e0f6e6a1a19172bb7d54a0597dd0c711194888efe1d"
+ + "bce82d47416df9577ca387219f06e45cd10964ff36f6711edbbea0e9595b0f66"
+ + "f72b755d70a46857e0aec98561a743d49370d8e572e212811273125f66cc30bf"
+ + "117d3221894c48012bf6e2219de91e064b01523517420a1e00f71c4cc04bab62"),
+ new Case(1024, 160, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "2e6a4cbf2ef05ea9c24b93e8d1de732ddf2739eb"),
+ new Case(1024, 224, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "1d6de19f37f7a3c265440eecb4b9fbd3300bb5ac60895cfc0d4d3c72"),
+ new Case(1024, 256, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "986a4d472b123e8148731a8eac9db23325f0058c4ccbc44a5bb6fe3a8db672d7"),
+ new Case(1024, 384, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "9c3d0648c11f31c18395d5e6c8ebd73f43d189843fc45235e2c35e345e12d62b"
+ + "c21a41f65896ddc6a04969654c2e2ce9"),
+ new Case(1024, 512, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "5d0416f49c2d08dfd40a1446169dc6a1d516e23b8b853be4933513051de8d5c2"
+ + "6baccffb08d3b16516ba3c6ccf3e9a6c78fff6ef955f2dbc56e1459a7cdba9a5"),
+ new Case(1024, 1024, "fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410",
+ "96ca81f586c825d0360aef5acaec49ad55289e1797072eee198b64f349ce65b6"
+ + "e6ed804fe38f05135fe769cc56240ddda5098f620865ce4a4278c77fa2ec6bc3"
+ + "1c0f354ca78c7ca81665bfcc5dc54258c3b8310ed421d9157f36c093814d9b25"
+ + "103d83e0ddd89c52d0050e13a64c6140e6388431961685734b1f138fe2243086"),
+
+ };
+
+ public String getName()
+ {
+ return "SkeinDigest";
+ }
+
+ public void performTest() throws Exception
+ {
+ runTest(TEST_CASES[7]);
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ Case test = TEST_CASES[i];
+ runTest(test);
+ }
+ }
+
+ private void runTest(Case dc)
+ {
+ SkeinDigest digest = new SkeinDigest(dc.getBlockSize(), dc.getOutputSize());
+
+ byte[] message = dc.getMessage();
+ digest.update(message, 0, message.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ if (!MessageDigest.isEqual(output, dc.getDigest()))
+ {
+ fail(digest.getAlgorithmName() + " message mismatch.\n Message " + new String(Hex.encode(dc.getMessage())),
+ new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ // Clone test
+ digest.update(message, 0, message.length / 2);
+
+ // clone the Digest
+ Digest d = new SkeinDigest(digest);
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing clone vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ d.update(message, message.length / 2, message.length - message.length / 2);
+ d.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing second clone vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ //
+ // memo test
+ //
+ Memoable m = (Memoable)digest;
+
+ digest.update(message, 0, message.length / 2);
+
+ // copy the Digest
+ Memoable copy1 = m.copy();
+ Memoable copy2 = copy1.copy();
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ m.reset(copy1);
+
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ digest.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo reset vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+
+ Digest md = (Digest)copy2;
+
+ md.update(message, message.length / 2, message.length - message.length / 2);
+ md.doFinal(output, 0);
+
+ if (!areEqual(dc.getDigest(), output))
+ {
+ fail("failing memo copy vector test", new String(Hex.encode(dc.getDigest())), new String(Hex.encode(output)));
+ }
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ // generateTests();
+ runTest(new SkeinDigestTest());
+ }
+
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SkeinMacTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SkeinMacTest.java
new file mode 100644
index 00000000..c40b8d68
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/test/SkeinMacTest.java
@@ -0,0 +1,159 @@
+package org.bouncycastle.crypto.test;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SkeinMacTest extends SimpleTest
+{
+ private static class Case
+ {
+ private byte[] message;
+ private byte[] digest;
+ private byte[] key;
+ private int blockSize;
+ private int outputSize;
+
+ public Case(int blockSize, int outputSize, String message, String key, String digest)
+ {
+ this.blockSize = blockSize;
+ this.outputSize = outputSize;
+ this.message = Hex.decode(message);
+ this.key = Hex.decode(key);
+ this.digest = Hex.decode(digest);
+ }
+
+ public int getOutputSize()
+ {
+ return outputSize;
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public byte[] getMessage()
+ {
+ return message;
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ public String toString()
+ {
+ return String.format("new Case(%d, %d, \"%s\", \"%s\", \"%s\"),", blockSize, outputSize,
+ new String(Hex.encode(message)), new String(Hex.encode(key)), new String(Hex.encode(digest)));
+ }
+
+ }
+
+ // Test cases from skein_golden_kat.txt in Skein 1.3 NIST CD
+ // Excludes empty '(none)' key 'random+MAC' tests, which are in effect digest
+ private static final Case[] TEST_CASES = {
+ new Case(256, 256, "", "cb41f1706cde09651203c2d0efbaddf8", "886e4efefc15f06aa298963971d7a25398fffe5681c84db39bd00851f64ae29d"),
+ new Case(256, 256, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "979422a94e3afaa46664124d4e5e8b9422b1d8baf11c6ae6725992ac72a112ca"),
+ new Case(256, 256, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "1d658372cbea2f9928493cc47599d6f4ad8ce33536bedfa20b739f07516519d5"),
+ new Case(256, 256, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "41ef6b0f0fad81c040284f3b1a91e9c44e4c26a6d7207f3aac4362856ef12aca"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "ca8208119b9e4e4057631ab31015cfd256f6763a0a34381633d97f640899b84f"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "9e9980fcc16ee082cf164a5147d0e0692aeffe3dcb8d620e2bb542091162e2e9"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "c353a316558ec34f8245dd2f9c2c4961fbc7decc3b69053c103e4b8aaaf20394"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf8", "b1b8c18188e69a6ecae0b6018e6b638c6a91e6de6881e32a60858468c17b520d"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "1dfd2515a412e78852cd81a7f2167711b4ca19b2891c2ea36ba94f8451944793"),
+ new Case(256, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "a097340709b443ed2c0a921f5dcefef3ead65c4f0bcd5f13da54d7ed"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "ac1b4fab6561c92d0c487e082daec53e0db4f505e08bf51cae4fd5375e37fc04"),
+ new Case(256, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "96e6cebb23573d0a70ce36a67aa05d2403148093f25c695e1254887cc97f9771d2518413af4286bf2a06b61a53f7fcec"),
+ new Case(256, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "0e95e597e71d6350f20b99c4179f54f43a4722705c06ba765a82cb0a314fe2fe87ef8090063b757e53182706ed18737dadc0da1e1c66518f08334052702c5ed7"),
+ new Case(256, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf8", "064abd4896f460b1953f5a357e7f7c5256e29cdb62b8740d0b52295cfa2ef4c7a2"),
+ new Case(256, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "edf220e43e048603bd16197d59b673b9974de5b8bcf7cb1558a4799f6fd3743eb5fb400cd6129afc0c60e7b741b7e5806f0e0b93eb8429fbc7efa222175a9c80fd"),
+ new Case(256, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e92", "f3f59fb07399c7b73aae02a8590883cb2fdfde75c55654e71846522301bde48d267169adcc559e038e8c2f28faa552b550d51874055384adea93c036c71a1f0af0c7bcc3bc923738d5307b9da7cb423d4e615c629c4aba71f70d4c9d1fa008176825e51bfa0203445a4083947ec19f6a0fbd082b5b970f2396fb67420639410447"),
+ new Case(256, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "80eb80d9b8836b32fa576fc84ba08edfbdfd6979123d61914e610a70a372b37f560a10909484f9f4a377c93e29ba681dfe522c41dc83b5ee0567e5370007c7bbe4df0b2b4a25e088f80d72fc30734cdcd76d817b42fbd44dca881019afb25306f19d4e91848778af306517d2072cef72caa327e877c5b6554f83cec3d00877131b47c4d3b557f5a13541c4d5080ee3ce7a658993d083efd0db3496a8752060c3c8552f44b290cabdcc867f691ad605836c08dbd59c9528d885b600b85fdfc8a9d0e636ac3ad8b4295bcb0169e78dc358e77eacc8c4b61bddfa9e5f32d2268a006cfe05c57150fe8e68cabd21cf6cf6035aa1fe4db36c922b765aad0b64e82a2c37"),
+ new Case(256, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "8f88de68f03cd2f396ccdd49c3a0f4ff15bcda7eb357da9753f6116b124de91d"),
+ new Case(512, 512, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "9bd43d2a2fcfa92becb9f69faab3936978f1b865b7e44338fc9c8f16aba949ba340291082834a1fc5aa81649e13d50cd98641a1d0883062bfe2c16d1faa7e3aa"),
+ new Case(512, 512, "d3", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "f0c0a10f031c8fc69cfabcd54154c318b5d6cd95d06b12cf20264402492211ee010d5cecc2dc37fd772afac0596b2bf71e6020ef2dee7c860628b6e643ed9ff6"),
+ new Case(512, 512, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "0c1f1921253dd8e5c2d4c5f4099f851042d91147892705829161f5fc64d89785226eb6e187068493ee4c78a4b7c0f55a8cbbb1a5982c2daf638fc6a74b16b0d7"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "478d7b6c0cc6e35d9ebbdedf39128e5a36585db6222891692d1747d401de34ce3db6fcbab6c968b7f2620f4a844a2903b547775579993736d2493a75ff6752a1"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "13c170bac1de35e5fb843f65fabecf214a54a6e0458a4ff6ea5df91915468f4efcd371effa8965a9e82c5388d84730490dcf3976af157b8baf550655a5a6ab78"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc235", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "a947812529a72fd3b8967ec391b298bee891babc8487a1ec4ea3d88f6b2b5be09ac6a780f30f8e8c3bbb4f18bc302a28f3e87d170ba0f858a8fefe3487478cca"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "7690ba61f10e0bba312980b0212e6a9a51b0e9aadfde7ca535754a706e042335b29172aae29d8bad18efaf92d43e6406f3098e253f41f2931eda5911dc740352"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "d10e3ba81855ac087fbf5a3bc1f99b27d05f98ba22441138026225d34a418b93fd9e8dfaf5120757451adabe050d0eb59d271b0fe1bbf04badbcf9ba25a8791b"),
+ new Case(512, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "5670b226156570dff3efe16661ab86eb24982cdf"),
+ new Case(512, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "c41b9ff9753e6c0f8ed88866e320535e927fe4da552c289841a920db"),
+ new Case(512, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "dfbf5c1319a1d9d70efb2f1600fbcf694f935907f31d24a16d6cd2fb2d7855a769681766c0a29da778eed346cd1d740f"),
+ new Case(512, 512, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "04d8cddb0ad931d54d195899a094684344e902286037272890bce98a41813edc37a3cee190a693fcca613ee30049ce7ec2bdff9613f56778a13f8c28a21d167a"),
+ new Case(512, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c193", "08fca368b3b14ac406676adf37ac9be2dbb8704e694055a0c6331184d4f0070098f23f0963ee29002495771bf56fb4d3d9ff3506abcd80be927379f7880d5d7703919fbf92184f498ac44f47f015ce676eded9165d47d53733f5a27abbc05f45acd98b97cc15ffdced641defd1a5119ef841b452a1b8f94ee69004466ccdc143"),
+ new Case(512, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "669e770ebe7eacc2b64caaf049923ad297a5b37cfa61c283392d81ccfcb9bbbc09"),
+ new Case(512, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e", "acc2e03f07f33e9820a6038421089429adcd6a7a83f733beec048c05bf37531a170a5537fcb565c348a70a83217f8be768ff6f95fd2b3d89cb7d8a3dc849505e3710eb4e65a8e7134bbf580d92fe18c9aa987563669b1f014aa5e092519089355534eaa9f0bdc99f6839f54080ffe74623254c906ecb8896b4346c3178a0bc2898"),
+ new Case(512, 2056, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "9f3e082223c43090a4a3ffbdcd469cbabfe0c1399d1edf45a5dfc18f4db5428928a76e979b8d0d5dffec0e6a59ada448c1ffbc06cc80a2006f002adc0c6dbf458563762228dce4381944e460ebebfe06f1237093634625107469a22a189a47f8b025899265d8890a1b39df64552394377e88ba2ad44a8c8d174f884ac8c3ae24ddb0affca5fceb6aa76e09706881e8371774b9b050a69b96ef5e97e81043f8b7e9479e287ab441bacd62caf768a82c8c3e3107be70eb8799a39856fe29842a04e25de0ef9de1b7e65bd0f1f7306835287fc957388e2035b7d22d3aa9c06a9fefbca16f3f60e1c4def89038d918942152a069aa2e0be8ae7475d859031adec84583"),
+ new Case(1024, 1024, "", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "bcf37b3459c88959d6b6b58b2bfe142cef60c6f4ec56b0702480d7893a2b0595aa354e87102a788b61996b9cbc1eade7dafbf6581135572c09666d844c90f066b800fc4f5fd1737644894ef7d588afc5c38f5d920bdbd3b738aea3a3267d161ed65284d1f57da73b68817e17e381ca169115152b869c66b812bb9a84275303f0"),
+ new Case(1024, 1024, "d3090c72", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "df0596e5808835a3e304aa27923db05f61dac57c0696a1d19abf188e70aa9dbcc659e9510f7c9a37fbc025bd4e5ea293e78ed7838dd0b08864e8ad40ddb3a88031ebefc21572a89960d1916107a7da7ac0c067e34ec46a86a29ca63fa250bd398eb32ec1ed0f8ac8329f26da018b029e41e2e58d1dfc44de81615e6c987ed9c9"),
+ new Case(1024, 1024, "d3090c72167517f7", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "3cfbb79cd88af8ee09c7670bcbab6907a31f80fa31d9d7c9d50826c9568f307a78bd254961398c76b6e338fd9ca5f351059350d30963c3320659b223b991fc46d1307686fe2b4763d9f593c57ad5adbc45caf2ea3dc6090f5a74fa5fa6d9e9838964ea0a2aa216831ab069b00629a1a9b037083403bdb25d3d06a21c430c87dd"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e59", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "0a1b960099fc9d653b0fd1f5b6b972fb366907b772cbce5a59b6171d7935506f70c212bd169d68c5cfd8618343611b7eb2e686ff1dc7c03a57e1a55ed10726848161eea903d53b58459be42d95df989c66c2eea4e51cde272c2d8be67bf3bca2aee633777eb8486781eaa060d0f538abd6c93dbd2d1bf66e6f50bfdcac3725a4"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "3e0cd7938d71c39ffbb08a6ba7995ade3ad140e2c0c45cdbafb099247e08e4c20b61c1f885ced5ed2f816680925034918236e5807f0eecf3f27e9cfca36675eb75873efa1fb41f17541dc2f7c2469eaecb35cc7ca58e489804caf56f09fb97c9f689c64ad49c6888f86c483e901bd3d25798b394ef93faf9154900f92f31f433"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdf", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "7266752f7e9aa04bd7d8a1b16030677de6021301f6a62473c76bae2b98bbf8aad73bd00a4b5035f741caf2317ab80e4e97f5c5bbe8acc0e8b424bcb13c7c6740a985801fba54addde8d4f13f69d2bfc98ae104d46a211145217e51d510ea846cec9581d14fda079f775c8b18d66cb31bf7060996ee8a69eee7f107909ce59a97"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "71f40bf2aa635125ef83c8df0d4e9ea18b73b56be4f45e89b910a7c68d396b65b09d18abc7d1b6de3f53fd5de583e6f22e612dd17b292068af6027daaf8b4cd60acf5bc85044741e9f7a1f423f5827f5e360930a2e71912239af9fc6343604fdcf3f3569854f2bb8d25a81e3b3f5261a02fe8292aaaa50c324101ab2c7a2f349"),
+ new Case(1024, 160, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "17c3c533b27d666da556ae586e641b7a3a0bcc45"),
+ new Case(1024, 224, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "6625df9801581009125ea4e5c94ad6f1a2d692c278822ccb6eb67235"),
+ new Case(1024, 256, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "6c5b671c1766f6eecea6d24b641d4a6bf84bba13a1976f8f80b3f30ee2f93de6"),
+ new Case(1024, 384, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "98af454d7fa3706dfaafbf58c3f9944868b57f68f493987347a69fce19865febba0407a16b4e82065035651f0b1e0327"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1", "211ac479e9961141da3aac19d320a1dbbbfad55d2dce87e6a345fcd58e36827597378432b482d89bad44dddb13e6ad86e0ee1e0882b4eb0cd6a181e9685e18dd302ebb3aa74502c06254dcadfb2bd45d288f82366b7afc3bc0f6b1a3c2e8f84d37fbedd07a3f8fcff84faf24c53c11da600aaa118e76cfdcb366d0b3f7729dce"),
+ new Case(1024, 264, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc81463134", "dc1d253b7cadbdaef18503b1809a7f1d4f8c323b7f6f8ca50b76d3864649ce1c7d"),
+ new Case(1024, 520, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "decd79578d12bf6806530c382230a2c7836429c70cac941179e1dd982938bab91fb6f3638df1cc1ef615ecfc4249e5aca8a73c4c1eebef662a836d0be903b00146"),
+ new Case(1024, 1032, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c042eb4187aa1c015a4767032c0bb28f076b66485f51531c12e948f47dbc2cb904a4b75d1e8a6d931dab4a07e0a54d1bb5b55e602141746bd09fb15e8f01a8d74e9e63959cb37336bc1b896ec78da734c15e362db04368fbba280f20a043e0d0941e9f5193e1b360a33c43b266524880125222e648f05f28be34ba3cabfc9c544", "440fe691e04f1fed8c253d6c4670646156f33fffaea702de9445df5739eb960cecf85d56e2e6860a610211a5c909932ab774b978aa0b0d5bbce82775172ab12dceddd51d1eb030057ce61bea6c18f6bb368d26ae76a9e44a962eb132e6c42c25d9fecc4f13348300ca55c78e0990de96c1ae24eb3ee3324782c93dd628260a2c8d"),
+ new Case(1024, 1024, "d3090c72167517f7c7ad82a70c2fd3f6443f608301591e598eadb195e8357135ba26fede2ee187417f816048d00fc23512737a2113709a77e4170c49a94b7fdff45ff579a72287743102e7766c35ca5abc5dfe2f63a1e726ce5fbd2926db03a2dd18b03fc1508a9aac45eb362440203a323e09edee6324ee2e37b4432c1867ed696e6c9db1e6abea026288954a9c2d5758d7c5db7c9e48aa3d21cae3d977a7c3926066aa393dbd538dd0c30da8916c8757f24c18488014668a2627163a37b261833dc2f8c3c56b1b2e0be21fd3fbdb507b2950b77a6cc02efb393e57419383a920767bca2c972107aa61384542d47cbfb82cfe5c415389d1b0a2d74e2c5da851", "cb41f1706cde09651203c2d0efbaddf847a0d315cb2e53ff8bac41da0002672e920244c66e02d5f0dad3e94c42bb65f0d14157decf4105ef5609d5b0984457c1935df3061ff06e9f204192ba11e5bb2cac0430c1c370cb3d113fea5ec1021eb875e5946d7a96ac69a1626c6206b7252736f24253c9ee9b85eb852dfc814631346c", "46a42b0d7b8679f8fcea156c072cf9833c468a7d59ac5e5d326957d60dfe1cdfb27eb54c760b9e049fda47f0b847ac68d6b340c02c39d4a18c1bdfece3f405fae8aa848bdbefe3a4c277a095e921228618d3be8bd1999a071682810de748440ad416a97742cc9e8a9b85455b1d76472cf562f525116698d5cd0a35ddf86e7f8a"),
+
+ };
+
+ public String getName()
+ {
+ return "SkeinMac";
+ }
+
+ public void performTest() throws Exception
+ {
+ for (int i = 0; i < TEST_CASES.length; i++)
+ {
+ Case test = TEST_CASES[i];
+ runTest(test);
+ }
+ }
+
+ private void runTest(Case dc)
+ {
+ Mac digest = new SkeinMac(dc.getBlockSize(), dc.getOutputSize());
+ digest.init(new KeyParameter(dc.getKey()));
+
+ byte[] message = dc.getMessage();
+ digest.update(message, 0, message.length);
+
+ byte[] output = new byte[digest.getMacSize()];
+ digest.doFinal(output, 0);
+
+ if (!MessageDigest.isEqual(output, dc.getDigest()))
+ {
+ fail(digest.getAlgorithmName() + " message " + (dc.getMessage().length * 8) + " mismatch.\n Message " + new String(Hex.encode(dc.getMessage()))
+ + "\n Key " + new String(Hex.encode(dc.getKey())) + "\n Expected "
+ + new String(Hex.encode(dc.getDigest())) + "\n Actual " + new String(Hex.encode(output)));
+ }
+
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ runTest(new SkeinMacTest());
+ }
+
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Threefish1024Test.java b/core/src/test/java/org/bouncycastle/crypto/test/Threefish1024Test.java
new file mode 100644
index 00000000..6425b0fc
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/test/Threefish1024Test.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Threefish1024Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[128]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "f05c3d0a3d05b304f785ddc7d1e036015c8aa76e2f217b06c6e1544c0bc1a90d" +
+ "f0accb9473c24e0fd54fea68057f43329cb454761d6df5cf7b2e9b3614fbd5a2" +
+ "0b2e4760b40603540d82eabc5482c171c832afbe68406bc39500367a592943fa" +
+ "9a5b4a43286ca3c4cf46104b443143d560a4b230488311df4feef7e1dfe8391e"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f" +
+ "303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f" +
+ "505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f" +
+ "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0" +
+ "dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0" +
+ "bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a0" +
+ "9f9e9d9c9b9a999897969594939291908f8e8d8c8b8a89888786858483828180",
+ "a6654ddbd73cc3b05dd777105aa849bce49372eaaffc5568d254771bab85531c" +
+ "94f780e7ffaae430d5d8af8c70eebbe1760f3b42b737a89cb363490d670314bd" +
+ "8aa41ee63c2e1f45fbd477922f8360b388d6125ea6c7af0ad7056d01796e90c8" +
+ "3313f4150a5716b30ed5f569288ae974ce2b4347926fce57de44512177dd7cde")
+ };
+
+ Threefish1024Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024), new KeyParameter(new byte[128]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-1024";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish1024Test());
+ }
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Threefish256Test.java b/core/src/test/java/org/bouncycastle/crypto/test/Threefish256Test.java
new file mode 100644
index 00000000..a462a913
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/test/Threefish256Test.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Threefish256Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[32]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "84da2a1f8beaee947066ae3e3103f1ad536db1f4a1192495116b9f3ce6133fd8"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0",
+ "e0d091ff0eea8fdfc98192e62ed80ad59d865d08588df476657056b5955e97df")
+ };
+
+ Threefish256Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256), new KeyParameter(new byte[32]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-256";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish256Test());
+ }
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Threefish512Test.java b/core/src/test/java/org/bouncycastle/crypto/test/Threefish512Test.java
new file mode 100644
index 00000000..4cdc40f2
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/test/Threefish512Test.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.crypto.test;
+
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class Threefish512Test
+ extends CipherTest
+{
+ // Test cases from skein_golden_kat_internals.txt in Skein 1.3 NIST CD
+ static SimpleTest[] tests =
+ {
+ new BlockCipherVectorTest(0, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(new byte[64]),
+ new byte[16]),
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b" +
+ "7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe"),
+ new BlockCipherVectorTest(1, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
+ new TweakableBlockCipherParameters(
+ new KeyParameter(Hex.decode(
+ "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f" +
+ "303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f")),
+ Hex.decode("000102030405060708090a0b0c0d0e0f")),
+ "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0" +
+ "dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0",
+ "e304439626d45a2cb401cad8d636249a6338330eb06d45dd8b36b90e97254779" +
+ "272a0a8d99463504784420ea18c9a725af11dffea10162348927673d5c1caf3d")
+ };
+
+ Threefish512Test()
+ {
+ super(tests, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), new KeyParameter(new byte[64]));
+ }
+
+ public String getName()
+ {
+ return "Threefish-512";
+ }
+
+ public static void main(
+ String[] args)
+ {
+ runTest(new Threefish512Test());
+ }
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/test/speedy/ThroughputTest.java b/core/src/test/java/org/bouncycastle/crypto/test/speedy/ThroughputTest.java
new file mode 100644
index 00000000..3a765b15
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/test/speedy/ThroughputTest.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.crypto.test.speedy;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.engines.ThreefishReferenceEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+import org.bouncycastle.util.encoders.Hex;
+
+public class ThroughputTest
+{
+
+ private static final int DATA_SIZE = 100 * 1024 * 1024;
+ private static final int RUNS = 1;
+ private static final long CLOCK_SPEED = 2400000000L;
+
+ private static SecureRandom rand= new SecureRandom();
+
+ public static void main(String[] args) throws InterruptedException, IOException
+ {
+// testTF_1024_1();
+// testTF_1024_2();
+ testTF_512_1();
+ testTF_512_2();
+// testTF_256_1();
+// testTF_256_2();
+ System.out.println("Initialising test data.");
+ byte[] input = new byte[DATA_SIZE];
+ rand.nextBytes(input);
+
+ System.out.println("Init complete.");
+// speedTestCipher(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256), input);
+ speedTestCipher(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), input);
+// speedTestCipher(new Skein3FishEngine(), input);
+// speedTestCipher(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024), input);
+// speedTestCipher(new ThreefishReferenceEngine(), input);
+ speedTestCipher(new AESFastEngine(), input);
+// speedTestCipher(new TwofishEngine(), input);
+// speedTestCipher(new BlowfishEngine(), input);
+ }
+
+ private static void testTF_512_1() throws IOException {
+ byte[] key = new byte[64];
+ byte[] tweak = new byte[16];
+ byte[] plaintext = new byte[64];
+ byte[] expected = Hex.decode("b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe");
+
+ runTestVector("Threefish-512-1: Fast", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512));
+ runTestVector("Threefish-512-1: Reference", key, tweak, plaintext, expected, new ThreefishReferenceEngine());
+ }
+
+ private static void testTF_256_1() throws IOException {
+ byte[] key = new byte[32];
+ byte[] tweak = new byte[16];
+ byte[] plaintext = new byte[32];
+ byte[] expected = Hex.decode("84da2a1f8beaee947066ae3e3103f1ad536db1f4a1192495116b9f3ce6133fd8");
+
+ runTestVector("Threefish-256-1: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256));
+ }
+
+ private static void testTF_1024_1() throws IOException {
+ byte[] key = new byte[128];
+ byte[] tweak = new byte[16];
+ byte[] plaintext = new byte[128];
+ byte[] expected = Hex.decode("f05c3d0a3d05b304f785ddc7d1e036015c8aa76e2f217b06c6e1544c0bc1a90df0accb9473c24e0fd54fea68057f43329cb454761d6df5cf7b2e9b3614fbd5a20b2e4760b40603540d82eabc5482c171c832afbe68406bc39500367a592943fa9a5b4a43286ca3c4cf46104b443143d560a4b230488311df4feef7e1dfe8391e");
+
+ runTestVector("Threefish-1024-1: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024));
+ }
+
+ private static void runTestVector(String name, byte[] key, byte[] tweak, byte[] plaintext, byte[] expected, BlockCipher cipher)
+ {
+ System.out.println("====");
+ System.out.println(name + ": ");
+ cipher.init(true, new TweakableBlockCipherParameters(new KeyParameter(key), tweak));
+
+ byte[] ciphertext = new byte[key.length];
+ cipher.processBlock(plaintext, 0, ciphertext, 0);
+
+ System.out.println("Plaintext : " + new String(Hex.encode(plaintext)));
+ System.out.println("Expected : " + new String(Hex.encode(expected)));
+ System.out.println("Ciphertext : " + new String(Hex.encode(ciphertext)));
+ System.out.println(" Encrypt : " + org.bouncycastle.util.Arrays.areEqual(expected, ciphertext));
+
+ cipher.init(false, new TweakableBlockCipherParameters(new KeyParameter(key), tweak));
+ byte[] replain = new byte[plaintext.length];
+ cipher.processBlock(ciphertext, 0, replain, 0);
+
+ System.out.println("Replain : " + new String(Hex.encode(replain)));
+ System.out.println(" Decrypt : " + org.bouncycastle.util.Arrays.areEqual(plaintext, replain));
+ }
+
+ private static void testTF_512_2() throws IOException {
+ byte[] key = Hex.decode("101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f");
+ byte[] tweak = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] plaintext = Hex.decode("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0");
+ byte[] expected = Hex.decode("e304439626d45a2cb401cad8d636249a6338330eb06d45dd8b36b90e97254779272a0a8d99463504784420ea18c9a725af11dffea10162348927673d5c1caf3d");
+
+ runTestVector("Threefish-512-2: Fast", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512));
+ runTestVector("Threefish-512-2: Reference", key, tweak, plaintext, expected, new ThreefishReferenceEngine());
+ }
+
+ private static void testTF_256_2() throws IOException {
+ byte[] key = Hex.decode("101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f");
+ byte[] tweak = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] plaintext = Hex.decode("FFFEFDFCFBFAF9F8F7F6F5F4F3F2F1F0EFEEEDECEBEAE9E8E7E6E5E4E3E2E1E0");
+ byte[] expected = Hex.decode("e0d091ff0eea8fdfc98192e62ed80ad59d865d08588df476657056b5955e97df");
+
+ runTestVector("Threefish-256-2: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256));
+ }
+
+ private static void testTF_1024_2() throws IOException {
+ byte[] key = Hex.decode("101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f");
+ byte[] tweak = Hex.decode("000102030405060708090a0b0c0d0e0f");
+ byte[] plaintext = Hex.decode("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a89888786858483828180");
+ byte[] expected = Hex.decode("a6654ddbd73cc3b05dd777105aa849bce49372eaaffc5568d254771bab85531c94f780e7ffaae430d5d8af8c70eebbe1760f3b42b737a89cb363490d670314bd8aa41ee63c2e1f45fbd477922f8360b388d6125ea6c7af0ad7056d01796e90c83313f4150a5716b30ed5f569288ae974ce2b4347926fce57de44512177dd7cde");
+
+ runTestVector("Threefish-1024-2: ", key, tweak, plaintext, expected, new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024));
+ }
+
+ private static void speedTestCipher(BlockCipher cipher, byte[] input) throws InterruptedException
+ {
+ byte[] key = new byte[cipher.getBlockSize()];
+ rand.nextBytes(key);
+
+ cipher.init(true, new KeyParameter(key));
+ speedTestCipherForMode("encrypt", cipher, input);
+ cipher.init(false, new KeyParameter(key));
+ speedTestCipherForMode("decrypt", cipher, input);
+ }
+
+ private static void speedTestCipherForMode(String mode, BlockCipher cipher, byte[] input) throws InterruptedException
+ {
+ System.out.println("======");
+ System.out.println("Testing "+ cipher.getAlgorithmName() + " " + cipher.getBlockSize()*8 + " " + mode);
+ System.out.println("Beginning warmup run.");
+
+ long warmup = testCipher(cipher, input);
+ System.out.println("Warmup run 1 in "+(warmup/1000000)+"ms");
+ Thread.sleep(100);
+ warmup = testCipher(cipher, input);
+ System.out.println("Warmup run 2 in "+(warmup/1000000)+"ms");
+
+ System.gc();
+ Thread.sleep(500);
+ System.gc();
+ Thread.sleep(500);
+
+ System.out.println("Beginning "+RUNS+" hot runs." );
+
+ long[] runtimes = new long[RUNS];
+ long total = 0;
+ for(int i = 0; i < RUNS; i++) {
+ runtimes[i] = testCipher(cipher, input);
+ total += runtimes[i];
+ System.out.println("Run " + ( i+1)+": " + runtimes[i]/100000 + "ms");
+ }
+ long averageRuntime = total/RUNS;
+ System.out.println(cipher.getAlgorithmName() + " Average run time: " + averageRuntime/1000000 + "ms");
+ final long mbPerSecond = (long)((double)DATA_SIZE / averageRuntime * 1000000000 / (1024*1024));
+ System.out.println(cipher.getAlgorithmName()+" Average speed: " + mbPerSecond + " MB/s");
+ System.out.println(cipher.getAlgorithmName()+" Average speed: " + CLOCK_SPEED / (double)(mbPerSecond * (1024*1024)) + " c/b");
+ }
+
+ private static long testCipher(BlockCipher cipher, byte[] input)
+ {
+ long start = System.nanoTime();
+ int blockSize = cipher.getBlockSize();
+ byte[] out = new byte[blockSize];
+
+ for(int i = 0; i < (input.length - blockSize); i += blockSize) {
+ cipher.processBlock(input, i, out, 0);
+// byte[] test = new byte[blockSize];
+// System.arraycopy(input, i, test, 0, test.length);
+// if (!Arrays.equals(out, test)) {
+// System.err.println(":(");
+// }
+ }
+
+ long end = System.nanoTime();
+ long delta = end - start;
+ return delta;
+ }
+}
diff --git a/docs/specifications.html b/docs/specifications.html
index 15d2e6e9..9942834a 100644
--- a/docs/specifications.html
+++ b/docs/specifications.html
@@ -206,6 +206,7 @@ used with the above modes.
<tr><td><b>SerpentEngine</b></td><td>128, 192, 256 </td><td>128 bit</td><td>&nbsp;</td></tr>
<tr><td><b>SkipjackEngine</b></td><td>0 .. 128 </td><td>64 bit</td><td>&nbsp;</td></tr>
<tr><td><b>TEAEngine</b></td><td>128</td><td>64 bit</td><td>&nbsp;</td></tr>
+<tr><td><b>ThreefishEngine</b></td><td>256/512/1024 </td><td>256 bit/512 bit/1024 bit</td><td>Tweakable block cipher</td></tr>
<tr><td><b>TwofishEngine</b></td><td>128, 192, 256 </td><td>128 bit</td><td>&nbsp;</td></tr>
<tr><td><b>XTEAEngine</b></td><td>128</td><td>64 bit</td><td>&nbsp;</td></tr>
</table>
@@ -284,6 +285,7 @@ implementations
<tr><td><b>SHA384Digest</b></td><td>384</td><td>FIPS 180-2</td></tr>
<tr><td><b>SHA512Digest</b></td><td>512</td><td>FIPS 180-2</td></tr>
<tr><td><b>SHA3Digest</b></td><td>224, 256, 288, 384, 512</td><td></td></tr>
+<tr><td><b>SkeinDigest</b></td><td>any byte length</td><td>256 bit, 512 bit and 1024 state sizes. Additional parameterisation using SkeinParameters.</td></tr>
<tr><td><b>TigerDigest</b></td><td>192</td><td>The Tiger Digest.</td></tr>
<tr><td><b>GOST3411Digest</b></td><td>256</td><td>The GOST-3411 Digest.</td></tr>
<tr><td><b>WhirlpoolDigest</b></td><td>512</td><td>The Whirlpool Digest.</td></tr>
@@ -304,6 +306,7 @@ implementations
<tr><td><b>GOST28147Mac</b></td><td>32 bits</td><td>&nbsp;</td></tr>
<tr><td><b>ISO9797Alg3Mac</b></td><td>multiple of 8 bits up to underlying cipher size.</td><td>&nbsp;</td></tr>
<tr><td><b>HMac</b></td><td>digest length</td><td>&nbsp;</td></tr>
+<tr><td><b>SkeinMac</b></td><td>any byte length</td><td>256 bit, 512 bit and 1024 state size variants. Additional parameterisation using SkeinParameters.</td></tr>
<tr><td><b>SipHash</b></td><td>64 bits</td><td>&nbsp;</td></tr>
<tr><td><b>VMPCMac</b></td><td>160 bits</td><td>&nbsp;</td></tr>
</table>
@@ -561,6 +564,9 @@ Note: default key sizes are in bold.
<tr><td>Serpent</td><td>128, 192, 256 <b>(256)</b></td><td>128 bit</td><td>&nbsp;</td></tr>
<tr><td>Skipjack</td><td>0 .. 128 <b>(128)</b></td><td>64 bit</td><td>&nbsp;</td></tr>
<tr><td>TEA</td><td>128 <b>(128)</b></td><td>64 bit</td><td>&nbsp;</td></tr>
+<tr><td>Threefish-256</td><td>256</td><td>256 bit</td><td>&nbsp;</td></tr>
+<tr><td>Threefish-512</td><td>512</td><td>512 bit</td><td>&nbsp;</td></tr>
+<tr><td>Threefish-1024</td><td>1024</td><td>1024 bit</td><td>&nbsp;</td></tr>
<tr><td>Twofish</td><td>128, 192, 256 <b>(256)</b></td><td>128 bit</td><td>&nbsp;</td></tr>
<tr><td>XTEA</td><td>128 <b>(128)</b></td><td>64 bit</td><td>&nbsp;</td></tr>
</table>
@@ -652,6 +658,9 @@ change as the draft is finalised.
<tr><td>SHA3-256</td><td>256</td><td>&nbsp;</td></tr>
<tr><td>SHA3-384</td><td>384</td><td>&nbsp;</td></tr>
<tr><td>SHA3-512</td><td>512</td><td>&nbsp;</td></tr>
+<tr><td>Skein-256-*</td><td>128, 160, 224, 256</td><td>e.g. Skein-256-160</td></tr>
+<tr><td>Skein-512-*</td><td>128, 160, 224, 256, 384, 512</td><td>e.g. Skein-512-256</td></tr>
+<tr><td>Skein-1024-*</td><td>384, 512, 1024</td><td>e.g. Skein-1024-1024</td></tr>
<tr><td>Tiger</td><td>192</td><td>&nbsp;</td></tr>
<tr><td>Whirlpool</td><td>512</td><td>&nbsp;</td></tr>
</table>
@@ -672,6 +681,16 @@ change as the draft is finalised.
<tr><td>HMac-SHA256</td><td>256</td><td>&nbsp;</td></tr>
<tr><td>HMac-SHA384</td><td>384</td><td>&nbsp;</td></tr>
<tr><td>HMac-SHA512</td><td>512</td><td>&nbsp;</td></tr>
+<tr><td>HMac-SHA3-224</td><td>224</td><td>&nbsp;</td></tr>
+<tr><td>HMac-SHA3-256</td><td>256</td><td>&nbsp;</td></tr>
+<tr><td>HMac-SHA3-384</td><td>384</td><td>&nbsp;</td></tr>
+<tr><td>HMac-SHA3-512</td><td>512</td><td>&nbsp;</td></tr>
+<tr><td>HMAC-Skein-256-*</td><td>128, 160, 224, 256</td><td>e.g. HMAC-Skein-256-160</td></tr>
+<tr><td>HMAC-Skein-512-*</td><td>128, 160, 224, 256, 384, 512</td><td>e.g. HMAC-Skein-512-256</td></tr>
+<tr><td>HMAC-Skein-1024-*</td><td>384, 512, 1024</td><td>e.g. HMAC-Skein-1024-1024</td></tr>
+<tr><td>Skein-MAC-256-*</td><td>128, 160, 224, 256</td><td>e.g. Skein-MAC-256-160</td></tr>
+<tr><td>Skein-MAC-512-*</td><td>128, 160, 224, 256, 384, 512</td><td>e.g. Skein-MAC-512-256</td></tr>
+<tr><td>Skein-MAC-1024-*</td><td>384, 512, 1024</td><td>e.g. Skein-MAC-1024-1024</td></tr>
<tr><td>HMac-Tiger</td><td>192</td><td>&nbsp;</td></tr>
</table>
diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java
new file mode 100644
index 00000000..727ebd02
--- /dev/null
+++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java
@@ -0,0 +1,665 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.digests.SkeinDigest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+
+public class Skein
+{
+ private Skein()
+ {
+ }
+
+ public static class DigestSkein256 extends BCMessageDigest implements Cloneable
+ {
+ public DigestSkein256(int outputSize)
+ {
+ super(new SkeinDigest(SkeinDigest.SKEIN_256, outputSize));
+ }
+
+ public Object clone() throws CloneNotSupportedException
+ {
+ BCMessageDigest d = (BCMessageDigest)super.clone();
+ d.digest = new SkeinDigest((SkeinDigest)digest);
+
+ return d;
+ }
+ }
+
+ public static class Digest_256_128 extends DigestSkein256
+ {
+ public Digest_256_128()
+ {
+ super(128);
+ }
+ }
+
+ public static class Digest_256_160 extends DigestSkein256
+ {
+ public Digest_256_160()
+ {
+ super(160);
+ }
+ }
+
+ public static class Digest_256_224 extends DigestSkein256
+ {
+ public Digest_256_224()
+ {
+ super(224);
+ }
+ }
+
+ public static class Digest_256_256 extends DigestSkein256
+ {
+ public Digest_256_256()
+ {
+ super(256);
+ }
+ }
+
+ public static class DigestSkein512 extends BCMessageDigest implements Cloneable
+ {
+ public DigestSkein512(int outputSize)
+ {
+ super(new SkeinDigest(SkeinDigest.SKEIN_512, outputSize));
+ }
+
+ public Object clone() throws CloneNotSupportedException
+ {
+ BCMessageDigest d = (BCMessageDigest)super.clone();
+ d.digest = new SkeinDigest((SkeinDigest)digest);
+
+ return d;
+ }
+ }
+
+ public static class Digest_512_128 extends DigestSkein512
+ {
+ public Digest_512_128()
+ {
+ super(128);
+ }
+ }
+
+ public static class Digest_512_160 extends DigestSkein512
+ {
+ public Digest_512_160()
+ {
+ super(160);
+ }
+ }
+
+ public static class Digest_512_224 extends DigestSkein512
+ {
+ public Digest_512_224()
+ {
+ super(224);
+ }
+ }
+
+ public static class Digest_512_256 extends DigestSkein512
+ {
+ public Digest_512_256()
+ {
+ super(256);
+ }
+ }
+
+ public static class Digest_512_384 extends DigestSkein512
+ {
+ public Digest_512_384()
+ {
+ super(384);
+ }
+ }
+
+ public static class Digest_512_512 extends DigestSkein512
+ {
+ public Digest_512_512()
+ {
+ super(512);
+ }
+ }
+
+ public static class DigestSkein1024 extends BCMessageDigest implements Cloneable
+ {
+ public DigestSkein1024(int outputSize)
+ {
+ super(new SkeinDigest(SkeinDigest.SKEIN_1024, outputSize));
+ }
+
+ public Object clone() throws CloneNotSupportedException
+ {
+ BCMessageDigest d = (BCMessageDigest)super.clone();
+ d.digest = new SkeinDigest((SkeinDigest)digest);
+
+ return d;
+ }
+ }
+
+ public static class Digest_1024_384 extends DigestSkein1024
+ {
+ public Digest_1024_384()
+ {
+ super(384);
+ }
+ }
+
+ public static class Digest_1024_512 extends DigestSkein1024
+ {
+ public Digest_1024_512()
+ {
+ super(512);
+ }
+ }
+
+ public static class Digest_1024_1024 extends DigestSkein1024
+ {
+ public Digest_1024_1024()
+ {
+ super(1024);
+ }
+ }
+
+ /**
+ * Skein HMac
+ */
+ public static class HashMac_256_128 extends BaseMac
+ {
+ public HashMac_256_128()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 128)));
+ }
+ }
+
+ public static class HashMac_256_160 extends BaseMac
+ {
+ public HashMac_256_160()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 160)));
+ }
+ }
+
+ public static class HashMac_256_224 extends BaseMac
+ {
+ public HashMac_256_224()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 224)));
+ }
+ }
+
+ public static class HashMac_256_256 extends BaseMac
+ {
+ public HashMac_256_256()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 256)));
+ }
+ }
+
+ public static class HashMac_512_128 extends BaseMac
+ {
+ public HashMac_512_128()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 128)));
+ }
+ }
+
+ public static class HashMac_512_160 extends BaseMac
+ {
+ public HashMac_512_160()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 160)));
+ }
+ }
+
+ public static class HashMac_512_224 extends BaseMac
+ {
+ public HashMac_512_224()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 224)));
+ }
+ }
+
+ public static class HashMac_512_256 extends BaseMac
+ {
+ public HashMac_512_256()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 256)));
+ }
+ }
+
+ public static class HashMac_512_384 extends BaseMac
+ {
+ public HashMac_512_384()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 384)));
+ }
+ }
+
+ public static class HashMac_512_512 extends BaseMac
+ {
+ public HashMac_512_512()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 512)));
+ }
+ }
+
+ public static class HashMac_1024_384 extends BaseMac
+ {
+ public HashMac_1024_384()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 384)));
+ }
+ }
+
+ public static class HashMac_1024_512 extends BaseMac
+ {
+ public HashMac_1024_512()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 512)));
+ }
+ }
+
+ public static class HashMac_1024_1024 extends BaseMac
+ {
+ public HashMac_1024_1024()
+ {
+ super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 1024)));
+ }
+ }
+
+ public static class HMacKeyGenerator_256_128 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_256_128()
+ {
+ super("HMACSkein-256-128", 128, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_256_160 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_256_160()
+ {
+ super("HMACSkein-256-160", 160, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_256_224 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_256_224()
+ {
+ super("HMACSkein-256-224", 224, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_256_256 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_256_256()
+ {
+ super("HMACSkein-256-256", 256, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_512_128 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_512_128()
+ {
+ super("HMACSkein-512-128", 128, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_512_160 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_512_160()
+ {
+ super("HMACSkein-512-160", 160, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_512_224 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_512_224()
+ {
+ super("HMACSkein-512-224", 224, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_512_256 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_512_256()
+ {
+ super("HMACSkein-512-256", 256, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_512_384 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_512_384()
+ {
+ super("HMACSkein-512-384", 384, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_512_512 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_512_512()
+ {
+ super("HMACSkein-512-512", 512, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_1024_384 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_1024_384()
+ {
+ super("HMACSkein-1024-384", 384, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_1024_512 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_1024_512()
+ {
+ super("HMACSkein-1024-512", 512, new CipherKeyGenerator());
+ }
+ }
+
+ public static class HMacKeyGenerator_1024_1024 extends BaseKeyGenerator
+ {
+ public HMacKeyGenerator_1024_1024()
+ {
+ super("HMACSkein-1024-1024", 1024, new CipherKeyGenerator());
+ }
+ }
+
+ /*
+ * Skein-MAC
+ */
+ public static class SkeinMac_256_128 extends BaseMac
+ {
+ public SkeinMac_256_128()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_256, 128));
+ }
+ }
+
+ public static class SkeinMac_256_160 extends BaseMac
+ {
+ public SkeinMac_256_160()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_256, 160));
+ }
+ }
+
+ public static class SkeinMac_256_224 extends BaseMac
+ {
+ public SkeinMac_256_224()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_256, 224));
+ }
+ }
+
+ public static class SkeinMac_256_256 extends BaseMac
+ {
+ public SkeinMac_256_256()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_256, 256));
+ }
+ }
+
+ public static class SkeinMac_512_128 extends BaseMac
+ {
+ public SkeinMac_512_128()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_512, 128));
+ }
+ }
+
+ public static class SkeinMac_512_160 extends BaseMac
+ {
+ public SkeinMac_512_160()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_512, 160));
+ }
+ }
+
+ public static class SkeinMac_512_224 extends BaseMac
+ {
+ public SkeinMac_512_224()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_512, 224));
+ }
+ }
+
+ public static class SkeinMac_512_256 extends BaseMac
+ {
+ public SkeinMac_512_256()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_512, 256));
+ }
+ }
+
+ public static class SkeinMac_512_384 extends BaseMac
+ {
+ public SkeinMac_512_384()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_512, 384));
+ }
+ }
+
+ public static class SkeinMac_512_512 extends BaseMac
+ {
+ public SkeinMac_512_512()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_512, 512));
+ }
+ }
+
+ public static class SkeinMac_1024_384 extends BaseMac
+ {
+ public SkeinMac_1024_384()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_1024, 384));
+ }
+ }
+
+ public static class SkeinMac_1024_512 extends BaseMac
+ {
+ public SkeinMac_1024_512()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_1024, 512));
+ }
+ }
+
+ public static class SkeinMac_1024_1024 extends BaseMac
+ {
+ public SkeinMac_1024_1024()
+ {
+ super(new SkeinMac(SkeinMac.SKEIN_1024, 1024));
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_256_128 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_256_128()
+ {
+ super("Skein-MAC-256-128", 128, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_256_160 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_256_160()
+ {
+ super("Skein-MAC-256-160", 160, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_256_224 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_256_224()
+ {
+ super("Skein-MAC-256-224", 224, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_256_256 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_256_256()
+ {
+ super("Skein-MAC-256-256", 256, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_512_128 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_512_128()
+ {
+ super("Skein-MAC-512-128", 128, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_512_160 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_512_160()
+ {
+ super("Skein-MAC-512-160", 160, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_512_224 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_512_224()
+ {
+ super("Skein-MAC-512-224", 224, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_512_256 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_512_256()
+ {
+ super("Skein-MAC-512-256", 256, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_512_384 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_512_384()
+ {
+ super("Skein-MAC-512-384", 384, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_512_512 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_512_512()
+ {
+ super("Skein-MAC-512-512", 512, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_1024_384 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_1024_384()
+ {
+ super("Skein-MAC-1024-384", 384, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_1024_512 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_1024_512()
+ {
+ super("Skein-MAC-1024-512", 512, new CipherKeyGenerator());
+ }
+ }
+
+ public static class SkeinMacKeyGenerator_1024_1024 extends BaseKeyGenerator
+ {
+ public SkeinMacKeyGenerator_1024_1024()
+ {
+ super("Skein-MAC-1024-1024", 1024, new CipherKeyGenerator());
+ }
+ }
+
+ public static class Mappings extends DigestAlgorithmProvider
+ {
+ private static final String PREFIX = Skein.class.getName();
+
+ public Mappings()
+ {
+ }
+
+ public void configure(ConfigurableProvider provider)
+ {
+ // Skein sizes as used in "The Skein Hash Function Family 1.3"
+
+ provider.addAlgorithm("MessageDigest.Skein-256-128", PREFIX + "$Digest_256_128");
+ provider.addAlgorithm("MessageDigest.Skein-256-160", PREFIX + "$Digest_256_160");
+ provider.addAlgorithm("MessageDigest.Skein-256-224", PREFIX + "$Digest_256_224");
+ provider.addAlgorithm("MessageDigest.Skein-256-256", PREFIX + "$Digest_256_256");
+
+ provider.addAlgorithm("MessageDigest.Skein-512-128", PREFIX + "$Digest_512_128");
+ provider.addAlgorithm("MessageDigest.Skein-512-160", PREFIX + "$Digest_512_160");
+ provider.addAlgorithm("MessageDigest.Skein-512-224", PREFIX + "$Digest_512_224");
+ provider.addAlgorithm("MessageDigest.Skein-512-256", PREFIX + "$Digest_512_256");
+ provider.addAlgorithm("MessageDigest.Skein-512-384", PREFIX + "$Digest_512_384");
+ provider.addAlgorithm("MessageDigest.Skein-512-512", PREFIX + "$Digest_512_512");
+
+ provider.addAlgorithm("MessageDigest.Skein-1024-384", PREFIX + "$Digest_1024_384");
+ provider.addAlgorithm("MessageDigest.Skein-1024-512", PREFIX + "$Digest_1024_512");
+ provider.addAlgorithm("MessageDigest.Skein-1024-1024", PREFIX + "$Digest_1024_1024");
+
+ addHMACAlgorithm(provider, "Skein-256-128", PREFIX + "$HashMac_256_128", PREFIX + "$HMacKeyGenerator_256_128");
+ addHMACAlgorithm(provider, "Skein-256-160", PREFIX + "$HashMac_256_160", PREFIX + "$HMacKeyGenerator_256_160");
+ addHMACAlgorithm(provider, "Skein-256-224", PREFIX + "$HashMac_256_224", PREFIX + "$HMacKeyGenerator_256_224");
+ addHMACAlgorithm(provider, "Skein-256-256", PREFIX + "$HashMac_256_256", PREFIX + "$HMacKeyGenerator_256_256");
+
+ addHMACAlgorithm(provider, "Skein-512-128", PREFIX + "$HashMac_512_128", PREFIX + "$HMacKeyGenerator_512_128");
+ addHMACAlgorithm(provider, "Skein-512-160", PREFIX + "$HashMac_512_160", PREFIX + "$HMacKeyGenerator_512_160");
+ addHMACAlgorithm(provider, "Skein-512-224", PREFIX + "$HashMac_512_224", PREFIX + "$HMacKeyGenerator_512_224");
+ addHMACAlgorithm(provider, "Skein-512-256", PREFIX + "$HashMac_512_256", PREFIX + "$HMacKeyGenerator_512_256");
+ addHMACAlgorithm(provider, "Skein-512-384", PREFIX + "$HashMac_512_384", PREFIX + "$HMacKeyGenerator_512_384");
+ addHMACAlgorithm(provider, "Skein-512-512", PREFIX + "$HashMac_512_512", PREFIX + "$HMacKeyGenerator_512_512");
+
+ addHMACAlgorithm(provider, "Skein-1024-384", PREFIX + "$HashMac_1024_384", PREFIX + "$HMacKeyGenerator_1024_384");
+ addHMACAlgorithm(provider, "Skein-1024-512", PREFIX + "$HashMac_1024_512", PREFIX + "$HMacKeyGenerator_1024_512");
+ addHMACAlgorithm(provider, "Skein-1024-1024", PREFIX + "$HashMac_1024_1024", PREFIX + "$HMacKeyGenerator_1024_1024");
+
+ addSkeinMacAlgorithm(provider, 256, 128);
+ addSkeinMacAlgorithm(provider, 256, 160);
+ addSkeinMacAlgorithm(provider, 256, 224);
+ addSkeinMacAlgorithm(provider, 256, 256);
+
+ addSkeinMacAlgorithm(provider, 512, 128);
+ addSkeinMacAlgorithm(provider, 512, 160);
+ addSkeinMacAlgorithm(provider, 512, 224);
+ addSkeinMacAlgorithm(provider, 512, 256);
+ addSkeinMacAlgorithm(provider, 512, 384);
+ addSkeinMacAlgorithm(provider, 512, 512);
+
+ addSkeinMacAlgorithm(provider, 1024, 384);
+ addSkeinMacAlgorithm(provider, 1024, 512);
+ addSkeinMacAlgorithm(provider, 1024, 1024);
+ }
+
+ private void addSkeinMacAlgorithm(ConfigurableProvider provider, int blockSize, int outputSize)
+ {
+ String mainName = "Skein-MAC-" + blockSize + "-" + outputSize;
+ String algorithmClassName = PREFIX + "$SkeinMac_" + blockSize + "_" + outputSize;
+ String keyGeneratorClassName = PREFIX + "$SkeinMacKeyGenerator_" + blockSize + "_" + outputSize;
+
+ provider.addAlgorithm("Mac." + mainName, algorithmClassName);
+ provider.addAlgorithm("Alg.Alias.Mac.Skein-MAC" + blockSize + "/" + outputSize, mainName);
+ provider.addAlgorithm("KeyGenerator." + mainName, keyGeneratorClassName);
+ provider.addAlgorithm("Alg.Alias.KeyGenerator.Skein-MAC" + blockSize + "/" + outputSize, mainName);
+ }
+
+ }
+}
diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java
new file mode 100644
index 00000000..60bf2bd8
--- /dev/null
+++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java
@@ -0,0 +1,110 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+
+public final class Threefish
+{
+ private Threefish()
+ {
+ }
+
+ public static class ECB_256 extends BaseBlockCipher
+ {
+ public ECB_256()
+ {
+ super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256));
+ }
+ }
+
+ public static class ECB_512 extends BaseBlockCipher
+ {
+ public ECB_512()
+ {
+ super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512));
+ }
+ }
+
+ public static class ECB_1024 extends BaseBlockCipher
+ {
+ public ECB_1024()
+ {
+ super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024));
+ }
+ }
+
+ public static class KeyGen_256 extends BaseKeyGenerator
+ {
+ public KeyGen_256()
+ {
+ super("Threefish-256", 256, new CipherKeyGenerator());
+ }
+ }
+
+ public static class KeyGen_512 extends BaseKeyGenerator
+ {
+ public KeyGen_512()
+ {
+ super("Threefish-512", 512, new CipherKeyGenerator());
+ }
+ }
+
+ public static class KeyGen_1024 extends BaseKeyGenerator
+ {
+ public KeyGen_1024()
+ {
+ super("Threefish-1024", 1024, new CipherKeyGenerator());
+ }
+ }
+
+ public static class AlgParams_256 extends IvAlgorithmParameters
+ {
+ protected String engineToString()
+ {
+ return "Threefish-256 IV";
+ }
+ }
+
+ public static class AlgParams_512 extends IvAlgorithmParameters
+ {
+ protected String engineToString()
+ {
+ return "Threefish-512 IV";
+ }
+ }
+
+ public static class AlgParams_1024 extends IvAlgorithmParameters
+ {
+ protected String engineToString()
+ {
+ return "Threefish-1024 IV";
+ }
+ }
+
+ public static class Mappings extends AlgorithmProvider
+ {
+ private static final String PREFIX = Threefish.class.getName();
+
+ public Mappings()
+ {
+ }
+
+ public void configure(ConfigurableProvider provider)
+ {
+ provider.addAlgorithm("Cipher.Threefish-256", PREFIX + "$ECB_256");
+ provider.addAlgorithm("Cipher.Threefish-512", PREFIX + "$ECB_512");
+ provider.addAlgorithm("Cipher.Threefish-1024", PREFIX + "$ECB_1024");
+ provider.addAlgorithm("KeyGenerator.Threefish-256", PREFIX + "$KeyGen_256");
+ provider.addAlgorithm("KeyGenerator.Threefish-512", PREFIX + "$KeyGen_512");
+ provider.addAlgorithm("KeyGenerator.Threefish-1024", PREFIX + "$KeyGen_1024");
+ provider.addAlgorithm("AlgorithmParameters.Threefish-256", PREFIX + "$AlgParams_256");
+ provider.addAlgorithm("AlgorithmParameters.Threefish-512", PREFIX + "$AlgParams_512");
+ provider.addAlgorithm("AlgorithmParameters.Threefish-1024", PREFIX + "$AlgParams_1024");
+ }
+ }
+}
diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
index 442dcddf..1636d475 100644
--- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
+++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
@@ -13,6 +13,7 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.SkeinParameters;
public class BaseMac
extends MacSpi implements PBE
@@ -74,6 +75,10 @@ public class BaseMac
{
param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV());
}
+ else if (params instanceof SkeinParameters)
+ {
+ param = new SkeinParameters.Builder((SkeinParameters) params).setKey(key.getEncoded()).build();
+ }
else if (params == null)
{
param = new KeyParameter(key.getEncoded());
diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
index 7a1465dc..9c275fcc 100644
--- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -70,7 +70,7 @@ public final class BouncyCastleProvider extends Provider
private static final String[] SYMMETRIC_CIPHERS =
{
"AES", "ARC4", "Blowfish", "Camellia", "CAST5", "CAST6", "DES", "DESede", "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA",
- "Noekeon", "RC2", "RC5", "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Skipjack", "TEA", "Twofish", "VMPC", "VMPCKSA3", "XTEA"
+ "Noekeon", "RC2", "RC5", "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Skipjack", "TEA", "Twofish", "Threefish", "VMPC", "VMPCKSA3", "XTEA"
};
/*
@@ -96,7 +96,7 @@ public final class BouncyCastleProvider extends Provider
private static final String DIGEST_PACKAGE = "org.bouncycastle.jcajce.provider.digest.";
private static final String[] DIGESTS =
{
- "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "Tiger", "Whirlpool"
+ "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "Tiger", "Whirlpool"
};
/*
diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
index 30489bff..ba2579d6 100644
--- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
+++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
@@ -204,6 +204,12 @@ public class BlockCipherTest
"6cd6f7c5d2c655556d7a9e98a1696d1875e9f1b2fc991e28a2d55b56861e80bd",
"Twofish/OFB/NoPadding",
"821c54b1b54ae113cf74595eefe10c83b61c9682fc81f92c52f39a3a693f88b8",
+ "Threefish-256/OFB/NoPadding",
+ "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373",
+ "Threefish-512/OFB/NoPadding",
+ "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2",
+ "Threefish-1024/OFB/NoPadding",
+ "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454",
"RC2/OFB/NoPadding",
"0a07cb78537cb04c0c74e28a7b86b80f80acadf87d6ef32792f1a8cf74b39f74",
"RC5/OFB/NoPadding",
@@ -220,6 +226,12 @@ public class BlockCipherTest
"6ca6078755b263f09787d830b6fda7b7748494634bdc73ab68540cf9f6b7eccf",
"Twofish/OFB8/NoPadding",
"825dcec234ad52253d6e064b0d769bc04b1142435933f4a510ffc20d70095a88",
+ "Threefish-256/OFB8/NoPadding",
+ "545fbd92313512127218262dd4394569aca96ba122e1432b661ecfc01af3a25c",
+ "Threefish-512/OFB8/NoPadding",
+ "15f6e7d215662c525ea982cab56409cf833157e1af06edd57a13c71487904fea",
+ "Threefish-1024/OFB8/NoPadding",
+ "03d80b67ff7139d9dd8b07280642f94074496e5fc37b1ba1f8593cdf64a1e4ca",
"RC2/OFB8/NoPadding",
"0aa26c6f6a820fe7d38da97085995ad62e2e293323a76300fcd4eb572810f7c6",
"RC5/OFB8/NoPadding",
@@ -236,6 +248,12 @@ public class BlockCipherTest
"6cd6f7c5d2c6555561167fe9b10665102206869339122f1ed89efa4a985397f6",
"Twofish/CFB/NoPadding",
"821c54b1b54ae113cf74595eefe10c8308b7a438277de4f40948ac2d172d53d2",
+ "Threefish-256/CFB/NoPadding",
+ "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373",
+ "Threefish-512/CFB/NoPadding",
+ "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2",
+ "Threefish-1024/CFB/NoPadding",
+ "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454",
"RC2/CFB/NoPadding",
"0a07cb78537cb04ca1401450d5cd411c7da7fa5b6baaa17bb2137bd95c9f26a5",
"RC5/CFB/NoPadding",
@@ -252,6 +270,12 @@ public class BlockCipherTest
"6ca63aaada9188d2410c07513cc0736b9888770768c25a5befc776beea5bdc4c",
"Twofish/CFB8/NoPadding",
"825d12af040721cf5ed4a4798647837ac5eb14d752aace28728aeb37b2010abd",
+ "Threefish-256/CFB8/NoPadding",
+ "545fbf0a4b925f399cf7540f1cc1cc6012e329ab2d4db0aa0dfa29ee2a2019d1",
+ "Threefish-512/CFB8/NoPadding",
+ "15f695964f20b95ed72afad75f905788839c53bed2ae5fdfdfb13e3241fd7f94",
+ "Threefish-1024/CFB8/NoPadding",
+ "03d897c89e740d2254f717b73315151d9a34c829e4162232b3cd5f5158ff367b",
"RC2/CFB8/NoPadding",
"0aa227f94be3a32ff927c5d25647ea41d7c2a1e94012fc7f2ad6767b9664bce5",
"RC5/CFB8/NoPadding",
@@ -265,7 +289,29 @@ public class BlockCipherTest
"Twofish/ECB/TBCPadding",
"70336d9c9718a8a2ced1b19deed973a3c58af7ea71a69e7efc4df082dca581c019d7daa58d02b89aab6e8c0d17202439",
"RC2/ECB/TBCPadding",
- "eb5b889bbcced12eb6b1a3da6a3d965bba66a5edfdd4c8a6b6b1a3da6a3d965b6b5359ba5e69b179"
+ "eb5b889bbcced12eb6b1a3da6a3d965bba66a5edfdd4c8a6b6b1a3da6a3d965b6b5359ba5e69b179",
+ "DES/CTR/NoPadding",
+ "537572e480c1714fb47081d35eb18eaca9e0a5aee982f105438a0db6cece1f6d",
+ "DESede/CTR/NoPadding",
+ "481e9872acea7fcfa93b7d4e34ec7bab340c10faba2e43b879d40d38e07c422d",
+ "SKIPJACK/CTR/NoPadding",
+ "71143a124e3a0cdeee98a7b843baa05bd1d59faee8ec9b89880e070314a04cc2",
+ "Blowfish/CTR/NoPadding",
+ "6cd6f7c5d2c65555d2b31f8614f54ec654f5e7888d515008d59302c3edfcc6cb",
+ "Twofish/CTR/NoPadding",
+ "821c54b1b54ae113cf74595eefe10c83d09e95d4599190b9bbd5bc71dd703730",
+ "Threefish-256/CTR/NoPadding",
+ "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373",
+ "Threefish-512/CTR/NoPadding",
+ "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2",
+ "Threefish-1024/CTR/NoPadding",
+ "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454",
+ "RC2/CTR/NoPadding",
+ "0a07cb78537cb04c8c5a0a39a15977a7eb19f3c48a42759c234868c391a99c63",
+ "RC5/CTR/NoPadding",
+ "c62b233df296283b97f17364d5f69a1ff91f46659cf9856caefd322a936203a7",
+ "IDEA/CTR/NoPadding",
+ "dd447da3cbdcf81f4694ab7715d79e3f90af5682e8c318b8f7dadbed6b5c9714",
};
static String[] cipherTests2 =
@@ -280,8 +326,127 @@ public class BlockCipherTest
"60fa2f8fae5aa2a38e9ac77d0246726b32df660db51a710ceb7511e451"
};
+ static String[] cipherTestsLargeBlock =
+ {
+ "Threefish-256",
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "31533aa864e6a40edc3e24b36260d94374893dc2e479793292e29c18a6ee01a9",
+ "Threefish-512",
+ "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+ "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+ "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+ "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+ "ad7ec86b2137af1ddb64794d714c4e1d7b687b19fc9781ef887a0ad7f88e18fc" +
+ "1baa6123ec8bc497e7eb7b5090cfd756fd5333425ed5a240cb96735dea9713d9",
+ "Threefish-1024",
+ "df6d789e301c6a5e22e0cff0b44666630d44ce774a41b628ebaff6adc86d9e66" +
+ "af50a282a4313552bc9b861cb286ab569e2e23b1c97cdb5cb1fde1bacfba9bfb" +
+ "de3b443218e16b6038537b3d803ff5dbd26b13c177a5bfb597ffccca142a5905" +
+ "8c0f74623daa96bff95b716674701034e7947ce0541426fa5177bc1a519b23ba" +
+ "462f1724989612e49ca5e92a0129ec7be576846fe2616664674e16a29ce8679c" +
+ "0adda9034fbd652910c2ae5afacde10281ab18dbeeb83464dc21ff66b0d358ff" +
+ "2328c73aca59e9095a7bca94acc79d10038eab6ef865545bcf73f4caeeba1844" +
+ "6add98350c8276e5abfb8709bb6c01ef3297b862818a4996b744f375b9126e5c",
+ "Threefish-256/CBC/NoPadding",
+ "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+ "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+ "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3" +
+ "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e",
+ "Threefish-512/CBC/NoPadding",
+ "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+ "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6" +
+ "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+ "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7",
+ "Threefish-1024/CBC/NoPadding",
+ "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87" +
+ "b59be63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb" +
+ "491f280f4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a" +
+ "419544ef5907f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003",
+ "Threefish-256/CBC/PKCS7Padding",
+ "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+ "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+ "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3" +
+ "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" +
+ "f96cb468a5cd39a003f976464a7d072c94cb72a3fe739f101aa7b5452bc3fbba",
+ "Threefish-512/CBC/PKCS7Padding",
+ "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+ "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6" +
+ "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+ "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" +
+ "03902162280012e59efa15c6beecfbf440a6a0c4474bbbb2f74a0ad31bcd398f" +
+ "b24728c3605a4ced3c92c30a5e231113abafaf6f83a3867978e3cdd74091d09f",
+ "Threefish-1024/CBC/PKCS7Padding",
+ "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87" +
+ "b59be63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb" +
+ "491f280f4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a" +
+ "419544ef5907f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003" +
+ "4ddd16ad731ad9a32d0f196a72284f7a8df98918e3e22f1708662edeb1810d2b" +
+ "bafd4200e849f3288b55634b37f99f0f7b2dd192a5944fc211ef9e37b67a829b" +
+ "005a5ec609f736875fdf8946bd79c1daa6c44c9d6733a2223cf8b7e5203b1cfd" +
+ "76995f67e570d9c403b2a2e3f3a89c63c7850ee8d47d4398ac377345a139dda4",
+ "Threefish-256/CTS/NoPadding",
+ "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+ "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+ "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" +
+ "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3",
+ "Threefish-512/CTS/NoPadding",
+ "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+ "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" +
+ "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+ "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6",
+ "Threefish-1024/CTS/NoPadding",
+ "1b68e5850bc140c36db314ea1fe24e663a53680b692316ab117208ff8a9ca2f7" +
+ "d6ecd94be000669c6e0c15efa9f43f4e004dc6481652d76b70f7ab6dd9f8e93a" +
+ "c5380585aa8e7e78e2db6bec246ecdc5ecb20cc58f4b977800b85b76c9ae384b" +
+ "088e3f302dc703eb6e07067b5e152fb2bb855796ce81dcbc227c2c3033a4e1a6",
+ "Threefish-256/CBC/WithCTS",
+ "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+ "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+ "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" +
+ "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3",
+ "Threefish-512/CBC/WithCTS",
+ "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+ "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" +
+ "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+ "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6",
+ "Threefish-1024/CBC/WithCTS",
+ "1b68e5850bc140c36db314ea1fe24e663a53680b692316ab117208ff8a9ca2f7" +
+ "d6ecd94be000669c6e0c15efa9f43f4e004dc6481652d76b70f7ab6dd9f8e93a" +
+ "c5380585aa8e7e78e2db6bec246ecdc5ecb20cc58f4b977800b85b76c9ae384b" +
+ "088e3f302dc703eb6e07067b5e152fb2bb855796ce81dcbc227c2c3033a4e1a6",
+ "Threefish-256/ECB/TBCPadding",
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+ "89c4e79b90153a821bdd4efd5eb1e2cda89b6a91540a003eef03868472d8cfce",
+ "Threefish-512/ECB/TBCPadding",
+ "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+ "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+ "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+ "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+ "dd6bfa1006e4df51298e382ca397a2c398cdb4d65009dce77c5f0a31f9807218" +
+ "a72372a8a0df3b1bacd5dbfb116ebbe314e0b0cd64fd2c8ae8a81491c2534a2a",
+ "Threefish-1024/ECB/TBCPadding",
+ "df6d789e301c6a5e22e0cff0b44666630d44ce774a41b628ebaff6adc86d9e66" +
+ "af50a282a4313552bc9b861cb286ab569e2e23b1c97cdb5cb1fde1bacfba9bfb" +
+ "de3b443218e16b6038537b3d803ff5dbd26b13c177a5bfb597ffccca142a5905" +
+ "8c0f74623daa96bff95b716674701034e7947ce0541426fa5177bc1a519b23ba" +
+ "7312262dc3a25984847d1b05cb624f5751946f136ee7bd0a9a4bbac5dd3bd213" +
+ "702390d3a53d1a4132f59383cce4fe61e08cd3c73c570190d1c8b60940031ef7" +
+ "42f6775b00fb0b4273a14b46a3fc0e760e02f75dc6100ca9c038c3f151e03145" +
+ "92686fd8cccbee74d246a8c59ad80205c9f9aaeb100ea5812837ee8699753301",
+ };
+
static byte[] input1 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f");
static byte[] input2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c");
+ static byte[] inputLargeBlock = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" +
+ "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" +
+ "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" +
+ "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f");
static RC2ParameterSpec rc2Spec = new RC2ParameterSpec(128, Hex.decode("0123456789abcdef"));
static RC5ParameterSpec rc5Spec = new RC5ParameterSpec(16, 16, 32, Hex.decode("0123456789abcdef"));
@@ -930,6 +1095,11 @@ public class BlockCipherTest
test(cipherTests2[i], input2, Hex.decode(cipherTests2[i + 1]));
}
+ for (int i = 0; i != cipherTestsLargeBlock.length; i += 2)
+ {
+ test(cipherTestsLargeBlock[i], inputLargeBlock, Hex.decode(cipherTestsLargeBlock[i + 1]));
+ }
+
//
// check for less than a block
//
diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java
index 85972a05..063dacae 100644
--- a/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java
+++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java
@@ -71,7 +71,8 @@ public class RegressionTest
new DSTU4145Test(),
new CRL5Test(),
new SipHashTest(),
- new SHA3Test()
+ new SHA3Test(),
+ new SkeinTest()
};
public static void main(
diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/SkeinTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/SkeinTest.java
new file mode 100644
index 00000000..cfd643aa
--- /dev/null
+++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/SkeinTest.java
@@ -0,0 +1,316 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.security.MessageDigest;
+import java.security.Security;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SkeinTest
+ extends SimpleTest
+{
+ final static String provider = "BC";
+
+ static private byte[] nullMsg = new byte[0];
+
+ static private String[][] nullVectors =
+ {
+ { "Skein-256-128", "07e8ff2191c5052e1a25914c7c213078" },
+ { "Skein-256-160", "ff800bed6d2044ee9d604a674e3fda50d9b24a72" },
+ { "Skein-256-224", "0fadf1fa39e3837a95b3660b4184d9c2f3cfc94b55d8e7a083278bf8" },
+ { "Skein-256-256", "c8877087da56e072870daa843f176e9453115929094c3a40c463a196c29bf7ba" },
+ { "Skein-512-128", "7c9aff5c3738e3faadc7a5265768def1" },
+ { "Skein-512-160", "49daf1ccebb3544bc93cb5019ba91b0eea8876ee" },
+ { "Skein-512-224", "1541ae9fc3ebe24eb758ccb1fd60c2c31a9ebfe65b220086e7819e25" },
+ { "Skein-512-256", "39ccc4554a8b31853b9de7a1fe638a24cce6b35a55f2431009e18780335d2621" },
+ { "Skein-512-384", "dd5aaf4589dc227bd1eb7bc68771f5baeaa3586ef6c7680167a023ec8ce26980f06c4082c488b4ac9ef313f8cbe70808" },
+ { "Skein-512-512", "bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af41fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a" },
+ { "Skein-1024-384", "1fdb081963b960e89eaa11b87dda55e8a55a3e1066b30e38d8ae2a45242f7dadfaf06d80ca8a73cd8242ce5eab84c164" },
+ { "Skein-1024-512", "e2943eb0bc0efabd49503a76edf7cfcf072db25bad94ed44fe537284163f3119c47ac6f78699b4272255966e0aba65c75a0a64bd23df6996d1bc3174afd9fa8b" },
+ { "Skein-1024-1024", "0fff9563bb3279289227ac77d319b6fff8d7e9f09da1247b72a0a265cd6d2a62645ad547ed8193db48cff847c06494a03f55666d3b47eb4c20456c9373c86297d630d5578ebd34cb40991578f9f52b18003efa35d3da6553ff35db91b81ab890bec1b189b7f52cb2a783ebb7d823d725b0b4a71f6824e88f68f982eefc6d19c6" },
+ };
+
+ static private byte[] shortMsg = Hex.decode("fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+ + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+ + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+ + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410");
+
+ static private String[][] shortVectors =
+ {
+ { "Skein-256-128", "9703382ea27dc2913e9d02cd976c582f" },
+ { "Skein-256-160", "0cd491b7715704c3a15a45a1ca8d93f8f646d3a1" },
+ { "Skein-256-224", "afd1e2d0f5b6cd4e1f8b3935fa2497d27ee97e72060adac099543487" },
+ { "Skein-256-256", "4de6fe2bfdaa3717a4261030ef0e044ced9225d066354610842a24a3eafd1dcf" },
+ { "Skein-512-128", "c901b1c04af3da4dce05d7975c419224" },
+ { "Skein-512-160", "ef03079d61b57c6047e15fa2b35b46fa24279539" },
+ { "Skein-512-224", "d9e3219b214e15246a2038f76a573e018ef69b385b3bd0576b558231" },
+ { "Skein-512-256", "809dd3f763a11af90912bbb92bc0d94361cbadab10142992000c88b4ceb88648" },
+ { "Skein-512-384", "825f5cbd5da8807a7b4d3e7bd9cd089ca3a256bcc064cd73a9355bf3ae67f2bf93ac7074b3b19907a0665ba3a878b262" },
+ { "Skein-512-512", "1a0d5abf4432e7c612d658f8dcfa35b0d1ab68b8d6bd4dd115c23cc57b5c5bcdde9bff0ece4208596e499f211bc07594d0cb6f3c12b0e110174b2a9b4b2cb6a9" },
+ { "Skein-1024-384", "9c3d0648c11f31c18395d5e6c8ebd73f43d189843fc45235e2c35e345e12d62bc21a41f65896ddc6a04969654c2e2ce9" },
+ { "Skein-1024-512", "5d0416f49c2d08dfd40a1446169dc6a1d516e23b8b853be4933513051de8d5c26baccffb08d3b16516ba3c6ccf3e9a6c78fff6ef955f2dbc56e1459a7cdba9a5" },
+ { "Skein-1024-1024", "96ca81f586c825d0360aef5acaec49ad55289e1797072eee198b64f349ce65b6e6ed804fe38f05135fe769cc56240ddda5098f620865ce4a4278c77fa2ec6bc31c0f354ca78c7ca81665bfcc5dc54258c3b8310ed421d9157f36c093814d9b25103d83e0ddd89c52d0050e13a64c6140e6388431961685734b1f138fe2243086" },
+ };
+
+ static private String[][] shortMacVectors =
+ {
+ { "Skein-Mac-256-128", "738f8b23541d50f691ab60af664c1583" },
+ { "Skein-Mac-256-160", "fe07fe50f99b7683bc16980041d8c045857f1189" },
+ { "Skein-Mac-256-224", "0bc19b185f5bfe50f0dba7ab49cd8ca9440260edd5a392d4bdcd2216" },
+ { "Skein-Mac-256-256", "9837ba53d23afcdabd9fcd614ce9e51c0ebecec7a210df4d3724ed591f026ef1" },
+ { "Skein-Mac-512-128", "6d34f46f2033947da7a9dfb068f4102d" },
+ { "Skein-Mac-512-160", "83cb2effecaa60674c2f9fb2fb6771a9899708ba" },
+ { "Skein-Mac-512-224", "e5f83c032875451f31977cd649c866708cb283a509e99cdfd4d995c5" },
+ { "Skein-Mac-512-256", "ed5507ec551ec944c6ed531990c32907eca885dd3af3d50dd09f1dbef422bb11" },
+ { "Skein-Mac-512-384", "b8f84a212723b92a591d6dc145c1655c70df710e9f3365064abdf79e9288dced2f0f895d81f465c811f1207b43b8cfce" },
+ { "Skein-Mac-512-512", "d13ba582467096a0f862114d97baa218512f39c82c984aa29deee724950d7f0929f726173dd42bc35566b0dbfbf5d2a1552ba6f132de301846714215b64e7f82" },
+ { "Skein-Mac-1024-384", "490dbbd049403e602ee3535181a70ee2eb5ade6d83b519953dd0d93c45729f098b679efcd64b5e3f03cd2fa9f1e70d69" },
+ { "Skein-Mac-1024-512", "ce7f1052fa486309d73058d1d4986f886d966a849c72d196bb2b97fc9fb0b1e69f43a521ebd979f5a5581bd12a0dbd0d1ee27af0929881f1d35c875cc0542ecf" },
+ { "Skein-Mac-1024-1024", "60cd8c755b331bcefe97be5a9fe6f63146d12520ca7b20dbc5c5370dae2ff9815c95fab564329a01eced76f0ecb1944ad52a74e89fa1b6cdcdcee4c71c2c18909c4d1324d279fac5ca2280eea0fa70521cf4ea8c616a3ac6082c2244bec5c1ab3a173faf29d84bec7fb852e278ed57785535c979b33b81465c437cd998c04b95" },
+ };
+
+ static private String[][] shortHMacVectors =
+ {
+ { "HMAC-Skein-256-128", "926a445d5218605286dfe0542a437012" },
+ { "HMAC-Skein-256-160", "5ebc30295e4562a879f94db531ada465073b8bb7" },
+ { "HMAC-Skein-256-224", "a05b3cfc6b86fda7f5dcf0afbb707dc745fa55279a3f80e2c9977ff1" },
+ { "HMAC-Skein-256-256", "51741f6e8ebf133216ac8e05c7a75a6339351fd2dcc4db04e418521c628a2111" },
+ { "HMAC-Skein-512-128", "ad51f8c7b1b347fe52f0f5c71ae9b8eb" },
+ { "HMAC-Skein-512-160", "e0d06c2d406f32bb14dbb2129176219b62d4f89f" },
+ { "HMAC-Skein-512-224", "e7e5327e2aaa88d0038049e8112db31df223be4c31da24abf03731a8" },
+ { "HMAC-Skein-512-256", "30177414f6e35019cacc2e3ae474b25765e6e0e541e16d754c3dad19df763ab0" },
+ { "HMAC-Skein-512-384", "7f0ba3c1c642cf09eb03d0e3760fe172f22fb263006b1fba5bdea1bfaf6e971c17e039abb0030d1a40ac94a747732cce" },
+ { "HMAC-Skein-512-512", "70d864e7f6cbd446778914a951d1961e646ee17a3da8eae551d29f4fafc540b0457cc9f8064c511b80dc29f8369fb5dc258559542abb5342c4892f22934bf5f1" },
+ { "HMAC-Skein-1024-384", "e7d3465b30b5089e24244e747a91f7cb255596b49843466497c07e120c5c2232f51151b185a1e8a5610f041a85cc59ee" },
+ { "HMAC-Skein-1024-512", "c428059ae2d17ba13e461384c4a64cb0be694909e7a04e4983a4fc16476d644c7764e0019b33ea2a8719f731a579f4f7015da7ec1bc56a4920071ac41da836fe" },
+ { "HMAC-Skein-1024-1024", "3ebd13ec7bf1533c343ac78e1b5146225ce7629787f3997b646139c1b80d6f54cd562b7625419ede8710d76410dfb8617514ca3f7abf17657d2bc96722071adb2a6ecd9795a1ef5e4734b450d588efcbc3220faf53c880e61438bb953e024e48db6a745d2368375ac792be858cd01915e28590d4d6d599be95f6e6ceed7d7d91" },
+ };
+
+ static private byte[] shortMacMessage = Hex.decode("d3090c72167517f7");
+ static private byte[] shortMacKey = Hex.decode("cb41f1706cde09651203c2d0efbaddf8");
+
+ static private byte[] keyIdentifier = "asecretkey".getBytes();
+ static private byte[] keyIdentifierVector = Hex.decode("ca9970a83997e1c346c4348b54cfc9ba7e19bfba");
+
+ public String getName()
+ {
+ return "Skein";
+ }
+
+ void test(String type, String algorithm, byte[] message, String expected) throws Exception
+ {
+ MessageDigest digest = MessageDigest.getInstance(algorithm, provider);
+
+ byte[] result = digest.digest(message);
+ byte[] result2 = digest.digest(message);
+
+ // test zero results valid
+ if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+ {
+ fail(type + " result not equal for " + algorithm, expected, new String(Hex.encode(result)));
+ }
+
+ // test one digest the same message with the same instance
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail(type + " result object 1 not equal");
+ }
+
+ if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+ {
+ fail(type + " result object 1 not equal");
+ }
+
+ // test two, single byte updates
+ for (int i = 0; i < message.length; i++)
+ {
+ digest.update(message[i]);
+ }
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail(type + " result object 2 not equal");
+ }
+
+ // test three, two half updates
+ digest.update(message, 0, message.length / 2);
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail(type + " result object 3 not equal");
+ }
+
+ // test four, clone test
+ digest.update(message, 0, message.length / 2);
+ MessageDigest d = (MessageDigest)digest.clone();
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail(type + " result object 4(a) not equal");
+ }
+
+ d.update(message, message.length / 2, message.length - message.length / 2);
+ result2 = d.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail(type + " result object 4(b) not equal");
+ }
+
+ // test five, check reset() method
+ digest.update(message, 0, message.length / 2);
+ digest.reset();
+ digest.update(message, 0, message.length / 2);
+ digest.update(message, message.length / 2, message.length - message.length / 2);
+ result2 = digest.digest();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail(type + " result object 5 not equal");
+ }
+ }
+
+ private void testMac(String algorithm, byte[] message, byte[] key, String expected) throws Exception
+ {
+ Mac mac = Mac.getInstance(algorithm, provider);
+
+ mac.init(new SecretKeySpec(key, algorithm));
+
+ byte[] result = mac.doFinal(message);
+ byte[] result2 = mac.doFinal(message);
+
+ // test zero results valid
+ if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+ {
+ fail("null result not equal for " + algorithm, expected, new String(Hex.encode(result)));
+ }
+
+ // test one digest the same message with the same instance
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 1 not equal");
+ }
+
+ if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+ {
+ fail("Result object 1 not equal");
+ }
+
+ // test two, single byte updates
+ for (int i = 0; i < message.length; i++)
+ {
+ mac.update(message[i]);
+ }
+ result2 = mac.doFinal();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 2 not equal");
+ }
+
+ // test three, two half updates
+ mac.update(message, 0, message.length / 2);
+ mac.update(message, message.length / 2, message.length - message.length / 2);
+ result2 = mac.doFinal();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 3 not equal");
+ }
+
+ // test five, check reset() method
+ mac.update(message, 0, message.length / 2);
+ mac.reset();
+ mac.update(message, 0, message.length / 2);
+ mac.update(message, message.length / 2, message.length - message.length / 2);
+ result2 = mac.doFinal();
+
+ if (!MessageDigest.isEqual(result, result2))
+ {
+ fail("Result object 5 not equal");
+ }
+
+ // test six, check KeyGenerator
+ KeyGenerator generator = KeyGenerator.getInstance(algorithm, provider);
+
+ mac = Mac.getInstance(algorithm, provider);
+ final SecretKey generatedKey = generator.generateKey();
+ if (generatedKey.getEncoded().length != mac.getMacLength())
+ {
+ fail("Default mac key length for " + algorithm);
+ }
+ mac.init(generatedKey);
+ mac.update(message);
+ mac.doFinal();
+ }
+
+ private void testParameters() throws Exception
+ {
+ Mac mac = Mac.getInstance("Skein-Mac-512-160", provider);
+
+ // test six, init using SkeinParameters
+ mac.init(new SecretKeySpec(shortMacKey, "Skein-Mac-512-160"),
+ new SkeinParameters.Builder().setKeyIdentifier(keyIdentifier).build());
+ byte[] result = mac.doFinal(shortMacMessage);
+
+ if (!MessageDigest.isEqual(result, keyIdentifierVector))
+ {
+ fail("Mac with key identifier failed.", new String(Hex.encode(keyIdentifierVector)), new String(Hex.encode(result)));
+ }
+ }
+
+ private void testMacKeyGenerators(String algorithm) throws Exception
+ {
+ KeyGenerator gen = KeyGenerator.getInstance(algorithm);
+
+ int outputSize = Integer.parseInt(algorithm.substring(algorithm.lastIndexOf('-') + 1));
+ SecretKey key = gen.generateKey();
+
+ if (key.getEncoded().length != (outputSize / 8)) {
+ fail(algorithm + " key length should be equal to output size " + (outputSize) + ", but was " + key.getEncoded().length * 8);
+ }
+ }
+
+ public void performTest() throws Exception
+ {
+ for (int i = 0; i < nullVectors.length; i++)
+ {
+ test("Null message", nullVectors[i][0], nullMsg, nullVectors[i][1]);
+ }
+ for (int i = 0; i < shortVectors.length; i++)
+ {
+ test("Short message", shortVectors[i][0], shortMsg, shortVectors[i][1]);
+ }
+ for (int i = 0; i < shortMacVectors.length; i++)
+ {
+ testMac(shortMacVectors[i][0], shortMacMessage, shortMacKey, shortMacVectors[i][1]);
+ testMacKeyGenerators(shortMacVectors[i][0]);
+ }
+
+ for (int i = 0; i < shortHMacVectors.length; i++)
+ {
+ testMac(shortHMacVectors[i][0], shortMacMessage, shortMacKey, shortHMacVectors[i][1]);
+ testMacKeyGenerators(shortHMacVectors[i][0]);
+ }
+ testParameters();
+ }
+
+ public static void main(String[] args)
+ {
+ Security.addProvider(new BouncyCastleProvider());
+
+ runTest(new SkeinTest());
+ }
+}